Before we get into my previous suggestions, I have a bug with the pickup system (and it’s neither of the bugs we have mentioned earlier…)
Currently, I can easily get into the pickup state, but I can’t easily get out of it. Instead, although the item gets destroyed (because it is picked up), the player goes into an infinity loop and repeats the pickup animation endlessly, and he keeps also spawning whatever he was supposed to pickup for infinity, and it always gives me this NRE everytime the inventory spawns an item we picked up:
MissingReferenceException: The object of type 'Pickup' has been destroyed but you are still trying to access it.
Your script should either check if it is null or you should not destroy the object.
GameDevTV.Inventories.Pickup.PickupItem () (at Assets/GameDev.tv Assets/Scripts/Inventories/Pickup.cs:58)
RPG.Inventories.PickupTarget.PickupItem () (at Assets/GameDev.tv Assets/Scripts/Inventories/PickupTarget.cs:30)
RPG.States.Player.PlayerPickupState.AnimationEventRelay_HandlePickup () (at Assets/Project Backup/Scripts/State Machines/PlayerPickupState.cs:51)
RPG.Control.AnimationEventRelay.PickupItem () (at Assets/Project Backup/Scripts/Control/AnimationEventRelay.cs:12)
I went through the entire page 3 times so far, and I still can’t find why this loop is happening. If it helps, I’ll leave a few scripts here:
PlayerBaseState.cs:
using UnityEngine;
namespace RPG.States.Player
{
public abstract class PlayerBaseState : State
{
protected PlayerStateMachine stateMachine;
protected float AnimatorDampTime = 0.1f;
public PlayerBaseState(PlayerStateMachine stateMachine)
{
this.stateMachine = stateMachine;
}
protected void SetLocomotionState()
{
stateMachine.SwitchState(stateMachine.Targeter.CurrentTarget ? new PlayerTargetingState(stateMachine) : new PlayerFreeLookState(stateMachine));
}
protected void Move(float deltaTime)
{
Move(Vector3.zero, deltaTime);
}
protected void Move(Vector3 direction, float deltaTime)
{
stateMachine.CharacterController.Move((direction + stateMachine.ForceReceiver.Movement) * deltaTime);
}
protected void FaceTarget(Vector3 target, float deltaTime)
{
Vector3 directionToTarget = target - stateMachine.transform.position;
directionToTarget.y = 0;
stateMachine.transform.rotation = Quaternion.Slerp(stateMachine.transform.rotation, Quaternion.LookRotation(directionToTarget), stateMachine.FreeLookRotationSpeed * deltaTime);
}
protected float GetNormalizedTime(string tag = "Attack")
{
var currentInfo = stateMachine.Animator.GetCurrentAnimatorStateInfo(0);
var nextInfo = stateMachine.Animator.GetNextAnimatorStateInfo(0);
if (stateMachine.Animator.IsInTransition(0) && nextInfo.IsTag(tag))
{
return nextInfo.normalizedTime;
}
else if (!stateMachine.Animator.IsInTransition(0) && currentInfo.IsTag(tag))
{
return currentInfo.normalizedTime;
}
return 0;
}
}
}
PlayerFreeLookState.cs:
using UnityEngine;
namespace RPG.States.Player
{
public class PlayerFreeLookState : PlayerBaseState
{
public PlayerFreeLookState(PlayerStateMachine stateMachine) : base(stateMachine) {}
private float timer = 0;
private static readonly int FreeLookBlendTreeHash = Animator.StringToHash("FreeLookBlendTree");
private static readonly int FreeLookSpeedHash = Animator.StringToHash("FreeLookSpeed");
public override void Enter()
{
stateMachine.InputReader.JumpEvent += InputReader_HandleJumpEvent;
stateMachine.InputReader.TargetEvent += InputReader_HandleTargetEvent;
stateMachine.InputReader.PickupEvent += InputReader_HandlePickupEvent;
stateMachine.Animator.CrossFadeInFixedTime(FreeLookBlendTreeHash, stateMachine.CrossFadeDuration);
}
public override void Tick(float deltaTime)
{
// Uncomment the if statement below, if you want the player to be able to attack enemies outside of combat targeting mode:
/* if (stateMachine.InputReader.IsAttacking)
{
stateMachine.SwitchState(new PlayerAttackingState(stateMachine, 0));
return;
} */
Vector3 movement = CalculateMovement();
Move(movement * stateMachine.FreeLookMovementSpeed, deltaTime);
/* if (stateMachine.InputReader.IsAttacking)
{
HandleAttackButtonPressed();
return;
} */
if (stateMachine.InputReader.MovementValue == Vector2.zero)
{
stateMachine.Animator.SetFloat(FreeLookSpeedHash, 0, AnimatorDampTime, deltaTime);
if (stateMachine.Animator.GetFloat(FreeLookSpeedHash) < .1f) stateMachine.Animator.SetFloat(FreeLookSpeedHash, 0f);
return;
}
stateMachine.Animator.SetFloat(FreeLookSpeedHash, movement.magnitude, AnimatorDampTime, deltaTime);
FaceMovementDirection(movement, deltaTime);
}
public override void Exit()
{
stateMachine.InputReader.JumpEvent -= InputReader_HandleJumpEvent;
stateMachine.InputReader.TargetEvent -= InputReader_HandleTargetEvent;
stateMachine.InputReader.PickupEvent -= InputReader_HandlePickupEvent;
}
private void FaceMovementDirection(Vector3 forward, float deltaTime)
{
if (forward == Vector3.zero) return;
Quaternion desiredRotation = Quaternion.LookRotation(forward, Vector3.up);
stateMachine.transform.rotation = Quaternion.Slerp(stateMachine.transform.rotation, desiredRotation, stateMachine.FreeLookRotationSpeed * deltaTime);
}
private Vector3 CalculateMovement()
{
Vector3 forward = stateMachine.MainCameraTransform.forward;
Vector3 right = stateMachine.MainCameraTransform.right;
forward.y = 0;
right.y = 0;
forward.Normalize();
right.Normalize();
Vector3 movement = right * stateMachine.InputReader.MovementValue.x;
movement += forward * stateMachine.InputReader.MovementValue.y;
return Vector3.Min(movement, movement.normalized);
}
private void InputReader_HandleJumpEvent()
{
Debug.Log($"I get up, and nothing gets me down");
}
private void InputReader_HandleTargetEvent()
{
if (stateMachine.Targeter.SelectTarget())
{
stateMachine.SwitchState(new PlayerTargetingState(stateMachine));
}
}
private void InputReader_HandlePickupEvent()
{
if (stateMachine.PickupFinder.GetNearestPickup() != null)
{
stateMachine.SwitchState(new PlayerPickupState(stateMachine, stateMachine.PickupFinder.CurrentTarget));
}
}
void HandleAttackButtonPressed()
{
if (stateMachine.Targeter.HasTargets)
{
stateMachine.SwitchState(new PlayerAttackingState(stateMachine, 0));
return;
}
if (stateMachine.PickupFinder.HasTargets)
{
InputReader_HandlePickupEvent();
return;
}
// stateMachine.SwitchState(new PlayerAttackingState(stateMachine, 0));
}
}
}
PlayerPickupState.cs (the most suspicious one):
using RPG.Control;
using RPG.Inventories;
using UnityEngine;
namespace RPG.States.Player
{
public class PlayerPickupState : PlayerBaseState
{
private static readonly int PickupHash = Animator.StringToHash("Pickup");
public PlayerPickupState(PlayerStateMachine stateMachine, PickupTarget target) : base(stateMachine)
{
this.target = target;
}
private PickupTarget target;
private Vector3 position;
public override void Enter()
{
if (target == null)
{
stateMachine.SwitchState(new PlayerFreeLookState(stateMachine));
return;
}
position = target.transform.position;
stateMachine.Animator.CrossFadeInFixedTime(PickupHash, AnimatorDampTime);
stateMachine.AnimationEventRelay.PickupItemEvent += AnimationEventRelay_HandlePickup;
}
public override void Tick(float deltaTime)
{
FaceTarget(position, deltaTime);
Move(deltaTime);
if (GetNormalizedTime() > 0.80f)
{
stateMachine.SwitchState(new PlayerFreeLookState(stateMachine));
}
}
public override void Exit()
{
stateMachine.AnimationEventRelay.PickupItemEvent -= AnimationEventRelay_HandlePickup;
}
void AnimationEventRelay_HandlePickup()
{
target.PickupItem(); // line 51 (for NRE)
}
}
}
PickupTarget.cs, line 30:
using GameDevTV.Inventories;
using RPG.Core;
using UnityEngine;
namespace RPG.Inventories
{
public class PickupTarget : MonoBehaviour, ITarget
{
Pickup pickup;
private Inventory inventory;
public event System.Action<PickupTarget> OnPickedUp;
private void Awake()
{
pickup = GetComponent<Pickup>();
inventory = Inventory.GetPlayerInventory();
}
public bool IsValid()
{
return inventory.HasSpaceFor(pickup.GetItem());
}
public void PickupItem()
{
OnPickedUp?.Invoke(this);
pickup.PickupItem(); // line 30 (for the NRE)
}
}
}
AnimationEventRelay.cs, line 12:
using UnityEngine;
namespace RPG.Control
{
public class AnimationEventRelay : MonoBehaviour
{
public event System.Action PickupItemEvent;
void PickupItem()
{
PickupItemEvent?.Invoke(); // line 12, for the NRE
}
}
}
Brian, please help