3
\$\begingroup\$

I'm currently at the start of a project, and I'm following the wisdoms of my day-to-day career as a C# developer for large web applications.

Currently I have set up a bunch of interfaces. Notable of these are IActor, IHuman, ISelectable, IUIAction & IHealth.

I also have a Person class. Person implements IHuman (which implements IActor), ISelectable and IHealth and also inherits from MonoBehaviour.

ISelectable has a property of IEnumerable<IUIAction> Actions. Actions have a property of Action click. Im sure you can see where I am going with this...

The intended goal is that if a class inheriting from MonoBehaviour (person in this case) implements ISelectable and is attached to a GameObject that is clicked, a list of buttons representing ISelectable.Actions is displayed on the screen. If a class implements IHealth, a healthbar will be displayed on the screen etc etc...

Now, the problem I see is I'm going to end up with huge classes. The classes are going to need properties for their health, theyre going to need properties for their actions, theyre going to need properties for what the UI panels title should be when selected, and anything else that comes up as development progresses... My other thought is to remove the interfaces like IHealth from the class itself and have them be their own class, such that IHealth will only mean a class has a property that is of the class Health and Health has all the properties relating to an objects health levels... but this removes authority from the class as to what control it has over its health.

Whats the industry standard for this sort of thing, and how can I implement it effectively in my game?

\$\endgroup\$
  • \$\begingroup\$ You might want to look into Entity-Component-System approach. It is said to deal well with such issues. \$\endgroup\$ – Kromster Mar 15 '17 at 19:36
  • 1
    \$\begingroup\$ Is this basically what unity does? Would my best approach to add a monobehaviour of health, a monobehaviour of selectable etc and make my logic based off of GameObject.GetComponent as opposed to whether it can be cast to an interface? \$\endgroup\$ – Trotski94 Mar 15 '17 at 19:46
9
\$\begingroup\$

Unity emphasizes a philosophy called Composition Over Inheritance.

The idea is that rather than thinking of what a particular entity "is":

A soldier is a unit which is selectable and which is damageable.

You think of the set of behaviours/features it "has":

A soldier has a selection behaviour and a taking-damage behaviour

So then, as you suggest in the question, you can break these responsibilities off into their own MonoBehaviour components:

  • Selectable (watch out for naming conflicts with UnityEngine.UI.Selectable) can respond to click events and hold information needed to draw a selection highlight.

  • Damageable can track the current health, resistances, and handle firing off low-health state change events or destruction animations.

etc.

Your "soldier" might not exist as a class anywhere in code - it's just a particular combination of components and parameter values that gives the set of behaviours you want, ie. an entity with both Selectable and Damageable components attached (among others).

This move from defining each entity in code to doing it in data gives us a number of outcomes that are useful in game development:

  • Non-programmers on the team can take a more active role in developing game behaviour. If your coders provide a good set of building-block components, the level designers can go to town experimenting & combining them in new ways, without waiting on a coder to write a class that glues-together all the bits they need in a new standalone class.

  • Occasionally you'll get new behaviour "for free" out of this combinatoric play. For example, take an enemy and remove the Damageable component - boom, now you have invulnerable enemies, without a coder creating a special mode or flag. ;)

  • We can create and modify these compound entities on the fly while the game is running, without recompiling code or relying on edit-and-continue functionality.

  • This style helps encourage small modular components, each easy to understand and modify on their own, rather than massive sprawling classes with many responsibilities as you describe - a common pitfall in game development, with player classes especially!

  • A more modular, data-driven architecture tends to have fewer bottlenecks for exclusive checkouts & merging when multiple developers are collaborating on a cluster of features.

  • This pattern can also make it easier to develop tools like level editors or support user generated content, since all the game entities are just data files that can be modified without code changes.

That's not to say you can't or shouldn't use interfaces - they're still a great tool to have in your toolbox, especially when you have some kind of well-defined need in your game and multiple ways you might want to serve that need.

For example, maybe your base AI movement logic knows it wants to move into firing range for whatever weapon it's using - but that weapon might vary from one unit archetype to another. You could introduce an IAttackBehaviour interface exposing methods to select a target, locate a good attack position for the movement behaviour to reach, and to execute an attack. Then you could implement this in concrete types likeMeleeAttackBehaviour, SniperAttackBehaviour, GrenadierAttackBehaviour, etc. Your movement behaviour just needs to know it "has" an IAttackBehaviour attached, but doesn't need to implement these details itself for every archetype flavour. This allows AI designers to mix & match components as they need, while maintaining a clear contract for how the movement & attack behaviours communicate.

| improve this answer | |
\$\endgroup\$
  • \$\begingroup\$ Thanks. After doing some reading online and speaking in the GameDev chat-room, this was basically the conclusion I had come to. It's nice to have some-one else confirm my readings. This is a very concise answer and sums up the key points very well. \$\endgroup\$ – Trotski94 Mar 16 '17 at 8:15
  • \$\begingroup\$ Say, I noticed a downvote on this answer. I'm not hung-up on rep (got plenty), but I'd love to take the opportunity to improve the answer if someone's spotted a problem or gap in it. Please feel free to comment (or edit) if there's something that would make this answer better. (Or post a new answer - there's usually many perspectives worth considering on architecture questions, with different development styles emphasizing different considerations) \$\endgroup\$ – DMGregory Mar 16 '17 at 19:36

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.