Skip to main content

Entity / Component Systems

I've been writing a new game recently -- and this time, I decided to use an entity/component system. Boy, does that approach make things easier! In other games I've written, I've struggled to fit new gameplay features into the existing code base. With a entity-component based systems, it's easy to add new features:
  • Need a way to track targetable objects in your game? Add a Targetable component that advertises the unit's position, name for display in the HUD, etc.
  • Need to add some new AI behavior to a unit? Add a component! Update all the AI components once per frame and you're on your way.
  • Does your game need networking? What should you do...? Add a component containing all the info for network serialization!
  • Need to control a unit from user input? Add a joystick component that updates the unit's position component in response to user input.

Basically, any new feature can be folded into a component. The best thing about components is that they are dynamic; you can add and remove components at runtime to change the behavior of a unit. If you think of the entity as a vector, then each component is like a separate "dimension" to your entity. Additionally, each component is a lot like a table in a relational database. The actual "entity" is just the sum of its components; that is, the sum of all its entries in each relational database table. I find this design incredibly elegant.

So, how have I implemented my entity/component system? I have a Table template class, which is basically a wrapper around std::unordered_map, indexed by the entity ID. To look up a component, you simply do a "query" (to borrow from relational database terminology) using the entity's ID and look up the component. For example:

Id id = ... 
// get the entity's ID
Ptr<Table<Position>> table = db->table(); 
// get position table
Position position = table->component(id);
// do something with the position
Say you want iterate over all the targetable enemies and select the closest one. This is really easy with entity/component systems:
Id nearestTargetableEnemy(Vector myPosition) {
    auto nearestId = Id();
    auto nearestDist = std::numeric_limits::max();
    for (auto i: *db->table()) {
        auto target = i.value;
        auto id = i.key;
        auto position = db->table(id);
        auto dist = position.origin.distance(myPosition);
        if (dist < nearestDist) {
            nearestDist = dist;
            nearestId = id;
        }
    }
    return id;
}
Simple! And, this code automatically works for any entity that has a Target and Position component! Pretty cool. The possibilities are pretty much endless. Here are some more things that are easy to implement with entity/component systems:
  • Recharge/powerup stations
  • Pathfinding
  • Serialization/save game
  • Camera tracking
  • Collision detection
  • Damage/hitpoints
  • Rendering effects
Next time you write a game, give the entity/component system approach a try. It's just too much fun not to use.

Comments

  1. Many of the steps can be completed by the machines or robotics controlled by a single operator, reducing labor prices and overhead. The precision machining process can also be|may also be|can be} performed extra shortly and effectively, rising manufacturing output. The high quality indexes revealed on this work all provide a great indication of the injection molding high quality beneath the thought-about processing circumstances.

    ReplyDelete

Post a Comment

Popular posts from this blog

Lua-Style Coroutines in C++

Lua's implementation of coroutines is one of my all-time favorite features of the language. This (short) paper explains the whole reasoning behind the Lua's coroutine implementation and also a little about the history of coroutines. Sadly, coroutines are not supported out-of-the box by many modern languages, C++ included. Which brings me to the subject of this post: Lua-style coroutines in C++! For those who don't know (or were too lazy to read the paper!), Lua's coroutines support three basic operations: Create: Create a new coroutine object Resume: Run a coroutine until it yields or returns Yield: Suspend execution and return to the caller To implement these three operations, I'll use a great header file: ucontext.h. #include <vector> #include <ucontext.h> class Coroutine { public: typedef void (*Function)(void); Coroutine(Function function); void resume(); static void yield(); private: ucontext_t context_; std

Warp

So, it turns out that I didn't use Criterium for the video game competition at Stanford.  I actually met a partner and went with another concept instead -- Warp.  It's kind of like Starfox and it's inspired by Rez, one of the first PS2 games.  Explosions and missiles fire in time with the music; we used ChucK , an audio processing language, to achieve this. We also made some destructible objects using rigid bodies, and I added some particle explosion effects.  We used Lua to for enemy AI, and wrote a small TCL-like script parser that reads in data for the level layout.  The buildings in the background are procedurally generated.  We used OGRE for the graphics (this was a loose requirement of the project) and Bullet for the physics.  I had a lot of fun with this project, and I've posted a video capture below.

Sparse Voxel Octrees

Terrain implementation for games is a subject with a lot of depth. At the surface, it's very easy to get rudimentary terrain working via a noise function and fixed triangle grid. As you add more features, though, the complexity builds quickly. Overhangs, caves Multiple materials Destructive terrain Collision detection Persistence Dynamic loading Regions, biomes Very large scale Smooth features Sharp features Dynamic level of detail Extremely long view distances In isolation, each of these features can make terrain difficult to implement. Taken together, it takes a lot of care and attention to make it all work. Minecraft is probably the canonical example for terrain generation in the last generation of games. In fact, I'd say Minecraft's terrain is the killer feature that makes the game so great. Minecraft does an excellent job at 1-8, but for a recent planetary renderer project I was working on, I really wanted 9-12 too. In a series of articles, I'm planning to break do