Hi there @Brian_Trotter
Just finished the current part of the merge tutorial, having a blast with it so far.
I’ve encountered a problem trying to bind all the interact action (dialogue, shop, pickup) to the same key (E).
I tried both having them as separate actions (in the control map, that is Shop / Dialogue /Pickup be separate actions all bound to “E”), and also tried having them as a single interaction (called interact, on the player map) and simply calling all the events. Had the same errors.
These are the errors I’m getting:
1:
MissingReferenceException: The object of type 'PickupTarget' 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.
RPG.Core.RangeFinder`1[T].FindNearestTarget () (at Assets/Scripts/Core/RangeFinder.cs:57)
RPG.States.Player.PlayerFreeLookState.InputReader_HandlePickupEvent () (at Assets/Scripts/States/Player/PlayerFreeLookState.cs:129)
RPG.States.Player.PlayerFreeLookState.HandleAttackButtonPressed () (at Assets/Scripts/States/Player/PlayerFreeLookState.cs:76)
RPG.States.Player.PlayerFreeLookState.Tick (System.Single deltaTime) (at Assets/Scripts/States/Player/PlayerFreeLookState.cs:35)
RPG.States.StateMachine.Update () (at Assets/Scripts/States/StateMachine.cs:18)
2:
MissingReferenceException: The object of type 'PickupTarget' 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.
RPG.Core.RangeFinder`1[T].FindNearestTarget () (at Assets/Scripts/Core/RangeFinder.cs:57)
RPG.States.Player.PlayerFreeLookState.InputReader_HandlePickupEvent () (at Assets/Scripts/States/Player/PlayerFreeLookState.cs:129)
RPG.InputReading.InputReader.OnInteract (UnityEngine.InputSystem.InputAction+CallbackContext context) (at Assets/Scripts/InputReading/InputReader.cs:141)
UnityEngine.InputSystem.Utilities.DelegateHelpers.InvokeCallbacksSafe[TValue] (UnityEngine.InputSystem.Utilities.CallbackArray`1[System.Action`1[TValue]]& callbacks, TValue argument, System.String callbackName, System.Object context) (at ./Library/PackageCache/com.unity.inputsystem@1.7.0/InputSystem/Utilities/DelegateHelpers.cs:46)
UnityEngine.InputSystem.LowLevel.<>c__DisplayClass7_0:<set_onUpdate>b__0(NativeInputUpdateType, NativeInputEventBuffer*)
UnityEngineInternal.Input.NativeInputSystem:NotifyUpdate(NativeInputUpdateType, IntPtr)
3:
MissingReferenceException while executing 'started' callbacks of 'Player/Interact[/Keyboard/e]'
UnityEngine.InputSystem.LowLevel.NativeInputRuntime/<>c__DisplayClass7_0:<set_onUpdate>b__0 (UnityEngineInternal.Input.NativeInputUpdateType,UnityEngineInternal.Input.NativeInputEventBuffer*)
UnityEngineInternal.Input.NativeInputSystem:NotifyUpdate (UnityEngineInternal.Input.NativeInputUpdateType,intptr)
4:
MissingReferenceException while executing 'performed' callbacks of 'Player/Interact[/Keyboard/e]'
UnityEngine.InputSystem.LowLevel.NativeInputRuntime/<>c__DisplayClass7_0:<set_onUpdate>b__0 (UnityEngineInternal.Input.NativeInputUpdateType,UnityEngineInternal.Input.NativeInputEventBuffer*)
UnityEngineInternal.Input.NativeInputSystem:NotifyUpdate (UnityEngineInternal.Input.NativeInputUpdateType,intptr)
5:
MissingReferenceException while executing 'canceled' callbacks of 'Player/Interact[/Keyboard/e]'
UnityEngine.InputSystem.LowLevel.NativeInputRuntime/<>c__DisplayClass7_0:<set_onUpdate>b__0 (UnityEngineInternal.Input.NativeInputUpdateType,UnityEngineInternal.Input.NativeInputEventBuffer*)
UnityEngineInternal.Input.NativeInputSystem:NotifyUpdate (UnityEngineInternal.Input.NativeInputUpdateType,intptr)
It’s not happening consistenly, only some of the times.
It appears to be happening when I loot too soon after a fight, or when I’m fighting next to loot I’m not entirely sure.
I tried following the guide as closely as possible, And I know there is a part where you say to be mindful of calling events in the particular order stated which I think I am doing correctly.
I have an event on the Pickup animation (which isn’t being played when the error happens, but it all works well when it does play).
Here’s my scripts :
PlayerPickupState .cs
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("Pickup") > 0.80f)
{
stateMachine.SwitchState(new PlayerFreeLookState(stateMachine));
}
}
public override void Exit()
{
stateMachine.AnimationEventRelay.PickupItemEvent -= AnimationEventRelay_HandlePickup;
}
void AnimationEventRelay_HandlePickup()
{
target.PickupItem();
}
}
}
PlayerFreeLookState.cs
using UnityEngine;
namespace RPG.States.Player
{
public class PlayerFreeLookState : PlayerBaseState
{
public PlayerFreeLookState(PlayerStateMachine stateMachine) : base(stateMachine)
{
}
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.InputReader.DialogueEvent += InputReader_HandleDialogueEvent;
stateMachine.InputReader.ShopEvent += InputReader_HandleShopEvent;
stateMachine.Animator.CrossFadeInFixedTime(FreeLookBlendTreeHash, stateMachine.CrossFadeDuration);
}
public override void Tick(float deltaTime)
{
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, 0.1f, deltaTime);
if (stateMachine.Animator.GetFloat(FreeLookSpeedHash) < .1f)
{
stateMachine.Animator.SetFloat(FreeLookSpeedHash, 0f);
}
return;
}
stateMachine.Animator.SetFloat(FreeLookSpeedHash, 1, 0.1f, deltaTime);
FaceMovementDirection(movement, deltaTime);
}
public override void Exit()
{
stateMachine.InputReader.JumpEvent -= InputReader_HandleJumpEvent;
stateMachine.InputReader.TargetEvent -= InputReader_HandleTargetEvent;
stateMachine.InputReader.PickupEvent -= InputReader_HandlePickupEvent;
stateMachine.InputReader.DialogueEvent -= InputReader_HandleDialogueEvent;
stateMachine.InputReader.ShopEvent -= InputReader_HandleShopEvent;
}
void HandleAttackButtonPressed()
{
if (stateMachine.Targeter.HasTargets)
{
stateMachine.SwitchState(new PlayerAttackingState(stateMachine, 0));
return;
}
if (stateMachine.PickupFinder.HasTargets)
{
InputReader_HandlePickupEvent();
return;
}
if (stateMachine.ConversantFinder.HasTargets)
{
InputReader_HandleDialogueEvent();
if (stateMachine.ConversantFinder.CurrentTarget) return;
}
stateMachine.SwitchState(new PlayerAttackingState(stateMachine, 0));
}
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");
stateMachine.SwitchState(new PlayerFreeLookState(stateMachine));
}
private void InputReader_HandleTargetEvent()
{
if (stateMachine.Targeter.SelectTarget())
{
stateMachine.SwitchState(new PlayerTargetingState(stateMachine));
}
}
private void InputReader_HandlePickupEvent()
{
if (stateMachine.PickupFinder.FindNearestTarget())
{
stateMachine.SwitchState(new PlayerPickupState(stateMachine, stateMachine.PickupFinder.CurrentTarget));
}
}
private void InputReader_HandleDialogueEvent()
{
if (stateMachine.ConversantFinder.FindNearestTarget())
{
PlayerConversantState conversantState =
new PlayerConversantState(stateMachine, stateMachine.ConversantFinder.CurrentTarget);
stateMachine.SwitchState(new PlayerTurningState(stateMachine,
stateMachine.ConversantFinder.CurrentTarget.transform.position, conversantState));
}
}
private void InputReader_HandleShopEvent()
{
if (stateMachine.ShopFinder.FindNearestTarget())
{
PlayerShoppingState nextState =
new PlayerShoppingState(stateMachine, stateMachine.ShopFinder.CurrentTarget);
stateMachine.SwitchState(new PlayerTurningState(stateMachine,
stateMachine.ShopFinder.CurrentTarget.transform.position, nextState));
}
}
}
}
InputReader.cs
using System;
using System.Collections;
using System.Collections.Generic;
using RPG.UI;
using UnityEngine;
using UnityEngine.InputSystem;
namespace RPG.InputReading
{
public class InputReader : MonoBehaviour, Controls.IPlayerActions, Controls.IUIActions
{
public bool IsAttacking { get; private set; }
public bool IsBlocking { get; private set; }
public Vector2 MovementValue { get; private set; }
public event Action JumpEvent;
public event Action DodgeEvent;
public event Action TargetEvent;
public event Action CancelEvent;
public event Action InventoryEvent;
public event Action AttackDownEvent;
public event Action PickupEvent;
public event Action DialogueEvent;
public event Action ShopEvent;
// public event Action InteractEvent;
private Controls controls;
private void Start()
{
controls = new Controls();
controls.Player.SetCallbacks(this);
controls.UI.SetCallbacks(this);
controls.Player.Enable();
controls.UI.Enable();
WindowController.OnAnyWindowOpened += DisableControls;
WindowController.OnAllWindowsClosed += EnableControls;
}
private void EnableControls()
{
controls.Player.Enable();
}
private void DisableControls()
{
controls.Player.Disable();
}
private void OnDestroy()
{
WindowController.OnAnyWindowOpened -= DisableControls;
WindowController.OnAllWindowsClosed -= EnableControls;
controls.Player.Disable();
controls.UI.Disable();
}
public void OnJump(InputAction.CallbackContext context)
{
if (!context.performed) { return; }
JumpEvent?.Invoke();
}
public void OnDodge(InputAction.CallbackContext context)
{
if (!context.performed) { return; }
DodgeEvent?.Invoke();
}
public void OnMove(InputAction.CallbackContext context)
{
MovementValue = context.ReadValue<Vector2>();
}
public void OnLook(InputAction.CallbackContext context)
{
}
public void OnTarget(InputAction.CallbackContext context)
{
if (!context.performed) { return; }
TargetEvent?.Invoke();
}
public void OnAttack(InputAction.CallbackContext context)
{
if (context.performed)
{
IsAttacking = true;
AttackDownEvent?.Invoke();
}
else if (context.canceled)
{
IsAttacking = false;
}
}
public void OnBlock(InputAction.CallbackContext context)
{
if (context.performed)
{
IsBlocking = true;
}
else if (context.canceled)
{
IsBlocking = false;
}
}
public void OnInteract(InputAction.CallbackContext context)
{
if (context.ReadValueAsButton()) ;
{
DialogueEvent?.Invoke();
ShopEvent?.Invoke();
PickupEvent?.Invoke();
// InteractEvent?.Invoke();
}
}
public void OnCancel(InputAction.CallbackContext context)
{
if (context.performed)
{
CancelEvent?.Invoke();
}
}
public void OnInventory(InputAction.CallbackContext context)
{
if (context.performed)
{
InventoryEvent?.Invoke();
}
}
// public void OnPickup(InputAction.CallbackContext context)
// {
// if (context.ReadValueAsButton())
// {
// PickupEvent?.Invoke();
// }
// }
// public void OnDialogue(InputAction.CallbackContext context)
// {
// if (context.ReadValueAsButton()) ;
// {
// DialogueEvent?.Invoke();
// }
// }
//
// public void OnShop(InputAction.CallbackContext context)
// {
// if (context.ReadValueAsButton()) ;
// {
// ShopEvent?.Invoke();
// }
// }
}
}
AnimationEventRelay.cs
using UnityEngine;
namespace RPG.Control
{
public class AnimationEventRelay : MonoBehaviour
{
public event System.Action PickupItemEvent;
void PickupItem()
{
PickupItemEvent?.Invoke();
}
}
}
Any suggestions?