Using Lua with C++ in practice. Part 1. Intro to ECS and basic principles

There are many reasons to use Lua with C++. One of them is that you can put some of the logic from C++ code into scripts, so you can easily change them without the need or recompilation. You can also write some good interfaces, so the scripts are easy enough for even non-coders to write them. Lua is free, Lua is fast, Lua is used in game development quite often.

While there are plenty of good articles about using Lua with C++, I think there are not enough articles about how to use Lua in real projects.

This article is one of the many articles I plan to write. Here are some topics which my articles will cover:

  •  Entity creation and other basic stuff (you’re reading this now)
  •  How to implement entity creation
  •  Managing Lua state and cleaning up
  •  Scriptable state machines
  •  Events and callbacks

The stuff described in the articles was mostly discovered during the development of my game called Re:creation.
I don’t think the methods here are perfect, but they’re good enough, fast and work well for me. So, your feedback is welcome. Feel free to leave comments and write e-mails to me about the stuff I can do better. I’m interested in hearing about your Lua/C++ usage!

While this is a C++ article, I think you can implement most of the things mentioned here in your favourite language. I’ll use Lua C API and LuaBridge for examples, so I recommend to read the following articles if you’re not familiar with them:

Entity creation

My game’s engine is build around entity/component/system (ECS) model. Because there are lots of different ways to implement ECS. I’ll explain my approach.

Entity is every object you see (and don’t see, e.g. collision boxes, triggers, etc.) in the game: rocks, tress, NPC’s, etc. Each entity has a number of components which are stored in std::vector<Component*>.

Component is a data about a particular aspect of an entity. There are a number of different components: GraphicsComponent (contains info about sprite, texture, animations, etc.), CollisionComponent (information about bounding box, collision response function, etc.), HealthComponent, etc.

Systems are where logic lives. They store a list of pointers to components of currently active entities which they iterate through. They don’t care which entity the component belongs to. For example, RenderingSystem iterates through GraphicsComponent‘s and renders them in a particular order. CollisionSystem checks collisions between entities using their CollisionComponent‘s , etc.

What’s cool about ECS is that there is no complex inheritance tree and this solves lots of problems (blob-classes, deadly diamond problem, etc.).

Entities are created by creating components with different parameters and adding them to entity’s component list. Here’s a simple example of how it looks in C++ code:

 Entity e;
 auto gc = new GraphicsComponent;
 gc->setSprite(entitySprite);
 ... // setting component properties
 e.addComponent(gc);
 ...

Here’s how I can get components by the class name (get function uses dynamic_cast to do this):

auto gc = e->get<GraphicsComponent>();
// gc is nullptr if entity doesn't have GraphicsComponent

Okay, pretty cool. But how do you create specific entities, like an NPC or a house? A Factory pattern may be used, but eventually you’ll have lots of different types of entities and customizing entity definitions would cause recompilation which is not very good. It will also be impossible for non-coders to create new entities or modify existing ones.

This is where Lua helps a lot.

Here’s an example of a simple Lua script:

Note, that this script doesn’t create an entity. It’s simply used to obtain data to set different components parameters and then create Entity in C++. I create template entity using information from the script and then create instances of this entity class by copying. Here’s how the entity creation process works in general:

When loading template entity:

  • Get entity table from script
  • Get table keys list (this gives us a list of components to create)
  • Create each component, passing corresponding table to component constructor (you’ll need to create an instance of class by class name. I’ll show one cool way to do that in the next article)

When creating other entities of this type

  • Copy every component from template entity
  • Assign unique properties of this entity (position, state, etc.)

Why didn’t I expose component classes to Lua?

You can easily create C++ objects in Lua with LuaBridge (or a binding of your choice) like this:

someObject = SomeClass()

More info here

So why do I use tables and create components in C++ instead of creating them in Lua? There’s a number of reasons for this:

  •  Scripts look simpler this way

Imagine if the script looked something like this:

tree = function()
    gc = GraphicsComponent()
    gc.setFilename("res/images/tree.png")
    gc.setZ(0)
    gc.setAnimated(false)
    gc.setFrame(64, 0, 64, 74)
    
    cc = CollisionComponent()
    cc.setType("Solid")
    cc.setBoundingBox(14, 60, 32, 10)
    
    tree = Entity()
    tree.addComponent(gc)
    tree.addComponent(cc)
    return tree
end

I don’t think that this script looks easier than the one I’ve shown before. And it may be difficult to read, understand and change for non-programmers.

  • There’s no need for Lua to know about components at all. It’s better to hide implementation details in C++

Here’s an example of how I can set animation in a Lua script in my game:

setAnimation(entity, "some_animation")

This calls this function in C++:

void setAnimation(Entity* e, const std::string& animationName) {
    auto gc = e->get<GraphicsComponent>();
    if(gc) {
        gc->setAnimation(animationName);
    } else {
        ... // write about error in a log!
    }
}

Imagine if I had to do it like this in Lua each time I wanted to set some animation:

gc = entity.get("GraphicsComponent")
if(gc ~= nil) then
    gc.setAnimation("some_animation")
else 
    ... -- print about error
end

Not that easy anymore. And I have to do error checking in Lua. I think it’s a lot better to do it in C++ and hide implementation details there. Some functions may be even more complex in C++ but look very simple in Lua. Writing error checking in Lua would make scripts confusing and error-prone.

  • You can optimize entity creation by moving some entity declarations into JSON/XML/binary files

You may notice that the Lua table presented before doesn’t hold any information which cannot be stored in JSON or XML. And this is true. If you create every component and entity in C++, you can easily move entity descriptions from Lua to JSON or XML to speed things up.

So what’s the point of using Lua then? Lua is used for adding different functions to entities and this makes scripting very enjoyable and lets me put lots of code in Lua instead of C++. This is very awesome, because it lets me easily change entity behaviour with no recompilation and hardcore a lot less. I can even change entity properties and functions while the game is still running! Isn’t this great?

Scripting entity behaviour with Lua

Here’s an example when Lua functions are used to provide different entity behaviour.
For example, different entities may react differently to collisions.
Suppose I want to create a cute ghost which blushes when it collides with some entity (it also damages it. This is absurd, but hey, it’s just an example!). Here’s how it would look in the script:

Here’s how it works: when CollisionSystem finds a collision between two entities, it calls a collide function which is stored in entity’s CollisionComponent. It also passes two arguments to the function: this – a pointer to the entity of the type which this function belongs to, second – a pointer to the entity which collided with this (this is a name of a variable I use, not a Lua keyword!)

Conclusion

This article got pretty big so I’ve decided to explain implementation details in the next part. You’ll learn some cool patterns and Lua stuff along the way. The next article will cover:

  • Entity creation process
  • Getting list of table keys
  • Creating components by their names in C++
  • Adding and using simple Lua callbacks to common events (collision, damage, etc.)

I hope that this article sparkled your interest in Lua. If you want to learn more, you can read my Lua articles on this blog (especially this one which will help you to configure Lua for the next article) to learn more about Lua or read some of my dev logs to see how I use Lua to solve problems during the development. Stay tuned, the next article will cover lots of stuff!

8 thoughts on “Using Lua with C++ in practice. Part 1. Intro to ECS and basic principles

  1. Hi,

    Awesome article! I’m also trying to make my own game engine for learning purpose and I’d like to integrate Lua as scripting language. I found your articles about Lua are very helpful!
    After I read this article,what I’m getting is that you have vector of Component in your Entity class, and you also have System class to create the desired Component object.
    Ex: You have RenderingSystem class that also holds any GraphicsComponent that Entity has. I’d like to ask how you implement RenderingSystem class since I’m new to ECS pattern.

    Sorry for bad english.

    Thank you.

    • Thanks!
      No, systems don’t create components, they simply have pointers to components they’re interested in.
      For example, you can have this function:

      void RenderingComponent::addEntity(Entity* e) {
          auto gc = e->get<GraphicsComponent>();
          if(gc) components.push_back(gc);
      }
      

      And then you simply iterate over pointers in this system like this:

      void RenderingSystem::process() {
          for(auto it = components.begin(); it != components.end(); ++it) {
              ... // draw
          }
      }
      
  2. Hello
    I am trying to implement lua into my game engine.
    When loading scripts, should I create new lua_State for every script or use one global state for all scripts ?

    Great article bdw :) Can’t wait for next part.

    • Hi.
      I use one lua_State for every script because it lets me bind some C++ functions in this lua_State and use them in every script I want. If you use several lua_States, you have to bind those functions to each of them which is not good.

      Thanks. Working on it. :D

  3. Hello
    I am currently trying to implement an ECS on my own and I would really like to know how you solved the communication between Systems. I am particularly interested in the RenderingSystem. It needs to know the location of the Entity to render the sprite/animation at the correct position on the screen. Where does it get this information from?
    Michalis

    Great work on the game btw, the re:creation mechanic is one of the most creative puzzle mechanics I have seen in a while :)

    • Hello. There are two solutions. One is to have systems which operate on multiple components, so RenderingSystem may have two lists of pointers: pointers to GraphicsComponent and pointers to PositionComponent. All components can have id of the entity they belong to, so you can easily find a corresponding PositionComponent by GraphicsComponent’s parent id. Or, you can just store pointers to a parent entity inside the component (this is what I currently do) and get PositionComponent from it.
      Another way to do it is store position data and graphics data together. But then you’ll see that you need position data and collision data together too. Some people may suggest to duplicate data, which makes sense if you store components contiguously. This may lead to great perfomance, because cache misses are minimized, but this is harder to implement and properly maintain.

      And thanks!

    • Didn’t try that out yet. But this is definitely possible with tables and metatables. Would be a cool thing to have, but I feel I don’t need it at the moment.
      For now I have around 12 components and don’t think I’ll need much more, but who knows!
      Lots of stuff is just easily scriptable. You don’t need DoorComponent, for example, you can just create DoorOpenState and DoorCloseState and write some script logic for those states. Same goes for other components which don’t need to have their own components.

      As for systems… same thing. Maybe it’s because my game is so simple, but I didn’t add new systems and components for like a year and I’ve made a lot of progress this year. :)

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s