Need some help with the RPG/3rdPerson

Hello Brian.
I’m following your course in the github and must admit I encounter some trouble with some part.
I’ve read the BAhaa topic (Bahaa’s problem), have the same trouble but not the same solution unfortunatly.
The targeting Problem.
Like Bahaa, i’m unable to target an enemy.
The Console says me:

NullReferenceException: Object reference not set to an instance of an object
RPG.Combat.Targeting.Target.Awake () (at Assets/Scripts/Combat/Targeting/Target.cs:26)

it sends me to the same’s Bahaa line:

        private void Awake()
        {
            health.onDie.AddListener(()=>
            {
                OnDeath?.Invoke(this);
            });
        }

in the PlayerAttackingState.
Bahaa seems sold this by removing the “!” in the boolean line on the top:

 public bool IsValid() => health.IsDead();

But i doesn’t change anything for me…

I’ve too an animator.GotoState errot:

Animator.GotoState: State could not be found
UnityEngine.Animator:CrossFadeInFixedTime (int,single)
RPG.States.Player.PlayerFreeLookState:Enter () (at Assets/Scripts/States/Player/PlayerFreeLookState.cs:20)
RPG.States.StateMachine:SwitchState (RPG.States.State) (at Assets/Scripts/States/StateMachine.cs:13)
RPG.States.Player.PlayerStateMachine:Start () (at Assets/Scripts/States/Player/PlayerStateMachine.cs:25)

Line 20 in Enter method in PlayerAttackingState is:

stateMachine.Animator.CrossFadeInFixedTime(FreeLookBlendTreeHash, stateMachine.CrossFadeDuration);

Line 13 in the same scrit, for SwitchState Method:

            currentState?.Enter();

and line 25:

SwitchState(new PlayerFreeLookState(this));

I’ve an invalide index error too, sending to the same lines (13-20-25):

Invalid Layer Index ‘-1’
UnityEngine.Animator:CrossFadeInFixedTime (int,single)
RPG.States.Player.PlayerFreeLookState:Enter () (at Assets/Scripts/States/Player/PlayerFreeLookState.cs:20)
RPG.States.StateMachine:SwitchState (RPG.States.State) (at Assets/Scripts/States/StateMachine.cs:13)
RPG.States.Player.PlayerStateMachine:Start () (at Assets/Scripts/States/Player/PlayerStateMachine.cs:25)

I’ve the parameter ForwardSpeed doesn’t find:

Parameter ‘ForwardSpeed’ does not exist.
UnityEngine.Animator:SetFloat (string,single)
RPG.Movement.Mover:UpdateAnimator () (at Assets/Scripts/Movement/Mover.cs:75)
RPG.Movement.Mover:Update () (at Assets/Scripts/Movement/Mover.cs:35)

The last line in UpdateAnimator method in mover Script:

        private void UpdateAnimator()
        {
            //Création de la variable de vitesse:
            Vector3 velocity = navMeshAgent.velocity;
            //On transforme l'informationd e vitesse gloable en locale
            Vector3 localVelocity = transform.InverseTransformDirection(velocity);
            //On récupère l'inforamtion locale de vitesse actuelle
            float speed = localVelocity.z;
            //On applique à l'Animator la valeur récupérée plus haut.
            GetComponent<Animator>().SetFloat("ForwardSpeed", speed);
        }

And in the Update when it calls the UpdateAnimator Method:

        void Update()
        {
            if(isPlayer) return;
            navMeshAgent.enabled = !health.IsDead();//Le navmesh est actif seulement si le joueur est vivant. sinon il est 
            UpdateAnimator();        
        }

When I approach my enemy, I’ve a problem with the Targeter script in the Targeter object in the Player Prefab:

NullReferenceException: Object reference not set to an instance of an object
RPG.Combat.Targeting.Target.IsValid () (at Assets/Scripts/Combat/Targeting/Target.cs:22)
RPG.Combat.Targeting.Targeter.OnTriggerEnter (UnityEngine.Collider other) (at Assets/Scripts/Combat/Targeting/Targeter.cs:30)

So line of Target Script (where Bahaa remove the !):

public bool IsValid() => !health.IsDead();

And in Targeter script, the if test line in the CurrentTarget method:

        private void OnTriggerEnter(Collider other)
        {
            if (other.TryGetComponent(out Target target) && target.IsValid())
            {
                Debug.Log($"Adding Target {target}");
                targets.Add(target);
                target.OnDeath += RemoveTarget;
            }
        }

I’m a little bit lost.
I assume following Nathan chapter step by step to match with your code modification but I must have miss something unfortunatly…

I’m actually in chapter 20 in the github, and have actually errors according Fighter’s calls:

        public PlayerAttackingState(PlayerStateMachine stateMachine, Attack attack) : base(stateMachine)
        {
            currentAttack = attack;
            
            stateMachine.Fighter.SetCurrentAttack(attack); //Inform fighter of new attack
            
        }

        public PlayerAttackingState(PlayerStateMachine stateMachine, int attack) : base(stateMachine)
        {
            currentAttack = stateMachine.Fighter.GetCurrentAttack(attack); 
        }

I’ve a missing method in the PlayerAttackingState part 19 of the tutorial.
The name “SetLocomotionState” doesn’t exist in thae actual context.

        public override void Enter()
        {
            
            if (currentAttack == null)
            {
                SetLocomotionState();
                return;
            }

and here:

        public override void Tick(float deltaTime)
        {
            float normalizedTime = GetNormalizedTime();
            if (hasCombo && normalizedTime>currentAttack.ComboAttackTime && stateMachine.InputReader.IsAttacking)
            {
                stateMachine.SwitchState(new PlayerAttackingState(stateMachine, currentAttack.NextComboAttack));
                return;
            }
            if (normalizedTime > .99f)
            {
                SetLocomotionState();   
            }
            Move(deltaTime);
        }

I haven’t SetLocomotionState method in this script actually.
Maybe it will be implemented later in the course?
I have a doubt because i’m in chapter 21 on the GitHub and the next lesson concern the inventory.

I precise the GamePad movment or the mouse control works perfectly, so I really want to go farther :slight_smile:

When I press the targeting button, TAB or Left Shoulder, i’ve the debug message displying in the console:
Selecting Target…
It’s the same for the Targeting or cancel targeting action lol :slight_smile:

Thank you for reading my prose and for your help.
François

1 Like

Hey François, sounds like you’re really digging in deep, nice work sticking with it. That NullReferenceException on health usually means the component isn’t assigned on the prefab or it’s missing from the enemy/player. Double-check that your Target script is only on objects with a Health component attached. As for the animator issues, make sure your Animator Controller has the “ForwardSpeed” parameter and that the correct state names exist in the blend tree. That missing SetLocomotionState() method might’ve been refactored out, try replacing it with a transition to PlayerFreeLookState if you’re stuck. Keep at it, you’re close!

Hello Paul.
Thanks for your reply.
Unfortunatly, when I launch my game, and i writte target in the hierarchy, all my enemies, object have health.cs in their roots in the inspector…
image

Looks like a few things are going on here.
For the Animation errors, it looks like possibly the strings in your Animator.StringToHash() aren’t matching up with the names of the states and parameter names in the Animator itself. This is usually caused by spelling errors or case errors. The animator’s state names and parameter names are case sensitive, so “Attack” is not the same as “attack”.

In terms of the Targeter and the null reference errors, this may actually be an issue that recently cropped up in another of our courses. Any chance you’re using Unity 6000? I can’t be certain, but I think Unity changed something under the hood and now interfaces can still be “not null” while the underlying MonoBehavior implementing the interface has become null. I’ll need to run some tests on the repo version of the tutorial.

The SetLocomotionState() method is in the Third Person course. It’s essentially deciding between the PlayerFreelookState and PlayerTargetingState based on whether or not the Targeter has an active target. This makes it so that attacks can return to the correct state every time based on the existence of the active target.

Hello Brian.
Thanks for your feedback.
I had an error with the case for FreeLookBlendTree.
I wrote FreelookBlendTree with low case “look” in the animator.
This error is fixed.

For the targeting"s problem, I use Unity 2022.3.14f1 in directX12 in HDRP.

For the setLocimotionState, I followed t the Nathan’s tutorial according to your script modification.
So Actually Nathan (i’m in section 4 chapter 36) doesn’t talk about this method.
I think it’s later, in the advanced movment section later.

I keep you aware.
Thank a lot.
François

It looks like in PlayerAttackingState, Nathan had the direct code:

            if (stateMachine.Targeter.CurrentTarget != null)
            {
                stateMachine.SwitchState(new PlayerTargetingState(stateMachine));
            }
            else
            {
                stateMachine.SwitchState(new PlayerFreeLookState(stateMachine));
            }

And later in the sections with jumping and falling, he made a method ReturnToLocomotionState(). It’s possible when I wrote the tutorial, I’d refactored all of it as SetLocomotionState for anything that should go back to locomotion

In terms of the targeting problem, neither the directX version or that it’s in HDRP should be a factor. That just deals with graphics, and the PhysX engine doesn’t really care about that part.

One thing I did notice was in your later conversation, you posted a pic of the Targeter apparently at the root of the heirarcy. You want the Targeter to be a child object of the Player so that it moves with the player to target the character.

I think, however, I see what the problem may be with Target…

We’re using a property to access and auto assign the health component:

    [RequireComponent(typeof(Health))]
    public class Target : MonoBehaviour, ITarget
    {
        private Health health;

        private Health Health
        {
            get
            {
                if (!health) health = GetComponent<Health>();
                return health;
            }
        }

And in Awake(), we should be automatically gathering that reference by calling Heatlh.OnDie()

        private void Awake()
        {
            Health.onDie.AddListener(()=>
            {
                OnDeath?.Invoke(this);
            });
        }

        public event System.Action<Target> OnDeath;

Now… this should mean that at the point that the Targeter encounters the Target, health (the backing field) should be fully implemented, but… it’s possible that if we start with a Target in range of the Targeter that OnTriggerEnter could fire before health has been initialized…
Try changing the IsValid() to:

public bool IsValid() => !Health.IsDead();

This will force the property check and auto fill the health variable if it hasn’t been set.

Hello Brian.
I double checked my player hierarchy and your in the depot.
My target GameObject is at the same level than your one :
image

My one:

image

I notice few things in my Target Script.
The private Health Health method is in grey and when I put my mouse on it it displays this message:

It says "private member ‘Target.Health’ isn’t used (IDE0051)
Target.cs(11,9) the private member ‘Target.Health’ isn’t used

I noticed in your script you added ITarget after MonoBehavior.
At this step (14) on the tutorial, there isn’t this ITarget.
If I add using RPG.core; , nothing change and i have an error.

Here is my actual complet target script, I have already you public bool line

using RPG.Attributes;
using UnityEngine;

namespace RPG.Combat.Targeting
{
    [RequireComponent(typeof(Health))]
    public class Target : MonoBehaviour
    {
        private Health health;

        private Health Health
        {
            get
            {
                if (!health) health = GetComponent<Health>();
                return health;
            }
        }

        //Preparing for generic TInRange class
        
        public bool IsValid() => !health.IsDead();//retrait du point d'exclamation

        private void Awake()
        {
            health.onDie.AddListener(()=>
            {
                OnDeath?.Invoke(this);
            });
        }

        public event System.Action<Target> OnDeath;//retiré system avant Action

    }
}

Erff… don’t understand too much why it isn’t working for me at this step…
Thanks for your patience and your help.
François

This should be a public method, not private. The variable health is private so it can’t be changed out side of this class. The property Health needs to be public so it can be accessed anywhere.

In the Awake(), it should be

Health.onDie.AddListener(()=>

not

health.onDie.AddListener()=>

Using the Health property initializes it and finds the health.

I was referring to the Targeter I saw in this screenshot.

It’s not tied to the Player, so it wouldn’t be moving with the player to catch targets. That’s what confused me there.

I wasn’t sure your exact location in the tutorial (nor could I remember exactly where I introduced the ITarget interface). The idea is that you’ll be able to create a custom targeter for items on the ground, AIConversants and Shops, and the ITarget will have the IsValid() method as it’s contract.

lol,
only one capital letter and you switch to hell from heaven :slight_smile:
What a nice console without any NullReferenceException :slight_smile: :
image

When I press TAB now, i’m focusing in the target.
My speed increases a lot, but I will check that this afternoon :slight_smile:
Thanks a lot you to to have spent time for my problem.

I think in section 16 here of your tutorial you must change the awake methode.
It’s written :

        //Preparing for generic TInRange class
        public bool IsValid() => !health.IsDead();

        private void Awake()
        {
            health.onDie.AddListener(()=>
            {
                OnDeath?.Invoke(this);
            });
        }

with lower case h for health.
But it’s fixed on the final depot.

Thanks again for all the time you spent for me.
Have a nice day.
François

PS maybe don’t close too fast this topic, I can have other questions following your tutorial :slight_smile:
At the end, I oplane to create a topic where I will say at wich lesson you talk in Nathan’s course to help future students in their twikings.

For the SetLocomotionState error in section 19, as you say it appear later in the tuto.
Just add the following code in your PlayerBaseState after the protected void FaceTarget method:

        protected void SetLocomotionState()//Ajout, manquant session 19
        {
            stateMachine.SwitchState(stateMachine.Targeter.CurrentTarget?new PlayerTargetingState(stateMachine) : new PlayerFreeLookState(stateMachine));
        }

According the targeter gameObject location which seems to be out of my player in the capture:
image

It’s only the result of a search from me of all object in the scene containing “Target” something :slight_smile:

1 Like