2
\$\begingroup\$

I am making a tile based roguelike game with different objects in the world, like Doors, Security Cameras, Storage Shelves. With that, there are objects that have an inventory, can detect the player, or need interaction to operate.

How can I add such functionalities to a base class without inheritance? Because there will be objects with a combination of these functionalities.

I'm considering the decorator pattern, where the base class has all the methods defined (in the most bare form) and the concrete decorators have the implementation of the functionality. Is that really the way to go, or is there another option?

\$\endgroup\$

1 Answer 1

2
\$\begingroup\$

Composition over inheritance.

The basic idea that instead of having one specific class for each kind of entity in your game that is part of a long inheritance chain with "Entity" as the abstract base class, the class "Entity" doesn't do much more than handle a list of abstract components. Those components hold the data the entity needs to interact with different mechanics in your game.

So every entity in the game is of class Entity, but differs from other entities by its specific combination of components.

This allows you to mix and match components to create new entities. For example:

  • Want a shelf to appear in the game? Create an entity with the "renderer" component that says what 3d model to use.
  • You don't want the player to walk right through it? Add a "collider" component that says that this shelf is a solid box with a certain width, height and depth.
  • Want it to also act as a container? Add the "container" component with a list of contents and a maximum capacity.
  • Want the shelf to also be a secret door that can open and close? Add the "door" component to it that remembers whether the door is currently open or closed.
  • Want the player to alternatively be able to smash it? Add the "destructible" component with an amount of hit points.

OK, so much for the data, but what about the code, you ask? Where does that go?

Well, there are two competing design philosophies here:

  1. Entity - Behavior. The components themselves implement the mechanics by implementing virtual methods like Initialize, Update, HandleEvent etc.. This is a bit closer to the object-oriented philosophies that any object should be a "smart" piece of data that governs its own behavior.
  2. Entity - Component - System. The components are just "dumb" data-holders that contain no or only minimal program logic. The actual code is in Systems. The systems each iterate over all entities that have certain combinations of components and implement the mechanics that use and manipulate that data. This approach seems a step back from the object-oriented design that was popular in the past two decades back to how games were designed in the age of C. But it recently became very popular again, because it makes it a lot easier to optimize for CPU cache usage and organize your logic in a way that makes it easy to parallelize on multiple CPU cores.
\$\endgroup\$
4
  • \$\begingroup\$ but then I need to do a lot of conditional checking. When the player interacts with an entity, it displays a UI with a title and description, but then it needs to check if that entity has a "container" component and render a list of its contents. This will always be a problem when other classes need to interact with the entity to check if it can perform a certain action or not. Right? \$\endgroup\$
    – RobinHood
    Feb 18 at 10:58
  • 1
    \$\begingroup\$ @RobinHood Well, you are going to program all that branching for all that conditional logic somewhere. Regardless of what architectural pattern you use to organize it. I could now muse about different ways to solve this particular problem in both E-B and E-C-S, but that would exceed the space available in comments. So if you would like me to elaborate, please open a new question. \$\endgroup\$
    – Philipp
    Feb 18 at 11:29
  • \$\begingroup\$ After more research i think you are right. I should use composite to added one or more functionality (container, door) to an entity. And use decorators to modify these functionality (simpleDoor, doorWithPassword) \$\endgroup\$
    – RobinHood
    Feb 18 at 11:57
  • 2
    \$\begingroup\$ In the E-B style, you could define an InteractionUI interface that multiple components implement. The Container component implements it by calling the draw container UI code, etc. Then all your player needs to do is look for a component matching this interface type and invoke its UI behaviour. Instead of manually writing all that branching code, it happens implicitly via virtual function calls. See this answer for an example of using this to simplify logic in an RTS. This OOP has some overhead, but for UI interactions once per frame, it's OK. \$\endgroup\$
    – DMGregory
    Feb 18 at 11:58

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .