Be careful about run order of Awake functions

Because you can’t start something if it isn’t enabled. As far as Unity is concerned, anything that isn’t enabled can be treated as not participating in the engines lifecycle. If it’s not in the lifecycle it cannot Start.

Likewise, disabling something doesn’t destroy it. It’s simply taken out of the lifecycle for a time. Re enabling won’t cause a reawaken or a restart.

I’m in the same situation, with a (single) “AppServicesHolder” singleton of which the Awake runs after an MB’s OnEnable that tries to use the singleton.Both are root GOs inside the “loading…” scene.

What I did was to put the singleton prior to the “Default Time” in editor: scripts execution order. However I would love to give a better (yet simple and future-proof) solution to this issue. Any ideas?

Daisy chaining delegate events is probably the only way for something completely in C#. I’d say the Unity way is more convenient since there’s lesser setup, whether its future proof or not, hard to say.

1 Like

I’m running into a similar situation. I have 3 components: Health, BaseStats and Experience.

  • XP variable of the Experience component is restored from a save file after Awake but before Start.
  • BaseStats calculates the currentLevel in start based off the XP from Experience.
  • Health then calculates the starting health based off the currentLevel.

My problem is with steps 2 and 3. If I put them both is Start then the health might get calculated before the currentLevel is ready. But I can’t put 2 in Awake because the save file state won’t be restored yet.

I was considering the following guidelines for solving this issue more generally:

  • You cannot call another components methods in Awake.
  • If you use another components method in Start, you must first call that component’s Start method.

This seems to fix the issue in my case above and seems like a pretty good guideline with less boilerplate than lazy initialisation or chaining of events.

Standard pattern to avoid ordering bugs is to restrict object access.

  1. In Awake/OnEnable script allowed only access itself.
  2. All talks to other scripts are made in Start and later.

Doesn’t help if you have a chain of initialisation to do like my example.

Rephrase your initialization algorith to meet those two rules and it will work.

None of the above helps if it some point, you’ll talk to

  1. is critical for various reasons. Start is defined to run only once and follows some certain rules, a manual call could easily mess things up, and you should not need to implement an “start has already be run” flag.

Generally, when you have initialization steps that have to run in sequence and appear to be dependent on each other in some way, it’s usually easier to make some sort of controller that controls the access, the activation and initialization process and possibly the communication between multiple components.

This makes those components a little more passive, but suppose the fetching-operation of the values becomes slower so that you take the decision of asynchronous loading. At this point, you’ll have to come up with a different approach anyway.

Another advantage is that you can avoid direct coupling between component types, because you have class that acts as middle man for the communication. You can also use those controlling components to effectively stay highly flexible, because you can always introduce new components without editing the existing components.
The latter is great because often enough, making lots of components depend directly on others leads to very bloated components, which keep growing because there’s a need for one feature that’s only used here, another that’s only used there…

3 Likes

This, a whole lot of this.

True, I was just considering making a separate Init method that can support being reentrant. That init could be called in Start but also before the class is used in another MonoBehaviour.

Maybe I’m a little on clear on how the controller would work. I wonder if you would be able to give a more concrete example?

I imagine you mean an object responsible for setting up components in order and injecting there dependencies and potentially calling init methods on them when there dependencies are ready.

I don’t quite get how it would reduce coupling. Don’t you still need to have those classes talk to each other? If they don’t, won’t that make the controller into a bit of a god class that knows too much about all the subsystems?

Thanks for the reply, good to get input on other ways folks are solving these issues.

Instead of Health retrieving BaseStats, it instead is handed the information it needs by the controller class. Thusly decoupling Health from BaseStats. (Same goes for BaseStats from Experience).

Also, this is not what a “god object” or “god class” is. A god object is when a single class/object does too much on its own. This is an anti-pattern that breaks the “single responsibility prinicipal”. The controller class being suggested to you doesn’t do a bunch of things… it does a single thing: “injects the necessary data needed by the various stat/ability scripts of the entity it’s attached to”.

In actuality, by having your Health do BOTH locating BaseStats as well as configuring itself and doing whatever else Health does makes it more a god object. It is responsible for more things than are necessary.

The reason this is a problem is for scalability. What if down the line you needed to have a Health script whose initial values are configured not based on ‘BaseStats’ but on some other data… you now have to refactor Health script to handle both BaseStats AND the other data type… or refactor the configuration job out and move that task elsewhere (effectively creating the controller Suddoha is suggesting).

1 Like

Yup that’s what I thought was meant. I can see that the controller makes the injection of data easier to change without refactoring. However, it also makes it harder to add new data. Suppose I want to pull in more data in health from another component. Now I have to change both the controller and health script to achieve this.

Another question is what level should the controller sit at? Would I put it at the level of the player in which case it would have to change whenever any of the dependencies change in any of the player’s components change. That seems like a violation of the SRP. Or do I make it specifically the controller between Health, BaseStats and Experience in which case the choice of these classes is a bit arbitrary and might later need to be change if other classes need to depend on each other.

Generally all for minimising direct dependencies and providing just what’s needed. Demeters law and all that. But maybe I’ve been in Unity land too long as I’m struggling to see a nice way to achieve this without the above issues.

Also, my Health component relies on the BaseStats component later on in its lifecycle too, not just in initialisation. It registers for events and restores health based on the values returned from BaseStats at runtime. So I think a hard dependency there is justified.

@lordofduct has already given the answer to that, so let’s think about your example.

Suppose you have Health Component, and the Experience / Level component as mentioned in your example.
So yes, in your current project, it may be a given fact that health and other stats depend on the experience / level.

The question is - why should it?
Health (in its very own little world) can be implemented in a way that it knows nothing about the experience. Health for enemies, would they even need experience at all? Or a wall?

Does health itself even require behaviour?

What if experiences changes, all the stats dependent on it have to be updated. So you’d have all of them either poll the experience or wait for a level up event - each instance would do so. That’s questionable, but could be considered “still acceptable”.

Sounds convenient I guess. But what if there’s a special bonus for certain levels? Would that go into the health component? If so, it’d need to handle very specific behaviour and responsibilities.

Next, what if you decide to add a health multiplier (temporary boosts, permanent boosts etc)? Well, from my experience I’d bet that this one will surely make it into the Health component. The problem: once again, the health component becomes more specific. It suddenly needs to track boosts.

We could come up with infinite additions that will most-likely all go into the health component, so it will either grow or be subclassed or anything alike.

The next thing to address:
Loosely coupled components are great, but if you only ever talk to a “Health component”, you’re forced to either have a “health god component” that takes everything into account that’s relevant for computing the new health value, or overridden methods for something simple as health, that you’ll re-implement for every specific case / variation - or, maybe something that knows all the details? A component that wraps all these details?

Example: A character that has it’s usual health, some bonuses and a shield for damage absorption, some resistance , perhaps even invincibility skills.

Your task, as the programmer: Apply some damage.

  1. Health takes all of the above into account: Invincibility, Shield, Resistance, Health+Bonus
    => here’s your god class
    Not good at all.

  2. The component that applies damage takes all of that into account
    => Easy to introduce bugs, because you have to query all the components every time you want to apply damage, calling them in sequence and somehow get the logic right.
    Not that great either.

  3. Speak to some type of component that represents a specific entity, or for this matter, coordinates “a set of components”. This can be a very specific class, because that’s what assembles a complex entity in your application.
    => Only tell that component what you wanna do, and it can take everything into account as an “implementation detail”. In this case, you can keep your self-contained components, and your controlling class turns them into something bigger.
    Seems reasonable (in my opinion).

I’ll just offer up a very basic implementation of what I believe your design is shooting for:

public class HealthMeter : MonoBehaviour
{

    #region Fields
  
    [SerializeField]
    private float _currentHealth;
  
    [SerializeField]
    private float _maxHealth;
  
    #endregion
  
    #region Properties
  
    public float CurrentHealth
    {
        get { return _currentHealth; }
    }
  
    public float MaxHealth
    {
        get { return _maxHealth; }
    }
  
    #endregion
  
    #region Methods
  
    public void Configure(float currentHealth, float maxHealth)
    {
        _maxHealth = maxHealth;
        _currentHealth = Mathf.Clamp(currentHealth, 0f, _maxHealth);
    }
  
    public void Strike(float damage)
    {
        _currentHealth = Mathf.Clamp(_currentHealth - damage, 0f, _maxHealth);
        //maybe dispatch an event
    }
  
    public void Heal(float heal)
    {
        _currentHealth = Mathf.Clamp(_currentHealth + heal, 0f, _maxHealth);
        //maybe dispatch an event
    }
  
    #endregion

}

public class BaseStats : MonoBehaviour
{
  
    public int CurrentLevel;
    public float MaxHealth;
    public float Strength;
    public float Dexterity;
    //etc
  
    public void ConfigureFromExperience(int experience)
    {
        //set currentlevel and stats based on the experience points
      
        //this method doesn't necessarily have to be here... it could be performed by some factory...
        //this way the factory can be swapped out based on the character. Allowing for different leveling paths based on the factory.
    }
  
}

public class Experience : MonoBehaviour, IPersistentDataMessage
{
  
    public string Id;
    public int ExperiencePoints;
  
    void IPersistentDataMessage.Save(SaveDataToken token)
    {
        token[Id + "*Experience"] = this.ExperiencePoints;
    }
  
    void IPersistentDataMessage.Load(SaveDataToken token)
    {
        ExperiencePoints = (int)token[Id + "*Experience"];
    }
  
}

//a message that can be broadcast to obejcts telling them to save their data into
//some token container which will then be saved to disk or loaded form disk
public interface IPersistentDataMessage
{
    void Save(SaveDataToken token);
    void Load(SaveDataToken token);
}

public class PartyMemberController : MonoBehaviour
{
  
    public Experience Experience;
    public BaseStats Stats;
    public HealthMeter Health;
    //you maybe could put here the 'type' of party member it is so that the factory I referred to can be determined
  
    private void Start()
    {
        //we wouldn't necessarily dispatch the load message here...
        //but I'm including this just to show that the load message should have already occurred prior to configuring health/stats
        MessagingSystem.Dispatch<IPersistentDataMessage>(this, o => o.Load(SaveManager.SaveToken));
      
        //make sure our stuff is configure correctly
        Stats.ConfigureFromExperience(Experience.ExperiencePoints);
        Health.Configure(Stats.MaxHealth, Stats.MaxHealth);
    }
  
}

Note that in my code I reference a messaging system, a save system, and a factory. I didn’t go into fleshing those aspects out and instead put adhoc code about the place just to show the principal of the matter. Those systems should be designed and fleshed out on your own terms.

I’m not saying this is necessarily how you should do it… but this is the sort of design I’d probably start at and scale from there.

Thank you both lordofduct and Suddoha for your indepth replies. I decided to have a go at refactoring towards a controller for the Character/Player/Enemy entities.

First it tried removing all direct dependencies between Health, BaseStats and Experience. This lead me to realise there is some functionality differences between an Enemy and Player as the former has no Experience. So I created a polymorphic solution with Player and Enemy classes inheriting from Character. Links to Github are included for the actual implementations I tried.

I don’t like the inheritance trap here. My entities might not nicely categorise in the future this way. If I add an NPC, that shares some behaviour with Player but some with Enemy, what do I do? So I reverted the polymorphic aspect and decided to flesh out just the Character controller class. I added into the mix the Fighter class which also has dependencies on BaseStats. You can see that the class is becoming large and would have to change for many different reasons.

So finally I stripped it all back and justed used the Character class for dependency injection. This is the approach that I liked most. But it still creates a class with dependencies on many different subsystems. Which leads me to wonder: if I’m just using this class for dependency injection, why not use GetComponent and Unity GameObjects as the dependency injection mechanism?

I’d like to start by apologizing for not going into depth explaining the code I posted yesterday. I was at work and was writing that in my spare time.

So… my motivations in my design I showed.

HealthMeter - note that HealthMeter has no dependency to BaseStats. The reason for this is that it doesn’t matter how ‘Configure’ on it gets called. It might get configured statically in the editor for an enemy that doesn’t level… or heck for a destructible item in the world. Otherwise it could be configured based on stats like BaseStats (such as how PartyMemberController does it). Or maybe there is a completely different way… like maybe there’s MobController that sets its health to always be 2x the player’s health. The point is HealthMeter doesn’t know, nor care, how that is all determined. All it cares is that SOMETHING configures it. By reducing the jobs it needs to do, it allows HealthMeter to be used anywhere. Then if you have anything that impacts the health of any entity you just say:

entity.GetComponentInChildren<HealthMeter>().Strike(damage);

BaseStats - so in my example I just have a really dumb method that configures it based on experience points. But really… I wouldn’t go about it that way. I mention a factory. The factory would configure BaseStats based on whatever that specific factory likes… you might have one that is based on experience points and has a leveling curve. Another might again be based on the player (I’ve played many RPGs where a boss’s stats are based on like 3x player stats to a maximum of N). Again, BaseStats doesn’t know how it got its values… it merely exists as a container to read/write the current stats (a buff system can be built around this which ALSO modifies the stats). This again makes BaseStats modular, any and all entities that need stats just get it. And anything that does logic and needs it can retrieve it. For example you might have an AOE that does:

private void OnTriggerStay(Collider other)
{
    var health == other.GetComponentInChildren<BaseStats>();
    if(health == null) return; //can't hurt something that has no health
    var stats = other.GetComponentInChildren<BaseStats>();

    float dmg = this.DamageAmount;
    if(stats != null) dmg *= CalculateDamageMultiplier(stats);

    health.Strike(dmg * Time.deltaTime); //it's a damage over time AOE
}

Things like PartyMemberController would just be the central piece that coordinates all these components together in a way that describes that sort of entity.

It decides the kind of factory is used to set the stats. It configures the health. It defines how these modular parts integrate together. It’s “single task” is to “relate the entities parts as a whole be defining their relationship with one another”.

I would avoid inheritance chains in defining this. At most I might have an “IEntityController” type (or to work around the annoying unity hating serializing refs by interface… a base abstract class with zero implementation, just abstract property/method definitions).

The upside to this is that a lot of your entities will probably have hierarchies of gameobjects… its hitbox collider might actually be inside the entity so the above code ‘GetComponentInChildren’ may actually fail. So instead, and this is how I do it in my games, you can say something like:

var entity = otherCollider.GetComponentInParent<IEntityController>();
var health = entity.GetComponentInChildren<HealthMeter>();

This is all embracing the ‘component/composite pattern’. None of the scripts don’t necessarily know that each has some specific component (maybe you have a ‘Death/Grim Reaper’ enemy, so it gets no health… all the code logic out there still works regardless… only thing is AOE no longer hurts it, but maybe your buffs still do since it has BaseStats).

So when you say:

It’s not just for dependency injection… it’s defining how those dependencies are resolved.

A PartyMember gets its stats factory based on the experience of that party member.
But the GrimReaper gets its stats factory as a “everything maxed out” configuration.
A ‘BasicMob’ gets its stats statically defined via the editor when you create its prefab.
A ‘MercinaryPartyMember’ (a party member you can temporarily hire) gets its stats factory based as a scale of the player’s experience (that scale could be calculated as some multiple based on how much money you paid for them, or what town they came from)

The concept of dependency injection isn’t just the injection of a dependency. It’s abstracting the idea of how dependencies are resolved, and then injected.

And back to the original problem, and how this benefits. It means Health isn’t waiting for BaseStats to initialize so it can initialize. The controller defines when it gets initialized, the health doesn’t concern itself with that (especially if it doesn’t NEED to be initialized). No more race condition there, since the race is no longer defined by “Unity’s deserialization/load processs” which is arbitrary… but instead the race is defined by your “entity controller”. Something that makes sense and isn’t arbitrary, since you have control over its ordering.

2 Likes

Great post, thanks so much for replying. I think you’ve gone above and beyond with your reply. I’ve certainly got something to mull over and chew on. Maybe my different entities need themselves some controllers. But perhaps I only need to do dependency injection on the classes that really need configuration dependencies at this stage. The rest can be left to self configure.

Do you have any best practices for which classes you choose to configure in the controller or how you group your controller classes?

Two concerns still outstanding:

  • Adding a new dependency to a class requires a change to two or more files. The class itself and any controllers that might be using it. I suppose this is acceptible and will be flagged by the compiler if you change the Configure signature.
  • I don’t think I can get away with not passing the BaseStats to Health. At the moment health configures itself with BaseStats but also reference the max health whenever asked for percentage health. It also gets notified of level up events and receives a health bonus.

But that aside I will consider this approach a bit harder. I think that part of my problem might just be that deserialisation of the save file doesn’t happend till after Awake. It this could happen before or give another callback before Start (something like “DeserialisationFinished”) then it might be possible to solve a lot of these dependency issues that way. But I think I’m rediscovering the idea of using a Controller class anyway.

Yes.

Your ‘ScriptThatHasDependency’ and ‘TheScriptThatInjectsTheDependency’ will have to change. Though this sort of thing shouldn’t happen very much… You may want to ask yourself why this script is gaining a new dependency? What is causing this change? There’s no reason I can think of that Health would need any additional dependencies. If it is, it’s probably taking on too many new roles… breaking that ‘single responsibility principle’.

See this is where I can see Health is gaining a new dependency… because Health has received new jobs on top of being a health script. Why should Health need to register for an event that notifies it when the entity levels up? What if your Health is attached to something that doesn’t level up? It’s now doing work it doesn’t need to. The concept that health increases when a level increases isn’t on the HealthMeter, it’s on the Entity who has the HealthMeter. That’s a behavioural aspect of the entity, not the health.

Think of it like this… lets replace Health with “Rechargeable Battery”. A battery can be told to recharge, but it doesn’t recharge itself. It’s up to whatever is using the battery to recharge it. The battery just has the ability to be recharged. Furthermore, if you want to upgrade your battery giving it a new maximum, it again isn’t the batteries job to upgrade itself… it’s again the job of whoever is using the battery to upgrade (due to the real world limitations this would mean replacing the battery).

So why should Health know about BaseStats? So it can know its MaxHealth? When you configure it, tell it its MaxHealth. That’s now its MaxHealth regardless of what BaseStats says. What if you wanted to buff your Health and give a 50 hp bonus while wearing the ‘gauntlets of good will’… are you going to now have to go into HealthMeter and write logic that not just checks BaseStats but also checks for inventory equipped and buffs attached and every possible way its MaxHealth is increased??? Talk about a god class!

Why is it not instead that when the ‘Gauntlets of Good Will’ are equipped, the healthmeter is just told “your new maxhealth is X”.

With all that said, this is just the way I look at the problem. It’s technically not the only/best way to do it. It’s what I consider to be my preferred way of approaching the problems outlined so far in this thread.

Yeah this does make sense. It might well be too much given the enemies don’t level up.

Maybe base stats isn’t the best name. It actually aggregates all bonuses already. So all you have to do is ask it for the stat and it will give it with all bonuses applied. I personally still think this is neater than caching maxHealth in the Heatlh component. But it could be distrupted with an interface potentially.