Saving Loading Weap Choice Breaks Enemy State

If I have an Enemy that has a Weapon with no override controller it dose not appear to be dead, it is playing the Idle Animation. This only Happens after going through a portal. If I exit Play Mode and press Play the Dead Enemies appear to be dead.

I can press S on the Keyboard and L on the Keyboard and the Enemies will still be dead, but if I go through a Portal and Come Back they do not appear to be dead, they remain in their Idle State, even if I try to press L on the keyboard.


I get the same Issue even after moving the Enemy Weapons into Resources Folders.

To Recap this is Only After Going through a portal and coming back to the Scene and only if the Weapon Does not have an Override Controller. I did not have this issue until Testing the Saving and Loading of the current weapon.

I suspect this is because the Health component is getting it’s RestoreState() called before the Fighter component. While we can’t guarantee the order in which scripts are executed (well, we can, through preferences, but this is bad practice), we do know that the CaptureState() and RestoreState() methods are called in the order the components appear in the inspector (natural consequence of GetComponents().

In your inspector, the Fighter is after the Health… usually, this is the other way around simply because we introduce Fighter before we implement Health.

Try dragging the Fighter Component up above the Health component (you’ll have to do that in the Prefab, you won’t be able to do it in the scene view.

I figured, I didn’t think about the the components are on the Prefab. I just tried moving them around and it did fix the weird issue. I don’t remember why I moved the order from going through the video. I think it was so the Fighter was closer to the AI Controller. I couldn’t move the AI Controller Up because it is attached to the enemy base prefab which is a variant of the Character prefab where the Fighter Component is. While testing the weapons I was contently changing the Chase Range.

This was a specific outlier as I think most of the others used the assets that came with the course or models that are set up the same. The enemy models that I am using have their weapons attached already in the model with animations that fit the model exactly, some of them have a weapon object already attached to one or both of their hands, but the weapon was not a part of that bone. So it only made sense to not add the same animation controller as an override., which at the time was the quick and easy fix.

I usually still fill in the override, but my setup is tweaked a bit…
My principle Animator component has blank animations in all of the states… i.e. animations just named Idle, Attack, Cast, Dead, etc. These animations are blank.

This forces me to create an override for every weapon, rather than relying on a fallback.

This is good in Theory, in practice it adds extra work = less pron to bugs when intended by use by others. As programmers we need to make sure to add an error or warning letting others know they are attempting to use a class/method/property in a manor not intended. In this case as the wearer of all hats for the game and since we went through the extra effort to add in the Fallback, I thought I would save time and utilize the fallback. In my case when I got unexpected results the first step was I must of did something wrong in the code. 45 minutes latter was nope, I think the problem lies in the order of execution, which I really did not want to mess with in Unity Script Execution Order settings even though I do not think that I am using or will be using RuntimeInitializeOnLoadMethod attribute, any where, this is why things are separated into Awake and Start which I fell is the best approach for most situations.

I have heard this mentioned, but why is this a bad practice? It is used By Unity with their added packages and some of the Samples.

What often ends up happening is that you get into a cyclical chain of needing to order, reorder, etc to get the scripts to work in the right order. One move in execution order leads to another race condition, making the next move, etc. It sounds ironic, as the very issue of this post has to do with the order of the components on the GameObject, and the solution was to re-order them.

It’s not always wrong, and the engineers have good reasons for SEO to be there and to have the scripts execute in the order that they do.
Interestingly enough, long ago, there used to be a warning in the SEO window about taking extra care in reordering scripts. That seems to be gone from the editor now.

Ok kinda of the reason that I really didn’t want to Mess with it in the first place for this situation. The way it is set up with Cinemachine installed no room for my one script before Default Time and I didn’t want to mess with making sure that all of my scripts where loading in the correct order after Default time.

In most situations, you can work through race conditions by following some simple rules

  • Awake() → Get Component References here. Do not actually access them.
  • OnEnable()/OnDisable() → Subscribe/Unsubscribe from components
  • Start()/Update() → You are now free to access methods.

The one hitch in the giddyup is that sometimes RestoreState() can throw a monkeywrench into everything. It’s called after Awake() (so all component references should be initialized), but before OnEnable() and Start() (so things in Start() can undo what happened in RestoreState() and create zombies).

What happened here is a corner case, something that following the rules above would totally be covered, but RestoreState causes a fail.

An alternative solution to reordering would be to add a priority to the ISaveable interface

public interface ISaveable
{
    object CaptureState();
    void RestoreState(object state);
    int SavePriority();
}

Then you could reorder the elements programatically in SaveableEntity with a quick Linq expression.

I typically follow these rules by default. In some situations I have accessed an Unity Component in the Awake() , this ensured that the Unity Component contained the needed data that other scripts used in their start methods.

I thought about Adding a Priority to the Restore State, the easiest solution was making sure that I set the override controller for all weapons at the time.

If I was working with a team then I may have set a Priority or Throw an error so the Designer new that they need to set the Override Controller. I also consider setting a flag when the Heath was restored from Restore State The in Start or Update consuming that flag and reseting the Animator.

This topic was automatically closed 24 hours after the last reply. New replies are no longer allowed.

Privacy & Terms