3
\$\begingroup\$

I am trying to use a component based pattern on my new project. The last projects I did the old fashioned way. Base class and derive from that until your brain hurts. This time I wanted to do it on a new way with a component approach.

Component Base Summary: The idea is that you build components and compose your objects out of different components e.g. the Player class holds GraphicComponent, PhysicComponent, InputComponent, CameraComponent, ScriptComponent and so on, but an decoration object like a plant only has GraphicComponent and maybe PhysicComponent.

I like this approach. I tried to implement it but I have a little understanding issue.

As far as I understand it, each component has the logic for itself in it, and barely know of other components. But this is the part I don't understand: Let's say we have a RenderComponent which holds the logic for rendering. But each concrete object like a player, a plant an UI-element and so on has to be rendered on a completly different way. The player hast special effects and different animations, the plant is only a sprite and the ui element is a combination of both. Do I put the logic for every different rendering in the RenderComponent all at once e.g. RenderPlayer(), RenderPlant(), RenderUIElement() which does not make sense to me or do I make an Interface out of the RenderComponent and create a specific RenderComponent for each concrete objects like PlayerRenderComponent : IRenderComponenent ?

Update

After reading the Answer from Nick, I did a quick UML-likish diagram to clearify if I got the idea/structure right. The diagram is just an example with a pseudo UML-like diagram an some pseudo example constelations. The Idea is, that the concrete composition objects holds and treats their component as a data structure. They set it, alter it, and read from it to do it's logics. They also can compare Components of other concrete objects for convience and provice their components to higher systems (like managers)

pseudo uml like diagram

Update 2 After some discussion and a bit of confusion due opposites arguments, I started to google with new keywords I got from the comments and found an article which helped me understand how the system is glued together.

\$\endgroup\$
  • \$\begingroup\$ I'd go with the second option. Though, I wouldn't make it so specific. Maybe your render component needs to be a little more general. There's really no reason why a single render component shouldn't be able to render just about everything. \$\endgroup\$ – Ben Nov 10 '14 at 10:44
  • \$\begingroup\$ thank you for your comment. It is not only about rendering, I took it as an example. I wanted to decide where I put special logic, in the component, in the concrete composition object or in a separate system or a mix of all? Let's say I have two projectiles, one is an concrete Arrow one is a Bullet. They both have a MovementComponent but who decides how the Bullet and how the Arrow acts (in case of speed, range, one flies straight the other flies as a bow). If I put the logic in either Arrow/Bullet I would have almost duplicate calculations, if I put it in the Component I'd need both methods \$\endgroup\$ – Ello Nov 10 '14 at 12:15
2
\$\begingroup\$

While components may have logic in them, the sort of logic you're talking about (loosely termed batch logic) doesn't exist at the entity level -- instead it exists in the game loop.

This makes sense when you look at a theoretical (but typical) deferred 3D renderer. This renderer may have to run through all objects of similar type (a data member of your entity class/struct); say we're doing a rendering pass for all entities of type == TRANSPARENT. Here, we have certain graphics API functions that must be called just once before and/or after all TRANSPARENT elements are processed, to allow for correct blending. This is common in game engines... you may have heard the term batching. The particles are treated as a batch. They don't act, they are acted upon.

Why? Well, as you've realised, if you were to have such logic contained in each entity, you would not be able to make your API calls just once for the batch of transparent particles; instead those calls would be made per particle, which may be impossible due to performance or other limitations (typically the purpose of batching is to tackle similar processing in the same place (in the code, and linearly in memory in order to improve cache performance, and as batching is such a common pattern in games development, CBEs are typically designed to support this). This is because no entity can know what other entities are doing (to enable this would be architectural folly), so none know whether the necessary rendering calls have or haven't been called yet. So we move this logic "up-and-out".

To conclude: Cycle through all elements of similar "type" together -- this may require a pre-sort or you may already maintain these in different lists -- and process them using one master block of code that is not embedded in entities or components, but instead exists directly or indirectly (through helpers) in your game loop. This will allow you to treat all similar components in the same way, in phases relating to their different properties in terms of physics / rendering / AI etc.

P.S. It is a good idea to start thinking of your entities as data / state rather than functionality, IMO. See Mick West's Evolve Your Hierarchy for more details.

\$\endgroup\$
  • \$\begingroup\$ Thank you for the link, I already read that article two days ago (besides other articles). So, you mean that I should treat the Components as a Data-Object without any logic? So at this hirarchy: Concrete Object Player has X Components as pure data, the logic of how the data will be processed decides wether the Object it holds or a separate system like a manager? e.g. concrete object Player has the logic for setting the current animation or alter the AnimationComponent data but RenderManager has the logic for actually drawing the current animation and reads it from the concrete Player object? \$\endgroup\$ – Ello Nov 10 '14 at 12:08
  • \$\begingroup\$ @Ello Yes, You have it. There is nothing wrong with keeping functions / logic on the entity itself (as then you have the benefit of accessing local data using this, but this approach is only appropriate where you do not need batch processing / overarching context as in the example I described above. \$\endgroup\$ – Engineer Nov 10 '14 at 12:50
  • \$\begingroup\$ Thank you @Nick Wiggill, I edited my original question and added a diagram to verify I got it right. \$\endgroup\$ – Ello Nov 10 '14 at 12:57
  • \$\begingroup\$ @Ello Your UML has a couple of mistakes: your RenderManager should reference one more more lists of Entity. Likewise should not be inheriting Player from Entity -- this is the whole point of CBEs -- no inheritance! You want generic lists of Entitys that are processed selectively based on their properties. So compose a player given the Entity structure, don't inherit to create one. What composes a player different from an AI? It replaces an AI control module with a human control module that interfaces with input devices, instead. Other components are as an AI Entitys might be. \$\endgroup\$ – Engineer Nov 10 '14 at 13:40
  • \$\begingroup\$ Ok @Nick Wigill now I got it, I guess. I did the inheritance from Entity because I wanted to provide a unique accessing way to get the components. But I guess I will create a new Entity object instead name it Player and add Components on runtime to it. The logic I will do with scripts? \$\endgroup\$ – Ello Nov 10 '14 at 15:07

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.