6
\$\begingroup\$

I've designed my game entities using a common pattern of separating the entity from its components. In my case, I've used the Strategy pattern to give each of my entities an IDrawable component.

I construct my entities using a factory, which constructs the entity and adds an appropriate drawable, e.g.:

public static PlayerEntity CreatePlayerEntity(...)
{
   PlayerEntity playerEntity = new PlayerEntity(...);
   SpriteDrawable spriteDrawable = new SpriteDrawable(...);
   playerEntity.Drawable = spriteDrawable;

   ...

   return playerEntity;
}

Now here's the question: If I later want to update the player drawable in some way (e.g. modify the sprite, change the animation, etc.), how and where should I do this?

My first thought is to have C# events that the PlayerEntity publishes and the Drawable subscribes to, but this gets tricky from a purist standpoint, because it's hard to do this without the Drawable requiring a reference to a PlayerEntity. My current hacky solution is to subscribe to these events from within the CreatePlayerEntity() method itself, using anonymous delegates, which can take advantage of the fact that you've still got a reference to the concrete IDrawable implementation. It just doesn't seem right for the factory to have this responsibility, though:

public static PlayerEntity CreatePlayerEntity(...)
{
   PlayerEntity playerEntity = new PlayerEntity(...);
   SpriteDrawable spriteDrawable = new SpriteDrawable(...);
   playerEntity.Drawable = spriteDrawable;

  playerEntity.SomeEvent += new EventHandler((sender, e) => { spriteDrawable.Texture = blah; });

   ...

   return playerEntity;
}

What is a better, approved way to accomplish this communication between concrete implementations?

\$\endgroup\$
2
  • \$\begingroup\$ Do you have a better reason than doesn't feel right? That's not a very substantial reason to consider a re-design. \$\endgroup\$ Commented Oct 25, 2010 at 14:18
  • \$\begingroup\$ Well, the reason would be a lack of cohesion if the factory was not only responsible for creating objects, but defining event handlers for any random event that I might need to handle on an entity. \$\endgroup\$
    – vargonian
    Commented Oct 26, 2010 at 4:12

5 Answers 5

2
\$\begingroup\$

Instead of having a reference to the Drawable in the Entity, I'd have a reference to the Entity in the Drawable. I'd also make the Drawables observe the Entity so that when the Entity changes, the Drawable is notified and can pull any relevant changes.

eg. Your entity changes action, so it notifies all observers that something has changed. The Drawable calls entity.getAction() to find out what action is underway, and sets its own animation accordingly.

\$\endgroup\$
2
  • \$\begingroup\$ I also suggest your first point in my solution, so +1! \$\endgroup\$ Commented Oct 25, 2010 at 14:34
  • \$\begingroup\$ Thanks, I considered this possibility, and though some part of me felt like I shouldn't have either hold a reference to one another, I'd feel much better having a component hold a reference to its associated entity than the other way around. \$\endgroup\$
    – vargonian
    Commented Oct 26, 2010 at 4:25
1
\$\begingroup\$

Now here's the question: If I later want to update the player drawable in some way (e.g. modify the sprite, change the animation, etc.), how and where should I do this?

Well, what should be responsible for making those changes? One thing is for sure: whatever is responsible it must know about the specific type of Drawable it is operating on, you can't change the texture without knowing it has a texture. You did this in CreatePlayerEntity, because at that point the type of drawable is known. If you want to do it elsewhere then you may need to rethink your design.

If the PlayerEntity has state that controls entirely how it is drawn, pass the PlayerEntity state into the SpriteDrawables draw function. That does mean that there's a coupling between PlayerEntity state and SpriteDrawable. Note that the PlayerEntity state could be a separate class (e.g. PlayerEntityState). If don't want this coupling, or you want to control arbitrarily how it is drawn depending on other state in your game then have the object that aggregates PlayerEntity directly aggregate the Drawable so you can control it there.

\$\endgroup\$
0
\$\begingroup\$

A lot of people are still getting used to component systems, and there can be debate on the good or bad ways of doing this. But why not use a reference to PlayerEntity?

I would suggest giving all components a reference to their parent Entity. Your player entity does have a base class, right? And that class should also have a method for adding components, where it sets the component.Parent = this. The property setter for the component can handle all the subscriptions. If you need, you can have a method on the entity that fetches any desired component. Myself, I used a generic method and stored a dictionary from types to components:

public Component GetComponent<T>()
{
    if (components.ContainsKey(T)) return components[T];
    return null;
}

That exact code may have syntax / semantic errors, it's off the top of my head.

\$\endgroup\$
0
\$\begingroup\$

Some of the approaches are described here:

http://gameprogrammingpatterns.com/component.html

I suggest you have a look at the Mediator pattern and messaging. This is a nice way to avoid coupling.

Why do you need to change Drawable from the outside? Maybe you need another type of component(s)?

I would do something like:

DyingComponent -> {send message via Entity/Mediator} -> DrawableComponent {receive message and switch to death animation}.

\$\endgroup\$
0
\$\begingroup\$

I've been researching component-based designs pretty heavily myself, and while I'm still in some pretty heavy confusion, I think I can offer some thoughts on this.

According to Gerold Meisinger in a blog post, there are basically two ways to address the problem of inter-component communication: looking it up on the parent, and message/event passing. A third would be reference holding, which is what you were debating. My own attempts to implement such a system basically showed the same thing.

The basic foundation is that you have an Entity class whose only job is to hold parts and manage them. If you wanted to change the animation (say the player's state changed) then you'd have a StateComponent or maybe a PlayerState component, and it would need to communicate "My state has changed" to any other parts that need to know that.

Method 1 (Look-ups) would be that the DrawComponent knows it needs to check state before drawing. It calls "GetComponentWithType(Type t)" to get the StateComponent (or more likely an IState interface), checks the state, then draws. Ditto position, rotation, etc.

Method 2 (Events) would be that the StateComponent emits a message when it changes, and any subscribers to that event get alerted. Typically the factory would wire that up, adding a DrawComponent, then setting it to have message handlers to watch StateComponent for changes.

You could also use the parent Entity as a kind of message board, holding a log of posted messages that each component checks for interesting messages. This is a slight optimization since it makes both the sender and the receiver "lazy." Each sender only posts one message (to the host) and each receiver only asks for messages when it needs them to update, and then only gets the latest message, ignoring any irrelevant in-between states.

Method 3 (Reference) is an optimization of method 1. Instead of asking for a reference to the component, it just has one already. Either the factory gave it one, or it stored the reference after it asked for it the first time.

You'll note that each method has complexities and problems that would have to be worked around. If you stick with OOP though, I would say that's just part of what you have to pay for flexibility though. I was very confused about the whole thing until I realized that the Factory pattern is essential because the factory is the only thing that actually knows how to hook it all up in the first place.

I think I've mostly come into agreement with Gerold Meisinger though, and have been looking into F# as an alternative. Because I think a post he made on StackOverflow is very accurate: the more componentized I've tried to make the design, the smaller and more function-like each component becomes until I've just got a messy bag of states and chained functions trying to pass the states around.

But that is to say that there's a spectrum: at present you've only got 1 entity containing some state and one component. I'd probably opt to have the drawable component receive packets of "DrawableInfo" through events. But just keep an eye on the complexity or pretty soon you too will be wondering if switching to a functional language might not be better.

\$\endgroup\$

You must log in to answer this question.

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