Jump to content
  • Advertisement
Sign in to follow this  
dmreichard

Multiple Lua States for Each Entity, Good or Bad?

This topic is 3455 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Hello everyone. I've recently been tinkering around with LuaInterface and decided to implement it into my rogue style text adventure I've been working on. The language I'm using is C#. To make a simple proof of concept I have an NPC which calls a "retaliation.lua" script every time he is attacked. It looks like this: if me.hp <= 20 then say("I'm out of here!") me:DoCommand("north") else me:DoCommand("attack " .. ch.Name) end Very simple. Everything works as expected, the NPC retaliates until their HP reaches 20 or under and then he flees north. Every time an NPC is created, I have a Lua object field which is assigned in the NPC constructor:
lua = new Lua();
lua.DoString("luanet.load_assembly(\"SimpleROGUE\")");
lua.DoString("luanet.import_type(\"SimpleROGUE.Player\")");
lua.RegisterFunction("say", this, this.GetType().GetMethod("Say"));
lua["me"] = this;

And the retaliation function:
public void Retaliate(Player player)
{
    lua["ch"] = player;
    lua.DoFile("scripts\\retaliation.txt");
}

(Yes, I know I should have a better approach instead of NPCs and the player sharing the same class. But I implemented NPCs and a basic attack command quickly for this proof of concept.) My main question, is this good practice to instantiate a new Lua object for every single entity in the game? Or should every entity share a single Lua object that is perhaps passed into the constructor? The one thing I like about this approach is the fact I can register each individual Say() function so that it only applies to the NPC being worked on. However, I'm not sure how efficient this approach is... if someone with more experience could give me some insight on this that would be great! If you require any more information / source code examples please let me know. -Dave

Share this post


Link to post
Share on other sites
Advertisement
You really should have only one Lua instance for a program.

The real problem is that you are treating Lua files as scripts. While you can do this, and it will work, I think there is a better way.

The way I do it is I register a bunch of types and functions with Lua. Then my game starts to run in Lua code. Lua can register a bunch of callbacks and create any objects it needs from the executable. So really the control is inverted, Lua is in command. The executable exposes a minimal set of functionality, like audio, graphics, etc that cannot be implemented in Lua.

There is a middle ground between the two extremes. This is what an older project of mine did. There was a mixture of Lua and C++ objects, with C++ being in control but most of the game-specific logic code in the Lua scripts.

This is a mock up of the kind of control I am talking about:

// player.lua

Player = create_entity_type()

function Player.new( --[[ args ]] )
local player = {}
set_type(player,Player)

player.args = args

player.score = 0

return player
end

function Player:update()
-- whatever
end

function Player:shoot(mousex, mousey)
world:add(Bullet.new( /* ... */ ) )
end

-- more functions

// primary lua file
player = Player.new( --[[ args ]])

input.bind('w',function() player:up() end)
input.bind('s',function() player:down() end)
input.bind('a',function() player:left() end)
input.bind('d',function() player:right() end)
input.bind('space',function() player:shoot() end)

world:add(player)

game.set_frame_callback(function()
if game_over() then
game.display_high_scores(player.score)
else
-- maybe do something else
end
end)

-- set more callbacks


Maybe its not the best way of doing it, but I found it very flexible. I found I was far more productive in Lua than C++, so more of the code started being written in Lua. If you are using C# then you might not find the gains to be quite as great, because C# is quite high level.

Share this post


Link to post
Share on other sites
Quote:

The real problem is that you are treating Lua files as scripts. While you can do this, and it will work, I think there is a better way.


Indeed, that is exactly what I intended. I would like to be able to script behavior into rooms, NPCs and objects along with whatever else comes to mind with an event driven interface.

Quote:

Maybe its not the best way of doing it, but I found it very flexible. I found I was far more productive in Lua than C++, so more of the code started being written in Lua. If you are using C# then you might not find the gains to be quite as great, because C# is quite high level.


So far C# has been great for my needs, although that is a definite alternative worth considering. For now my usage has been small scripts attached to entities, which was what made having multiple Lua objects so nice. I can see where this will be a big performance hit when thousands of game entities start popping up though.

One game which has sort of been an inspiration for me to do this is Aardwolf MUD. The have integrated LUA into their game world, and have documented it fairly well here. Now one thing I noticed is in their implementation, when for instance an NPC script is executed, the say() function always corresponds to the NPC in question. That leads me to believe that they have multiple Lua states as I had done.

Is there a way to change a function registration in Lua? And if so, how is it done? Granted, another way I can do this is always pass information to global variables, so I could do something like me:Say("Hi there!"). But Aardwolf's implementation is what has made me curious.

Share this post


Link to post
Share on other sites
Quote:
Original post by dmreichard
Is there a way to change a function registration in Lua? And if so, how is it done?
Yes. See function environments in the manual. Honestly, though, that's fitting a square peg in a round hole. There are elegant, efficient ways of doing what you're trying to do in Lua, without having to fight the language.

Share this post


Link to post
Share on other sites
Quote:
Original post by Sneftel
Quote:
Original post by dmreichard
Is there a way to change a function registration in Lua? And if so, how is it done?
Yes. See function environments in the manual. Honestly, though, that's fitting a square peg in a round hole. There are elegant, efficient ways of doing what you're trying to do in Lua, without having to fight the language.


I was almost positive there had to be a better way. My only problem is I can not figure out without creating multiple Lua objects or re-registering functions how to accomplish what I want to do. Could you please provide some pointers on how to do this? That is, if getting Say() or the like to correspond to the NPC in question is even a viable option in the first place.

Thanks!

Share this post


Link to post
Share on other sites
In this case, I might be inclined to use something like:


Entity { -- Here, "Entity" is a global function which takes a table argument. Useful little syntactic sugar, no?
name = "MyNPC",
onretaliation = function(me, ch) -- the "me" argument is not entirely necessary with function environments, but it would be confusing to leave it out of the example
if me.hp <= 20 then
me:say("I'm out of here!") -- it is the NPC saying it, right?
me:north() -- what's this "docommand" malarkey?
else
me:attack(ch) -- And why attack by name?
end
end

-- other functions making up the NPC's behavior go here
}

Share this post


Link to post
Share on other sites
Quote:
Original post by Sneftel
In this case, I might be inclined to use something like:


Entity { -- Here, "Entity" is a global function which takes a table argument. Useful little syntactic sugar, no?
name = "MyNPC",
onretaliation = function(me, ch) -- the "me" argument is not entirely necessary with function environments, but it would be confusing to leave it out of the example
if me.hp <= 20 then
me:say("I'm out of here!") -- it is the NPC saying it, right?
me:north() -- what's this "docommand" malarkey?
else
me:attack(ch) -- And why attack by name?
end
end

-- other functions making up the NPC's behavior go here
}


Thank you very much for that example. It really forced me to think in a completely different way for this problem. I believe I know what I want to do with this now. [smile]

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

Participate in the game development conversation and more when you create an account on GameDev.net!

Sign me up!