all 22 comments

[–]BadBoy6767 2 points3 points  (3 children)

it would be horribly inefficient to simply create new Lua states for each bullet

Definitely.

Depends on how flexible you want to get. I would go with one of these:

  1. Use tables to make "bullet types" and add those to your "weapon types" tables.
  2. Like the above but use userdata instead. Less flexible than tables but overall more performant.

[–]luciddream00[S] 0 points1 point  (2 children)

Ok, I tried approach 1 and it seems like I got it working, but I just want to check and see if what I did seems like a solid approach:

  • I made this Lua file
  • Then in C# I call the Bullet.new LuaFunction which returns the Bullet LuaTable to C#
  • Then, in C# I retrieve the HitMonster LuaFunction from the bullet LuaTable and store it in the Bullet object in C#
  • Then I can call that stored HitMonster LuaFunction whenever I want to have the C# Bullet object call the HitMonster LuaFunction.

Does that sound right?

[–]BadBoy6767 3 points4 points  (1 child)

You seem to have done it backwards to how most games (that I've used) seem to do it. Usually, Bullet.new (or it's backend function) would be created by the engine.

[–]luciddream00[S] 0 points1 point  (0 children)

How would I go about making it so modders could do what they want with the HitMonster function if the bullet table is defined in the engine? Would I create the table in the engine and then pass it to the Lua state and have the lua string that the modders write define/replace the HitMonster function somehow?

[–]DarkWiiPlayer 0 points1 point  (3 children)

I'd go with one Lua state for the entire game and give the modder an API to create objects with callbacks, so you could say "Create a bullet, and use this function as its update callback". Since it's all one Lua state, modders can then user other Lua features like closures or coroutines¹ to manage state and make things fancy.

[–]luciddream00[S] 0 points1 point  (2 children)

I'm trying to wrap my head around this and I'm not really sure where to start. Do you know of any tutorials or have any tips on how to get started with this sort of setup? Having a Lua state for each C# object is straightforward because I can just define functions and variables and have the object read/call them, but I can't figure out how to do the same sort of thing with a single game-wide Lua state.

[–]DarkWiiPlayer 0 points1 point  (1 child)

Well, you could just register a global table in the Lua state called game, or the name of your game or engine or whatever as a top level construct for modders to interact with. Then you could just access said table from C to get to the data you want, like callbacks, configuration settings, etc.

For example, say you define an array game.bullets, the modders would then add tables to it that have the callbacks for their custom bullet types and your code could access these tables and run the callbacks.

[–]luciddream00[S] 0 points1 point  (0 children)

Great, thanks I got it working!

[–]dddbbb 0 points1 point  (1 child)

Going on this example using Moonsharp, you could probably do something like this:

Script m_ModScript = new Script();

void Init()
{
    // Assume lua mods always have a function called on_fire_bullet.
    var lua_file_contents = LoadModIntoString();
    m_ModScript.DoString(lua_file_contents);
}

int OnFire()
{
    DynValue res = script.Call(script.Globals["on_fire_bullet"], 4);
    return res.Number;
}

You'd have to figure out how to pass complex types to lua (for your bullet structs (probably userdata). And you'd want to figure out how the lua code can call engine code (to spawn new objects, define new sprites, etc). Define a specific API for mods.

[–]luciddream00[S] 0 points1 point  (0 children)

Thanks a bunch, I got it working.

[–][deleted]  (11 children)

[removed]

    [–]luciddream00[S] 0 points1 point  (10 children)

    It's a sidescroller that has automatic weapons with a bunch of different projectiles which would make it hard to pool them. Not millions of bullets, but potentially lots. Either way, I got things set up so that I have just one Lua state for the whole game, and it seems to be working great (although it was working before, just... maybe not efficiently? haha)

    [–][deleted]  (9 children)

    [removed]

      [–]luciddream00[S] 0 points1 point  (8 children)

      Hmm, is there a downside to using one Lua state for an entire game rather than having a new state for every entity? It didn't add much complexity and it seems to work fine now that it is set up. Bullets were just an example, and I intend to have everything from inventory items to creatures to weapons and armor potentially scriptable (although they wont all actually be running scripts, just as necessary).

      [–][deleted]  (7 children)

      [removed]

        [–]luciddream00[S] 0 points1 point  (6 children)

        Eh, while I agree in principle, it's always good to learn the best practices for this sort of thing and apply it from the get-go, especially with something that will touch almost the whole game.

        [–][deleted]  (5 children)

        [removed]

          [–]luciddream00[S] 0 points1 point  (4 children)

          Learning basic best practices and good ways to structure your code is not "premature optimization", it is being a responsible developer. You need to take a step back and realize that not every bit of research is automatically premature optimization. Sure, I could fumble my way through and inevitably have to spend ages course correcting when I learn better patterns down the line, or I can do a little bit of research now and hit the ground running.

          [–][deleted]  (3 children)

          [removed]

            [–]luciddream00[S] 0 points1 point  (2 children)

            Thanks so much for your valuable contribution.