Wednesday, March 28, 2018

Why is it a bad idea to store methods in Entities and Components? (Along with some other Entity System questions.)


This is a followup to this question, which I answered, but this one tackles with a much more specific subject.



This answer helped me understand Entity Systems even better than the article.


I've read the (yes, the) article about Entity Systems, and it told me the following:


Entities are just an id and an array of components (the articles says that storing entities in components isn't a good way of doing things, but doesn't provide an alternative).
Components are pieces of data, that indicate what can be done with a certain entity.
Systems are the "methods", they perform manipulation of data on entities.


This seems really practical in many situations, but the part about components being just data classes is bothering me. For example, how could I implement my Vector2D class (Position) in an Entity System?


The Vector2D class holds data: x and y coordinates, but it also has methods, which are crucial to its usefulness and distinguish the class from just a two element array. Example methods are: add(), rotate(point, r, angle), substract(), normalize(), and all other standard, useful, and absolutely needed methods that positions (which are instances of the Vector2D class) should have.


If the component was just a data holder, it wouldn't be able to have these methods!


One solution that could probably pop up would be to implement them inside systems, but that seems very counter-intuitive. These methods are things that I want to perform now, have them be complete and ready to use. I don't want to wait for the MovementSystem to read some expensive set of messages that instruct it to perform a calculation on the position of an entity!


And, the article very clearly states that only systems should have any functionality, and the only explanation for that, which I could find, was "to avoid OOP". First of all, I don't understand why should I refrain from using methods in entities and components. The memory overhead is practically the same, and when coupled with systems these should be very easy to implement and combine in interesting ways. The systems, for example, could only provide basic logic to entities/components, which know the implementation themselves. If you ask me - this is basically taking the goodies from both ES and OOP, something that can't be done according to the author of the article, but to me seems like a good practice.



Think about it this way; there are many different types of drawable objects in a game. Plain old images, animations (update(), getCurrentFrame(), etc), combinations of these primitive types, and all of them could simply provide a draw() method to the render system, which then doesn't need to care about how the sprite of an entity is implemented, only about the interface (draw) and the position. And then, I would only need an animation system which would call animation-specific methods that have nothing to do with the rendering.


And just one other thing... Is there really an alternative to arrays when it comes to storing components? I see no other place for components to be stored other than arrays inside an Entity class...


Maybe, this is a better approach: store components as simple properties of entities. For example, a position component would be glued to entity.position.


The only other way would be to have some kind of a strange lookup table inside systems, that references different entities. But that seems very inefficient and more complicated to develop than simply storing components in the entity.



Answer



I think it's totally fine to have simple methods for accessing, updating or manipulating the data in components. I think the functionality that should stay out of components is logical functionality. Utility functions are just fine. Remember, the entity-component system is just a guideline, not strict rules you need to follow. Don't go out of your way to follow them. If you think it makes more sense to do it one way, then do it that way :)


EDIT


To clarify, you're goal is not to avoid OOP. That would be pretty difficult in most of the common languages used these days. You're trying to minimize inheritance, which is a large aspect of OOP, but not required. You want to get rid of the Object->MobileObject->Creature->Bipedal->Human type inheritance.


However, it's OK to have some inheritance! You're dealing with a language that's heavily influenced by inheritance, it's very difficult to not use any of it. For example, you can have a Component class or interface that all your other components extend or implement. Same deal with your System class. This makes things a lot easier. I strongly recommend you take a look at the Artemis framework. It's open source and it has some example projects. Open those things up and see how it works.


For Artemis, entities are stored in an array, simple. However, their components are stored in an array or arrays (separate from the entities). The top level array groups the lower level array by component type. So each component type has its own array. The lower level array is indexed by entity ID. (Now I'm not sure if I'd do it that way, but that's the way it's done here). Artemis reuses entity IDs, so the max entity ID doesn't get larger than your current number of entities, but you can still have sparse arrays if the component is not a frequently used component. Anyway, I won't pick that apart too much. This method for storing entities and their components seems to work. I think it would make a great first pass at implementing your own system.



The entities and components are stored in an separate manager.


The strategy you mention, making entities store their own components (entity.position), is kind of against the entity component theme, but it's totally acceptable if you feel that makes the most sense.


No comments:

Post a Comment

Simple past, Present perfect Past perfect

Can you tell me which form of the following sentences is the correct one please? Imagine two friends discussing the gym... I was in a good s...