In my last post I started a little sample project to show you how our component-based entity framework is used in practice. The game is a little top-down shooter, but so far only movement is possible, so it’s more of a top-down walker. Time to add some shooting!
Starting with the interfaces
One easy way to get started with a new feature is to think about the actions and events you want to implement with it. Our new feature would be to add weapons to the game, so we call it the “Weapon” feature. The new actions our player (and of course also all other entities in the game) would have, would be to fire a weapon and to reload it:
1 2 3 4 5 6 |
public enum WeaponAction { Fire, Reload, } |
On the output side of the WeaponSystem which would take care about the logic of the new feature, there would be two events:
1 2 3 4 5 6 |
public enum WeaponEvent { Fired, Reloaded, } |
Next we can go and implement the fire action in detail.
Firing a weapon
Weapons have some data which is stored in its own component, the WeaponComponent:
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 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 |
[InspectorComponent] public class WeaponComponent : EntityComponent { #region Constants /// <summary> /// Attribute: Number of bullets left. /// </summary> public const string AttributeBulletCount = "WeaponComponent.BulletCount"; /// <summary> /// Attribute: Maximum number of bullets. /// </summary> public const string AttributeMaxBulletCount = "WeaponComponent.MaxBulletCount"; /// <summary> /// Attribute: Projectiles this weapon fires. /// </summary> public const string AttributeProjectileBlueprintId = "WeaponComponent.ProjectileBlueprintId"; /// <summary> /// Attribute: Speed with which the bullets are fired. /// </summary> public const string AttributeSpeed = "WeaponComponent.Speed"; /// <summary> /// Attribute default: Number of bullets left. /// </summary> public const int DefaultBulletCount = 0; /// <summary> /// Attribute default: Maximum number of bullets. /// </summary> public const int DefaultMaxBulletCount = 10; /// <summary> /// Attribute default: Projectiles this weapon fires. /// </summary> public const string DefaultProjectileBlueprintId = null; /// <summary> /// Attribute default: Speed with which the bullets are fired. /// </summary> public const float DefaultSpeed = 0; #endregion #region Properties /// <summary> /// Number of bullets left. /// </summary> [InspectorInt(AttributeBulletCount, Default = DefaultBulletCount, Description = "Number of bullets left.")] public int BulletCount { get; set; } /// <summary> /// Maximum number of bullets. /// </summary> [InspectorInt(AttributeMaxBulletCount, Default = DefaultMaxBulletCount, Description = "Maximum number of bullets.")] public int MaxBulletCount { get; set; } /// <summary> /// Projectiles this weapon fires. /// </summary> [InspectorString(AttributeProjectileBlueprintId, Default = DefaultProjectileBlueprintId, Description = "Projectiles this weapon fires.")] public string ProjectileBlueprintId { get; set; } /// <summary> /// Speed with which the bullets are fired. /// </summary> [InspectorFloat(AttributeSpeed, Default = DefaultSpeed, Description = "Speed with which the bullets are fired.") ] public float Speed { get; set; } #endregion } |
Let’s continue to approach the feature from the action flow. After the Fire action was send to the game, it is catched by the WeaponSystem. The system needs to know which entity fired, so its Id is passed with the event.
We have to check then if the entity has a weapon anyway to determine, e.g. which projectiles are fired and if any bullets are left. Those information are gathered by getting the WeaponComponent of the entity and checking its attributes.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
private void OnFire(GameEvent e) { // Get entity which fired. 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; } // Check if bullets left. if (weaponComponent.BulletCount <= 0) { // No bullets left. return; } // Shoot. // ... } |
Now we can be sure that the entity has the ability to fire a weapon. Shooting it just means to create a bullet, so let’s do it.
Creating and initializing a bullet
The initial values of the bullet are dependent of the position of the entity which fired it and the speed with which the weapon fires its bullets. All other information are included in the blueprint of the projectile itself.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
private int Shoot(int entityId, WeaponComponent weaponComponent) { var transformComponent = this.EntityManager.GetComponent<TransformComponent>(entityId); // Compute initial velocity. var velocity = Vector2F.UnitX * weaponComponent.Speed; // Gather initial attributes of projectile. var configuration = new AttributeTable { { TransformComponent.AttributePosition, transformComponent.Position }, { MovementComponent.AttributeVelocity, velocity } }; // Create projectile. var projectileBlueprint = this.BlueprintManager.GetBlueprint(weaponComponent.ProjectileBlueprintId); var projectileEntityId = this.EntityManager.CreateEntity(projectileBlueprint, configuration); return projectileEntityId; } |
After creating the projectile our system just decreases the bullet count and queues an event, that a weapon was fired and adds the projectile entity id as the event data:
1 2 3 4 5 6 7 8 |
// Shoot. var projectileEntityId = this.Shoot(entityId, weaponComponent); // Decrease bullets. --weaponComponent.BulletCount; // Fire event. this.EventManager.QueueEvent(WeaponEvent.Fired, projectileEntityId); |
Call to action
To make sure our player is able to fire his weapon on a button or key press, our input system has to be adjusted. We add a new InputAction.Fire event and just forward it to the weapon feature:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
private void OnFire(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.Fire, entityId); } |
On the Unity side we have to adjust the Update method of our InputBehaviour to map a button to the input action:
1 2 3 4 5 |
// Fire weapon. if (Input.GetButtonDown("Fire")) { this.Entity.Game.EventManager.QueueEvent(InputAction.Fire, this.Entity.EntityId); } |
Now each time the user presses the button/key which is mapped to the command “Fire”, the WeaponSystem will get notified about the desired action.
Giving the player entity a weapon
Nothing happens right now as we haven’t given our character a weapon yet. We can do so by giving the player entity a WeaponComponent and defining the kind of projectiles which are fired.
To define the blueprint of the projectiles that are fired, we just have to add a blueprint with a unique id to the BlueprintManager where we define the components the entities of this blueprint will have:
1 2 3 4 5 6 7 8 9 |
private void CreateProjectileBlueprint() { // Create and add projectile blueprint. var projectileBlueprint = new Blueprint() { ComponentTypes = new List<Type>() { typeof(TransformComponent), typeof(MovementComponent) } }; this.BlueprintManager.AddBlueprint(ProjectileBlueprintId, projectileBlueprint); } |
Now we can adjust the player character we create in our LevelSystem by giving him a WeaponComponent, 10 initial bullets and defining which projectiles are fired:
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 |
private void CreatePlayerCharacter() { // Create player blueprint and instantiate the player character. var playerBlueprint = new Blueprint { ComponentTypes = new List<Type>() { typeof(TransformComponent), typeof(MovementComponent), typeof(InputComponent), typeof(WeaponComponent) } }; var playerConfiguration = new AttributeTable { { WeaponComponent.AttributeProjectileBlueprintId, ProjectileBlueprintId }, { WeaponComponent.AttributeBulletCount, 10 }, { WeaponComponent.AttributeSpeed, 1.0f } }; // Create player entity. this.EntityManager.CreateEntity(playerBlueprint, playerConfiguration); } |
Note: In a real game you wouldn’t create the blueprints in the code but via a XML configuration file. This would be a topic for a separate blog post, though, so let’s keep it quick and dirty for the moment.
If you try again to fire the weapon now, you should be able to spawn 10 entities which move to the right.

There are two problems though: The spawned entities are too slow for projectiles and they look like the player character. While the first problem is easily fixed by increasing the WeaponComponent.AttributeSpeed value when adding the WeaponComponent to the player character, the second problem is a bit more complicated.
Let there be bullets
To have different visualizations for the entities we instantiate, we have to split the entity prefab from the visualizations. Right know we just used one that also contained the visualization, so all our entities looked the same.
First we add a component that holds the path to the prefab which should be used for the visualization:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
public class VisualizationComponent : EntityComponent { /// <summary> /// Attribute: Prefab to use for visualization /// </summary> public const string AttributePrefab = "VisualizationComponent.Prefab"; /// <summary> /// Attribute default: Prefab to use for visualization /// </summary> public const string DefaultPrefab = null; /// <summary> /// Prefab to use for visualization /// </summary> [InspectorString(AttributePrefab, Default = DefaultPrefab, Description = "Prefab to use for visualization")] public string Prefab { get; set; } } |
Now we can remove the visualization from our general prefab and add a VisualizationBehaviour instead that creates the visualization from the specified prefab as soon as the entity is instantiated:
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 |
public class VisualizationBehaviour : EntityComponentBehaviour<VisualizationComponent> { #region Methods protected override void Start() { base.Start(); this.CreateVisualization(); } private void CreateVisualization() { if (this.Component == null || string.IsNullOrEmpty(this.Component.Prefab)) { return; } // Create prefab. var visualization = this.gameObject.AddChild(Resources.Load<GameObject>(this.Component.Prefab)); visualization.name = "Visualization"; } #endregion } |
Now all there is to do is creating prefabs that contains the visualizations of the player character and the projectile and tell the blueprints where to find them:
1 2 3 4 5 6 7 8 9 10 11 |
var projectileBlueprint = new Blueprint() { ComponentTypes = new List<Type>() { typeof(TransformComponent), typeof(MovementComponent), typeof(VisualizationComponent) }, AttributeTable = new AttributeTable() { { VisualizationComponent.AttributePrefab, "Projectile" } } }; |
If you start the game now, the bullets look a lot more like bullets.

Reloading your weapon
As you notice you can only fire 10 bullets right now. Our action for reloading already exists, but is neither handled by the WeaponSystem yet, nor fired on a button/key press.
For now, I leave it as an exercise for you to implement the weapon reloading. If you understood how the events in the framework move, it should be straight forward. In the next blog post you’ll find one possible solution.
You can find the source code of the whole project on Bitbucket.
Conclusion
Hopefully adding weapons to our game let you realize the nice, uncoupled way the features of a component-based game behave. We just had to adjust some existing core features (Input, Visualization) to make the new feature work with them. Other features like the movement wasn’t touched at all, but instead were reused for brand new entities (bullets) that didn’t exist before.
For the bullets we didn’t need a single new component for now, we just had to setup the entities the way we needed them. The more components we get, the more possible combinations we get. And if they are designed in a good way, we can reuse them for a lot of different types of entities.
In the next post I’ll implement some nice additions like the reloading and a UI for the game, before moving on to new features like rotating our player character and really hitting stuff with our bullets.
If you’ve got any feedback or questions, just leave a comment or send me a mail!
Pingback: CBES – Creating a component-based game (Part III: Reloading) – Coding with Style in Unity3D()