Implementing Component-Entity-Systems

Published October 02, 2013 by Boreal Games, posted by Klutzershy
Do you see issues with this article? Let us know.
Advertisement

This is the follow-up to my article from April, Understanding Component-Entity-Systems. If you haven't read that article yet, I suggest looking it over because it explains the theory behind what I am about to show you. To summarize what was written:

  • Components represent the data a game object can have
  • Entities represent a game object as an aggregation of components
  • Systems provide the logic to operate on components and simulate the game
The purpose of this article is to show how to implement the architecture that I described in an efficient way, and to provide solutions for some sample problems. All the code samples that I provide will be written in C.

Implementation

Components

I wrote in the last article that a component is essentially a C struct: plain old data, so that's what I used to implement them. They're pretty self-explanatory. I'll implement three types of component here:
  1. Displacement (x, y)
  2. Velocity (x, y)
  3. Appearance (name)
Here's the sample of code defining the Displacement component. It is a simple struct with two members that define its vector components. typedef struct { float x; float y; } Displacement; The Velocity component is defined the same way, and the Appearance has a single string member. In addition to the concrete data types, the implementation makes use of an enum for creating "component masks", or bit fields, that identify a set of components. Each entity and system has a component mask, the use of which will be explained shortly. typedef enum { COMPONENT_NONE = 0, COMPONENT_DISPLACEMENT = 1 << 0, COMPONENT_VELOCITY = 1 << 1, COMPONENT_APPEARANCE = 1 << 2 } Component; Defining a component mask is easy. In the context of an entity, a component mask describes which components the entity has. If the entity has a Displacement and a Appearance, the value of its component mask will be COMPONENT_DISPLACEMENT | COMPONENT_APPEARANCE.

Entities

The entity itself will not be defined as a concrete data type. In accordance with data-oriented-design (DOD) principles, having each entity be a structure containing each of its components, creating an "array of structs", is a no-no. Therefore, each component type will be laid out contiguously in memory, creating a "struct of arrays". This will improve cache coherency and facilitate iteration. In order to do this, the entity will be represented by an index into each component array. The component found at that index is considered as part of that entity. I call this "struct of arrays" the World. Along with the components themselves, it stores a component mask for each entity. typedef struct { int mask[ENTITY_COUNT]; Displacement displacement[ENTITY_COUNT]; Velocity velocity[ENTITY_COUNT]; Appearance appearance[ENTITY_COUNT]; } World; ENTITY_COUNT is defined in my test program to be 100, but in a real game it will likely be much higher. In this implementation, the maximum number of entities is constrained to this value. I prefer to use stack-allocated memory to dynamic memory, but the world could also be implemented as a number of C++-style vectors, one per component. Along with this structure, I have defined a couple of functions that are able to create and destroy specific entities. unsigned int createEntity(World *world) { unsigned int entity; for(entity = 0; entity < ENTITY_COUNT; ++entity) { if(world->mask[entity] == COMPONENT_NONE) { return(entity); } } printf("Error! No more entities left!\n"); return(ENTITY_COUNT); } void destroyEntity(World *world, unsigned int entity) { world->mask[entity] = COMPONENT_NONE; } The first does not "create" an entity per se, but instead returns the first "empty" entity index, i.e. for the first entity with no components. The second simply sets an entity's component mask to nothing. Treating an entity with an empty component mask as "non-existent" is very intuitive, because no systems will run on it. I've also created a few helper functions to create a fully-formed entity from initial parameters such as displacement and velocity. Here is the one that creates a tree, which has a Displacement and an Appearance. unsigned int createTree(World *world, float x, float y) { unsigned int entity = createEntity(world); world->mask[entity] = COMPONENT_DISPLACEMENT | COMPONENT_APPEARANCE; world->displacement[entity].x = x; world->displacement[entity].y = y; world->appearance[entity].name = "Tree"; return(entity); } In a real-world engine, your entities would likely be defined using external data files, but that is beyond the scope of my test program. Even so, it is easy to see how flexible the entity creation system is.

Systems

The systems are easily the most complex part of the implementation. Each system is a generic function which is mapped to a certain component mask. This is the second use of a component mask: to define which components a certain system operates on. #define MOVEMENT_MASK (COMPONENT_DISPLACEMENT | COMPONENT_VELOCITY) void movementFunction(World *world) { unsigned int entity; Displacement *d; Velocity *v; for(entity = 0; entity < ENTITY_COUNT; ++entity) { if((world->mask[entity] & MOVEMENT_MASK) == MOVEMENT_MASK) { d = &(world->displacement[entity]); v = &(world->velocity[entity]); v->y -= 0.98f; d->x += v->x; d->y += v->y; } } } Here is where the component mask becomes really powerful. It makes it trivial to select an entity based on whether or not it has certain components, and it does it quickly. If each entity was a concrete structure with a dictionary or set to show which components it has, it would be a much slower operation. The system itself adds the effect of gravity and then moves any entity with both a Displacement and a Velocity. If all entities are initialized properly, every entity processed by this function is guaranteed to have a valid Displacement and Velocity. The one downside of the component mask is that the number of possible components is finite. In this implementation it is 32 because the default integer type is 32 bits long. C++ provides the std::bitset class, which is N bits long, and I'm sure other languages provide similar facilities. In C, the number of bits can be extended by using multiple component masks in an array and checking each one independently, like this: (EntityMask[0] & SystemMask[0]) == SystemMask[0] && (EntityMask[1] & SystemMask[1]) == SystemMask[1] // && ...

Source Files

I've zipped up the source code here. Main.c runs a sample program that creates three entities and runs each system once. CES.zip

Real-World Problems

This implementation works very well in the small scope of my program and can easily be extended to use more components and systems. It can also easily be extended to run in a main loop, and extended to read entities from data files with some work. This section will tackle some problems with transferring gameplay mechanics and advanced features over to my implementation of CES.

Power-ups and collision filtering

This problem was pitched to me by Krohm in a comment on the original article. He was asking about gameplay-specific behaviours in general, but provided the specific example of a power-up that stopped collisions with a certain entity type. Dynamic components to the rescue! Let's create a component, say GhostBehaviour, that has a list of qualifiers for determining which entities an object can pass through. For example, a list of component masks, or possibly material indices. Any component can be added or removed (technically, enabled or disabled) from any entity at any time, simply by changing that entity's component mask. When the player grabs the powerup, the GhostBehaviour component will be added. It could also have a built-in timer to automatically remove itself after a few seconds. To actually disable the necessary collisions, the typical collision response in a physics engine can be exploited. In most physics engines, there is first a step to detect collisions and produce contacts, and then a step to actually apply the contact forces to each body. Let's say that each of those steps is implemented in a system, and that there is a component that keeps track of each entity's collision contacts (Collidable). To permit the desired behaviour, each contact should store the index of the other entity. By injecting a system that operates on a GhostBehaviour and a Collidable in between the two physics steps, the contacts between the entities that should pass through each other can be deleted before they are acted upon by the physics engine. This will have the effect of a disabled collision. The same system can also disable the GhostBehaviour after a few seconds. A similar approach can be used to perform a certain action upon collision. There could be a unique system for every action, or the same system could govern all the actions. In any case, the system would read the collision contacts to determine whether the entity collided with some other entity and then act accordingly. In fact, this would be needed to give the player the power-up in the first place.

The Big F***ing Spell - Destroying all monsters

Another problem I received was how to kill all monsters with a spell. Thanks to smorgasbord for this one! The key to solving this one is that a system can be run somewhere outside of the top-level main loop, and that any entity that is a monster, according to CES rules, satisfies the same component mask. For example, every entity with both Health and AI is a monster, and this can be described with a component mask. Remember how a system is just a function and a component mask? Let's define the "kill all monsters" spell as a system. The function, at its core, is destroyEntity, but could also create particle effects or play a sound. The component mask of the system can be COMPONENT_HEALTH | COMPONENT_AI. In terms of actually casting the spell, I mentioned in the previous article that each entity can have one or more input components, which store boolean or real values that map to various inputs, including AI and networked players. Let's create a MagicInputComponent that has a boolean value that says when the entity should cast the spell, and an ID corresponding to the spell that should be cast. Each spell has a unique ID, which is actually a key in a lookup table. This lookup table maps a spell ID to the function that "casts" that spell. In this case, the function would run the system..

Conclusion

Remember that this is just a sample implementation. It works for the test program, but is probably not ready for a full game. However, I hope that the design principles have been made clear and that it is easy to understand and implement in your own way in a different language. If you have any more problems you'd like me to solve you can write a comment or send me a message. I'll update this article periodically with my solutions. Keep in touch for the next article!

Article Update Log

29 September 2013 - Initial submission 2 October 2013 - Initial publication; simplified system code (thanks TiagoCosta!); clarified 2nd example (thanks BeerNutts!)] 21 October 2013 - Reflected code changes in the source archive

Cancel Save
3 Likes 23 Comments

Comments

Aqua Costa

Great article!

I would suggest moving the looping through all entities and masking inside each system function. Try to follow Mike Acton's tip "Where there's one, there's more than one."

Before you know it, with the current implementation you will be calling each system function thousands of times every frame.


#define THIS_SYSTEM_MASK 0x12345678

void movementFunction(World *world)
{
    unsigned int entity;
    for(entity = 0; entity < ENTITY_COUNT; ++entity)
    {
        // The most important line in the implementation!
        if((world->mask[entity] & THIS_SYSTEM_MASK) == THIS_SYSTEM_MASK)
        {
	    Displacement *d = &(world->displacement[entity]);
	    Velocity *v = &(world->velocity[entity]);

	    v->y -= 0.98f;

	    d->x += v->x;
	    d->y += v->y;
         }
}

void runSystem(System *system, World *world)
{
    system->function(world);
}

You don't even need a System struct, it can simply be a function.

And then again you can also remove the runSystem function and simply run each system directly, but since you probably won't have that many systems the perfomance impact will be smaller.

October 02, 2013 03:05 PM
apatriarca

One problem I see in your implementation is the large number of "unused components". Particularly if the number of different component types is large. But I like the simplicity of your approach. Good article.

October 02, 2013 03:26 PM
BeerNutts

Thank you for the article, this simplistic approach is a good one to get the idea across. I think there are some obvious places for efficiency we could do, but, assuming a small-ish game, this should be fine.

You've made me think about a few things about this approach. Specifically, I want to go deeper into the casting a spell that hits enemies.

If I'm thinking about it from the ground up, let's assume there are these components:

InputComponent - This component defines which entity handles user input. A system runs on the entities with this component, and polls the user input. And, if a user is pressing a key, then a mask in the component is set detail which action key has been presed. Let's assume the user presses the Enter key, which maps to ActiveSpellKey.

SpellComponent - This component defines all the spells available to an entity, and which spell is currently "active" (ie, the 'Big F***ing Spell' has been selected in a menu).

PlayerComponent - This component defines an entity as being a player, and the player system runs on it.

BigF***ingSpellComponent - this component defines an entity as being the big f***ing spell. It has range and damage values.

So, they user presses Enter to shoot the selected spell, Big F***ing Spell. The input system runs from the top level (assuming it is one of the 1st systems to run). It records in the InputComponent the ActiveSpellKey is pressed.

After, the PlayerSystem runs. It checks if the ActiveSpellKey has been pressed from, and it has. Assuming all the previous is OK, this is what I'm curios about.

So, now it takes the active spell from the SpellComponent (lets assume it is a component mask). Does it now loop over all the SpellSystems (assuming such a thing exists), and calls the one that matches the BigF***ingSpellComponent? Or would it simply create an entity with the user's Displacement, Velocity, and the BigF***ingSpellComponent, and let it run the next time through the main loop?

I guess I'm really curious about is directly calling systems from other systems, or simply have all the systems be run from the top level, and, in this case, the BigF***ingSpellSystem would run on the entity created from it. As I wrote this, I would have to vote for the latter, since, many spells would be an entity unto itself (like a Fireball), and this way it can just be an entity with a BigF***ingSpellComponent, or the FireballSpellComponent.

Anyway, good article, I think it paints the idea well. BTW, in my journal, I cover the Entity Component System in my own implementation.

October 02, 2013 06:12 PM
Klutzershy

I would suggest moving the looping through all entities and masking inside each system function. Try to follow Mike Acton's tip "Where there's one, there's more than one."

That would probably be better. I'll change up the article to reflect that. My rationale of "factoring out" the selection algorithm probably made the code more complex than necessary...

One problem I see in your implementation is the large number of "unused components". Particularly if the number of different component types is large. But I like the simplicity of your approach. Good article.

The worst-case (universal-case) memory usage of my implementation is the same as (technically better than) the worst-case memory usage of an implementation using dynamic memory. Besides, the way all my components are laid out in memory is much better on the cache.

In any case, memory consumption by components should still be negligible compared to assets.

So, now it takes the active spell from the SpellComponent (lets assume it is a component mask). Does it now loop over all the SpellSystems (assuming such a thing exists), and calls the one that matches the BigF***ingSpellComponent? Or would it simply create an entity with the user's Displacement, Velocity, and the BigF***ingSpellComponent, and let it run the next time through the main loop?

For a non-projectile spell like I imagine the BFS would be, there would definitely be no need to create an entity and introduce a one-frame latency. And a loop would not be needed to call the BFS system. The way I envision this is to implement each spell as a function, and store pointers to those functions in a lookup table. The lookup key for each spell function is that spell's ID, which is stored in SpellComponent.

I will clarify the casting procedure in the article as well, thanks.

October 02, 2013 07:12 PM
kremvax

This is an interesting article, however I'm not really convinced by a full component-entity architecture, throwing away all OOP design. It's still possible to use composition inside a restricted inheritance tree, an entity being not just an ID but an object composed with delegate classes. The inheritance tree would only reflect the nature of the objects, in that it is very unlikely to change over time (I would say, if the nature itself of an object has to change, probably it means that a new Object should be created instead), and composition is used to implement some specific behaviours only. For instance I would see a "Character" class ; an AI would be Character composed with AI behavior, a player would be Character + input-related behavior, but they really share the same nature.

Describing entities as objects + delegates may also speed up debugging I think (I've never try it but having to look for an ID in X differents tables when doing step-by-step debugging doesn't sound to be as convenient as seeing the whole object's properties in one sight).

October 06, 2013 08:16 AM
Klutzershy

This is an interesting article, however I'm not really convinced by a full component-entity architecture, throwing away all OOP design. It's still possible to use composition inside a restricted inheritance tree, an entity being not just an ID but an object composed with delegate classes. The inheritance tree would only reflect the nature of the objects, in that it is very unlikely to change over time (I would say, if the nature itself of an object has to change, probably it means that a new Object should be created instead), and composition is used to implement some specific behaviours only. For instance I would see a "Character" class ; an AI would be Character composed with AI behavior, a player would be Character + input-related behavior, but they really share the same nature.

Describing entities as objects + delegates may also speed up debugging I think (I've never try it but having to look for an ID in X differents tables when doing step-by-step debugging doesn't sound to be as convenient as seeing the whole object's properties in one sight).

A pure component-entity-system architecture is most useful if you value flexibility over the benefits of using OOP (such as debugging). In particular, CES lets you data-drive your entities and even components and systems, which is invaluable for games with external editors or modding.

October 06, 2013 11:45 PM
Irlan Robson

Just a Brief Review:

A CES becomes flexible in the hands of the Artists/Game Designers. For the programmers there is a lot of code to keep it up. It's just a question of the data-driven and multiple inheritance ?benefits. Is good to deep into other game engines documentation before trying to implement these types of systems.

October 09, 2013 01:16 AM
Moe091

I'm only half way through but I just had to scroll down and comment, Great article! I've been looking for something exactly like this because I want to make an ecs in java for my next game and couldn't find any good resources on how to actually implement one. Even though this is in c++ it's very easy to translate over to java with a few minor changes.

Before I understood what an ecs was and why I wanted to use one, but I was really foggy on how to actually implement one. I'm halfway through the article and I already feel like it's cleared away the fog and I can finally see the structure clearly enough that I could put it into code

October 09, 2013 01:40 AM
ultramailman

Hello, thanks for this. I've heard about ECS many times, but I never understood what system is. After reading about your implementation, I think I understand what systems are now.

Also, I've downloaded the source you included, and noticed you included <memory.h>. Do you mean <string.h>? <memory.h> seems to be a linux header, while <string.h> has the memset you used.

October 15, 2013 08:30 PM
serumas

Some time ago , I was lurking info about entity systems and come up to facts (maybe I understood wrongly ):

1. entities can be made from vorious components and that components for each entity are individual, some objects could have 20 different components others just 2. That minimizes memory usage and the same components are in the same block of memory.

2. One type components can be processed independantly from other components. Its can speed up things... becouse of maybe smaller array of one type component and memory block cache...

So imagine I am very dissapointed with this article.

1. All entites still has all components even if they are not used. So if I have 1000 entities and just 10 of them have velocity, that means there are still unused 990 velocities in memory. That shows

int mask[ENTITY_COUNT];
Displacement displacement[ENTITY_COUNT];
Velocity velocity[ENTITY_COUNT];

2. There are no independant components processing systems in article.

And you are talking about better memory cache. where you see better cache in your code?

Better cache means - less memory usage and sequential memory access.

In your code I think would be better even not to have entity system at all (becouse no less memory usage), but entity class has all components inside. As I mentioned there are no component independent processing and all objects still processing every component stored in 4 different arrays(its not sequential memory access)...

Am I wrong or what?

Thanks I corected therminology

October 16, 2013 01:46 PM
ultramailman

Am I wrong or what?

You are wrong, in your use of terminology and your understanding of good usage of the cache.

Your "objects" should be called entities, and your "entities" should be called components. It's less confusing when you use the right terminologies.

While less memory usage can result in better use of the cache, good use of the cache does not mean "less memory usage". Good use of the cache is spatial (sequential memory access), or temporal (reaccessing previously accessed memory).

I don't know about temporal, but I'm sure this implementation has good cache usage.

1. It uses arrays, a sequential data structure. That in itself hints at good spatial locality.

2. By using structure of arrays instead of array of structures, cache misses are less because each system only accesses the components it need, instead of all the components of an entity. It also allows multiple components of the same type to occupy one cache line.

October 16, 2013 05:31 PM
serumas

I think you miisunderstood me, we are talking about curient implementation

Less memory accessing and processing poteanialy leads to less caching and cash missings, ofcouse its highly dependant on data processing algorithms.

And becouse mask, velocity, displacement are still accessed in every loop , that would be better to marge thouse arrays in to a single array, that goes as a single block in cache. They are processed together so would be better to bring them spatial locality...

This article shows me not benefits of entity systems but weakness. Velocity and displacement is too much connected to be used as sepatrate components of entity (thanks you corected in therminology). I would make instead more mobile physics and rendering components (more clear for novice), will make some simple mechanism of comunicating between those components. And will show INDEPENDANT systems operate with one component: hysics system that dont know anything about object rendering, and component rendering which access physics component only than its must to...

Does this article shows that?

Still wrong?

October 17, 2013 07:18 AM
Klutzershy

serumas, there are a lot of techniques to further improve cache coherency. For example, commonly-grouped components can be interlaced or simply packed into one component type. To decrease cache misses, components can be packed and then indexed using a sparse array. However, none of these techniques were included in this article for the sake of simplicity and brevity. The next article will highlight areas that can be optimized.

October 17, 2013 07:03 PM
Irlan Robson

One of the things that you can do is:


for all entities
   for all components
      long componentMask |= component.componentType;

Now ompare the bitwise values and use the correct system

October 20, 2013 09:39 PM
Klutzershy

irlanrobson, that's pointless. Maintaining a component mask is much more efficient than remaking it every frame.

October 20, 2013 10:47 PM
Irlan Robson

irlanrobson, that's pointless. Maintaining a component mask is much more efficient than remaking it every frame.

I mean when you're creating the entity with dynamic components. eg.

entity.add( new Component() );

...

then, run the loop and set the entity mask based on the new created components instead of setting them manually. Also, when adding a new component run a |= NEW_COMPONENT, and when removing a &= NEW_COMPONENT operator.

ps: the add method is just a example (entities doesn't have methods)

October 21, 2013 02:41 PM
Klutzershy

Ah, I just didn't want to overcomplicate the code with unnecessary abstraction.

October 21, 2013 06:42 PM
fuerchter

How would you suggest reacting to collisions in general? As it stands the logic is only supposed to be implemented in the systems if I got that right, but that would mean that the CollisionSystem for example would have to react to different entities/components in a different way (as they are colliding). Wouldn't that make the system unusable in a second project if it still contains code specific to the first project (and its entities)?

I am thinking about having the CollisionSystem call a virtual component onCollision method (similar to the way Unity does things, I think?), so the entities/components can define the collision reaction themselves. Is that a good approach to the problem or am I barking up the wrong tree? xD

(This seems to be a similar problem to this: link)

October 31, 2013 10:35 AM
Klutzershy

Not all the systems need to be written to be reused. If you were writing an engine, there would be a set of "core" systems and then another set of "gameplay" systems. Special reactions to collisions would fall under "gameplay", so they wouldn't be reused. However, the "core" systems can all be written so that any game can make use of them, like physics or rendering.

October 31, 2013 08:03 PM
AlanSmithee

Very nice article! Helped me a lot.

I read this after you changed it according to TiagoCosta's comment, but I'm assuming that before updating it, you looped over the enteties and called called each system once for each entity, instead of what you are doing now, where you are looping inside the functions.

A couple of questions:

1.

In a situation where there is ~1000 enteties and ~30 systems, the difference would be:

loop inside systems: 30 function calls and 1000 loops in each system (30.000 loops) each timestep

loop outside systems: 1000 loops and 30 function calls each loop (30.000 function calls) each timestep.

Ofcourse there are more factors to take in consideration, such as the systems complexity, the density of entities etc, but could someone please offer me some insight to the difference between the two implementations in terms of effectiveness (and anything else related).

2.

Also, what is the impact of keeping a dynamic collection for the enteties? Again, I guess this is realtive to the density of a static collection, but it seems wasteful to have a static structure if the game is dynamic in terms of enteties getting removed/deleted, which in many games is the situation. How big are the gains from caching and memory layout you get from a static collection in relation to tighter loops that you get from a dynamic collection?

Thanks in advance! =) / AS

November 29, 2013 12:00 PM
Cincinesh

Nice article. It explains the basics of CES implemented with a struct of arrays very well. I understand you want to keep it simple, but I think there are some basic optimizations worth mentioning in the article if not in a follow-up article.

1. Maintain a stack of "free" entity ids instead of iterating over every id until you find one with a zero mask.

2. Maintain a "highest used entity id" variable that you can use as the upper-limit of your iterations instead of ENTITY_COUNT. This way, if you have ENTITY_COUNT set to 1000 but all your in-use entities have an id of 50 or lower, you don't waste time checking the masks of entities 51 though 999.

January 31, 2014 10:05 PM
Mikepicker

I have a question:

I am implementing my ECS engine following this (interesting) article. Now I would like to implement a system which should handle collision detection. The problem is that I want to check the collision between entities with a different list of components.

So, for instance, entity_1 has COMPONENT_COLLISIONBOX | COMPONENT_POSITION while entity_2 has COMPONENT_CIRCLE | COMPONENT_POSITION. How should I change the framework in order to handle these cases?

Thank you.

April 01, 2014 04:30 PM
SuperG
Figuring out ECS DOD solution to. But I agree with @serumas. This solution will do very well for 100 entity. But might be very bad solution when you scale up in components and entitys. For 2Ghz cpu this single threaded solution has for FPS of 100 2G / (100 * 100 ) = 200.000 CP U cycles per entity to blow. So you can have a few 100 cachmisses of the slowes memory like 1000 cycles.penalty. You must go prety wild to get that with cache unfriendly solutions. But you will get there when you scale up. This solution works if you need solution for very light weight game. Now imagine game object could be made out of many entity with each pletora of components And scale it up to few dozen types of components and 10.000 entity. Then that second and third and Firth core running at 2GHz get probaly needed to. How ever if this low requierment is all you need and make sense for your use of very small game, then use it. Aspacialy if this design make much sense or are comfy with. But making it cache friendly avoid enums branch checks in critical loops doesn't need to be that complex. The way modern cpu like it is keep the same code in cache and compute the data sequential all nice inline in memory. Array is very basic solution. If you implement it like each component type has its own array. Where component manager compute everything in one loop. You get better way of cache utilisation. And bonus is it get trival to make it multitreaded. And because it all the same kind of data some cases possible to use SSE AVX .SIMD. Even offload it. To GPU using cuda openCL or C++AMP. Computeshaders But the simple form of it doesn't need to look complex. It might be even easy to read to. To me to test a good solution you stess test it. Because with today hardware and highlevel language making game like phong is so not demanding. So the very wrong wat might do the job. What I seek is a very basic solution that scale well up to large numbers of entity and components. The type of game is 3D space sandbox game and those game it often where there is one there are huge number more of.
February 08, 2015 12:24 PM
You must log in to join the conversation.
Don't have a GameDev.net account? Sign up!

This implementation works very well in the small scope of my program and can easily be extended to use more components and systems. It can also easily be extended to run in a main loop, and extended to read entities from data files with some work.

Advertisement
Advertisement