[Question] Lua/C++ Balance, sequencing and Lua questions.

Anything related in any way to game development as a whole is welcome here. Tell us about your game, grace us with your project, show us your new YouTube video, etc.

Moderator: PC Supremacists

Post Reply
User avatar
LeonBlade
Chaos Rift Demigod
Chaos Rift Demigod
Posts: 1314
Joined: Thu Jan 22, 2009 12:22 am
Current Project: Trying to make my first engine in C++ using OGL
Favorite Gaming Platforms: PS3
Programming Language of Choice: C++
Location: Blossvale, NY

[Question] Lua/C++ Balance, sequencing and Lua questions.

Post by LeonBlade »

Hello everyone,

It's been a while since I've made a topic in here, I hope everyone is doing well.

I'm currently working Lua into my Engine, and I'm stuck on exactly how much should be done in Lua, and how much should be hardcoded into C++.
I guess I should show kind of what I'm doing with my Lua and if I'm doing something stupidly, let me know.

When my Engine initializes with SDL and OpenGL, it moves on to initializing Lua and I use a LuaController class which holds a lua_State pointer so I can handle Lua from any class needed through a static controller. I'm trying to implement a way to call Lua functions and a file easier for things like handling key presses through a lua_push[type] call.

So, basically I have a main.lua file that looks like this:

Code: Select all

-- Blade Brothers Engine
-- main.lua

dofile("define.lua")

-- create instances of the classes we need
entity = Entity:new()
player = Player:new()
window = Window:new()

-- called when the engine is initialized
function init()
	-- print a lua initializing message
	print("[LUA] initializing ...\n")
	
	-- create our player
	hero = player:addPlayer("player")
	
	-- create the debug_window
	debug_window = window:addWindow()
	
	-- set the position and size
	window:setPosition(debug_window, 5, 5)
	window:setSize(debug_window, 165, 45)	
	window:setMessage(debug_window, "^c5Blade Brothers Engine^n^c0Debug Window")
end

-- called on each game loop
function loop()	
	-- set the debug window's text
	-- window:setMessage(debug_window, "^c5Blade Brothers Engine^n^c0Debug Window")
end

-- called whenever a key is pressed
function key_press(key)
	if key == KEY_SPACE then
		print("[LUA] The SPACE key was pressed!")
	end
end
I'm not passing types through to Lua, instead I'm passing back integers that stand for the ID of the element in a vector.
I create overloaded functions for Lua that call the normal functions so I can reference IDs instead of objects.
Currently maps are still hard coded, but those will get Lua overloads as well.

NPCs look like this:

Code: Select all

-- on initialize
function init()

end

-- this function is called whenever you talk to the entity
function talk()
	-- create our window and return the window index
	mywindow = window:addWindow()

	-- now we can set our parameters
	window:setPosition(mywindow, 180, 350)
	window:setSize(mywindow, 200, 60)
	window:setMessage(mywindow, "^c3Veteran^c0^n\"Greetings, Hero!\"")
end
What I want to do is have a single function for displaying messages, like window:showMessage("Greetings, Hero!") and then have a way to wait until ENTER or something is pressed before continuing on with the rest of the function.
For example:

Code: Select all

function init()
	-- create this NPC
	old_man = entity:addEntity("old_man")
end

function talk()
	-- disable player logic
	player:setLogic(hero, false)

	-- face this NPC to player
	entity:faceEntity(old_man, hero)

	-- converse with this old man
	window:showMessage("Why, hello there!")
	window:showMessage("What brings you to these parts?")
	window:showMessage("Oh! It's you isn't it?! The hero!")
	window:showMessage("Please, take this!")
	
	-- give you a potion
	item:giveItem(Item:getItemId("potion"))

	-- one more message
	window:showMessage("Be safe oh brave hero!")

	-- re-enable logic
	player:setLogic(true)
end
Would I need to handle coroutines in Lua in order to pause the Lua script from executing everything at once?
How should I handle waiting until a key is pressed before returning to the Lua script without halting other things going on in the game?

And for entity talking for example, should I handle the talk event in C++ or Lua. It's currently in C++ that when you're in front of an entity you trigger it's talk() function, is this okay?

Please let me know if you see anything that is just stupid, or I should improve upon.

Thanks.
There's no place like ~/
User avatar
lotios611
Chaos Rift Regular
Chaos Rift Regular
Posts: 160
Joined: Sun Jun 14, 2009 12:05 pm
Current Project: Game engine for the PC, PSP, and maybe more.
Favorite Gaming Platforms: Gameboy Micro
Programming Language of Choice: C++

Re: [Question] Lua/C++ Balance, sequencing and Lua questions

Post by lotios611 »

I have no idea how to handle waiting until a key is pressed before returning to your Lua script without halting other things going on in your game, but I think you should handle the talk event in Lua so you can change what the entity says without having to recompile. That's pretty much the whole reason that you'd have Lua in your game.
"Why geeks like computers: unzip, strip, touch, finger, grep, mount, fsck, more, yes, fsck, fsck, fsck, umount, sleep." - Unknown
User avatar
LeonBlade
Chaos Rift Demigod
Chaos Rift Demigod
Posts: 1314
Joined: Thu Jan 22, 2009 12:22 am
Current Project: Trying to make my first engine in C++ using OGL
Favorite Gaming Platforms: PS3
Programming Language of Choice: C++
Location: Blossvale, NY

Re: [Question] Lua/C++ Balance, sequencing and Lua questions

Post by LeonBlade »

lotios611 wrote:I have no idea how to handle waiting until a key is pressed before returning to your Lua script without halting other things going on in your game, but I think you should handle the talk event in Lua so you can change what the entity says without having to recompile. That's pretty much the whole reason that you'd have Lua in your game.
The actual talk function is in Lua, I just meant how you would call the actual function.
For example, would Lua check each loop if you're talking to an NPC or would I do that in C++?
I'm pretty sure you would do that in C++, I'm just using that as an example as to what should or shouldn't be done in Lua, as I'm not entirely sure.

I know that the Elysian Shadows engine has a good message system in it; the kind I'm talking about that all RPGs have. I guess I'll just look up some Engines later and look at their source and see how to do it.
There's no place like ~/
User avatar
short
ES Beta Backer
ES Beta Backer
Posts: 548
Joined: Thu Apr 30, 2009 2:22 am
Current Project: c++, c
Favorite Gaming Platforms: SNES, PS2, SNES, SNES, PC NES
Programming Language of Choice: c, c++
Location: Oregon, US

Re: [Question] Lua/C++ Balance, sequencing and Lua questions

Post by short »

For example, would Lua check each loop if you're talking to an NPC or would I do that in C++?
I'm pretty sure you would do that in C++, I'm just using that as an example as to what should or shouldn't be done in Lua, as I'm not entirely sure.

I know that the Elysian Shadows engine has a good message system in it; the kind I'm talking about that all RPGs have. I guess I'll just look up some Engines later and look at their source and see how to do it.
You want to check each loop? Aka polling? Why not use an interrupt based system? IE have your npcs send a message to lua that someone is talking to them. That way you don't get all the expensive-ness of polling?

I guess it all depends on how your engine is structured.

My guess to your orig question (never implemented lua, its a guess) is you would want to do as much work as you can in C++ (it runs faster) and everything else (like the talk fn) in lua.

Here's one way of approaching your scenario. When you construct your window class, you create it with the entire message at once (I know it's not exactly how you wanted it, but short of creating another thread to handle your NPC I don't see any _simple_ way of doing it)

Instead of this:

Code: Select all

-- converse with this old man
   window:showMessage("Why, hello there!")
   window:showMessage("What brings you to these parts?")
   window:showMessage("Oh! It's you isn't it?! The hero!")
   window:showMessage("Please, take this!")
Perhaps consider something like:

Code: Select all

   -- converse with this old man
   window:AddMessage("Why, hello there!")
   window:AddMessage("What brings you to these parts?")
   window:AddMessage("Oh! It's you isn't it?! The hero!")
   window:AddMessage("Please, take this!")
   

Within your window class you have a stack of messages. Every time the user presses a button C++ sends the event button to wherever and you advance the conversation. The advantage of this is you can at any time call AddMessage (probably through lua) if you for example want to have a branching conversation based on user actions.

I'm running late i'll edit this later w/more. I hope this gives you an idea of a way of making this work.
My github repository contains the project I am currently working on,
link: https://github.com/bjadamson
User avatar
Falco Girgis
Elysian Shadows Team
Elysian Shadows Team
Posts: 10294
Joined: Thu May 20, 2004 2:04 pm
Current Project: Elysian Shadows
Favorite Gaming Platforms: Dreamcast, SNES, NES
Programming Language of Choice: C/++
Location: Studio Vorbis, AL
Contact:

Re: [Question] Lua/C++ Balance, sequencing and Lua questions

Post by Falco Girgis »

I think short summarized it best. You are doing too much in Lua.

You're instantiating windows, entities, and players from within your script? What exactly is your engine doing? Just rendering and checking collision? Are those constructors even registering the objects to C/++, so that they aren't existing solely in Lua?

There is one sentence that should always drive your design decisions with Lua: Keep as much logic as possible in statically compiled C/++, and use Lua simply to "extend" the engine.

The act of "polling" through Lua is pretty expensive. A single function call is easily 10x slower (probably more) than invoking the same function from C/++. With that being said, "triggers" are the optimal way to handle scripts. You are keeping the frame-by-frame checks away from Lua, and are simply using the script to extend the engine by reacting to preset events. Your Lua scripts should be extending a gigantic, rich codebase that is your engine. Your engine should be handling all generic game logic, not serving as a glorified wrapper between the API and your scripts (as you're doing with the keypress trigger).

Your main.lua should honestly not exist. That is all things that should be kept inside your engine. You're instantiating things and even initializing the windows themselves? It's honestly serving no purpose, other than slowing things down that should be happening within your engine. A function that is called whenever a keypress occurs is not being abstract enough for Lua. Lua isn't supposed to handle every little event like that. If I push a button to move the character, pop the menu, pause the game, advance a dialog, or do almost ANYTHING like that, it really has nothing to do with Lua and is only relevant to the engine itself.

Your NPC script is actually much, much better. Instead of performing things that your engine should handle, it is actually "extending" the engine by performing game-specific logic (custom dialog actions).

But your question still reveals that Lua is doing far, far too much in your engine:
LeonBlade wrote:What I want to do is have a single function for displaying messages, like window:showMessage("Greetings, Hero!") and then have a way to wait until ENTER or something is pressed before continuing on with the rest of the function.
For example:
Waiting for the user to push a key before advancing to the next screen is something that every conversation dialog box for every NPC in every scenario is going to be doing. That is functionality that should be provided to your script by your engine. Making each individual Lua script now essentially implement the core functionality of a dialog box is basically revealing that your engine is not much of an engine at all.

The Dialog box or Dialog "System" handling all generic dialog logic (such as proceeding to the next screen), should be handled fully by your engine, not Lua. The only parts of a conversation that are not completely generic are 1) the text to be displayed and 2) any special reactions to the text ("Here, take this item!").

Your structure is completely wrong for this kind of system. The type of script you are going to want is something more along the lines of:
npc.lua

Code: Select all

function InitTrigger 
    npc = getNPCFromEngine();
    screen1 = npc.AddDialogScreen();
    screen1.AddLine("Hello, welcome to my house.");
    screen2.AddLine("I notice you didn't bother knocking before entering...");
    screen2 = npc.AddDialogScreen();
    screen2.AddLine("I really outta kick your ass for that...");
    screen2.AddLine("But you know what, you have balls. I like that.");
    screen3 = npc.AddDialogScreen();
    screen3.AddLine("Take this, audacious one.");
    screen3.AddLine("It will help you on your journey");
end function

function AdvanceScreenTrigger(screenNumber)
     if screenNumber == 3 then
         player.GiveItem("whateverTheFuck");
     endif
end
Take note of a few things:
1) I am not instantiating anything in Lua. I am not creating a new NPC upon initialization. Rather, I am accessing and manipulating entities that already exist within my engine.
2) I am not dynamically creating a conversation as I go. Rather, the conversation is constructed ONCE upon initialization of the entity. Since the engine has access to my NPC (it exists in the engine), its ConversationSystem is able to check for initial collision with the NPC, check if I pushed A to talk, load the conversation if I did, begin displaying the text, advance to the next screen when the user pushes a button--all generic conversation logic.

The only time anything special happens in a conversation is when you reach a particular screen. That's when the engine's ConversationSystem invokes the lua script's "AdvanceScreenTrigger" and passes the screen number.

With this approach we've effectively abstracted away all generic conversation logic from Lua which ultimately makes our Lua scripts smaller, more abstract, and more lightweight and our engine more powerful and faster, because Lua is doing less shit.
LeonBlade wrote:The actual talk function is in Lua, I just meant how you would call the actual function.
While my above response resolved this issue, I'm concerned with the fact that you even have to ask this to begin with (for the sake of your Lua/Engine structure). A Lua function should be invoked just like any other engine function. It's a nonblocking call that should execute then immediately return control to your engine.
LeonBlade wrote:For example, would Lua check each loop if you're talking to an NPC or would I do that in C++?
God no! Except for very, very specific occasions where the custom logic is essentially completely unrelated to the engine, a Lua script should NEVER be doing any frame-to-frame polling. Most engines shouldn't even allow that kind of behavior. The better alternative is to ALWAYS abstract some sort of event away from Lua, have the engine check for it, then invoke the Lua script once it has occurred.
LeonBlade wrote:I know that the Elysian Shadows engine has a good message system in it; the kind I'm talking about that all RPGs have. I guess I'll just look up some Engines later and look at their source and see how to do it.
Another thing I'd like to mention relating to this matter... There are two balances that you have to strike:

1) The balance between Lua and the Engine
2) the balance between Lua and the Level Editor

While it is completely possible to dynamically pull entities out of your ass, construct their collision geometry, create dialogs at runtime, and manipulate them as I did in the above script, what's the point? You're essentially creating creative content for your game through a series of script text files... That's both inefficient and ugly. In ES, our non-dynamic NPCs and their conversations are loaded with the level as part of the data created by the level editor.

This gives us the ability to create a "conversation editor" in our Toolkit (where we can create our own custom branches and stuff) and leaves even LESS tedious bullshit for a Lua script to be responsible for. A conversation is custom CONTENT. CONTENT is created with a Level Editor. The act of giving the player a special item during a conversation is custom LOGIC--and that, my friend, is the ONLY thing Lua should ever be doing. So in my previous example, you should essentially have only that one function trigger with that single if statement as Lua's contribution to the solution to your entire question...

We plan to go into quite a bit of detail on both our Lua implementation and at least parts of our dialog system in the AiGD18. </shamelessSelfPromotion>
User avatar
LeonBlade
Chaos Rift Demigod
Chaos Rift Demigod
Posts: 1314
Joined: Thu Jan 22, 2009 12:22 am
Current Project: Trying to make my first engine in C++ using OGL
Favorite Gaming Platforms: PS3
Programming Language of Choice: C++
Location: Blossvale, NY

Re: [Question] Lua/C++ Balance, sequencing and Lua questions

Post by LeonBlade »

Thank you very much both short and GyroVorbis.

It's apparent to me now that I fucked up big time with how I was going about all this.
I think I'll create a small little terminal based project to test out getting a good DialogSystem implemented and then
throw that into the Engine once I'm happy with it.

I think the reason I wasn't sure how much needed to be done in Lua was because of how awkward it was for me to check if you're talking to an NPC; it just felt wrong I suppose.

I was going to post this question on my actual Engine topic, but I'll just provide a link back to it on my topic and this can be left here as an example of how NOT to implement Lua (me) and how TO implement lua (you guys).

Thank you very much for the advice and guidelines, I'll be sure get to work on this right now.

For those that don't know where my topic is, it's here: http://elysianshadows.com/phpBB3/viewto ... &start=240
There's no place like ~/
User avatar
Falco Girgis
Elysian Shadows Team
Elysian Shadows Team
Posts: 10294
Joined: Thu May 20, 2004 2:04 pm
Current Project: Elysian Shadows
Favorite Gaming Platforms: Dreamcast, SNES, NES
Programming Language of Choice: C/++
Location: Studio Vorbis, AL
Contact:

Re: [Question] Lua/C++ Balance, sequencing and Lua questions

Post by Falco Girgis »

'atta boy. Glad to see you weren't discouraged by the wall of text. I like your attitude, soldier.
User avatar
Khearts
ES Beta Backer
ES Beta Backer
Posts: 50
Joined: Sun Oct 10, 2010 5:07 pm
Current Project: Super Mario Bros Clone and 3D Engine
Favorite Gaming Platforms: Dreamcast
Programming Language of Choice: C/++

Re: [Question] Lua/C++ Balance, sequencing and Lua questions

Post by Khearts »

Thank you for making this thread. I had some similar issues that I could not particularly articulate. Definitely a universal benefit.
Post Reply