This is part III of a series where I’d like to show with the help of a practical example how to start a small game with a component-based entity framework. Hope it gives you some ideas how to structure your own games.
This one covers the reloading of the weapon, a feature that I left out on part II. Furthermore I’d like to have a look at a helpful utility class called CompoundEntities.
Small addition for correct bullet spawning
In the last part we just spawned the bullet right at the position of the weapon. This is almost never the correct position where the bullet enters the world. So I added another component called AnchorComponent which defines an anchor for an entity as a relative offset.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
[InspectorType] public class AnchorComponent : EntityComponent { #region Constants /// <summary> /// Attribute: Relative anchor offset. /// </summary> public const string AttributeOffset = "AnchorComponent.Offset"; #endregion #region Properties /// <summary> /// Relative anchor offset. /// </summary> [InspectorVector(AttributeOffset, Description = "Relative anchor offset.")] public Vector2F Offset { get; set; } #endregion } |
This anchor is used to spawn the bullets at the correct place, we just add the offset when spawning the bullet in the WeaponSystem.Shoot method:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
private int Shoot(int entityId, WeaponEntity weaponEntity) { var transformComponent = this.EntityManager.GetComponent<TransformComponent>(entityId); // Compute initial position. var position = transformComponent.Position; if (weaponEntity.Anchor != null) { position += weaponEntity.Anchor.Offset; } // Same code as before ... } |
It is optional for the weapon entity, but this we can easily define:
1 2 3 4 5 6 7 8 9 10 11 12 |
private class WeaponEntity { #region Fields [CompoundComponent(IsOptional = true)] public AnchorComponent Anchor; [CompoundComponent] public WeaponComponent Weapon; #endregion } |
So if we add a AnchorComponent to the weapon and set up the offset, the bullets should spawn at the correct place relative to the weapon position.
Not running out of bullets anymore
A feature I left out last time was to reload the player weapon. Maybe some of you tried or at least thought about how it could be implemented. There are definitively multiple good ways to add this feature. I thought about some different ones myself:
- Adding a completely new feature called “Reloading”
- Adding just a ReloadableComponent that is used by the WeaponSystem to implement the feature
- Having everything in the WeaponComponent for now
The first idea sounded really nice at first. We would need a ReloadableComponent that gives an entity the ability to be reloaded. Furthermore the current load and the maximum load has to be stored for an entity. E.g. in a CapacityComponent and a LoadComponent . The bullet count would then be moved out of the WeaponComponent and moved into the LoadComponent .
Than I thought this might be a bit over-engineered at the moment. I could still have added the ReloadableComponent , but there would be no data in it (there could be e.g. a reload duration later).
So in the end I took the easiest way and put the maximum bullet count right into the WeaponComponent :
1 2 3 4 5 6 7 8 9 10 11 12 13 |
public class WeaponComponent : EntityComponent { /// ... /// <summary> /// Maximum number of bullets. /// </summary> [InspectorInt(AttributeMaxBulletCount, Default = DefaultMaxBulletCount, Description = "Maximum number of bullets.")] public int MaxBulletCount { get; set; } /// ... } |
And that’s completely okay! As tempting as it might seem to split everything up right in the beginning to give your designers new ways to combine components to new entities, you should always weight this advantage against the time you invest and the complexity you add.
During game development features might change quite radical and all your new components might be obsolete. Nonetheless, if the game goes in a direction where you might need already existing logic (e.g. power packs that can be recharged) or entities only need a part of a component (e.g. one-way weapons that can’t be reloaded), you should definitively refactor your existing code. The systems are really decoupled plus you know what you want to achieve, so it shouldn’t be very difficult.
Adding new logic without influencing existing code
The decoupled structure of a component-based architecture makes adding new features very less prone to error. For the reloading feature I touched a handful existing classes, but the code was mostly alongside the existing one than changing it, e.g. the WeaponSystem got the handler for the Reload action:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
private void OnReload(GameEvent e) { // Get entity which should be reloaded. var entityId = (int)e.EventData; // Check if entity has weapon. var weaponComponent = this.EntityManager.GetComponent<WeaponComponent>(entityId); if (weaponComponent == null) { // Entity has no weapon. return; } // Reload weapon if it isn't full of bullets already. if (weaponComponent.BulletCount >= weaponComponent.MaxBulletCount) { return; } weaponComponent.BulletCount = weaponComponent.MaxBulletCount; this.EventManager.QueueEvent(WeaponEvent.Reloaded, entityId); } |
Same with the InputBehaviour and the InputSystem which map the player input to the correct action:
1 2 3 4 5 |
// Reload weapon. if (Input.GetButtonDown("Reload")) { this.Entity.Game.EventManager.QueueEvent(InputAction.Reload, this.Entity.EntityId); } |
1 2 3 4 5 6 7 8 9 10 11 12 13 |
private void OnReload(GameEvent e) { var entityId = (int)e.EventData; var inputEntity = this.inputEntities.GetEntity(entityId); if (inputEntity == null) { return; } // Forward action to weapon feature. this.EventManager.QueueEvent(WeaponAction.Reload, entityId); } |
And we are done with the new feature! Press R or Right Shift to reload your weapon and shoot as much as you like. If you have any feedback or a completely different implementation, I’m very eager to hear about it in the comments or send me a mail!
Systems that only care about a specific combination of components
Let’s move backwards and have another look at the MovementSystem we added in the very first part of the series:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
public class MovementSystem : GameSystem { private CompoundEntities<MovementEntity> movementEntities; public override void Init(IAttributeTable configuration) { base.Init(configuration); this.movementEntities = new CompoundEntities<MovementEntity>(this.EntityManager); } public override void Update(float dt) { foreach (var movementEntity in this.movementEntities) { // Update position. movementEntity.Transform.Position += movementEntity.Movement.Velocity * dt; } } private class MovementEntity { [CompoundComponent] public MovementComponent Movement; [CompoundComponent] public TransformComponent Transform; } } |
The interesting part here is the field movementEntities which is an object of the generic type CompoundEntities . It takes a lot of work off us by providing an easy method to get only the entities a system is interested in. Each system has a specific set of components it expects the entities to have so its logic can be applied to them.
You can specify the components that are required (and optional) by passing a class to CompoundEntities which contains fields or members that are attributed with the CompoundComponent attribute. The class will monitor the entity manager to get informed about new entity that match and will even take care about the components of the objects being set correctly.
There are three common use cases how you deal with the entity combinations:
- Checking if an entity with a specific id is made of the combination. CompoundEntities has a method GetEntity(int entityId) therefore.
- You can iterate over all entities that are made of the required components.
- Last but not least there are two events that the class provides: EntityAdded and EntityRemoved .
We dealt with finding the appropriate entities for a system for a long time without this utility class by listening to the EntityInitialized and/or ComponentAdded/Removed events from the entity manager in each system. This created quite a lot of boilerplate code which is now capsuled in the CompoundEntities class.
Other frameworks do it, too
After implementing CompoundEntities , we stumbled upon very similar concepts in other frameworks. It would have been much more useful if we found it a bit earlier, of course 😉
For example the Ashley framework has a concept called Family which is a much nicer name in my opinion. So we might go and rename our utility class. They also have a utility class called ComponentMapper which cares about exactly one component type and provides a performant way to iterate over all components of a type.
The more your components get split up, the more common it is that a system doesn’t work on a single component alone, but on a combination of them. So it gets more and more useful to have a utility class to take over the work of finding the right entities.
Conclusion
Not much has been added to the game this time, but I hope the post was a good read anyway. The reloading feature showed the main advantages of a decoupled structure. And you might find the CompoundEntities class helpful when adding your own systems.
I’ll try to add some more features again next time. Particularly with regard to such important missing features like rotating and shooting things! At least I added an offset for the bullets, so they are not spawned in the middle of the player character anymore:

Have a look at the repository on bitbucket if you like to see how it was implemented.
In the meantime and like every time: If you’ve got any feedback or questions, just leave a comment or send me a mail!