Toggle between different lock on targets

@Brian, asked me to to post this here instead of on Udemy.com.

This was my original message:

So I made it until the end of lesson 33 of the Unity 3rd Person Combat & Traversal course. I’m now able to lock on to the closest target. But lets say there are two or more enemies in front of me and I want to be able to switch between targets using my dpad arrow keys or a shoulder button or something. How would I do this?

I tried using ChatGPT to generate a script and it eventually kinda worked, but it kept giving me different control types like using ‘horizontal’ from the old input system, or a serializedfield where it just let me select one button. I could not figure out how to refactor the script to subscribe to the action… So I thought maybe I need to create a new state… like ActiveTargetingState and have the toggling going on there… but still I couldn’t figure it out. Any help would be welcome!

This is a script it gave me

public List<Target> targets; // List of all available targets
public CinemachineTargetGroup cineTargetgroup; // Reference to the Cinemachine target group
public Camera mainCamera; // Reference to the main camera

private Target currentTarget; // The current target that the player is locked onto
private Target selectedTarget; // The target currently selected by the player using the d-pad

public bool SelectTarget()
{
    if (targets.Count == 0) { return false; }

    Target closestTarget = null;
    float closestTargetDistance = Mathf.Infinity;

    foreach (Target target in targets)
    {
        Vector2 viewPosition = mainCamera.WorldToViewportPoint(target.transform.position);

        if (viewPosition.x < 0 || viewPosition.x > 1 || viewPosition.y < 0 || viewPosition.y > 1)
        {
            continue;
        }

        Vector2 toCenter = viewPosition - new Vector2(0.5f, 0.5f);

        if (toCenter.sqrMagnitude < closestTargetDistance)
        {
            closestTarget = target;
            closestTargetDistance = toCenter.sqrMagnitude;
        }
    }

    if (closestTarget == null) { return false; }

    // If there is no selected target, lock onto the closest target
    if (selectedTarget == null)
    {
        currentTarget = closestTarget;
        cineTargetgroup.AddMember(currentTarget.transform, 1f, 2f);
    }
    // If there is a selected target, switch to it if it's on screen
    else if (targets.Contains(selectedTarget))
    {
        currentTarget = selectedTarget;
        cineTargetgroup.AddMember(currentTarget.transform, 1f, 2f);
    }

    return true;
}

private void UpdateSelectedTarget()
{
    // Get input from the d-pad left and right buttons
    float horizontalInput = Input.GetAxisRaw("Horizontal");

    // If there is no horizontal input, don't do anything
    if (horizontalInput == 0f)
    {
        return;
    }

    // Find the index of the current target in the list of targets
    int currentIndex = targets.IndexOf(selectedTarget);

    // If the current target is not found in the list, select the first target
    if (currentIndex == -1)
    {
        currentIndex = 0;
    }

    // Determine the index of the next target based on the horizontal input
    int nextIndex = currentIndex + (horizontalInput > 0f ? 1 : -1);

    // If the next index is out of range, wrap around to the other end of the list
    if (nextIndex < 0)
    {
        nextIndex = targets.Count - 1;
    }
    else if (nextIndex >= targets.Count)
    {
        nextIndex = 0;
    }

    // Update the selected target to be the next target in the list
    selectedTarget = targets[nextIndex];

    // If the selected target is different from the current target, switch to it
    if (selectedTarget != currentTarget)
    {
        cineTargetgroup.RemoveMember(currentTarget.transform);
        currentTarget = selectedTarget;
        cineTargetgroup.AddMember(currentTarget.transform, 1f, 2f);
    }
}

private void Update()
{
    SelectTarget();
    UpdateSelectedTarget();
}

Then it gave me this code when I asked to use the new input system, but I could not get the action event to work

private void UpdateSelectedTarget()
{
    // Get input from the D-pad's horizontal axis
    float horizontalInput = Input.GetAxisRaw("DPadHorizontal");

    // If the horizontal input is not left or right, don't do anything
    if (horizontalInput != -1f && horizontalInput != 1f)
    {
        return;
    }

    // Find the index of the current target in the list of targets
    int currentIndex = targets.IndexOf(selectedTarget);

    // If the current target is not found in the list, select the first target
    if (currentIndex == -1)
    {
        currentIndex = 0;
    }

    // Determine the index of the next target based on the horizontal input
    int nextIndex = currentIndex + (horizontalInput > 0f ? 1 : -1);

    // If the next index is out of range, wrap around to the other end of the list
    if (nextIndex < 0)
    {
        nextIndex = targets.Count - 1;
    }
    else if (nextIndex >= targets.Count)
    {
        nextIndex = 0;
    }

    // Update the selected target to be the next target in the list
    selectedTarget = targets[nextIndex];

    // If the selected target is different from the current target, switch to it
    if (selectedTarget != currentTarget)
    {
        cineTargetgroup.RemoveMember(currentTarget.transform);
        currentTarget = selectedTarget;
        cineTargetgroup.AddMember(currentTarget.transform, 1f, 2f);
    }
}

Then this code seemed to work but with the wrong input

using System.Collections;
using System.Collections.Generic;
using Cinemachine;
using UnityEngine;

public class Targeter : MonoBehaviour
{
    [SerializeField] private CinemachineTargetGroup cineTargetgroup;
    [SerializeField] private KeyCode switchLockOnKey = KeyCode.Tab;

    private Camera mainCamera;
    [SerializeField] private List<Target> targets = new List<Target>();
    public Target CurrentTarget { get; private set; }
    public Target SelectedTarget { get; private set; }

    private void Start()
    {
        mainCamera = Camera.main;
    }

    private void Update()
    {
        SelectTarget();
        UpdateSelectedTarget();
    }

    private void OnTriggerEnter(Collider other)
    {
        if (!other.TryGetComponent<Target>(out Target target))
        {
            return;
        }

        targets.Add(target);
        target.OnDestroyed += RemoveTarget;
    }

    private void OnTriggerExit(Collider other)
    {
        if (!other.TryGetComponent<Target>(out Target target))
        {
            return;
        }

        RemoveTarget(target);
    }

    public bool SelectTarget()
    {
        if (targets.Count == 0) { return false; }

        Target closestTarget = null;
        float closestTargetDistance = Mathf.Infinity;

        foreach (Target target in targets)
        {
            Vector2 viewPosition = mainCamera.WorldToViewportPoint(target.transform.position);

            if (viewPosition.x < 0 || viewPosition.x > 1 || viewPosition.y < 0 || viewPosition.y > 1)
            {
                continue;
            }

            Vector2 toCenter = viewPosition - new Vector2(0.5f, 0.5f);

            if (toCenter.sqrMagnitude < closestTargetDistance)
            {
                closestTarget = target;
                closestTargetDistance = toCenter.sqrMagnitude;
            }
        }

        if (closestTarget == null) { return false; }

        if (SelectedTarget == null)
        {
            CurrentTarget = closestTarget;
            cineTargetgroup.AddMember(CurrentTarget.transform, 1f, 2f);
        }
        else if (targets.Contains(SelectedTarget))
        {
            CurrentTarget = SelectedTarget;
            cineTargetgroup.AddMember(CurrentTarget.transform, 1f, 2f);
        }

        return true;
    }

    public void UpdateSelectedTarget()
    {
        if (Input.GetKeyDown(switchLockOnKey))
        {
            if (SelectedTarget == null)
            {
                SelectedTarget = targets.Count > 0 ? targets[0] : null;
            }
            else
            {
                int index = targets.IndexOf(SelectedTarget);
                SelectedTarget = targets[(index + 1) % targets.Count];
            }

            if (SelectedTarget != CurrentTarget)
            {
                cineTargetgroup.RemoveMember(CurrentTarget.transform);
                CurrentTarget = SelectedTarget;
                cineTargetgroup.AddMember(CurrentTarget.transform, 1f, 2f);
            }
        }
    }

    public void Cancel()
    {
        if (CurrentTarget == null) { return; }

        cineTargetgroup.RemoveMember(CurrentTarget.transform);
        CurrentTarget = null;
    }

    private void RemoveTarget(Target target)
    {
        if (CurrentTarget == target)
        {
            cineTargetgroup.RemoveMember(CurrentTarget.transform);
            CurrentTarget = null;
        }

        target.OnDestroyed -= RemoveTarget;
        targets.Remove(target);
    }
}

I then figured instead of using the dpad left and right, I’d just use one button to toggle between the enemies in viewport. So I created an action called ‘SwitchLockOn’ and set it to Button North on the gamepad. But I cannot get it to work. Could someone help refactor the code a bit?

Thanks!!

You probably shouldn’t be using the code from ChatGPT. It’s ignoring the fact that we are using the new input system, and it’s also moved the input to Targeter. We’d prefer to stick to the pattern/mechanic we’ve been using throughout the course. Things will get confusing very quickly if you start mixing patterns and stuff.

So if we are just going by a single button to cycle through enemies in range, we will set up that button in the input config - let’s call the action ‘CycleTarget’. It can be whatever you want, but for this we’ll go with that.
Next, we need to decide how we are going to select the targets. For this, I will go with just cycling through the enemies by distance. Pick the closest enemy that is not the current target.
The Targeter is holding a reference to the CurrentTarget so we’ll just keep doing that.

Now, let’s create a method to select the next target. This will just pick the next closest enemy in the list of enemies and update CurrentTarget. I’ve also ‘extracted’ the distance calculation and visibility check from SelectTarget

public void SelectNextTarget()
{
    if (targets.Count == 0) return;

    var orderedTargets = targets
        .Where(t => IsVisible(t))
        .OrderBy(t => CalculateViewDistance(t))
        .ToList();

    foreach(Target target in orderedTargets)
    {
        if (target == CurrentTarget) continue;

        CurrentTarget = target;
        return;
    }
}

private bool IsVisible(Target target)
    => target.GetComponentInChildren<Renderer>().isVisible;

private float CalculateViewDistance(Target target)
{
    var viewPos = mainCamera.WorldToViewportPoint(target.transform.position);
    var toCenter = viewPos - new Vector2(0.5f, 0.5f);
    return toCenter.sqrMagnitude;
}

The enemies are moving and the distance will constantly change, so this may not be exactly what you want. I will post a different solution after this.

Now, in the PlayerTargetingState we just need to listen for the cycle action and pick a new target - Remember to add the event to the InputReader

public override void Enter()
{
    stateMachine.InputReader.TargetEvent += OnTarget;
    stateMachine.InputReader.DodgeEvent += OnDodge;
    stateMachine.InputReader.JumpEvent += OnJump;
    // subscribe to cycle event
    stateMachine.InputReader.CycleTargetEvent += OnCycleTarget;

    stateMachine.Animator.CrossFadeInFixedTime(TargetingBlendTreeHash, CrossFadeDuration);
}

public override void Exit()
{
    stateMachine.InputReader.TargetEvent -= OnTarget;
    stateMachine.InputReader.DodgeEvent -= OnDodge;
    stateMachine.InputReader.JumpEvent -= OnJump;
    // unsubscribe from cycle event
    stateMachine.InputReader.CycleTargetEvent -= OnCycleTarget;
}

private void OnCycleTarget()
{
    stateMachine.Targeter.SelectNextTarget();
}

That’s all you have to do.


As mentioned above, the enemies are moving and the distance will constantly change, so this may not be exactly what you want. What we can do is to find the index of the current target and move to the next one. This is still a little ‘meh’ because targets can be moving in and out of range and the list is constantly changing. However, to do this we can stick to the above, but we’ll change the new SelectNextTarget() method

public void SelectNextTarget()
{
    if (targets.Count == 0) return;
    // Find the current target's index
    var currentIndex = targets.IndexOf(CurrentTarget);
    if (currentIndex == -1)
    {
        // The current target is no longer in the list. Just select the closest target
        SelectTarget();
        return;
    }

    // move to the next index, wrapping around if we're at the end
    var nextIndex = (currentIndex + 1) % targets.Count;
    CurrentTarget = targets[nextIndex];
}

This should cycle through all the enemies


BIG DISCLAIMER
I have written all this code here in the post. I have no idea if any of it will work because I have not tested it. Also, the code that is ‘existing’ was taken from the course’s source repo

Thank you for the awesome help! It works pretty well. The only issue now is that the Cinemachine Targetgroup does not switch targets. The list still contains the first target, and when switching to the next, it doesn’t update. Then the camera doesn’t follow and stays focused on the first target. How do we fix that? :slight_smile: Thank you for your help!

Also, when I leave the PlayerTargetingState by either pressing the shoulder button or walking out of range, and then press it again to focus, I cannot switch between enemies anymore. When I then get out of the PlayerTargetingState again and get back in a second time, I can switch between enemies again. It’s like every 2nd time you press it the code doesn’t run. Same goes for when I swing my sword, it also won’t let me switch enemies anymore

The SelectNextTarget() is very much like the SelectTarget() that’s already there, so you should somewhat keep them aligned. If Selecttarget() sets the target group, then the SelectNextTarget() should, too. When you set the CurrentTarget, just add the same code

I don’t know with which of my suggestions you went, so I’m going to show the fix for the first. Shouldn’t be difficult to do the second if that’s what you need

public void SelectNextTarget()
{
    if (targets.Count == 0) return;

    var orderedTargets = targets
        .Where(t => IsVisible(t))
        .OrderBy(t => CalculateViewDistance(t))
        .ToList();

    foreach(Target target in orderedTargets)
    {
        if (target == CurrentTarget) continue;

        // Remove current target from cm target group
        cineTargetGroup.RemoveMember(CurrentTarget.transform);
        CurrentTarget = target;
        // Add new target to cm target group
        cineTargetGroup.AddMember(CurrentTarget.transform, 1f, 2f);
        return;
    }
}

Edit
Actually, I can show both.
You make a simple method

private void SetCurrentTarget(Target newCurrent)
{
    // Remove current target from cm target group
    cineTargetGroup.RemoveMember(CurrentTarget.transform);

    // Set the new current target
    CurrentTarget = newCurrent;

    // Add new target to cm target group
    cineTargetGroup.AddMember(CurrentTarget.transform, 1f, 2f);
}

Now it doesn’t matter which option you chose. Just call the method

Option 1

public void SelectNextTarget()
{
    if (targets.Count == 0) return;

    var orderedTargets = targets
        .Where(t => IsVisible(t))
        .OrderBy(t => CalculateViewDistance(t))
        .ToList();

    foreach(Target target in orderedTargets)
    {
        if (target == CurrentTarget) continue;

        SetCurrentTarget(target);
        return;
    }
}

Option 2

public void SelectNextTarget()
{
    if (targets.Count == 0) return;
    // Find the current target's index
    var currentIndex = targets.IndexOf(CurrentTarget);
    if (currentIndex == -1)
    {
        // The current target is no longer in the list. Just select the closest target
        SelectTarget();
        return;
    }

    // move to the next index, wrapping around if we're at the end
    var nextIndex = (currentIndex + 1) % targets.Count;
    SetCurrentTarget(targets[nextIndex]);
}
1 Like

This is odd. It doesn’t look like there is state being kept that could cause this. Can you share your PlayerTargetingState

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class PlayerTargetingState : PlayerBaseState
{
    private readonly int TargetingBlendTreeHash = Animator.StringToHash("TargetingBlendTree");

    private readonly int TargetingForwardHash = Animator.StringToHash("TargetingForward");

    private readonly int TargetingRightHash = Animator.StringToHash("TargetingRight");
    public PlayerTargetingState(PlayerStateMachine stateMachine) : base(stateMachine){}

    private const float CrossFadeDuration = 0.12f;

    public override void Enter()
    {
        stateMachine.InputReader.CancelEvent += OnCancel;

        stateMachine.InputReader.CycleTargetEvent += OnCycleTarget;

        stateMachine.Animator.CrossFadeInFixedTime(TargetingBlendTreeHash, CrossFadeDuration);
        Debug.Log("Switching Camera to Targeting");
    }


//--------------------------------

    public override void Tick(float deltaTime)
    {

        if(stateMachine.InputReader.IsAttacking)
        {
            stateMachine.SwitchState(new PlayerAttackingState(stateMachine, 0)); 
            return; //
        }

        if(stateMachine.Targeter.CurrentTarget == null)
        {
            stateMachine.SwitchState(new PlayerFreeLookState(stateMachine));
            return;
        }

        Vector3 movement = CalculateMovement();

        Move(movement * stateMachine.TargetingMovementSpeed, deltaTime);

        UpdateAnimator(deltaTime);

        FaceTarget();

    }

    private void UpdateAnimator(float deltaTime)
    {
        if(stateMachine.InputReader.MovementValue.y == 0)
        {
            stateMachine.Animator.SetFloat(TargetingForwardHash, 0, 0.12f, deltaTime);
        }
        else
        {
            float value = stateMachine.InputReader.MovementValue.y > 0 ? 1f : -1f; 
            stateMachine.Animator.SetFloat(TargetingForwardHash, value, 0.12f, deltaTime);
        }


        if(stateMachine.InputReader.MovementValue.x == 0)
        {
            stateMachine.Animator.SetFloat(TargetingRightHash, 0, 0.12f, deltaTime);
        }
        else
        {
            float value = stateMachine.InputReader.MovementValue.x > 0 ? 1f : -1f; 

            stateMachine.Animator.SetFloat(TargetingRightHash, value, 0.12f, deltaTime);
        }
    }

    public override void Exit()
    {
        stateMachine.InputReader.CancelEvent += OnCancel;
        Debug.Log("Exiting Targeting State");
    }

    private void OnCancel()
    {
        stateMachine.Targeter.Cancel();
        stateMachine.SwitchState(new PlayerFreeLookState(stateMachine));
    }

    private void OnCycleTarget()
{
    stateMachine.Targeter.SelectNextTarget();
}

    private Vector3 CalculateMovement()
    {
        Vector3 movement = new Vector3();

        movement += stateMachine.transform.right * stateMachine.InputReader.MovementValue.x; // a-d key, left-right arrow 
        movement += stateMachine.transform.forward * stateMachine.InputReader.MovementValue.y;

        return movement;
    }






}

I shared my code. I also notice that for instance when I’m swinging my weapon in the TargetingState, and while swingining I leave the state by pressing the shouder button, it stops the swinging animation halfway and doesn’t finish it. Is that easy to fix?

First, you are not unhooking from the event

public override void Exit()
{
    // ... other code ...
    // unsubscribe from cycle event
    stateMachine.InputReader.CycleTargetEvent -= OnCycleTarget;
}

Yes. I also see that you are not unhooking the cancel event. It’s not supposed to be listening in the attacking state but because you are not unhooking it, it is still active and can cancel the targeting. This is what your OnExit() should look like

public override void Exit()
{
    stateMachine.InputReader.CancelEvent -= OnCancel; // should be -= not +=
    stateMachine.InputReader.CycleTargetEvent -= OnCycleTarget; // unhook target cycle
    Debug.Log("Exiting Targeting State");
}
1 Like

You are amazing. It works like a charm now! Turned out they were pretty straight forward fixes. I’m sorry I’m still very much a beginner.

Could I bother you one last time with a question? Maybe it happens later on in the course’s advanced movements, not sure… but right now the attacking is set up in a way that you have to hold down the action button to execute the 3-part combo. However, I’d much rather just press the action button 3 times to attack rather than holding it. Does not seem so intuitive. But how do I go about doing that?

This is my PlayerAttackingState script

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class PlayerAttackingState : PlayerBaseState
{

    private float previousFrameTime;
    private bool alreadyAppliedForce = false;
    private AttackData attack;


    public PlayerAttackingState(PlayerStateMachine stateMachine, int attackIndex) : base(stateMachine)
    {
        attack = stateMachine.Attacks[attackIndex];
    }

    public override void Enter()
    {
        stateMachine.Weapon.SetAttack(attack.Damage);
        stateMachine.Animator.CrossFadeInFixedTime(attack.AnimationName, attack.TransitionDuration);
    }
    public override void Tick(float deltaTime)
    {
        Move(deltaTime);
        
        FaceTarget();


        float normalizedTime = GetNormalizedTime();
        if(normalizedTime >= previousFrameTime && normalizedTime < 1f) 
        {
                if(normalizedTime >= attack.ForceTime)
                {
                    TryApplyForce();
                }

                if(stateMachine.InputReader.IsAttacking)
                {
                    TryComboAttack(normalizedTime);
                }
        }
        else{
            // go back to locomotion
            if(stateMachine.Targeter.CurrentTarget != null)
            {
                stateMachine.SwitchState(new PlayerTargetingState(stateMachine));
            } else
            {
                stateMachine.SwitchState(new PlayerFreeLookState(stateMachine));
            }
        
        }

        previousFrameTime = normalizedTime;
    }

    public override void Exit()
    {

    }


    private void TryComboAttack(float normalizedTime)
    {
        if(attack.ComboStateIndex == -1) { return; } // if we don't have a combo, return

        if(normalizedTime < attack.ComboAttackTime) { return;} // if it can combo, we need to make sure we are far enough through the animation to execute, if its less, we're not ready to combo attack

        stateMachine.SwitchState // now we can switch state if the above 2 statements are not true
        (
            new PlayerAttackingState
            (
                stateMachine,
                attack.ComboStateIndex
            )
        );
    }

    private void TryApplyForce()
    {
        if(alreadyAppliedForce){ return;}

        stateMachine.ForceReceiver.AddForce(stateMachine.transform.forward * attack.Force);
        alreadyAppliedForce = true;
    }


    private float GetNormalizedTime()
    {
        AnimatorStateInfo currentInfo = stateMachine.Animator.GetCurrentAnimatorStateInfo(0);
        AnimatorStateInfo nextInfo = stateMachine.Animator.GetNextAnimatorStateInfo(0);

        if (stateMachine.Animator.IsInTransition(0) && nextInfo.IsTag("Attack"))
        {
            return nextInfo.normalizedTime; 

        }
        else if(!stateMachine.Animator.IsInTransition(0) && currentInfo.IsTag("Attack")) 
        {
            return currentInfo.normalizedTime;
        }
        else 
        {
            return 0f;
        }

    }

}

I think this may help. It’s something I answered a while ago


Edit
Out of curiosity; which of the cycle options did you go with?

Thank you I’ll check out that post!

I went with the first option. Seems to work fine!

I just noticed another weird issue. In the Input Actions I deleted the Look input for the mouse so I can only look around with the controller’s right stick. However, when I am in thge PlayerTargetingState, I can no longer rotate my camera around the player using the right stick. HOWEVER, I can rotate with the mouse…?! I cannot use the mouse outside of the targetingstate, why would it work during the targeting state?

It may be because the targeting state uses a different camera than the other states. The free look camera has a Cinemachine Input Provider component which the targeting camera doesn’t. It is a little odd that the mouse overrides the target group. What does your Targeting Camera’s inspector look like?

Like this. I would like to be able to rotate around my player in targetingstate though, to see enemies coming. Just not with the mouse.

The targeting camera is set to point the camera at the target. There’s no rotating around the player in targeting state. Not with the way the course has done things. I see your Aim is set to POV but in the course we used Group Composer.

I suspect you may want to just add a Cinemachine Input Provider like the one we have on the free look camera. It may help. I don’t know Cinemachine very well and would not be able to help much with that

1 Like

Right, I changed it to Group Composer and the mouse now no longer interferes with the Targeting state. Thanks! I tried the Input Provider, that seems to work, but the camera is wayyy too floaty. I think I’ll just leave it out and follow the course as is. May be better after all :slight_smile:

I’m not sure if I’m going to be able to figure out how to get the 3x button press combo done, seems quite a long thread, but I’ll give it a try to see if I comprehend. Downside of being a complete beginner, it all sounds complicated lol.

I’m also looking to figure out how to reduce camera snapping when you go out of the targeting state while moving backwards. If you do that, the camera rotates really quickly and the screen kinda snaps. I tried messing with the ease in out settings but that doesn’t help. Any quick tip you have for me? I promimse Ill stop bothering you now. I am realllyyy greatful of you.Learned a lot from you today and from the course.

What it boiled down to was that you change the attack to be like a jump. It fires once, instead of ‘on’ when you press and ‘off’ when you let go. Then, when the press event fires, you attack. Or move to the next attack.

I think stick to the course. I believe at some point we fix that

1 Like

I think I’ll finish the course first since I haven’t gotten to the advanced moves yet. Perhaps it will click then. I’d like to thank you once again for your help today. :slight_smile: Can’t wait to create my dream game.

It’s my pleasure. Enjoy the rest of the course

I’m kind of upset… I saved everything yesterday, closed Unity…now I reopened Unity and the animations won’t play when playing the game. Only idle is playing. I can see from the animator that it is running through the blend trees, also in the targeting state and when attacking. I can see those little bars fill up while executing the movement… however on screen he’s just stuck in an idle animation. No walk, no running, no attacks. How do you bug test something like this? What could it be?

It did throw an error a few times but I can’t seem to replicate it. It just appeared randomly a few times. It was something about object not set as an instance of something and it referred to line 31 in the Inputreader.cs which is

void OnDestroy()
{
controls.Player.Disable();
}

Do you have any idea what it could be?? I can’t get the error to show now that I’m trying. Just wish the animations would play again. Its not that the animation files are broken either because in the previews it does play the animations. Sorry to bother you again.

Privacy & Terms