Jump to content

Accessing Lua via C++


xtreampb
 Share

Recommended Posts

Hello all,

 

So i have a C++/Lua hybrid project. I would like to access and temporary store data held in a lua script object/entity when a change the map. As this is a C++ project, i have to do my map changes in my C++ back end as my app::Loop is in my C++ source. How would i change maps first off in C++ i know I can set the new map name in my entity using the changemaptrigger script. but that does nothing b/c i'm in a cpp project. I know this isn't well written and seems like rambling. I need to change maps when my character collides with the trigger and i would like that handled in lua. When i change maps i want to retain the character information like health and any other entity (or reference to entity) associated.

bool Life()
{
 while(death=false)
 {
   if(death==true)
   return death;
 }
}

 

I have found the secret to infinite life

 

Did I help you out? Like my post!

Link to comment
Share on other sites

There are a few solutions,

  1. you could use the source from a lua project ( like Crazycarpet suggests here )
     
  2. world->FindEntity("StorageEntity")->getKeyValue("pauseMouseLook");
     
  3. expose a C++ 'store/retrieve' function to lua ( I go over some options here that does not require massive external libraries )

 

#2 only deal with strings so you would have to call it quite a few times

System:

Linux Mint 17 ( = Ubuntu 14.04 with cinnamon desktop ) Ubuntu 14.04, AMD HD 6850, i5 2500k

Link to comment
Share on other sites

Hello all,

 

So i have a C++/Lua hybrid project. I would like to access and temporary store data held in a lua script object/entity when a change the map. As this is a C++ project, i have to do my map changes in my C++ back end as my app::Loop is in my C++ source. How would i change maps first off in C++ i know I can set the new map name in my entity using the changemaptrigger script. but that does nothing b/c i'm in a cpp project. I know this isn't well written and seems like rambling. I need to change maps when my character collides with the trigger and i would like that handled in lua. When i change maps i want to retain the character information like health and any other entity (or reference to entity) associated.

 

Use the Interpreter.

 

Interpreter::NewTable();

Interpreter::SetGlobal();

Interpreter::GetGlobal();

Interpreter::GetTable();

Interpreter::IsTable();

Interpreter::IsFunction();

Interpreter::IsBool();

Interpreter::ToBool();

 

example from Josh's App.cpp:

int stacksize = Interpreter::GetStackSize();
//Get the global error handler function
int errorfunctionindex = 0;
#ifdef DEBUG
Interpreter::GetGlobal("LuaErrorHandler");
errorfunctionindex = Interpreter::GetStackSize();
#endif
//Create new table and assign it to the global variable "App"
Interpreter::NewTable();
Interpreter::SetGlobal("App");
//Invoke the start script
if (!Interpreter::ExecuteFile("Scripts/App.lua"))
{
System::Print("Error: Failed to execute script \"Scripts/App.lua\".");
 return false;
}
//Call the App:Start() function
Interpreter::GetGlobal("App");
if (Interpreter::IsTable())
{
 Interpreter::PushString("Start");
 Interpreter::GetTable();
 if (Interpreter::IsFunction())
 {
	 Interpreter::PushValue(-2);//Push the app table onto the stack as "self"
#ifdef DEBUG
errorfunctionindex = -(Interpreter::GetStackSize()-errorfunctionindex+1);
#endif
if (!Interpreter::Invoke(1,1,errorfunctionindex)) return false;
	 if (Interpreter::IsBool())
	 {
		 if (!Interpreter::ToBool()) return false;
	 }
	 else
	 {
		 return false;
	 }
 }
}
//Restore the stack size
Interpreter::SetStackSize(stacksize);

 

As for the map changing, why not use a C++ Derivative of Josh's Lua version?

world->Clear();
Time::Pause();
Map::Load("Maps/" + map + ".map");
Time::Resume();

Edit: You'd need to do quite a bit more to take map changing off the Loop so I'd recommend just copying the style of the normal Lua project, It'd be easiest to use a Lua project's App.cpp, App.h and main.cpp files.

Link to comment
Share on other sites

And that doesn't fail? It's interesting because it used to because if you think about it, your Script:Collision is an entity stored in C++ that has it's function being called (collision in this case)and inside there you are calling a function that is clearing the world, effectively removing all entities (even the entity your script is attached to which should destroy the script object as well that your stack call is currently in). Josh must have added some better error checking in to handle that because generally deleting an object from inside its own function, which is what you're doing, would cause it to crash. This is the reason Josh has the variable.

Link to comment
Share on other sites

And that doesn't fail? It's interesting because it used to because if you think about it, your Script:Collision is an entity stored in C++ that has it's function being called (collision in this case)and inside there you are calling a function that is clearing the world, effectively removing all entities (even the entity your script is attached to which should destroy the script object as well that your stack call is currently in). Josh must have added some better error checking in to handle that because generally deleting an object from inside its own function, which is what you're doing, would cause it to crash. This is the reason Josh has the variable.

 

Edit: Hmm, I dropped it into a new project and it does crash, and yeah, it makes sense. I'll try to figure out what I did in my main project to fix it, I totally forgot - this would definitely cause a stack overflow.

 

Edit2: I got home, and you're right Rick: in order to do it I had to use my 'events' library and call an event in the Script which handled everything elsewhere. Edited first post accordingly.

 

Another thing I noticed is I made "ChangeMap" a global, it has to be part of "App" to use "self.world".

Link to comment
Share on other sites

ok so this is what i got and it works until i try to access objects creating in my C++ code

 

app.lua

--This function will be called once when the program starts
function App:Start()

--Initialize Steamworks (optional)
--Steamworks:Initialize()

--Set the application title
--[[self.title="MyGame"

--Create a window
local windowstyle = Window.Titlebar
if System:GetProperty("fullscreen")=="1" then windowstyle=windowstyle+Window.FullScreen end
self.window=Window:Create(self.title,0,0,System:GetProperty("screenwidth","1024"),System:GetProperty("screenheight","768"),windowstyle)
self.window:HideMouse()

--Create the graphics context
self.context=Context:Create(self.window,0)
if self.context==nil then return false end

--Create a world
self.world=World:Create()
self.world:SetLightQuality((System:GetProperty("lightquality","1")))

--Load a map
local mapfile = System:GetProperty("map","Maps/start.map")
if Map:Load(mapfile)==false then return false end--]]

return true
end
--This is our main program loop and will be called continuously until the program ends
function App:Loop()

--If window has been closed, end the program
--if self.window:Closed() or self.window:KeyDown(Key.Escape) then return false end

--Handle map change
if changemapname~=nil then

--Clear all entities
self.world:Clear()

--Load the next map
Time:Pause()
if Map:Load("Maps/"..changemapname..".map")==false then return false end
Time:Resume()

changemapname = nil
end

--Update the app timing
--Time:Update()

--Update the world
--self.world:Update()

--Render the world
--self.world:Render()

--Render statistics
--[[self.context:SetBlendMode(Blend.Alpha)
if DEBUG then
self.context:SetColor(1,0,0,1)
self.context:DrawText("Debug Mode",2,2)
self.context:SetColor(1,1,1,1)
self.context:DrawStats(2,22)
self.context:SetBlendMode(Blend.Solid)
else
--Toggle statistics on and off
if (self.window:KeyHit(Key.F11)) then self.showstats = not self.showstats end
if self.showstats then
self.context:SetColor(1,1,1,1)
self.context:DrawText("FPS: "..Math:Round(Time:UPS()),2,2)
end
end]]--

--Refresh the screen
--self.context:Sync(true)

--Returning true tells the main program to keep looping
return true
end

 

my C++ code

#include "App.h"

using namespace Leadwerks;

App::App() : window(NULL), context(NULL), world(NULL), camera(NULL) {}

App::~App() { delete world; delete window; }

bool App::Start()
{
//Initialize Steamworks (optional)
/*if (!Steamworks::Initialize())
{
System::Print("Error: Failed to initialize Steam.");
return false;
}*/

window = Leadwerks::Window::Create("Arena",500,500);

context = Context::Create(window);

world = World::Create();

window->HideMouse();

std::string mapname = System::GetProperty("map", "Maps/start.map");
Map::Load(mapname);

window->SetMousePosition(context->GetWidth() / 2, context->GetHeight() / 2);

int stacksize = Interpreter::GetStackSize();

//Get the global error handler function
int errorfunctionindex = 0;
#ifdef DEBUG
Interpreter::GetGlobal("LuaErrorHandler");
errorfunctionindex = Interpreter::GetStackSize();
#endif

//Create new table and assign it to the global variable "App"
Interpreter::NewTable();
Interpreter::SetGlobal("App");

//Invoke the start script
if (!Interpreter::ExecuteFile("Scripts/App.lua"))
{
System::Print("Error: Failed to execute script \"Scripts/App.lua\".");
return false;
}

//Call the App:Start() function
Interpreter::GetGlobal("App");
if (Interpreter::IsTable())
{
Interpreter::PushString("Start");
Interpreter::GetTable();
if (Interpreter::IsFunction())
{
Interpreter::PushValue(-2);//Push the app table onto the stack as "self"
#ifdef DEBUG
errorfunctionindex = -(Interpreter::GetStackSize() - errorfunctionindex + 1);
#endif
if (!Interpreter::Invoke(1, 1, errorfunctionindex)) return false;
if (Interpreter::IsBool())
{
if (!Interpreter::ToBool()) return false;
}
else
{
return false;
}
}
}

//Restore the stack size
Interpreter::SetStackSize(stacksize);



return true;
}

bool App::Loop()
{
if (window->Closed())
return false;

if (window->KeyHit(Key::Escape))
return false;

//Get the stack size
int stacksize = Interpreter::GetStackSize();

//Get the global error handler function
int errorfunctionindex = 0;
#ifdef DEBUG
Interpreter::GetGlobal("LuaErrorHandler");
errorfunctionindex = Interpreter::GetStackSize();
#endif

//Call the App:Start() function
Interpreter::GetGlobal("App");
if (Interpreter::IsTable())
{
Interpreter::PushString("Loop");
Interpreter::GetTable();
if (Interpreter::IsFunction())
{
Interpreter::PushValue(-2);//Push the app table onto the stack as "self"
#ifdef DEBUG
errorfunctionindex = -(Interpreter::GetStackSize() - errorfunctionindex + 1);
#endif
if (!Interpreter::Invoke(1, 1, errorfunctionindex))
{
System::Print("Error: Script function App:Loop() was not successfully invoked.");
Interpreter::SetStackSize(stacksize);
return false;
}
if (Interpreter::IsBool())
{
if (!Interpreter::ToBool())
{
Interpreter::SetStackSize(stacksize);
return false;
}
}
else
{
Interpreter::SetStackSize(stacksize);
return false;
}
}
else
{
System::Print("Error: App:Loop() function not found.");
Interpreter::SetStackSize(stacksize);
return false;
}
}
else
{
System::Print("Error: App table not found.");
Interpreter::SetStackSize(stacksize);
return false;
}

//Restore the stack size
Interpreter::SetStackSize(stacksize);

Leadwerks::Time::Update();
world->Update();
world->Render();
context->Sync(true);

return true;
}

 

what i want to do is be able to access my world object and such from my lua script to change the world as shown in the change world tutorial.

 

In the future i would like to be able to access the entity that caused to collision in my C++ code to retain it between map changes. If not retain the entity itself, then a table of some sort to pass from the entity to the c backend and once the map is loaded, the stored data is used to reconstruct my entity.

bool Life()
{
 while(death=false)
 {
   if(death==true)
   return death;
 }
}

 

I have found the secret to infinite life

 

Did I help you out? Like my post!

Link to comment
Share on other sites

Got this figured out with the gracious help of our engine creator, Josh. Key Points. Use the global stack.

 

C++

Interpreter::PushObject(your_object_pointer);
Interpreter::SetGlobal("global_variable_name_used_in_lua_to_access_previously_pushed_object");

 

lua

self.object=global_variable_name_used_in_lua_to_access_previously_pushed_object
--yes the global name is case sensitive

 

the global object can now be used in any script (unless josh comes by and tells us otherwise)

  • Upvote 2

bool Life()
{
 while(death=false)
 {
   if(death==true)
   return death;
 }
}

 

I have found the secret to infinite life

 

Did I help you out? Like my post!

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

 Share

×
×
  • Create New...