Lua and C++: separation of duties

2019-03-18 23:27发布

问题:

Please help to classify ways of organizing C++/Lua game code and to separate their duties. What are the most convenient ways, which one do you use?

For example, Lua can be used for initializing C++ objects only or at every game loop iteration. It can be used for game logic only or for graphics, too. Some game engines provide full control to all subsytems from scripts! I really do not like this approach (no separation at all).

Is it a good idea to implement all game objects (npc, locations) as Lua tables without C++ objects? Or it is better to mirror them (Lua tables to control C++ objects)? Or something else?

Thank you.

Edit. My classification: Lua and C++: separation of duties.

Topic's continuation: Lua, game state and game loop

回答1:

My approach has been to limit what is exposed to Lua as much as possible. I have never found a need for a "main" or other such function which is called every time the scene is rendered (or more). Some Lua engines (like LOVE) do this however. I prefer to define objects with optional callback functions for common events that you may want the object to respond to such as a collision, mouse click, entering or leaving the game world, etc..

The end result is very declarative, almost a config file for objects. I have a function for creating classes or types of objects and another for creating objects based on these types. The objects then have a collection of methods which can be called when responding to various events. All of these Lua methods map to C/C++ methods which in turn modify the object's private properties. Here is an example of a bucket object which can capture ball objects:

define {
    name='ball';
    texture=png('images/orb.png');
    model='active';
    shape='circle';
    radius=16;
    mass=1.0; 
    elastic=.7;
    friction=.4; 
}

define {
    name='bucket';
    model='active';
    mass=1;
    shape='rect';
    width=60;
    height=52;
    texture=png('images/bucket.png');
    elastic=.5;
    friction=.4; 
    oncontact = function(self, data)
        if data.subject:type() == 'ball' then
            local a = data.subject:angleTo(self:getxy())
            if a < 130 and a > 50 then
                --update score etc..
            end
        end
    end;
}

I would not take this as the "one true way" to implement your scripting API. One of the beauties of Lua is that it supports many different styles of API. This is just what I have found works well for the games that I make - 2D physics based games.



回答2:

I propose this classification:

  1. Extreme variant: Lua scripts control everything (game logic, graphics, AI etc.). Even more: script works as a host-program, owns game loop. Some engines do such thing. Ba-ad thing: no separation of duties at all and no script safety.

  2. Lua scripts maintain game state and process game logic. Probably scripts are called at each game loop iteration.

  3. Lua scripts are rarely used for initializations, configurations, callbacks. A host program provides (binds) a very minimalistic interface for the scripts. So scripts are built from such well-designed and provided blocks.



回答3:

Start small. Allow access to game entities so that you can perform map/level-specific scripting. Behaviour that is consistent across maps/levels probably doesn't need scripting.

Also, only give access to the public interface of your objects.