Entity-Component game programming using JRuby and libGDX – part 2

Introduction

In Part 1 we explored EC nomenclature to set the stage for this part: learning how to work with entities, components and systems using an Entity Manager. (And if you are finding this post via Google or another entry, you can find the index to the full series in Part 1.)

As you’ll recall, your game-world “things” (Entities) are not traditional OO classes. EC Entities are skinny beings that exist merely to have a unique ID. Only after the Components are “attached” to an entity does it attain any personality.

Entities

Are you ready for your first entity? OK, here goes:

2acca0fb–2725–4c3d–9347-b202b012ef75

Don’t look at me with that dubious expression. You’re wondering, where’s the entity? That is it right there: a unique ID (in the form of a UUID in this case, but it could even just be an integer if you wanted). That’s a Tank, or Blue Player Base, or an Alien.

I can imagine you thinking, “All right wise guy, where’s the Entity class? Surely there’s an entity class to hold the ID attribute, right? After all, how can you expect to instantiate instances of the very game objects without an entity class?”

I remind you again, this is not OO, this is EC. In EC there is no need — no need — for an Entity class. Entities exist purely as IDs within the EntityManager.

The Entity Manager

The EntityManager is a class that governs the EC system. It is the fundamental kernel of EC. Its responsibilities include:

  • Create and kill entities
  • Maintain a list of all known entities
  • Map entities to their components
  • Retrieve entities’ component functionality on demand

In other words, the EntityManager is a bookkeeping device that serves as the master authority of all entities and their functionality.

[Note: yes, the EntityManager itself is a class, as are Components and Systems. I did promise that EC was a departure from OO, and no, using OO for the foundation framework doesn’t make me a liar.]

Here is a very basic start of our EntityManager:

class EntityManager
  def create_basic_entity
    uuid = java.util.UUID.randomUUID().to_s
    return uuid
  end
end

You do actually instantiate an EntityManager; in fact, you are not limited in how many you instantiate, but you must have one. We only need one for our basic game but the flexibility is there if your game’s complexity demands it.

@entity_manager = EntityManager.new
element=@entity_manager.create_basic_entity

Now, let’s agree that for convenience sometimes it’s nice to be able to grab a chunk of entities that are similar. It would be nice if we had a way to “tag” entities in some way, yes? We might want all the entities of a particular genre, or on a certain side. Let’s add this functionality. The tag is simply a free-form text you can use for entity grouping. That’ll come in handy.

class EntityManager
  def initialize(game)
    @id = java.util.UUID.randomUUID().to_s
    @ids_to_tags = Hash.new
    @tags_to_ids = Hash.new
  end
  def create_basic_entity
    uuid = java.util.UUID.randomUUID().to_s
    return uuid
  end
  def create_tagged_entity(human_readable_tag)
    raise ArgumentError, "Must specify tag" if human_readable_tag.nil?
    uuid=create_basic_entity
    @ids_to_tags[uuid]=human_readable_tag
    if @tags_to_ids.has_key? human_readable_tag
      @tags_to_ids[human_readable_tag]<<uuid
    else
      @tags_to_ids[human_readable_tag]=[uuid]
    end
    return uuid
  end
end

You’ll notice that so far we are creating entities but the EntityManager isn’t managing or maintaining them at all. For this we need to establish a data structure to hold them. It’ll be a hash-of-hashes called @component_stores.

Component Storage

The Component Stores data structure is a hash-of-hashes. Each inner hash is called a component store. In aggregate, component stores.

This is one of those situations where a picture is worth a thousand words:

ECS presentation.011

The outer hash — the hash with the purple braces — is keyed by component class. In this contrived example there are two Components that an entity could have: Gun, and GravitySensitive. As you can see, this outer hash could allow us to discover which entities have a particular component by querying on that component’s class name.

Each component store itself — the hash with the gray braces, circled in the shaded oval — maps the entities to their associated components. Remember, each entity is an ID, so it would be tautological to say “entity IDs” here, and certainly incorrect to refer to them as “entity instances”. (Recall that entities are not classes and therefore are not instantiable. While you are learning EC you can be forgiven for thinking of them as “entity instances” if it helps make the transition, but try to discard that habit as soon as you can. Repeat to yourself the mantra: entities are IDs and nothing else.)

On the other hand, components (which are simple unintelligent bags of data, you’ll recall) are instantiable.

Consulting the picture you can easily see that Entity 12456 has two Gun components (two gun instances) whereas Entity 23456 has but one. The actual gun data itself — shots per second, damage per hit, accuracy, name, type — all live inside the Gun component instances. The Component Stores structure merely links components to their entities.

Expanding the Entity Manager

Let’s expand our EntityManager to utilize a component_stores data structure.

class EntityManager
  def initialize(game)
    @id          = java.util.UUID.randomUUID().to_s
    @game        = game
    @ids_to_tags = Hash.new
    @tags_to_ids = Hash.new
    @component_stores = Hash.new
  end

  # [ Snip... ]

  def add_component(entity_uuid, component)
    raise ArgumentError, "UUID and component must be specified" if entity_uuid.nil? || component.nil?

    # Get the store for this component class.
    # If it doesn't exist, make it.
    store = @component_stores[component.class]
    if store.nil?
      store = Hash.new
      @component_stores[component.class]=store
    end

    if store.has_key? entity_uuid
      store[entity_uuid] << component unless store[entity_uuid].include? component
    else
      store[entity_uuid] = [component]
    end
  end

  def has_component_of_type(entity_uuid, component_class)
    raise ArgumentError, "UUID and component class must be specified" if entity_uuid.nil? || component_class.nil?

    store = @component_stores[component_class]
    if store.nil?
      return false # NOBODY has this component type
    else
      return store.has_key?(entity_uuid) && store[entity_uuid].size > 0
    end
  end

  def has_component(entity_uuid, component)
    raise ArgumentError, "UUID and component must be specified" if entity_uuid.nil? || component.nil?

    store = @component_stores[component.class]
    if store.nil?
      return false # NOBODY has this component type
    else
      return store.has_key?(entity_uuid) && store[entity_uuid].include?(component)
    end
  end

We have expanded our EntityManager functionality. At creation time we instantiate a blank component_stores instance variable. We can now link a component with an entity. Furthermore, we can now query whether a particular entity has a specific component or a component of a certain type (e.g. Gun).

It is worth noting that this data structure is extremely fast to work with. The lookups are speedy and inexpensive. The entity keys are small, being mere ID values, and the components are agreeably small too since they are simple data.

Concluding this post, consider this parting thought: the EntityManager data structure represents the entirety of your game state. Let that sink in for a moment, and marry that with your knowledge of Ruby’s built-in data serialization. Saving and loading game state just became trivial, a topic that I will address in a later post of this series…

Coming Up

In the next installment we’ll learn about components…

8 thoughts on “Entity-Component game programming using JRuby and libGDX – part 2

  1. Hi, I’ve read up to part 4 of this article series and am now writing a basic game with E-C, but I’ve come over a small bump :

    What is the “game” variable to you pass the EntityManager constructor?

    • You’re absolutely right – that is clearly unnecessary and is just leftover cruft from an earlier iteration. I just pushed the cleanup to Github.

  2. I’m curious. In this part, it seems you allow multiple components of the same class per entity. In part 4, it appears you’ve removed that functionality as your ‘get_component_of_type’ appears to return a single component. I wondering about your reasoning behind the change. (or am I missing something?)

    • I’m glad you pointed this out; it was an accidental omission. I returned get_components_of_type() to the EM (see Github) because that is indeed a useful routine.

  3. why do you use a hash of component keys and uuid values
    and a hash of tags to uuid
    and a hash of uuid to tag
    instead of using one hash of key uuid and value components and create a “tag” component

  4. Very nice article. My recent reading around entity-systems has solidified some concepts I’ve been mulling over for a while now. It’s the sort of perspective change that gets me out of bed early in the morning and coding while the rest of the house slumbers on!

    One point on the example code: not a (J)Ruby programmer, but I think

    @tags_to_ids = Hash.new

    should be

    @tags_to_ids = Hash.new { [] }

    as it stores an array of uuid’s as its value, not a single uuid

  5. Further to my comment “February 14, 2014 at 1:44 am”…

    The more thinking and reading I do on ES, the more it seems that assigning to an entity multiple components of the same type is a world of pain… See for a long discussion on this.

    Maybe it is just my inexperience with the *design* of entity systems, but the question of ownership is unresolved in my mind!

    The problems are obvious. Taking the engine example, if I want to add a fuel source per engine I could do away with the FuelComponent and instead make an EngineComponent have a “Fuel” member. Then it is clear which engine will stop working when its fuel runs out. However, because fuel is no longer a component, I now cannot have another system that operates over fuel.

    If I reinstate the FuelComponent and attempt to add two EngineComponent and two FuelComponent to the player… which FuelComponent relates to which EngineComponent? Which engine stops with one of the FuelComponent runs dry?

    Some discussions begin to suggest components with fields referencing (i.e. storing the ID of) other entities. In my example I would create two Fuel entities and add a “Quanity” component to each, and the EngineComponent would have an Entity field holding the ID of its particular Fuel entity… I don’t even want to think about the overheads this would bring (with entity deletion, nevermind entity caching & reuse).

    This problem of ownership is making my comprehension of ES so hard.

Leave a reply to Rafaël Cancel reply