Component Pattern
Game Programming Pattern.
Pattern explenation
Intent
Allow a single entity to span multiple domains without coupling the domains to each other
What it solve
The creation of big classes that are hard to mantain.
Applications
- You have a class that touches multiple domains which you want to keep decoupled from each other.
- A class is getting massive and hard to work with
- You want to be able to define a variety of objects that share different capabilities, but using inheritance doesn’t let you pick the parts you want to reuse precisely enough.
Actors
- Abstract Component
- Specifies an interface for performing a generic operation
- Concrete Component
- Takes a specific job to be done
- Handler
- uses the concrete component to delegate its jobs
Schema
Unity example
In this example we will see
Abstract Component
this class is used from the GameObjectAggregate to manage easily the list of it’s components
interface BaseComponent
{
void Update(RPGGame game);
}
Handler Class
Here is the creation and handling of the needed components
public class GameObjectAggregate : MonoBehaviour
{
public int velocity;
public int x = 0, y = 0;
public WorldX worldX = new WorldX();
public GraphicsX graphicsX = new GraphicsX();
private InputComponent inputComponent;
private PhysicsComponent physicsComponent;
private GraphicsComponent graphicsComponent;
public List<BaseComponent> ComponentList = new List<BaseComponent>();
int componentAmount = -1;
private void Start()
{
inputComponent = new PlayerInputComponent();
physicsComponent = new PlayerPhysicsComponent();
graphicsComponent = new PlayerGraphicsComponent();
ComponentList.Add(inputComponent);
ComponentList.Add(physicsComponent);
ComponentList.Add(graphicsComponent);
Debug.Log("Game Components Initialization Finish...");
Debug.Log("Please enter LeftArrow or RightArrow button to play...");
}
private void Update()
{
if (ComponentList == null)
{
return;
}
componentAmount = ComponentList.Count;
for (int i = 0; i < componentAmount; i++)
{
ComponentList[i].Update(this);
}
}
}
Concrete Component Classes
These classes do the specifics job
class PlayerInputComponent : BaseComponent
{
public void Update(RPGGame game)
{
if (Input.GetKeyDown(KeyCode.LeftArrow))
{
game.velocity -= WALK_ACCELERATION;
Debug.Log(" game velocity= " + game.velocity.ToString());
}
if (Input.GetKeyDown(KeyCode.RightArrow))
{
game.velocity += WALK_ACCELERATION;
Debug.Log(" game velocity= " + game.velocity.ToString());
}
}
private int WALK_ACCELERATION = 1;
}
class PlayerPhysicsComponent : BaseComponent
{
public void Update(RPGGame game)
{
game.x += game.velocity;
//Handle Physics...
}
}
class PlayerGraphicsComponent : BaseComponent
{
public void Update(RPGGame game)
{
//Handle Graphics...
if (game == null || game.graphicsX == null)
{
return;
}
Sprite sprite = spriteStand;
if (game.velocity < 0)
{
sprite = spriteWalkLeft;
}
else if (game.velocity > 0)
{
sprite = spriteWalkRight;
}
game.graphicsX.Draw(sprite, game.x, game.y);
}
private Sprite spriteStand;
private Sprite spriteWalkLeft;
private Sprite spriteWalkRight;
}
Conclusion
The Component pattern adds a good bit of complexity over simply making a class and putting code in it. Each conceptual “object” becomes a cluster of objects that must be instantiated, initialized, and correctly wired together. Communication between the different components becomes more challenging, and controlling how they occupy memory is more complex.
For a large codebase, this complexity may be worth it for the decoupling and code reuse it enables, but take care to ensure you aren’t over-engineering a “solution” to a non-existent problem before applying this pattern.