Embracing Component based design. What are best practices?

I’m finding it really hard to embrace component-based design. I can see the benefits, but some things feel foreign or wrong to me coming from an oldschool OOD background.

The thing that bugs me the most is communication between the various components internal to different GameObjects. In this case, I’ll lay out some diagrams of the different ways GameObject#1 can interact with GameObject#2:

Note: The Components internal to each GameObject are unaware of Components in the other GameObject. The task is to get each Component to interact with the corresponding Component in the other GameObject.

Direct Approach
2224234--148109--upload_2015-7-27_14-27-34.png

Opposite of Indirect Approach. Controller#1 tells each Component to interact with Object#2 Components. Controller#1 interacts directly with Controller#2.

Hybrid Approach


Controller#1 calls some function to handle an event on Controller#2. Controller#2 calls functions on Object#2 to interact directly with Object#1 components.

Indirect Approach
2224234--148108--upload_2015-7-27_14-14-13.png

The most elegant communication pattern. I’m not even sure how you would program this communication pattern as everything would have to be routed through the Object Controllers.

Just as a note, the Component Pattern is OOD. By oldschool I’m assuming that you are talking about pure inheritance strategy.

In my projects, I use a mix of both strategies, driven by a simple design question on entities relationship: is a or has a.

On communication between objects, I tend to prefer anonymous dispatcher / observer pattern, centered on events instead of entities.

Along many projects, this is the combination of strategies with best results for me.

yes, I am fairly familiar with pure inheritance strategy, and it can become a nightmare lol.

I’m planning on an approach where GameObject#1 tries to do everything to GameObject#2 and controllers either succeed or fail independently. In this case, I can group Controllers using interfaces, such as IConsumeItem and iterate them through from some other Controller.

Could you elaborate on your Event approach? I have heard this thrown around a bit and I am curious to how it works.

Sure. The simplest approach would be to have a basic (non-monobehaviour) Message class. It is then inherited by specialized messages (like entities taking damage, some construction finished and so on), for example DamageMessage:Message.

Then, build a centralized (static) message dispatcher visible through the entire project. It is responsible for both dispatching messages on request and for registering watchers to specific messages classes. Leverage from C# event Action<> to have easily attachable message handlers with the += operator (do not forget to use -= on destruction as it is a common source of memory waste on GC environments).

This way, arbitrary objects can listen to the said damage messages for wherever it came, totally decoupled.

1 Like

I use essentially this. I don’t represent it like an event, I represent it like a pub/sub system (events I generally think of as hooking to the event’s originator, pub/sub is totally disconnected and centralized). But any which way, this kind of thing is super useful.

As a side note - I would also be cautious about using a static instance for this, since you need to take a care in managing object lifespan and it’s subscriptions and potentially garbage collection.

1 Like

So let me try to understand this…You could have Player Pick and Item up and have the Item subscribe to Player waiting for a ConsumeItem message?

Player then ConsumesItem.
HungerController may try to process Food Component on item. If successful, it sends Message “ConsumeItem”. When received by the Item, the Item knows to handle item removal?

Could you provide a simpler example… on something that u need in your project? I will try to answer with a simple code implementing it using dispatch/obs pattern as I use.

ItemGameObject
Item
Food (contains calories)
Drink (contains hydration)

Player
CalorieController (can consume Items with Food component to increase calories)
HydrationController (can consume Items with Drink component to increase hydration)
NeedsController(references CalorieController and HydrationController)

When NeedsController is told to consume some item,

  1. CalorieController tries to Consume Food component
  2. HydrationController tries to Consume Drink component
  3. NeedsController tells the Item that it was consumed if 1 or 2 is successful.

I know it isn’t “simple”. I chose this use-case to build because it will teach me most of what I need to know about inter-object communications. I have many ideas on how I can process the above using GetComponent(). I wonder if this entire process could be simplified by raising some “ConsumeItem” event and listening for it on the Item.

Nice. I will post a basic example code later, when I reach home.

I actually simplified this from Food/Drink components into a single “Consumable” component. Each component will just pull the appropriate value out of the Consumable component. Much simpler

1 Like

If you can get away with less engineering, always pick that! For reference, here’s basically what my pub/sub system looks like and how you might use it.

// this interface is entirely optional, I like it because it enforces certain conventions
// but it really doesn't do anything
public interface ISubscriber<T> : ISubscriber{
void HandleMessage( T msg );
}

// this is what message pump actually uses, it keeps lists of ISubscribers and notifies them using the type passed into Publish<T>
public interface ISubscriber{
  void HandleMessage<T>( T msg );
}

// obviously you can get rid of this, but I like it just to be explicit.
public interface IMessage{}

public class MessagePump{
  // This class is just a simple wrapper on this dictionary.
  private Dictionary<T, List<ISubscriber>> m_IDoAllTheWork;

  public void Publish<T>( T msg ){ ... }
  public void Subscribe<T>( ISubscriber subscriber ){  ... }
}

That’s pretty much the entire thing.

You could dump a ‘messagepump’ into a static object if you want, although I’d suggest against it. The code for this class can be stupidly simple, the only real catch is that if a subscriber publishes a new message in it’s handler, you should really assure that all of the original messages subscribers receive notice before you broadcast the next message (you can do this by queueing messages that come in while you’re ‘publishing’).

Here’s an example of how you use it.

public class ThingThatNeedsToKnowAllActiveCharacters: ISubscriber< CharacterCreatedMessage >, ISubscriber<CharacterKilledMessage>{
  // subscribe to CharacterCreatedMessage and CharacterKilledMessage whenever is appropriate (perhaps in constructor, or if this guys a monobehaviour on awake or whatever)

  public void HandleMessage( CharacterCreatedMessage msg ){
    // this guy keeps track of all the characters that are active, so he can update his state here
  }
  public void HandleMessage( CharacterKilledMessage msg ){
  }

  // this just routes the messages to its handler. A poor man's dynamic dispatch or late binding without reflection.
  public void HandleMessage<T>( T msg ){
    ((ISubscriber<T>)this).HandleMessage( msg );
  }
}

// the messages can either be highly specific, or they can have more fields that provide more
// detail. So for instance instead of separate Created/Killed, you could do
// CharactersChanged and include a flag for if it's a killed or created. Either way works.
public class CharacterCreated : IMessage{
  public Character Character;
}

You can elaborate as much as needed. I prefer it to be fast and clean rather than feature rich, but to each their own.

It should be noted that this implementation is about the simplest implementation that’s possible in c#.

Very nice.

Well… I’m kind of weird in that I’m not a professional programmer, and I do everything wrong, but it works great without issues and it’s easy.

So what I’ve been doing is I have events and effects, and events are things like “on click” or “on collision” and they’re just components I add to gameobjects, then I use public enumeration representing different kinds of things in the scene, like Fireball or Player, and then I add a component that has essentially one method… trigger… and it does something, like change the scene or request a sound effect to play. And I link them up and customize them right in the editor. And setting it all up is a pain, but toward the final stretch when its just content adding/tweaking and its a mad dash for the finish, it is so easy to add new stuff and change things… so using components that way it’s almost like visual scripting or drag and drop game making, but you can customize your drag and drop components.

And if you don’t add any of your specific game logic to these components, but make sure they’re highly generalized, they become completely reusable for the next game.

I can’t be bothered with diagrams. Too busy writing code.

However, Graphs are fun, kids:

2224569--148129--windowsopen.png

2 Likes

I tend to think of my components in terms if a system level, rather then at an entity level.

In most cases you are designing and building systems, rather then entities. An entity is just something that occurs when a various systems interact.

Components within a system are allowed to be reasonably closely coupled. The class Eater probably knows about Eatable. This kind of dependency is often not a huge deal. When replacing code you normally have to consider the whole system anyway.

What is not allowed is random components from different systems to be dependent. Shooting and Eating are fundamentally different systems, and should not interact except through a very narrowly defined pathway.

These systems might end up spread across dozens of disparate GameObjects. But as long as data flow through systems and between systems is understood and controlled then you’ve got a maintainable structure.

Under this system building an entity is simply a case of gathering the requisite components onto a single GameObject.

1 Like

Well, for example, let’s say I want my character to shoot a fireball.

Does the object responsible for spawning the fireball have to be a part of my character, since the fireballs come from my character?

If you say yes… I would say, then how many objects are going to be running around with identical spawners? And how are you going to keep track of all the spawned items?

If you say no… I would say, then how are you going to make the fireballs spawn? The most efficient way is a single object responsible for spawning and managing a pool of fireballs. But then that raises the question of how do you talk to it from your character object? An explicit reference is no bueno…

One school of thought says no dependencies… decouple…
Makes sense, too. What if you start a new level and that reference you thought you had gets broken. How far are you willing to go to maintain coupled, but unrelated, objects?

I like a central static class dispatching stuff out to everybody. Not necessarily doing anything, just letting objects know what they ought to do through method calls, then the objects can run their own logic and figure out their business.

I’m also a big fan of sending messages and letting objects interpret them (or not) depending on what kind of handlers they have. Fast and loose is my motto. Components just fit that mentality. Drop 'em in, take 'em out… switch them around, copy and paste. Multi-edit, done done done.

I wouldn’t make something highly generalized unless I need it. Sure you may be tempted into doing so, but if your game is made only of squares, don’t make your collision engine support triangles.

1 Like

I would say yes. A weapon would be responsible for instantiating and initialising it’s own projectile. This could be done with a prefab reference. Or it could be done through a more sophisticated factory.

The projectile itself would take care of it’s own lifecycle. So in general there would be no need to ‘keep track’ of it.

And who cares how many identical spawners you have in the game. The memory foot print of a component is fairly light. Reusing code this way is the entire point of OOP.

There is no “entire point” of anything, right off the bat… there’s so many ways of using a screwdriver it would make your head spin, infinitely more so when it comes to programming computers and the complexity of these languages we write in.

The projectile should manage itself. Everything should manage itself. The trouble comes when two of these self-managing things come into contact with one another. The trouble doubles when thing A calls a method on thing B, because now thing A is partially responsible for thing B working correctly. So if you change thing A, you might break thing B without even thinking about it, and vice versa. What I find untenable is that these inter-dependencies actually have the effect of multiplying as the complexity of the game increases. It seems virtually unavoidable that things require lots of references to lots of other things in order to gain information about their states or, as is probably very often the case, to call a method on the other object that affects its state.

To me, this is a giant spider web.

Another thing, there being no need to keep track of your projectiles… this is absolutely baseless.

Imagine a scenario where you want to create a chain lightning spell, like I am going to have in my next game. How do you do that if you have no idea where the other objects in the scene are? Do I have to iterate through each scene object, each frame? Checking if they are valid targets, checking to see if they are not already currently being affected by the chain lightning, seeing if they are a certain distance from the other nearest object being affected? Think about that for a moment… that’s a lot of processing. If you already knew where all the valid targets were, you could just iterate that collection. You could build methods to find the nearest member of group x to point x,y,z, etc.

Or, take another scenario where you wish to build a simple heatseaking missile. How do you know which object is the hottest and nearest if you have no clue where everything in the scene is?

Truthfully, if you’re making pong or space invaders, it doesn’t matter. But, if you’re trying to start doing things more complex, involving lots of decisions or data, it gets really messed up. You need the MVC model to keep your head straight. So to me, step one is creating a model of all the objects in the scene that can be queried. The view part in Unity is already handled. What’s left is that tricky issue of control…

And that’s where I think components come in for the win. It’s a lot easier to control everything in the scene if controls don’t have to be hard-wired for every single thing you create. I can’t even think of a single real-world thing that isn’t designed to be modular, at this point. All appliances, every piece of plumbing in your house, your furniture, entertainment devices… everything is designed to be modular and has repair and replacement parts. Things can be modified and upgraded.

I think at its core component design IS modular design, is inherently decoupled, works great, is easy to understand and just generally rocks. Writing long classes for each individual object to get the behavior you desire, just doesn’t make much sense when you consider the alternative.

Ultimately, every thing that happens in a game has what, a “cause” right? And every thing that happens is an “effect” of that “cause”. And the vast majority of all your in-game operations involve activating/deactivating things, or modifying a single property of some other object’s state… and this isn’t new. Microsoft already broke down application development into events and the design of applications into reusable components. HTML and the DOM already have this as well. Unity actually has a Heirarchy which is very much like the DOM, except it’s a lot more flexible, the drawback being you have to figure it out on your own. To me, this concept… event-based programming utilizing reusable, generalized components… this is not even really up for debate, it’s just the way things are now. Unity allows you to not do it that way, though, which is really interesting.

And if I could write every piece of code to be 100% general, I would. I’m a game designer and developer. I have to write code to do what I want, but my goal is less code, more functionality.

1 Like

It’s true, my games are pretty simple, so I can get away with simpler structures. @zombiegorilla is the one that you want I talk to if you want big, complex structures for large games. And that basically amounts to using Unity only for visualisation, and running the entire game in your own code.

Down to specific examples, you can always use the physics system to find local targets for chain lightening. You can also roll your own global list of all targets if you liked. I just get nervous anytime someone uses the terms static or global. Except for a few services and stateless helper methods, nothing should be global.

As to the messaging system I normally find Unity’s built in EventSystem sufficient.

I think its easy to get bogged down in all these diagrams and stuff… Like @Kiwasi eludes, you don’t really need insane structures on simple/small scale games so a great deal of this could be a moot point unless you’re actually applying these principles on projects with larger teams and scale and I don’t think that applies as much as we think around here.

So with that being said, I usually just try to think about how communication needs to work with the minimal amount of work. For instance in my case (right now) I use a Subject class which is basically anything I can interact with. In the Subject class you’ll define what this thing is as an enum and this basic structure serves as a good gateway for communication.

It works good for communicating between objects because you only need one GetComponent() call. All of the sub components on that GameObject will be using the Subject as a bottleneck that can hold information or direct something how to get to the component that it might need. I can usually just do a switch case on the type if what I’m trying to access isn’t part of the fundamental Subject and figure out where and how to access a more specific component pretty easily.

Basically, the data is always there and the idea is to use one component to answer questions rather than cross communicating between other components and dealing with unique situations on the fly. I do want to experiment with more abstract structure, but for now this is incredibly simple to scale up and works great for a lot of typical game styles.

1 Like