There are some problem about saving weapon choice

Hi,

I followed the course, in the Fighter script, added two functions of the ISaveable interface, after picking up the weapon in the game and saving, and then starting the game, the following error occurred ,

NullReferenceException: Object reference not set to an instance of an object

And it also occurs that FadeIn() is not executed normally when the scene is switched.

These errors come from the overrideController code written in the previous lesson (// var overridedAnimatorController = animator.runtimeAnimatorController as AnimatorOverrideController;//)

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using RPG.Combat;
using RPG.Movement;
using RPG.Control;
using RPG.Core;
using RPG.Saving;
using System;

namespace RPG.Combat
{
  
    public class Fighter : MonoBehaviour, IAcction, ITakeDemage, ISaveable
    {   
        
        [SerializeField] Weapon defaultWeapon = null;
        [SerializeField] Transform rightHandTransform = null;
        [SerializeField] Transform leftHandTransform = null;

        RuntimeAnimatorController preCpntroller = null;
        Weapon currentWeapon = null;

        float attackRange = 2f;
        [SerializeField]  float timeBetweenAttack = 2f;
        float demage = 20f;
        float timeSinceLastAttack;
        Transform targetTransform;
        Animator m_animator;
        
        bool hasTarget = false;
        bool isEquipment = false;
        public bool isAttacking = false;


        private void Start()
        {   
            m_animator = GetComponent<Animator>();
            preCpntroller = m_animator.runtimeAnimatorController;

            if(currentWeapon == null)
            {
                EquipWeapon(defaultWeapon);
            }

        }
        // Start is called before the first frame update
        private void Update()
        { 
            timeSinceLastAttack -= Time.deltaTime;
            if (GetComponent<PlayerController>())
            {
                GetComponent<PlayerController>().HasTarget(ref hasTarget);
            }
            if (GetComponent<AIController>())
            {
                GetComponent<AIController>().HasTarget(ref hasTarget);
            }

            if (targetTransform && hasTarget && !CanAttack(targetTransform.gameObject) )
            {
                float distance = Vector3.Distance(transform.position, targetTransform.position);
                isAttacking = true;
                if (targetTransform.GetComponent<Health>().IsDead()) return;
                if (distance < attackRange)
                {
                    transform.LookAt(targetTransform); 
                    GetComponent<Mover>().Cancel();
                    AttackBehaviour();
                }
                else
                {
                    GetComponent<Mover>().MoveTo(targetTransform.position);
                }
            }
            else
            {
                isAttacking = false;
            }

            void AttackBehaviour()
            {   
                if(timeSinceLastAttack <= 0)
                {
                    m_animator.SetTrigger("attack");
                    timeSinceLastAttack = timeBetweenAttack;
                }
                
            }
            Debug.Log(currentWeapon);
        }
        public void Attack(CombatTarget combatTarget)
        {
            GetComponent<ActionSchedul>().StartAction(this);
            targetTransform = combatTarget.gameObject.transform;
        }

        public void Cancel()
        {
            m_animator.SetTrigger("stopAttack");

            targetTransform = null;
        }

       

        //Animation Event
        void Hit() 
        {   
            if(targetTransform == null) { return; }
            Health target = targetTransform.GetComponent<Health>();
            if (currentWeapon.HasProjectile())
            {
                currentWeapon.LaunchProjectile(rightHandTransform, leftHandTransform, target);
            }
            else
            {
                targetTransform.GetComponent<Health>().takeDamege(currentWeapon.GetDemage());
            }
        }

        void Shoot()
        {
            Hit();
        }

        public void TakeDemage(float Demage)
        {
            if (targetTransform)
            {
                targetTransform.GetComponent<Health>().takeDamege(Demage);
            }
        
        }

        bool CanAttack(GameObject combatTarget)
        {   
            if(combatTarget == null) { return false; }
            return targetTransform.GetComponent<Health>().IsDead() || GetComponent<Health>().IsDead(); ;
        }
         
        public void EquipWeapon(Weapon weapon)
        {   
            currentWeapon = weapon;
            weapon.Spawn(leftHandTransform, rightHandTransform, m_animator);
            attackRange = currentWeapon.GetAttackRange();
            timeBetweenAttack = currentWeapon.GetAttackCoolTime();
            demage = currentWeapon.GetDemage();
            isEquipment = true;
        }

        public void StoreWeapon()
        {
            if (isEquipment)
            {
                Destroy(currentWeapon);
                attackRange = currentWeapon.GetAttackRange();
                timeBetweenAttack = currentWeapon.GetAttackCoolTime();
                demage = currentWeapon.GetDemage();
                isEquipment = false;
                m_animator.runtimeAnimatorController = preCpntroller;
            }
        }

        public Weapon GetCurrentWeapon()
        {
            return currentWeapon;
        }

        public Transform GetTarget()
        {
            return targetTransform;
        }

        public object CaptureState()
        {
            return currentWeapon.name;
        }

        public void RestoreState(object state)
        {
            string weaponName = (string)state;
            Weapon lastWeapon = Resources.Load<Weapon>(weaponName);
            Debug.Log((string)state);
            /*EquipWeapon(lastWeapon);*/
        }

    }
}

In addition, when I was investigating, I found that Debug.log(currentWeapon) was called in Update() of the Fighter script, and the result showed that currentWeapon will also display Unarmed
Bow
and one of the other results (for example, if you take sword, display sword; if you take fireball, display fireball)
Can you help me find a possible cause of the problem?
Thanks!

For best results, can you paste in the extended info for the error message (if you click on the null reference error message in the console, you’ll get details about the error message in a pane below the console, which will give you the line number (and often the position in the line) of the error), as well as the name of the method, and a the chain of methods that led to the call. The error is generally on the first line in that method chain.

The following is extended information:

NullReferenceException: Object reference not set to an instance of an object
RPG.Combat.Weapon.Spawn (UnityEngine.Transform leftHand, UnityEngine.Transform rightHand, UnityEngine.Animator animator) (at Assets/Scripts/Combat/Weapon.cs:25)
RPG.Combat.Fighter.EquipWeapon (RPG.Combat.Weapon weapon) (at Assets/Scripts/Combat/Fighter.cs:145)
RPG.Combat.Fighter.RestoreState (System.Object state) (at Assets/Scripts/Combat/Fighter.cs:184)
RPG.Saving.SaveableEntity.RestoreState (System.Object state) (at Assets/Scripts/Saving/SaveableEntity.cs:39)
RPG.Saving.SavingSystem.RestoreState (System.Collections.Generic.Dictionary`2[TKey,TValue] state) (at Assets/Scripts/Saving/SavingSystem.cs:85)
RPG.Saving.SavingSystem+d__0.MoveNext () (at Assets/Scripts/Saving/SavingSystem.cs:23)
UnityEngine.SetupCoroutine.InvokeMoveNext (System.Collections.IEnumerator enumerator, System.IntPtr returnValueAddress) (at <0086ce0b9b00409a8713a9c4fe866b9d>:0)

This is saying that in WeaponConfig.Spawn(), either the leftHand, rightHand or animator are null. This is likely because either the left or right hand transform isn’t set in the inspector on either the Player or Enemy.
Try putting this Debug in the beginning of the WeaponConfig.Spawn() method:

if(leftHand==null)
{
    Debug.Log($"{animator.gameObject.name} does not have the left hand transform set. in Fighter.");
}
if(rightHand==null)
{
    Debug.Log($"{animator.gameObject.name} does not have the right hand transform set in Fighter.");
}

This will pinpoint any characters that don’t have these values set in the Fighter’s inspector.

Through some tests, it should be that the animator is null. But it can’t display which gameobject’s animator is, as long as you add animator.gameObject.name to Debug, this alarm will not be displayed.

And I checked each character to make sure they all set leftHand transform, rightHand transform and animator。

Thank you, I found my own problem, I defined the Animator in the Start method of the Fighter script (Animator m_animator = GetComponent()), instead of assigning it in the EquipWeapon method, this should be the core of all the problems , I can save my weapon selection normally after fixing the above problem.

Is this a question of precedence?

Yes, this is because the Saving System RestoreState is called after Awake(), but before Start().
Any cached component reference that is relied upon in RestoreState() must be cached in Awake().

1 Like

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