StackOverflowException on LazyValue... but weapon.Spawn()... is correct

Hi together,
after implementing the LazyValue I get this error of StackOverflow and I can’t find why this is happening.

StackOverflowException: The requested operation caused a stack overflow. UnityEngine.Transform.Find (System.String n) (at <9baebf9af86541678fd15bfdbf5f26eb>:0) RPG.Combat.Weapon.DestroyOldWeapon (UnityEngine.Transform rightHand, UnityEngine.Transform leftHand) (at Assets/Game/Scripts/Combat/Weapon.cs:39) RPG.Combat.Weapon.Spawn (UnityEngine.Transform rightHand, UnityEngine.Transform leftHand, UnityEngine.Animator anim) (at Assets/Game/Scripts/Combat/Weapon.cs:21) RPG.Combat.Fighter.AttachWeapon (RPG.Combat.Weapon weapon) (at Assets/Game/Scripts/Combat/Fighter.cs:176) RPG.Combat.Fighter.SetupDefaultWeapon () (at Assets/Game/Scripts/Combat/Fighter.cs:45) GameDevTV.Utils.LazyValue1[T].ForceInit () (at Assets/Game/Scripts/Utils/LazyValue.cs:56) GameDevTV.Utils.LazyValue1[T].get_value () (at Assets/Game/Scripts/Utils/LazyValue.cs:38) RPG.Combat.Fighter+<GetAdditiveModifiers>d__30.MoveNext () (at Assets/Game/Scripts/Combat/Fighter.cs:144) RPG.Stats.BaseStats.GetAdditiveModifier (RPG.Stats.Stat stat) (at Assets/Game/Scripts/Stats/BaseStats.cs:78) RPG.Stats.BaseStats.GetStat (RPG.Stats.Stat stat) (at Assets/Game/Scripts/Stats/BaseStats.cs:65) RPG.Combat.Fighter.AttachWeapon (RPG.Combat.Weapon weapon) (at Assets/Game/Scripts/Combat/Fighter.cs:177) RPG.Combat.Fighter.SetupDefaultWeapon () (at Assets/Game/Scripts/Combat/Fighter.cs:45) GameDevTV.Utils.LazyValue1[T].ForceInit () (at Assets/Game/Scripts/Utils/LazyValue.cs:56) GameDevTV.Utils.LazyValue1[T].get_value () (at Assets/Game/Scripts/Utils/LazyValue.cs:38) RPG.Combat.Fighter+<GetAdditiveModifiers>d__30.MoveNext () (at Assets/Game/Scripts/Combat/Fighter.cs:144) RPG.Stats.BaseStats.GetAdditiveModifier (RPG.Stats.Stat stat) (at Assets/Game/Scripts/Stats/BaseStats.cs:78) RPG.Stats.BaseStats.GetStat (RPG.Stats.Stat stat) (at Assets/Game/Scripts/Stats/BaseStats.cs:65) RPG.Combat.Fighter.AttachWeapon (RPG.Combat.Weapon weapon) (at Assets/Game/Scripts/Combat/Fighter.cs:177)
and so on… the game is running but this error drives me mad.

I’ve also implemented a bool to protect the AttachWeapon method to be executed, but unity seems to be not very interested about the bool :smiley:

     public void EquipWeapon(Weapon weapon)
    {
        if (weaponEquipped != true)
        {
            currentWeapon.value = weapon;
            AttachWeapon(weapon);
        }
    }
    public bool IsWeaponEquipped()
    {
        return weaponEquipped;
    }
    public bool UnEquippWeapon()
    {
        weaponEquipped = false;
        return weaponEquipped;
    }

    private void AttachWeapon(Weapon weapon)
    {
        if (weaponEquipped != true)
        {
            weapon.Spawn(rightHandTranfsorm, leftHandTranfsorm, anim);
            weaponDamage = basestat.GetStat(Stat.Damage);
            weaponRange = weapon.GetWeaponRange();
        }
    }

the bool is initialised as false and is resettet by the weaponpickUp…

Any suggestions?

Greetz Umoy

Usually, when the LazyValue causes a stack overflow it is because the Init() assigment is self-referential. Paste in the method you assign to LazyValue.Init

Hi Brian,

this is the fighter.cs:

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

namespace RPG.Combat
{
public class Fighter : MonoBehaviour, IAction, ISaveable, IModifierProvider
{
    
    [SerializeField] float timeBetweenAttacks = 1f;
    [SerializeField] Weapon defaultWeapon = null;
    [SerializeField] Transform rightHandTranfsorm;
    [SerializeField] Transform leftHandTranfsorm;


    Animator anim;
    Health target;
    BaseStats basestat;
    public event Action onTargetChange;
    float timeSinceLastAttack = Mathf.Infinity;
    private float weaponDamage;
    private float weaponRange;
    LazyValue<Weapon> currentWeapon;
    private bool weaponEquipped = false;

    private void Awake()
    {
        anim = GetComponent<Animator>();
        basestat = GetComponent<BaseStats>();
        currentWeapon = new LazyValue<Weapon>(SetupDefaultWeapon);
    }
    private void Start()
    {
        currentWeapon.ForceInit();
    }

    private Weapon SetupDefaultWeapon()
    {
        AttachWeapon(defaultWeapon);
        weaponEquipped = true;
        return defaultWeapon;
        
    }

    private void OnEnable()
    {
        basestat.onStatChange += RecalculateStats;
    }
    private void OnDisable()
    {
        basestat.onStatChange -= RecalculateStats;
    }

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

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

        if (!GetIsInRange())
        {
            GetComponent<Mover>().MoveTo(target.transform.position);
        }
        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()
    {
        anim.ResetTrigger("stopAttack");
        anim.SetTrigger("attack");
    }

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

        if (currentWeapon.value.HasProjectile()) currentWeapon.value.LaunchProjectile(rightHandTranfsorm, leftHandTranfsorm, target, gameObject, weaponDamage);
        else target.TakeDamage(gameObject, weaponDamage);
    }

    void Shoot()
    {
        Hit();
    }

    private bool GetIsInRange()
    {
        return Vector3.Distance(transform.position, target.transform.position) < weaponRange;
    }

    public bool CanAttack(GameObject combatTarget)
    {
        if (combatTarget == null) { 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>();
        onTargetChange?.Invoke();
    }

    public void Cancel()
    {
        StopAttack();
        target = null;
    }

    private void StopAttack()
    {
        anim.ResetTrigger("attack");
        anim.SetTrigger("stopAttack");

    }
    public IEnumerable<float> GetAdditiveModifiers(Stat stat)
    {
        if(stat == Stat.Damage)
        {
            yield return currentWeapon.value.GetWeaponDamage();
        }
    }
    public IEnumerable<float> GetPercentageModifiers(Stat stat)
    {
        if (stat == Stat.Damage)
        {
            yield return currentWeapon.value.GetPercentageBonus();
        }
    }
    public void EquipWeapon(Weapon weapon)
    {
        if (weaponEquipped != true)
        {
            currentWeapon.value = weapon;
            AttachWeapon(weapon);
        }
    }
    public bool IsWeaponEquipped()
    {
        return weaponEquipped;
    }
    public bool UnEquippWeapon()
    {
        weaponEquipped = false;
        return weaponEquipped;
    }

    private void AttachWeapon(Weapon weapon)
    {
        if (weaponEquipped != true)
        {
            weapon.Spawn(rightHandTranfsorm, leftHandTranfsorm, anim);
            weaponDamage = basestat.GetStat(Stat.Damage);
            weaponRange = weapon.GetWeaponRange();
        }
    }

    private void RecalculateStats()
    {
        weaponDamage = basestat.GetStat(Stat.Damage);
    }

    public Health GetTarget()
    {
        return target;
    }

    public float GetWeaponRange()
    {
        return weaponRange;
    }

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

    public void RestoreState(object state)
    {
        anim = GetComponent<Animator>();
        string weaponName = (string)state;
        Weapon weapon = Resources.Load<Weapon>(weaponName);
        EquipWeapon(weapon);
        
    }

    
}

}

Hmmm getting into mystery territory. The LazyValue appears to be set up correctly, and not self-referentially (sometimes students wind up referencing the currentWeapon.value, which causes a stack overflow… meaning the actual error may not have anything to do with the LazyValue…
The next place to investigate is in your Weapon.cs script, specificlaly Weapon.Spawn()

Finally I found a solution. The problem seems to be in my weaponDamage setting.
It’s calling the baseStats. So I’ve put it into the EquipWeapon method and call this right after init in Start. Now it seams to work…

    private void AttachWeapon(Weapon weapon)
    {
        if (weaponEquipped != true)
        {
            weapon.Spawn(rightHandTranfsorm, leftHandTranfsorm, anim);
        }
    }

    public void EquipWeapon(Weapon weapon)
    {
        if (weaponEquipped != true)
        {
            currentWeapon.value = weapon;
            AttachWeapon(weapon);
            weaponDamage = basestat.GetStat(Stat.Damage);
            weaponRange = weapon.GetWeaponRange();
        }
    }

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

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

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

Privacy & Terms