Introduction
In this tutorial I’m going to explore component-based game entities, look at why you might want to use them, and suggest a pragmatic approach to dip your toe in the water.As it’s a story about code organisation and architecture, I’ll start by dropping in the usual “get out of jail” disclaimer: this is just one way of doing things, it’s not “the one way” or maybe even the best way, but it might work for you. Personally, I like to find out about as many approaches as possible and then work out what suits me.
Final Result Preview
Throughout this two-part tutorial, we’ll create this Asteroids game. (The full source code is available on GitHub.) In this first part, we’ll focus on the core concepts and general game engine.What Problem Are We Solving?
In a game like Asteroids, we might have a few basic types of on-screen “thing”: bullets, asteroids, player ship and enemy ship. We might want to represent these basic types as four separate classes, each containing all the code we need to draw, animate, move and control that object.While this will work, it might be better to follow the Don’t Repeat Yourself (DRY) principle and try to reuse some of the code between each class — after all, the code for moving and drawing a bullet is going to be very similar to, if not exactly the same as, the code to move and draw an asteroid or a ship.
So we can refactor our rendering and movement functions into a base class that everything extends from. But
Ship
and EnemyShip
also need to be able to shoot. At this point we could add the shoot
function to the base class, creating a “Giant Blob” class that can do basically everything, and just make sure asteroids and bullets never call their shoot
function. This base class would soon get very large, swelling in size each time entities need to be able to do new things. This isn’t necessarily wrong, but I find smaller, more specialised classes to be easier to maintain.Alternatively, we can go down the root of deep inheritance and have something like
EnemyShip extends Ship extends ShootingEntity extends Entity
. Again this approach isn’t wrong, and will also work quite well, but as you add more types of Entities, you will find yourself constantly having to readjust the inheritance hierarchy to handle all the possible scenarios, and you can box yourself into a corner where a new type of Entity needs to have the functionality of two different base classes, requiring multiple inheritance (which most programming languages don’t offer).I have used the deep hierarchy approach many times myself, but I actually prefer the Giant Blob approach, as at least then all entities have a common interface and new entities can be added more easily (so what if all your trees have A* pathfinding?!)
There is, however, a third way…
Composition Over Inheritance
If we think of the Asteroids problem in terms of things that objects might need to do, we might get a list like this: [...]Read more: Avoiding the Blob Antipattern: A Pragmatic Approach to Entity Composition
No comments:
Post a Comment