In part one of this article, I describe the data required to create entities in the classic game Missile Command.
In this series, I take games and rebuild them using Unity’s Entity Component System (ECS).
Play it in WebGL here. For full source code of this project, checkout my github
Table of Contents
Overview
Missile Command is a game in which you must defend your base, composed of multiple buildings, by clicking to spawn defensive missiles to intercept and destroy enemy attackers. Each time a missile hits one of your buildings, it is reduced in health, destroying it if being reduced to 0. The game is over when all of your buildings have been destroyed.
Entities
In order for our systems to differentiate between our entities, we will create some tag components that we will attach to our entities. Tag components have no data in them and serve solely as a filtering mechanism when running entity queries in our systems.
public struct Building : IComponentData { }
public struct Missile : IComponentData { }
public struct Defense : IComponentData { }
public struct Player : IComponentData { }
The buildings can take a certain number of hits before being destroyed, so they have a Health component
[GenerateAuthoringComponent]
public struct HealthInt : IComponentData
{
public int curr;
public int max;
}
Missiles move in a constant direction and do damage to buildings when they collide, so they have speed, direction, and damage components.
[GenerateAuthoringComponent]
public struct Speed : IComponentData
{
public float value;
}
[GenerateAuthoringComponent]
public struct Direction : IComponentData
{
public float3 value;
}
[GenerateAuthoringComponent]
public struct Damage : IComponentData
{
public float value;
}
The defense is what players spawn in order to intercept and destroy the missiles. They appear for a period of time, then disappear, so we’ll need a component to track their lifetime.
[GenerateAuthoringComponent]
public struct Lifetime : IComponentData
{
// seconds before destruction
public float value;
}
There are two different sets of collisions that we have to handle.
- Missiles and buildings
- Missiles and defense
We’ll use a simple radius component to describe the bounding volume of these entities.
[GenerateAuthoringComponent]
public struct Radius : IComponentData
{
public float value;
}
Game Settings
This is the data structure that we will use to store game difficulty and spawn positions.
/// <summary>
/// Difficulty settings for the game
/// </summary>
[GenerateAuthoringComponent]
public class GameSettings : IComponentData
{
/// <summary>
/// how quickly the missiles spawn, in seconds
/// </summary>
public float spawnRate;
/// <summary>
/// the number of spawns when the spawn rate is ready
/// </summary>
public int spawns;
/// <summary>
/// minimum missile speed
/// </summary>
public float speedMin;
/// <summary>
/// maximum missile speed
/// </summary>
public float speedMax;
/// <summary>
/// minimum x pos to spawn
/// </summary>
public float posMin;
/// <summary>
/// maximum x pos to spawn
/// </summary>
public float posMax;
/// <summary>
/// starting missile y pos
/// </summary>
public float spawnYPos;
/// <summary>
/// destory the missile when its y pos drops below this value
/// </summary>
public float worldEndThreshold;
}
We’ll need a way to keep track of how often the player can spawn a defense entity to defend their base.
[GenerateAuthoringComponent]
public struct AttackSpeed : IComponentData
{
public float speed;
// keeps track of time passed
public float counter;
}
Each time the player destroys a missile with defense, they score a point. The Score structure will store that
[GenerateAuthoringComponent]
public struct Score : IComponentData
{
public int value;
}
Conclusion
This about does it for the data that we’ll need. In part two, you’ll learn about the systems that will operate on this data in order to bring our game to life.