Problem with saving currentWeaponConfig at the end Shops section

I just finished the Shops section of the 4th RPG course. When I tried to test saving the shop and purse info, I received an error related to fighter.cs. I do not know how long the error has been there. Here is the error code:
NullReferenceException: Object reference not set to an instance of an object
RPG.Combat.Fighter.CaptureState () (at Assets/Scripts/Combat/Fighter.cs:190)
GameDevTV.Saving.SaveableEntity.CaptureState () (at Assets/Scripts/Saving/SaveableEntity.cs:45)
GameDevTV.Saving.SavingSystem.CaptureState (System.Collections.Generic.Dictionary`2[TKey,TValue] state) (at Assets/Scripts/Saving/SavingSystem.cs:93)
GameDevTV.Saving.SavingSystem.Save (System.String saveFile) (at Assets/Scripts/Saving/SavingSystem.cs:43)
RPG.SceneManagement.SavingWrapper.Save () (at Assets/Scripts/SceneManagement/SavingWrapper.cs:45)
RPG.SceneManagement.SavingWrapper.Update () (at Assets/Scripts/SceneManagement/SavingWrapper.cs:34)

Here is my fighter.cs:

using UnityEngine;
using RPG.Movement;
using RPG.Core;
using GameDevTV.Saving;
using RPG.Attributes;
using RPG.Stats;
using System.Collections.Generic;
using GameDevTV.Utils;
using System;
using GameDevTV.Inventories;

namespace RPG.Combat
{
    public class Fighter : MonoBehaviour, IAction, ISaveable
    {

        [SerializeField] float timeBetweenAttacks = 1f;
        [SerializeField] Transform rightHandTransform = null;
        [SerializeField] Transform leftHandTransform = null;
        [SerializeField] WeaponConfig defaultWeapon = null;
        


        Health target;
        Equipment equipment;
        float timeSinceLastAttack = Mathf.Infinity;
        WeaponConfig currentWeaponConfig;
        LazyValue<Weapon> currentWeapon;

        private void Awake()
        {
            currentWeaponConfig = defaultWeapon;
            currentWeapon = new LazyValue<Weapon>(SetUpDefaultWeapon);
            equipment = GetComponent<Equipment>();
            if (equipment)
            {
                equipment.equipmentUpdated += UpdateWeapon;
            }
        }

        private void UpdateWeapon()
        {
            var weapon = equipment.GetItemInSlot(EquipLocation.Weapon) as WeaponConfig;
            if (weapon == null)
            {
                EquipWeapon(defaultWeapon);
            }
            else
            {
                EquipWeapon(weapon);
            }
        }

        private Weapon SetUpDefaultWeapon()
        {
            return AttachWeapon(defaultWeapon);
        }

        private void Start()
        {
            currentWeapon.ForceInit();
        }

        private void Update()
        {
            timeSinceLastAttack += Time.deltaTime;

            if (target == null) return;
            if (target.IsDead()) return;

            if (!GetIsInRange(target.transform))
            {
               GetComponent<Mover>().MoveTo(target.transform.position, 1f);
            }
           else
            {
                GetComponent<Mover>().Cancel();
                AttackBehaviour();
            }
        }

        private void AttackBehaviour()
        {
            transform.LookAt(target.transform);
            if (timeSinceLastAttack > timeBetweenAttacks)
            {
                // This will trigger the Hit() event
                TriggerAttack();
                timeSinceLastAttack = 0;
            }
        }

        private void TriggerAttack()
        {
            GetComponent<Animator>().ResetTrigger("stopAttack");
            GetComponent<Animator>().SetTrigger("attack");
        }

        // Animation Event
        void Hit()
        {
            if (target == null) { return; }

            float damage = GetComponent<BaseStats>().GetStat(Stat.Damage);

            if (currentWeapon.value != null)
            {
                currentWeapon.value.OnHit();
            }
            
            if (currentWeaponConfig.HasProjectile())
            {
                currentWeaponConfig.LaunchProjectile(rightHandTransform, leftHandTransform, target, gameObject, damage);
            }
            else
            {
               
                target.TakeDamage(gameObject, damage);
            }
            
        }

        void Shoot()
        {
            Hit();
        }

        private bool GetIsInRange(Transform targetTransform)
        {
            return Vector3.Distance(transform.position, targetTransform.position) < currentWeaponConfig.GetRange();
        }

        public bool CanAttack(GameObject combatTarget)
        {
            if (combatTarget == null)
            {
                return false;
            }

            if (!GetComponent<Mover>().CanMoveTo(combatTarget.transform.position) && 
                !GetIsInRange(combatTarget.transform))
            {
                return false;
            }

            Health targetToTest = combatTarget.GetComponent<Health>();
            return targetToTest != null && !targetToTest.IsDead();
        }

        public void Attack(GameObject combatTarget)
        {
            GetComponent<ActionScheduler>().StartAction(this);
            target = combatTarget.GetComponent<Health>();
            
        }

        public void Cancel()
        {
            StopAttack();
            target = null;
            GetComponent<Mover>().Cancel();
        }

        private void StopAttack()
        {
            GetComponent<Animator>().ResetTrigger("attack");
            GetComponent<Animator>().SetTrigger("stopAttack");
        }

        public void EquipWeapon(WeaponConfig weapon)
        {
            currentWeaponConfig = weapon;
            currentWeapon.value = AttachWeapon(weapon);
        }

        private Weapon AttachWeapon(WeaponConfig weapon)
        {
            Animator animator = GetComponent<Animator>();
            return weapon.Spawn(rightHandTransform, leftHandTransform, animator);
        }

        public Health GetTarget()
        {
            return target;
        }


        public object CaptureState()
        {
            return currentWeaponConfig.name;    <==== Line 190
        }

        public void RestoreState(object state)
        {
            string weaponName = (string)state;
            WeaponConfig weapon = UnityEngine.Resources.Load<WeaponConfig>(weaponName);
            
            EquipWeapon(weapon);
        }
    }
}

I have been working on the RPG courses in my spare time as a hobby and I am thoroughly enjoying the process. This is the first time I have encountered an issue that I could not solve myself or find a solution in the forum. Thanks in advance for any help.

My first thought was you had a character (perhaps the shop keeper) who doesn’t have a default weapon set. I noticed, however that while you are using the Equipment component from the Inventory course, you’re still saving/loading the weapons using currentWeaponConfig.name instead of currentWeaponConfig.GetItemID() and restoring it with Resources.Load instead of InventoryItem.GetFromID().

It’s possible that you just never changed this RestoreState to conform to the Inventory system’s RestoreState.

This actually leaves two possibilities:

  • A Fighter component in the game does not have a Default Weapon
  • An equipped weapon isn’t in a folder named Resources. With the Inventory system, the item can be in child folders of a folder named Resources, but not in Fighter.cs.

If your Equipment is functioning properly, you actually don’t even need Fighter to be an ISaveable, and can remove the CaptureState and RestoreState. This is because you’re subscribing to Equipment.equipmentUpdated. If you’re using a weapon other than the default weapon, then the Equipment will be storing the saved reference to the weapon, and you won’t need to worry about restoring/saving it because Equipment will have it handled already. Just make sure your Equipment.RestoreState() calls equipmentUpdated at the end of the method.

So, currentWeaponConfig is null. A fighter (player, npc or enemy) somewhere in your game does not have a weapon. Perhaps it’s one of the shop keepers. One of them may have a fighter script, but no weapon.


Edit
@Brian_Trotter beat me to it. I had to check the video and my own ‘default’ character prefab to make sure there is actually a Fighter on it

I have to beat you to the punch at least once a week. It’s in my contract. :stuck_out_tongue:

1 Like

Thank you @Brian_Trotter. The problem was solved when I removed the ISavable from fighter.cs and added equipmentUpdated to the restore state. I have learned a lot from reading other peoples problems that you solved. Almost doubles the value of the courses.

Thanks @bixarrio. I have learned a lot reading many of your replies to other people.

1 Like

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

Privacy & Terms