Design – How to allow for custom Rules in a Entity Component System designed game engine

compositiondesigndesign-patternsentity-component-systemgame development

So I've been doing a lot of research into game engines, and Entity Component System (ECS) seems to be the most flexible and extendable design to use.

Just to summarize, in ECS the basic idea is that you have Entities which are just containers for Components, and different Components correspond to different functionality, but they only store data. So for example, in a tower defense game, you could have a tower entity which has a moveable component, but all the component really stores would be the towers current direction and velocity.

The actual logic of moving the tower is controlled in a System. What a System does is it iterates through all the entities that have specific components, and modifies them accordingly. So you would have a Movement System which would only look at the Movable Entities (it has a moveable component), and update the data accordingly.

So all this makes sense to me in terms of how to design a Game Object that stores all the info about a Game. My question is how would you incorporate custom user defined rules into this model? Because what I have is a game engine, it needs to be able to handle specific user defined rules which may vary from Game to Game, or even from Level to Level within a Game.

For example, let's say I want a rule saying that I want all towers to move, except when it's within a certain range of an enemy. Where would this logic reside? If it resides in the Movement system, that would me that I would need to create a new movement system for each game which is not how game engines are supposed to work right? The whole idea is that you feed it any game and the systems can handle it. Would I need a separate "Rules" system?

Best Answer

To begin with, a caveat: even more than other domains of programming, every design decision in game development is a tradeoff. The "right" answer is "whatever works for the game you are making."

Okay. There are a few approaches we can take:

  1. If there are a small, known set of general behaviors, write them inline and allow the user to tweak parameters. (i.e., which enemies does this tower avoid, how far away does it need to be, etc.) This is the most straightforward and maintainable approach, but it scales poorly the more different behaviors you have.

  2. Functions can be data! In C, this is a function pointer, other languages have equivalents. The code that creates the entity is responsible for giving it a function telling it how to act.

In C it'd look something like this:

// Define a function pointer type.
typedef void BehaviorFunc(Entity * this);

struct FooComponent {
    BehaviorFunc * my_behavior;
    // Other data as needed...
}

// User code:
void MyBehaviorFunc(Entity * this) {
    DoTheThing();
}
// ...
{
    FooComponent * f = AddFooComponent();
    f->my_behavior = MyBehaviorFunc;
}

// Update:
void UpdateFooComponent(FooComponent * f) {
    // stuff
    f->my_behavior(GetEntityFromComponent(f));
    // more stuff
}

We actually have other options here as well. If you need to allow non-programmers to define behaviors, you could replace the function pointer with a behavior tree, compiled script code, etc. All of these are much more complicated, though, so don't bother unless you actually need the extra flexibility. Keep in mind that the more general you make the system, the harder it is to implement (and optimize), for both the engine and client code.

Related Topic