Game Engine design for many-typed many-copied entities

Started by
58 comments, last by nfries88 8 years, 1 month ago

What I am looking for is some advice on possible implementations of a game engine that is able to handle many (100+) different entity types (e.g. Wrenches, Clothes, Wall segments, Grilles, Chairs, etc. etc.) and also efficiently allocate resources for many instances of that entity (50+).

Some context: I am programming a 2D tile-based map where each tile can hold a certain number of items with which the player can interact. Each entity has some properties which can be shared (e.g. a wrench and a cigarette can both be picked up) but differ in many other ways, and some entities can be interacted with whilst still on a tile (chairs can be sat upon, items placed on tables...).

I have looked into entity component systems and have tried looking at an implementation that would work, but the problems i would need to overcome to make such a system usable are:

  • Per entity type data needs to be retained and not stored on each instance of that entity type (every single chair entity should not need to store a reference to it's texture, or a component stating that it can be wrenched to the floor)
  • Actions that aren't greatly suited to the use of systems and better suited to a reactional/event based design (drinking, welding...)

So do any of you have suggestions for either ways to fix the above problems or alternative architectures that work better in these circumstances?

UPDATE:

Thank you for your contributions. A post below by me details the chosen implementation based on the answers above it that I deem satisfactory for the requirements of the game.

Advertisement

Entity component systems do not do well with projects of greater complexity. How you approach this truthfully up to you.

Keep in mind that many old styles of programming still has their uses, and are still in effect today. Source Engine and Dota 2 REBORN uses the inheritance model still. Halo from what I gathered from free resources used mostly monolith classes for everything.

Unreal uses a mixed system. both inherritance and component based.

Unity uses a Component model.

I use a Mix of Unreal and Unity. My model is heavily inspired off of Unreal's Actors. Which are base entities with low level components. Then a hard core component system is implemented in lua.

It's not the same as Entity Component, in the way that everything is just data. That's absolutely asinine if you're not a simple game.

Basically, anything of higher level is defined in Lua. My types are just congregated components. But there are no systems. There's only a few functions that controls the update loop. And everything else that happens will happen by delayed events that gets collected in the actor's mailbox.

On the next round of updates, each actor will do their thing. First starting with delivering the events in their mailbox to their components.

So... Player strikes at an enemy. A physics check states that the enemy gets damaged. A letters is sent to the Enemy. On the next round before the updates, the Entity reads his mail, and delivers it to the components. The components notice that he's "Taken Damage". The Animation, the Health, and the Sound Components care about this. So they will act upon it.


Per entity type data needs to be retained and not stored on each instance of that entity type (every single chair entity should not need to store a reference to it's texture, or a component stating that it can be wrenched to the floor)

That's a strange requirement to have. That suggests to be like you plan on having code like


if (type == Chair)
{
    do this...
}
else if (type == Clothes)
{
    do this...
}
else ....

If, as you say, you plan on having 100+ types of entities, that kind of coding is just doesn't scale and will be a maintenance mess.

Or, if this requirement is just to save space, then you can just have a field in a component that references a descriptor for that object that contains its static properties.


Actions that aren't greatly suited to the use of systems and better suited to a reactional/event based design (drinking, welding...)

Using an entity component system doesn't mean you need to use it *everywhere*, and it doesn't mean you can't mix it with other types of designs. There's nothing preventing you from supporting generic behaviors/scripts that can be attached to entities to perform actions or respond to events.


Per entity type data needs to be retained and not stored on each instance of that entity type (every single chair entity should not need to store a reference to it's texture, or a component stating that it can be wrenched to the floor)

I'm not really sure what this issue here is about. It sounds like you need a template/blueprint/prefab system, which isn't really at odds with an Entity system. All blue chairs have a texture component, which has a reference to the blue material. That material should be shared, so they're not all loading the same blue texture, sure, but they all still need to reference the texture (smart pointers as a simple example) Or maybe you're referring to shared data of a more basic sort, like I dunno, starting hitpoints on a goblin. But then they should all be referring to the same goblin blueprint/prefab/template, but would still have hitpoint components on each instance.


Per entity type data needs to be retained and not stored on each instance of that entity type

relational databases is the traditional solution to this. you have a list of entity_types, and a second list of entity_instances. each entity_instance has a member variable called entity_type, which is what type of entity it is.

the entity_types list contains type specific constant info such as max hit points, turn speed, drawing info, etc. the entity_instances list contains variable entity specific info like current damage level of a given entity, its location and orientation, etc.


Actions that aren't greatly suited to the use of systems and better suited to a reactional/event based design (drinking, welding...)

actions are a different sort of beast from objects.. actions have a means of being triggered. they often have pre-requisites which must be checked. they often have a duration and chance of success, and results on success and failure. they can also have animations and sfx's associated with them. and stat and skill bonuses apply to actions.

Norm Barrows

Rockland Software Productions

"Building PC games since 1989"

rocklandsoftware.net

PLAY CAVEMAN NOW!

http://rocklandsoftware.net/beta.php


What I am looking for is some advice on possible implementations of a game engine that is able to handle many (100+) different entity types (e.g. Wrenches, Clothes, Wall segments, Grilles, Chairs, etc. etc.) and also efficiently allocate resources for many instances of that entity (50+).
5000 entities then? Thats nothing. Anything will work fine with that, unless you do something terribly wrong of course.

Also if say, a chair is something static, and a wall is static, its not a different entity. They're the same static kind of entity, with different textures. I very much doubt you'll have that many different kind of entities.

A game like Skyrim was made with probably less than 100 different kinds of entities (npcs, quests, statics, animated, weapon, armor, spell, and a couple more, thats it). Yours wont have as many as you think.

"I AM ZE EMPRAH OPENGL 3.3 THE CORE, I DEMAND FROM THEE ZE SHADERZ AND MATRIXEZ"

My journals: dustArtemis ECS framework and Making a Terrain Generator

There is an article in the first Game Programming Gems book called "Data Driven Design", and has a section about avoiding data duplication. I suggest reading it! The particularly useful idea is to use data inheritance. The example from the book looks like:


Goblin
{
	speed = 10
	life = 20
	attack = 15
}

Fast Goblin : References Goblin
{
	speed = 20
}

Instance : Goblin
{
	position = (5, 0, 0)
	Facing = (1, 0, 0)
}

The nice thing about the diagram the explanation differences between type definitions and type instances. Definitions can reference/inherit one another, and instances fill in the fields. In the context of some kind of aggregation/component system data files can be used to describe an aggregate (i.e. the Goblin has X Y and Z components), the fast goblin data file can reference the Goblin file, and modify some attributes or add/subtract components. An instance fills in the memory of the components.


Per entity type data needs to be retained and not stored on each instance of that entity type (every single chair entity should not need to store a reference to it's texture or a component stating that it can be wrenched to the floor)

This shouldn't be an issue. Implement a new component which includes functions implementing the use of per-entity-type data. You can subclass the component as needed for more complex objects. Tiles, and perhaps even the entire gameworld, could be implemented as entities this way.


Actions that aren't greatly suited to the use of systems and better suited to a reactional/event based design (drinking, welding...)

Again, just create a component for each specific type of behavior you need. You can create an AI component, a decaying corpse component, etc.

You can wrap every piece of code you could ever write for a game in a component if you wanted, not just in-game objects.

Thank you for your contributions. After reviewing the problem with the help of your replies i have decided to try a more general component-based solutions where what would usually be an entity is instead an entity type (which i like to call a behaviour), and each instance of that type is stored as a singe element of a vector within that entity type. That is to say (very, very simply):


Behaviour
{
    addComponent( Component* com )
    removeComponent( unsigned int comType )

    Component* getComponent( unsigned int comType ) 
};

TextureComponent
{
    textureid getTexture()
}

UsableComponent
{
    Script onUse( Id id )
}

ChairBehaviour : Behaviour
{
    Chair
    {
        int     direction
        Player* occupied
    }

    ChairBehaviour()
    {
        TextureComponent tComp = createTextureComponent( "chair.png" )
        UsableComponent uComp = createUsableComponent( "sit.script", [](Id id){ return this->chairs[id]->occupied } )
        addComponent( tComp )
        addComponent( new UsableComponent )
    }

    Chair  chairs[]
}

This adds a layer between the systems and individual items/objects that can be used to insert data that does not need to be stored in every object. Now the texture reference and script references just need to be stored in the chairbehaviour, of which there will only ever be one, and as you can see only the direction which the chair faces and the id of anyone sitting in it (null if empty) is stored for each chair on the map.

As I am programming in C++ which requires strictly typed variables and functions, some components (e.g. the texturecomponent) can store function pointers which are filled by the Behaviour so that a system (e.g. graphics system) only needs to know about that one component to retrieve necessary information.

Thank you for your help.

Entity component systems do not do well with projects of greater complexity.


What problems have you encountered (or do you foresee) using ECS architectures on complex game projects?
Are the problems you encountered merely from how you implemented your ECS, or are they inherent in all ECS architectures?

Per entity type data needs to be retained and not stored on each instance of that entity type (every single chair entity should not need to store a reference to it's texture, or a component stating that it can be wrenched to the floor)


What's wrong with storing a reference to a texture? Your graphics component needs to do that anyway, and you might have ten or twenty different chairs (several couches, benches, window seats, wooden chairs, computer chairs, etc...).

Actions that aren't greatly suited to the use of systems and better suited to a reactional/event based design (drinking, welding...)


Games are complex things, and you aren't required to cram every part of your game into a single paradigm. GUIs, for example, are recognized as poor fits for ECS, so for the GUI part of a game engine, you don't use an ECS, even if you are using ECS for other areas.

This topic is closed to new replies.

Advertisement