Entity Component Manager

I was prototyping a game in Ruby using Chris Powell’s example of an entity/component setup. I’ve started writing up the game in Java, and after doing a rough translation of the system, I’ve noticed it is a little awkward in Java. Here is the Manager.java https://gist.github.com/kabbotta/a12ed6d08cd491aaec60 that I’ve got. It works, but I was hoping to get some ideas on how it might be done better in Java. A lot of Chris’s system revolves around the dynamic nature of Ruby and the fact that all collections are pretty much the same.

In particular, one of the things I’m missing from Ruby is that I was able to use a direct reference to a class instead of a String in methods like comp(entity, comp_class). In Ruby, the method looked like this:


  def comp(entity, component_class)
    store = @component_stores[component_class]
    
    return nil if store.nil?

    components = store[entity]
    return nil if components.nil? || components.empty?

    components.first
  end

And it could be used in this way:


manager.comp(player, Position)

Where Position is an actual class name and can be recovered by calling component.class. As opposed to how in Java I’m taking in a String and then pulling the class name out of what getClass() returns:


manager.comp(player, "Position");

Thanks for any suggestions you can offer!

You can do exactly the same in java. component_class would be of type Class and then you can pass in Position.class.

I’m really not sure what you’re asking. Maybe you’d be better off posting the Java code (as an MCVE) instead of the Ruby code?

If you’re just asking how to add Class instances to a collection, then here is an MCVE of my own:

import java.util.ArrayList;
import java.util.List;

public class Test {

	public static void main(String... args) {
		List<Class<?>> classes = new ArrayList<Class<?>>();
		classes.add(List.class);
		classes.add(ArrayList.class);
		classes.add(Test.class);
	}
}

You also should use generic methods. Following you manager -> comp method example:


public final <T extends IComponent> T comp(IEntity entity, Class<T> compType) {}

IComponent being whatever your base component interface type may be. I suppose you could just leave it T and be able to stuff any objects into it. I’ve found in my ES efforts that there is a benefit to having a base component interface with a few extra utility methods useful for a component architecture defined over just Object.

IEntity can be your base entity interface. You could also pass in any other ID (integers have been popular in various blog posts on ES). In my ES efforts I actually store the components internally to an entity or “component manager” and don’t have a separate entity object really.

The nice thing about the above is that it provides for a generic, but explicit return type based on the Class provided when calling the method.

Postion p = manager.comp(entity, Position.class);

Another nice thing is that you can have wildcard / generic collections backing the internal implementation of “manager” such that if you only have typed getters / setters like the above “comp” method then you can do generic type casts safely in the “comp” method

You can also use the compType as an key into any backing collection (HashMap, etc.)

ES / component architectures are definitely possible in Java.

Thanks guys! This was all really helpful, especially your suggestions, Catharsis. Everything seems to be a bit smoother now, and the generics helped get rid of a bunch of unnecessary casting. The only thing is that I now have an “unchecked cast” warning in the comp method that I have to suppress. Is there any way around that?

This is the comp method I’m using now along with the declaration of component_stores for reference:


private HashMap<Class<?>, HashMap<String, Component>> component_stores = new HashMap<Class<?>, HashMap<String, Component>>();

public final <T extends Component> T comp(String entity, Class<T> comp_type) {
    HashMap<String, Component> store = component_stores.get(comp_type);

    if (store != null) {
        @SuppressWarnings("unchecked")
        T comp = (T)store.get(entity);

        if (comp != null) return comp;
    }

    return null;
}

And this is the add_comp method I use to add a component to an entity:


public Component add_comp(String entity, Component comp) {
    HashMap<String, Component> store = component_stores.get(comp.getClass());

    if (store == null) {
        store = new HashMap<String, Component>();
        component_stores.put(comp.getClass(), store);
    }

    if (store.containsKey(entity)) {
        return null;
    } else {
        store.put(entity, comp);
        return comp;
    }
}

Thanks for the tip about producing a MCVE to get the best results as well!

Handling the unchecked cast how you are doing currently is fine as long as the “store” Map is encapsulated such that components of a different type can’t be added to it surreptitiously. An example of that could be a child class of the “manager” class having access to the collections. In the code you listed below you didn’t mark add_comp final for instance, so a child class could change the behavior if access to the collections were possible.

If add_comp and comp methods are the only way to access those internal implementation collections then you are guaranteeing type safety by the method definition / class type for external use of your API.

Thanks for all your help. I didn’t mark add_comp final, because I was under the impression final meant the method would not modify any of the class members. I think I was mixing it up with C++ const.

final does mean “const” when applied to variables, but is even stricter than C++.

final on a method or class prevents further modification by subclasses which is important for consistency of a component architecture.

I noticed you are probably new to Java with naming “add_comp” method as typical Java style is “addComp”…

Welcome! ;D