3
\$\begingroup\$

I was reading about component-based entity systems and I was wondering why there is usually a large EntityBuilder class with methods like CreatePlayer(), CreateEnemySpaceship(), and CreatePowerup(), all creating an entity, attaching some components, and returning the entity.

The class has the potential to get quite large given enough different entities and having to search this large code-file can eventually become quite cumbersome. Also, with this system, since all entities are of type Entity, to determine what something actually is, you would have to add some sort of TypeComponent to the entity which may contain a string like "Player", "Enemy", and "Powerup". Now you lose some benefits of strong typing and have to rely on personal memory more (bad in my opinion).

Another problem with this setup is the inability to use function overloads based on what type the entity is. Instead one would have to use hashtables based on some identifying component which is a bit ugly.

I was wondering, instead of having these large builder classes and cumbersome ways of determining what an entity really is, why not just have separate classes for each entity? Well, that is what we were doing but they resulted in large and complex hierarchies, right? Well, I'm not advocating large hierarchies. Rather, move every method from this monolithic builder class to a separate class deriving from Entity and place that code in the constructor. No multiple-inheritance. No large trees. Example: Both Ship, Spaceship, and EnemySpaceship would all derive from Entity and only Entity.

This could be useful for determining the types of entities when a collision occurs and how to proceed with handling the event. The classes themselves would basically be empty except for the constructor logic in how to construct the entity (attaching components and whatnot). This solution seems to be a lot more extendable as well, considering if one would ever need to modify the creation process of an EnemyEliteFighterSpaceship, rather than searching through some large builder class, just go to that class file.

Am I missing something here? I suppose, if all else, my real question would be of an elegant way to determine what an entity is when it collides with another entity so some action could be taken (increase score, play sound, etc.). I initially tried setting up some sort of TypeComponent but it just seems a bit hackish as opposed to simply deriving from the base Entity class. Also, deriving from a base entity class gives strong-typing and type-safety support, gets rid of the public static builder class, and allows for polymorphic behavior and ease of determining an entity's type.

So I'm wondering, with all these positives and no perceivable negatives (other than .. more files? organization trumps file count in my opinion), why do most component systems favor these large builder/factory classes rather than my proposed approach?

\$\endgroup\$
4
  • \$\begingroup\$ gamedev.stackexchange.com/faq#dontask avoid asking subjective questions where ... there is no actual problem to be solved: “I’m curious if other people feel like I do.” ... we are being asked an open-ended, hypothetical question: “What if ______ happened?” ... it is a rant disguised as a question: “______ sucks, am I right?” \$\endgroup\$
    – Tetrad
    Oct 29, 2011 at 6:57
  • 1
    \$\begingroup\$ All that being said, if you have a "real question", you should ask that question. \$\endgroup\$
    – Tetrad
    Oct 29, 2011 at 7:01
  • 2
    \$\begingroup\$ Or you could have neither. The combination of components that become some entity is data, not code. \$\endgroup\$
    – Blecki
    Oct 29, 2011 at 12:19
  • \$\begingroup\$ I'm confused - what are you actually asking here? \$\endgroup\$ Jan 24, 2013 at 23:24

3 Answers 3

1
\$\begingroup\$

You seem to have a couple of questions here so I will try and give a few answers.

IComponent
{
    int iObjectId = MyUniqueObjectThatImAPartOf;
}

Every component should know by some unique id which object/entity it is a part of. I am not an advocate of using an Entity class to bind the pieces together. I do however know that people do do it that way. I honestly thing OOP is just such a line of thought for developers that its hard to get rid of them. The best argument I ever heard was the first one I ever heard however. If you have a central object you always wonder what should be in a component and what should be in the central object. Just avoiding having to ask that question all the time and come up with an answer was good enough of a reason for me to give up the 'Entity' object and link all components together by an ID.

Remember to not think like a player in this instance. If you are a player you are looking on the screen and seeing an object that represents your player. On the display you can see how much health you have, how close to the lava you are, and that big giant rock about to smack you in the head. But to the game engine, to show your character it doesn't care what your health is, if that lava is close enough to cause you damage or strain or that the rock is now hitting you. For rendering a character all it needs is the mesh and texture data, positional information.. things specifically for rendering.

The reason not to have separate classes for each entity is that you get alot of over lap of code.. Everything is like to have collision and health and a graphical representation and an action for it to go off and do, etc. The ways to solve having to write that over and over again is to either have trees to inherit functionality from or component systems so you can reuse the sub sections to build a larger object. I personally go with component simply because of ease of maintenance and update. I have been in the situation too many times where complex and ever growing inheritance trees make things annoying to deal with. (Might also be why I can let go of the Entity so easily.)

The large builder class is like going back two steps, before there was inheritance at all so it all had to be in the giant FactoryThatMakesMeObjects class so I would have to say Really stay away from them. Having a PhysicsComponentFactory makes sense to me, something that deals with creating, maintain and updating all the physic components for the game.

I am a bit confused as to why, in C#, a simple GetType() is not giving you enough Run Time Type Information. If you were using C++ then I would suggest using a template that would create a static pointer to RTTI for the templated parameter.. It would give you a nice static pointer uniquely identifying it as that type, great for comparison and going up the tree to see if you are a child for some base type.. but I would think the very nicely done internal reflection capabilities of C# would solve that for you.

Taking RTTI into a component system and you should also be ideally keeping like components within their own groups. So you are not mixing the component that tracks what an object uses to render itself with the object that tracks its health, its gear, its physics.. any bound AI to the object, a motion object.. etc.. They may all be linked together by an ID but they have nothing else to do with each other so should not be in the same list.. there should never be a need for a large LinkedList_AllTheComponentsInMyGame.. But a LinkedList_PhysicsComponents, LinkedList_RenderableComponents and so on.

All else failing, in a component system here is a potential way that you could determine what an entity is when it collides with another entity so some action can be taken.

PhysicsComponentManager
{
    // I make the assumption that the manager that contains the physics components
    //  that check for collisions with each other (Bounding Volumes) also contains
    //  the components that handle collisions with other objects.
    CheckForCollisions()
    {
        // If Comp1 has collided with Comp2
        foreach(IBoundingComponent Comp1 in AllBoundingComponents)
        {
            foreach(IBoundingComponent Comp2 in AllBoundingComponents)
            {
                if (Comp1.CollidesWith(Comp2))
                {
                    // Let Comp1 know of the collision
                    NotifyObjectOfCollision(Comp1.ObjectId, Comp2.ObjectId);
                    // Let Comp2 know of the collision
                    NotifyObjectOfCollision(Comp2.ObjectId, Comp1.ObjectId);
                    // Probably mark Comp1 and Comp2 as collision resolved for this
                    //  frame so we dont do this twice or the like.. or just let the
                    //  physics system do its thing if you have one.
                }
            }
        }
    }

    NotifyObjectOfCollision(ObjectId, CollidedWithObjectId)
    {
        ICollideable Comp = FindCollideable(ObjectId);
        if (Comp == null)
            return;
        Comp.DoCollision(CollidedWithObjectId);
    }
}

Hope this helps, I know its rather long but a good detailed question deserves a good amount of detail in the answer. (And please take the code as suggestions, no way are they optimal!! Just meant to show the flow :))

\$\endgroup\$
3
\$\begingroup\$
  1. Read this answer on composition over inheritance.
  2. Use one single method to create different entities, this leads to more maintainable code since configuration-specifics belong in external data files, not in code. So avoid createPlayer(), createEnemyOne(), createAsteroid(), in favour of createEntity(int id, string type, PhysicsComponent physics, SpriteComponent sprite, ...) with parameters being either primitives or complex objects themselves, and collectively specifying a unique object such as an asteroid.
  3. Use the builder pattern specifically because you need a class dedicated to translating said configuration data into concrete entities, while maintaining separation of concerns between classes (e.g. your game class "Main" should not be constructing entities, but it should be delegating the task to another class).
  4. Read this answer on collision detection and entity destruction.
\$\endgroup\$
0
\$\begingroup\$

Rather, move every method from this monolithic builder class to a separate class deriving from Entity and place that code in the constructor. No multiple-inheritance. No large trees. Example: Both Ship, Spaceship, and EnemySpaceship would all derive from Entity and only Entity.

You're trying to accomplish two things with this design.

1) Determine the "type" of an entity.
2) Separate out some code that constructs entities out of some "monolithic builder class".

Comment on point #1:

I'm going to frame my context within how Unity handles component based design, as that's how I'm most familiar with it.

In their parlance, you have GameObject (which is analagous to Entity in your example, I would imagine) which, basically, just contains Components. You derive from Component (specifically a subtype of Component, but that's unimportant). Nothing derives from GameObject.

When a collision occurs (because you have game objects with some kind of physics components attached), the engine sends a message to every component on those game objects that such a collision occurred along with some appropriate variables.

If you need to determine what course of action you can do a couple of things.

1) Check the name of the game object and do some logic. This is probably a bad approach.

2) Check the tags on the game object you collided with. The component that handles that for the player could check to see if they collided with something that's a friend or a foe. This is an okay approach, but limiting.

3) Check to see if the game object collided with has a specific type of component attached, and then do appropriate things. So the player component could see if the thing it collided with has a component of type PowerUp, get that instance of that component, then do something with it. This is probably the most powerful approach.

Either way, from the way you're describing it, having things that derive from your Entity class in a component-based system is kind of missing the point.

On #2:

It seems like a method that constructs a "Enemy Space ship" can just be a helper function somewhere else. It probably doesn't even need to be inside a class, depending on how your engine is set up with globals. You can just as easily separate them out in multiple files without doing something like setting up yet another inheritance tree.

Also, it's worth noting that Unity data drives game object construction, generally. Implementing a system that lets you design an entity in an editor will reduce some of this monolithic code you have an issue with.

\$\endgroup\$

You must log in to answer this question.

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