Entity creationMy 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. (I'll explain some implementation details in the article for my blog I'm currently writing)
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 particular aspect of entity. There are 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 have a list of components of currently active entities which they iterate through. They don't care which type of entity the component belongs to. For example,
RenderingSystem iterates through
GraphicsComponent's and renders them in particular order.
CollisionSystem checks collisions between entities usign 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.). In fact, there are no classes which inherit from
Entity class.
Entities are created by attaching components with different parameters. Here's a simple example of how it looks in C++ code:
Entity e;
auto gc = new GraphicsComponent;
gc->setSprite(entitySprite);
...
e.addComponent(gc);
...
Here's how I can get components by the class name:
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.
This is where Lua helps me a lot.
Here's an example of a simple Lua script:
Note, that this script doesn't create entity. It's simply used to obtain data to set different components parameters and then create Entity in C++.
I get all table's keys and now I have a list of components I need to create. Then I pass
luabridge::LuaRef's to each component constructors (
LuaRef is a reference to a table in
LuaBridge, C++/Lua binding I use). Components get parameters from these tables and then they're attached to what I call "
template entity". Template entities are created with scripts and other objects of this type are created using this C++ object (by calling copy-constructor and copying each component). This means that only one entity is created using information from the script. Instances of entity type are created much quicker by copying.
I could have easily used JSON or XML for this, so what's the point of using Lua? 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. I can even change entity properties and functions
when the game is still running! Isn't this great?
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
collide function which is stored in entity's CollisionComponent. It also passes two arguments to the function:
this - pointer to the entity of the type which this function belongs to,
second - pointer to the entity which collided with
this (
this is a name of a variable I use, not a Lua keyword!)
setAnimation is one of the C++ functions which I bind to Lua. Here's how it looks in C++:
void setAnimation(Entity* e, const std::string& animationName) {
auto gc = e->get<GraphicsComponent>();
if(gc) {
gc->setAnimation(animationName);
} else {
... // write error in debug console
}
}
This is an example of why I chose not to expose components to scripts: it makes error checking easier. (No need to make it in Lua, just check everything in C++). Another reason is that some functions can be more complex but still remain simple in scripts (even non-coders can easily use them). For example, if entity needs to say something, I can just write this in script:
say("TALK_TAG")
But in C++ it will do a lot more: check if NPC component exists, get text by tag from it, set dialogue state for GUI, set text for that state, etc.
Once the entity template is loaded, I can easily create it with in-game level editor (I'll write about it some day)
And that's just a small simple example. Complex entities have state machines and callback functions which are easily scriptable too (more about that later).
This lets me easily create new types of entities without touching C++ code. No hardcoding required. This also makes my game very moddable.
What about the perfomance? So far I've noticed no problems at all. Even when Lua functions are called 60 times per second for
different entities, it's pretty fast and makes no noticeable difference.