-
Notifications
You must be signed in to change notification settings - Fork 1
Entity Component System
This going to be a very basic overview of the DumbLibrary implementation of an Entity Component System, and how to use it.
To begin, let's iterate through what an Entity Component System, or ECS for short, actually is: In short, ECS is an architecture used to distribute properties to entities. It has two main parts: A Component, and a System. A component is the property itself. For example: Metabolism or a certain AI. While, a system is what makes changes to the corresponding component. Not every component needs a system, but every system needs a component. In DumbLibrary, the systems are ran every tick to update the components.
If you're interested in reading more about ECS, click here for the wiki article.
Before we implement a component and system, you must have the entity you want to support components on extend the class ComposableCreatureEntity. This class extends the EntityCreature class and just makes things a little easier for you. If you really want, you can just implement the ComponentWriteAccess interface and add your own implementation.
Let's walk through a very basic implementation of a component. For this example, I'm going to use the idea of our metabolism component.
This is what a basic component looks like:
public class MetabolismComponent implements FinalizableComponent {
@Override
public NBTTagCompound serialize(NBTTagCompound compound) {
return null;
}
@Override
public void deserialize(NBTTagCompound compound) {
}
@Override
public void finalizeComponent(ComponentAccess entity) {
}
}
Note: that the component class doesn't have to implement FinalizableComponent and may instead implement EntityComponent. FinalizableComponent implements EntityComponent and adds a method that will be called on the compontents initialization.
Now let's add some functionality to this component. What are some of the things a metabolism has? Food, Water, and the rate they decrease. So let's add these types of things into our component.
public class MetabolismComponent implements FinalizableComponent {
public int food;
public int water;
public int foodRate;
public int waterRate;
@Override
public NBTTagCompound serialize(NBTTagCompound compound) {
compound.setInteger("food", this.food);
compound.setInteger("water", this.water);
return compound;
}
@Override
public void deserialize(NBTTagCompound compound) {
this.food = compound.getInteger("food");
this.water = compound.getInteger("water");
}
@Override
public void finalizeComponent(ComponentAccess entity) {
// Other stuff you wanted to be done before the component is made.
}
If you wanted to stop here, this could be your entire component. Remember that a component is really just an entity property. Most of the time they aren't going to be very large. However, we are going to add in some more functionality. DumbLibrary supports Component storage classes, which allows you to easily create components from Json values. Let's take a look at the storage class for the metabolism component.
public static class Storage implements EntityComponentStorage<MetabolismComponent> {
// Max Food and water that the entity can have.
private int maxFood;
private int maxWater;
// Rate that the food and water decrease every second
private int waterRate = 1;
private int foodRate = 1;
@Override
public MetabolismComponent construct() {
MetabolismComponent component = new MetabolismComponent();
component.food = this.maxFood;
component.water = this.maxWater;
component.waterRate = this.waterRate;
component.foodRate = this.foodRate;
return component;
}
@Override
public void readJson(JsonObject json) {
this.maxFood = json.get("max_food").getAsInt();
this.maxWater = json.get("max_water").getAsInt();
this.waterRate = json.get("water_rate").getAsInt();
this.foodRate = json.get("food_rate").getAsInt();
}
@Override
public void writeJson(JsonObject json) {
json.addProperty("max_food", this.maxFood);
json.addProperty("max_water", this.maxWater);
json.addProperty("water_rate", this.waterRate);
json.addProperty("food_rate", this.foodRate);
}
}
Remember that this is a nested class inside of the metabolism component.
Note: If there is anything about this that you don't understand or don't find intuitive, please ask us questions or snoop around the dumblibrary code to get a better understanding.
Registering a new component is fairly easy. Just register it through a normal event like so:
@Mod.EventBusSubscriber(modid = YourMod.MODID)
@GameRegistry.ObjectHolder(YourMod.MODID)
public class EntityComponentTypes {
public static final EntityComponentType<MetabolismComponent, MetabolismComponent.Storage> METABOLISM = InjectedUtils.injected();
@SubscribeEvent
public static void onRegisterComponents(RegisterComponentsEvent event) {
event.getRegistry().register(
SimpleComponentType.builder(MetabolismComponent.class, MetabolismComponent.Storage.class)
.withIdentifier(new ResourceLocation(YourMod.MODID, "metabolism"))
.withStorage(MetabolismComponent.Storage::new)
.withConstructor(MetabolismComponent::new)
.build()
);
}
}
TODO:
TODO:
Somthing missing or broken? Tell Wyn or Jack about it on discord @Wyn Price#0001
or @Jack | JTGhawk137 #4033