1
\$\begingroup\$

I am using sol2 lua wrapper for my 2D game-engine to implement some lua scripting,but I have some questions about what should functions should i call from Lua scripts in c++, and i cannot find any good resources about this topic.

For example. lets say that i am trying to render a sprite on the screen, which sprite is created inside a lua script. As usually the sprite should call render() function every frame, my question is that, should i call every frame functions from Lua in my game-engine loop ? or its bad habit? i mean can i have a structure in lua like below :

function init() 
   --called only once
end

function render()
  --called every frame in C++ gameloop
end

or should I implemented in other way? Can someone suggest me any books about this matter or any good resources ?

\$\endgroup\$

1 Answer 1

4
\$\begingroup\$

I would generally recommend to avoid making high volume calls into Lua. That is partly about performance but mostly about engine architecture.

To get the perf bit out of the way: the bridging code between C++/Lua has overhead compared to just calling Lua from Lua or calling C++ from C++. Of course, even good Lua itself is significantly slower than decent C++ code. Some constructs very common to games - like such simple things as a 2D vector type - incur even more overhead in Lua due to its inability to have user-defined value types; that's even worse as treating such "fundamental" mathematical concepts as reference types opens one to all sorts of non-obvious bugs and hardships. The lack of static typing in Lua also starts to really hurt as the size of the code grows and the need for good accurate code navigation grows with it. In short, write as little real "game code" in Lua as possible for the best perf and likely best developer experience.

Now, on to engine architecture. You don't want a "sprite" to have its own render function, independent of whether you're working in Lua or C++ or any language. You also likely don't want many other sprite-specific functions that would be called frequently like that. Most of these features are much better suited to dedicated systems that handle all the specifics. For instance, for rendering a sprite, your sprite object should instead create a node in the graphics scene graph with the required properties (position, size, texture, etc.) and the graphics renderer then can figure out how to put the "sprite" on the screen every frame all by itself. This has several advantages:

First, it insulates the rendering code from the rest of the engine. You don't necessarily want to have a core renderer being dependent on your scripting module, or on physics, or input, or AI, or so on. Rendering should be fairly self-contained. That's going to make your code easier to read, easier to maintain, easier to optimize, and just all-around easier in general.

Second, putting everything related to rendering a sprite under the renderer's control allows for a lot of tricks that are very difficult to pull off otherwise. For instance, resource swapping, texture streaming, multi-threaded rendering, culling, shadowing, etc. If the graphics engine has to call back into game code or script code every time it needs to render, it's going to be very hard to branch that renderer out across multiple CPU cores and likewise hard to do many multi-pass algorithms.

The above are just as true for systems like physics, AI, and so on.

The idea then is that your game objects (sprites) should tell the low-level systems what to create and when to change them, rather than the low-level systems constantly re-querying the game objects every frame. Your script might then look something like:

-- asteroid.lua

function init(self)
  self.transform = game.transform.create{
    position=random_position(),
    scale=random_scale(),
    rotation=random(0, 2*PI)
  }
  self.renderable = game.graphics.createRenderable{
    texture='/images/asteroid.png',
    transform=self.transform
  }
  self.body = game.physics.createRigidBody{
    shape=game.physics.CIRCLE,
    linear_velocity=random_direction() * random_speed(),
    angular_velocity=random_number()
    transform=self.transform
  }
end

The individual systems like physics now have no idea that scripts exist or that other systems exist. In fact, the only thing binding these systems together at all here are that (a) they depend on a common Transform object so they can agree on a position, rotation, and scale, and (b) there's a Lua table somewhere that has handles to the individual systems' internal objects.

The structure above never needs to call into Lua after the object is initialized. You could of course still use Lua to hookup object-specific logic, like code to run on collision events or the like, for the high-level game scripting. The core systems though should have nothing to do with Lua.

The general architecture here is a form of component-oriented design, and with the muddling of the term one could also see it as a loose form of an "ECS" architecture (Entity-Component-System architecture).

\$\endgroup\$
1
  • \$\begingroup\$ Wow, thank you for the time you spent writing all this. You really helped me with the idea of the nodes and engine architecture, actually I think that I saw something similar in the godot game engine. My problem is that I can't find any good resources or books on that matter. \$\endgroup\$
    – pureofpure
    Commented Jun 23, 2017 at 2:04

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .