Merging the Third Person Controller Course with the RPG Course

I had that issue for a while to be honest, even in my swimming state (I developed my own swimming state for my player because, well… why not? And it’s kinda fun to be honest, but I implemented a trick that introduces a random, occasional bug that’s been bothering me for a while (the price I’m paying for building that thing… :sweat_smile:)). From experience working on that, I found that squaring the value that calls it (the second parameter in ‘Animator.SetFloat(…)’) gets the number closer to what the threshold has, usually eliminating the problem…

What a very silly mistake! Thanks for catching that!

1 Like

There’s always a little sliding happening, even with the added calculations to determine the true speed of the character. Ideally, you need to know the exact speed that any given animation moves per second, and that will vary from animation to animation. It’s relatively easy to tune the movement speed for one animation, it gets a bit more complex when you have to calculate movement speeds for 12 of them (and that’s just three weapons)…

I may explore in a future lecture the concept of storing the speeds needed to calculate movement in the WeaponConfig (this is the most logical place for them as Freelook and Targeting animation speeds vary most based on the current weapon) in a future tutorial.

1 Like

lol mine isn’t a “little sliding”… mine is DEFAULT SLIDING, and if you’re lucky, you’ll see him walk :man_shrugging: (OK seriously, where do I start fine-tuning to fix that? The thresholds or the code…?!)

On a serious note, please have a look at this. I’m not sure if the error is there or not, but I think it’s a good starting point:

and please check your messages. I almost forgot I asked a question about Synty’s materials, and color changing :slight_smile:

Edit: I removed the asterisk from this line in ‘EnemyPatrolState.Enter()’, and that fixed the problem:

movementSpeed *= stateMachine.MovementSpeed; // just delete the asterisk sign (the '*' sign)

Apart from the occasional hiccups where he plays no animations and just slides. This is usually fixed by exiting the game scene to the main menu and then coming back later

now we can have a look at this, until bixarrio comes back online :slight_smile:

question. Why don’t we add a bit of camera vibration when we unleash an attack? The value of how strong that attack would be would rely on how much damage we have done, and for now we can make it for swords-only (for ranged weapons, that would be an explosion for specific explosive arrows). It’s basically something that strongly indicates that we did a strong attack. Considering the name of the next section as “Polish”, I figured I’d ask about that :slight_smile:

And how do you stabilize a freeLook camera when you’re moving at high speed?

I’m not planning a specific lecture on camera shake, as this has been covered in a lot of other areas. I’ll give you a hint, though… Cinemachine Impulse Listener | Cinemachine | 2.9.7

1 Like

Hello again. OK so… there are a few things that I am struggling to fix:

  1. The enemy won’t patrol for some reason. I made a copy off my working enemy, but this one is stubborn and just won’t patrol. Nothing changed, but I can’t get a patrol path from the scene itself on his prefab (I’m guessing that’s the problem), and unsurprisingly, he won’t move… Is there any other steps I missed out on to fix this? (and when he respawns, he’s… standing airborne to say the least :sweat_smile: - he’s basically not taking any type of gravity). He won’t attack, patrol, get impacted on hits, or play any death animations… Something is completely off here

  2. It’s an Out-of-context problem. I’ll leave it at the very end (but I do need your help addressing it)

  3. For now, it’s perfectly fine that the arrow won’t hit anything beyond a few centimeters, right?

  4. We shall address the experience gain in the future as well, right?

I followed the tutorial exactly as is, but these 4 issues still exist… I’m guessing at least 1,3 and 4 (I will address 2 later. It’s a shame to have a brilliant system like a quiver and leave it out to be honest) will be addressed soon, right?

I also have to mention that although the lecture did not mention adding the ‘Shoot()’ event (or at least I didn’t catch it), I placed it on the animation myself


  1. I had a Quiver System that I introduced in the past, and I was trying to integrate it into my game. Frankly speaking, I have a bit of a funny approach to this one, and I’d like some help on it

So, to begin with, I managed to get my player to lose a single arrow per hit, which means ultimately he will run out of arrows, using my own Quiver system, as follows (this is what my ‘Shoot()’ function looks like after implementing it):

void Shoot() {

        // In very simple terms, this function is the 'Hit()' function for 
        // Ranged (Projectile) Weapons, to be called in 'Projectile.cs' (for simplicity in naming's sake)
        // Hit();

        // RPG to Third Person Conversion change:
        if (!currentWeaponConfig.HasProjectile()) return;

        if (TryGetComponent(out ITargetProvider targetProvider)) 
        {
            float damage = GetDamage();
            GameObject targetObject = targetProvider.GetTarget();

            if (targetObject != null) 
            {
                // use the 'LaunchProjectile' function with 5 arguments (as we do have a target)
                currentWeaponConfig.LaunchProjectile(rightHandTransform, leftHandTransform, targetObject.GetComponent<Health>(), gameObject, damage);
            }
            else 
            {
                // use the 'LaunchProjectile' function with 4 arguments (as we don't have a target)
                currentWeaponConfig.LaunchProjectile(rightHandTransform, leftHandTransform, gameObject, damage);
            }

                // (Arrow Deduction here) If you have enough ammo, fire and use the ammo
                if (currentWeaponConfig.GetAmmunitionItem())
                {
                    GetComponent<Quiver>().SpendAmmo(1);
                }
            }
        }

However, I also wanted him to be able to take the bow off his hands (like I did before) if you’re out of ammo, and you attempt to attack something or someone, so I introduced these 2 functions:

    void CheckAmmoQuantity() 
    {
        // If the weapon demands ammo (ranged weapon), but you don't have enough ammo, unequip the weapon and go fight with your hands
        if (currentWeaponConfig.GetAmmunitionItem() && !GetComponent<Quiver>().HasSufficientAmmunition(currentWeaponConfig.GetAmmunitionItem()))
        {
            UnequipRangedWeapon();
        }
    }

the function above is assigned as an animation event, played only at the start of the bow-firing animation

and this:

    private void UnequipRangedWeapon()
    {
            if (!inventory.HasSpaceFor(currentWeaponConfig)) return;
            GetComponent<Fighter>().GetComponent<Inventory>().AddToFirstEmptySlot(currentWeaponConfig, 1, true);
            GetComponent<Fighter>().GetComponent<Equipment>().RemoveItem(EquipLocation.Weapon);
    }

This function unwields the ranged weapon basically

However, the last step I want to do is to avoid playing the ‘Bow’ animation entirely if the player doesn’t have any ammo on him, and the ‘CheckAmmoQuantity()’ function turns out as true (no clue why I didn’t make it a boolean tbh…), and instead punch whoever is ahead of him (you’re out of ammo, and you have nothing in your hand by default)


[UNNECESSARY, BUT YOU MIGHT FIND THIS HELPFUL]

To help out though, I also found this function I once created, but I don’t recall what it does:

bool CheckForProjectileAndSufficientAmmo() {

        // Step 1: If the weapon doesn't have a projectile (swords or basically any melee weapons), you're good
        // Step 2: If the weapon doesn't use Ammunition (maybe an infinity staff or some sort of infinite projectile), you're good
        // Step 3: If you have enough ammo for the right weapon you're holding, you're good
        // Step 4: If you have ammo, but for the wrong weapon, then unequip your weapon and rush to hand-to-hand combat (you're NOT good)

        // Step 1:
        if (!currentWeaponConfig.HasProjectile()) {
            return true;
            }
        // Step 2:
        if (currentWeaponConfig.GetAmmunitionItem() == null) {
            return true;
        }
        // Step 3:
        if (GetComponent<Quiver>().HasSufficientAmmunition(currentWeaponConfig.GetAmmunitionItem())) {
            return true;
        }
        // Step 4:
        if (currentWeaponConfig.GetAmmunitionItem() && !GetComponent<Quiver>().HasSufficientAmmunition(currentWeaponConfig.GetAmmunitionItem())) {
            // If you want to keep pointing at your player without attacking them because of insufficient/wrong ammo, delete 'UnequipRangedWeapon()' below:
            UnequipRangedWeapon();
            return false;
        }

        // Anything else is just false:
        return false;

    }

Yes, assigning something from the hierarchy to a prefab is not supposed to work. I thought you and Brian had addressed this in the respawn topic.

I have a Respawn Manager in my own scene… that’s what makes things harder, because these enemies are never on the scene, they are just instantiated

And you’re just mentioning this now? I’m not sure what you mean about can’t get a patrol path from the scene, but without a patrol path, no the enemy won’t patrol.

The Respawn Manager should have the link to the Patrol Path, and should assign the Patrol Path to the enemy. In the regular RPG course, that is assigned to the AIController, but… The AI Controller no longer does anything… so… you’ll probably have to set the path on the EnemyStateMachine… and you’ll probably need to write a function to set that Patrol path on the StateMachine for that to work…

If you didn’t set the Layers and the Physics intersections at the end of the lecture, then yeah, the arrow will get stopped by your own RangeFinders. If that didn’t do the trick, put a Debug at the beginning of OnTriggerEnter and log out what was hit…

It should already be handled by passing the Instigator to the Health component. It’s working just fine in the course project.

By this point in the tutorial, you should already be familiar with adding Animation Events to the animations when need it. I didn’t explicitly say “You’ll have to add a Shoot Event at the right time”, but I did mention the Shoot Event in the context that it exists on the bow animation that comes with the RPG course, and that we’ll be using it.

Try calling Fighter.CheckAmmoQuantity() in PlayerAttackingState.Exit().

That was added after I started typing this response… um… I have NO CLUE what’s going on with that. Is the EnemyStateMachine on the enemy? Is it enabled?

the enemy I duplicated to replace with my new ranger test enemy had one and it is working perfectly fine. I’m as confused as you as to why this new one refuses to patrol…

I mean that I can’t use the Prefab Copy of the Patrol Path on the game hierarchy with the enemy’s prefab… I’m just trying to get it to work :sweat_smile:

already done all of that before

I did that, here’s a screenie:

not for me, I’ll address that later. For now I just want the enemy to patrol

just making sure…

will give that a go in a bit (again, get the enemy to work properly first)

yes, and yes…

You can’t put a prefab in the Patrol Path, you have to drag a Patrol Path in from the scene (i.e. it must already be in the scene).

So this whole time no experience has been gained???

Well… the StateMachine is not actually running… so I’m not sure what’s going on with that. Are you setting an intial state in Start()? Is there some big red error message you’re not mentioning?

with the new projectile system that I just implemented an hour ago? Nope

Everything else, it’s fine for now (I haven’t checked in a while, was working on construction the past week, and it’s nowhere near done either)

I only found 3 so far, and 2 of them are something I’m very familiar with:

NullReferenceException: Object reference not set to an instance of an object
RPG.States.Player.PlayerTargetingState.Tick (System.Single deltaTime) (at Assets/Project Backup/Scripts/State Machines/Player/PlayerTargetingState.cs:40)
RPG.States.StateMachine.Update () (at Assets/Project Backup/Scripts/State Machines/StateMachine.cs:18)
"SetDestination" can only be called on an active agent that has been placed on a NavMesh.
UnityEngine.AI.NavMeshAgent:SetDestination (UnityEngine.Vector3)
RPG.States.Enemies.EnemyPatrolState:Enter () (at Assets/Project Backup/Scripts/State Machines/Enemy/EnemyPatrolState.cs:61)
RPG.States.StateMachine:SwitchState (RPG.States.State) (at Assets/Project Backup/Scripts/State Machines/StateMachine.cs:13)
EnemyIdleState:Enter () (at Assets/Project Backup/Scripts/State Machines/Enemy/EnemyIdleState.cs:14)
RPG.States.StateMachine:SwitchState (RPG.States.State) (at Assets/Project Backup/Scripts/State Machines/StateMachine.cs:13)
RPG.States.Enemies.EnemyImpactState:HandleOnForceCompleted () (at Assets/Project Backup/Scripts/State Machines/Enemy/EnemyImpactState.cs:29)
RPG.Movement.ForceReceiver:Update () (at Assets/Project Backup/Scripts/Movement/ForceReceiver.cs:76)
"ResetPath" can only be called on an active agent that has been placed on a NavMesh.
UnityEngine.StackTraceUtility:ExtractStackTrace ()
RPG.States.Enemies.EnemyPatrolState:Exit () (at Assets/Project Backup/Scripts/State Machines/Enemy/EnemyPatrolState.cs:115)
RPG.States.StateMachine:SwitchState (RPG.States.State) (at Assets/Project Backup/Scripts/State Machines/StateMachine.cs:11)
RPG.States.Enemies.EnemyPatrolState:Tick (single) (at Assets/Project Backup/Scripts/State Machines/Enemy/EnemyPatrolState.cs:77)
RPG.States.StateMachine:Update () (at Assets/Project Backup/Scripts/State Machines/StateMachine.cs:18)
"ResetPath" can only be called on an active agent that has been placed on a NavMesh.
UnityEngine.StackTraceUtility:ExtractStackTrace ()
RPG.States.Enemies.EnemyChasingState:Exit () (at Assets/Project Backup/Scripts/State Machines/Enemy/EnemyChasingState.cs:67)
RPG.States.StateMachine:SwitchState (RPG.States.State) (at Assets/Project Backup/Scripts/State Machines/StateMachine.cs:11)
RPG.States.Enemies.EnemyChasingState:Tick (single) (at Assets/Project Backup/Scripts/State Machines/Enemy/EnemyChasingState.cs:33)
RPG.States.StateMachine:Update () (at Assets/Project Backup/Scripts/State Machines/StateMachine.cs:18)

I’ll go get you the error locations and update this comment. Give me a minute

it’s already there…

Update: First error (for the enemy errors) leads to this line in ‘EnemyPatrolState.Enter()’:

            stateMachine.Agent.SetDestination(targetPatrolPoint);

Second error leads to this line in ‘EnemyPatrolState.Exit()’:

            stateMachine.Agent.ResetPath();

Same location as the second error is the third error

and these errors only show up when my player pushes the enemy around. Normally, they don’t show up, and he won’t be patrolling either… (the new enemy, that is. Old one has no issues)

as for the player, it’s an error that just shows up when he quits the range of the enemy, quitting the targeting state (it’s no biggie for now)

this is my ‘EnemyStateMachine.Start()’:

private void Start() 
    {
        Agent.updatePosition = false;
        Agent.updateRotation = false;
        PlayerChasingRangedSquared = PlayerChasingRange * PlayerChasingRange;
        Player = GameObject.FindGameObjectWithTag("Player");
        Blackboard["Level"] = BaseStats.GetLevel(); // immediately stores the enemies' combat level in the blackboard (useful for determining the chances of a combo, based on the enemies' attack level, in 'EnemyAttackingState.cs', for now...)
        
        // The following check ensures that if we kill an enemy, save the game, quit and then return later, he is indeed dead
        // (the reason it's here is because 'RestoreState()' happens between 'Awake()' and 'Start()', so to counter for the delay, we do it in start too)
        if (Health.IsDead()) SwitchState(new EnemyDeathState(this));
        else SwitchState(new EnemyIdleState(this));

        Health.onDie.AddListener(() => 
        {
            SwitchState(new EnemyDeathState(this));
        });
        Health.onResurrection.AddListener(() => {
            SwitchState(new EnemyIdleState(this));
        });

        ForceReceiver.OnForceApplied += HandleForceApplied;
    }

Update 1: OK SO… I fixed the first major issue that had to do with the fact that the enemy won’t attack the player. The reason for the bug? I still had an animator override on the NPC Version of my bow, which meant that my enemy was trying to access a non-existent animator, and override it too (and my personal blocking state is literally ineffective against any hits that come at me…)

However, the Patrolling State bug (and pretty much everything else) is still a major headache…

the range of the shots are literally still 1-2 centimeters though, making the “weapon range” variable in ‘WeaponConfig.cs’ pretty much ineffective… Are we fixing this anytime soon?

That’s an odd place for a null reference…

if(stateMachine==null) Debug.Log($"Ok, I don't know why, but this class doesn't have a stateMachine reference!");
if(stateMachine.Agent == null) Debug.Log("${stateMachine.gameObject.name}'s Statemachine doesn't have a reference to the NavMesh Agent.");
stateMachine.Agent.SetDestination(targetPatrolPoint);

If the player pushes the enemy off of the NavMesh, this is the result. This should be handled by the ImpactState movement code in EnemyImpactState, but it occurs to me that throttling the impact states would cause this to bypass… may need to do similar checks in Patrol and Chasing state as well. I’ll review this some time this week.

centimeters or meters? (Remember 1 unit is 1 meter).

Are you saying they won’t fire until they’re within 1-2 meters (the normal attack range)? That’s the next lecture. Are you saying the arrows fire and instantly flag as hit? That should have been covered by filtering out the owner and filtering out the RangeFinders.

neither of them get called. The only thing that happened is that my enemy struck me with an arrow so hard, my player flew back on to the back of the horse and accidentally drove away (literally)…

1-2 centimeters… like you really gotta be incredibly close for a hit to register for you or the enemy. Any further and it won’t register…

not exactly sure what this means to be honest :sweat_smile:

I don’t think my patrolling issue has anything to do with this. I genuinely don’t know where it roots from, but it’s literally happening just because the enemy is… being himself I guess is the right term for this one? In other words, the original enemy that used to work can hold a bow with no issues, and (almost - animations are still messy during cooldown) attack with a bow as expected…

The new copy of the enemy is a total mess though (in terms of patrolling. He won’t move, he can only be pushed around and when let go, he’ll just look at Waypoint zero and stay still as he looks at it), and I have absolutely zero idea why that’s the case, although he’s literally just a copy-paste of something that works

and there’s no other sort of errors that I can provide…

You can’t physically get that close, the Colliders won’t let you. But I did find you had to get within melee range, and they didn’t feel like chasing. As I said, that’s covered in the next section. I ran out of time this weekend.

A hint, though… you might check the ranges on the archer’s EnemyStateMachine…

“The original enemy”? “The new copy?”…
Maybe compare the two to see what’s going on between them?

don’t be surprised, but… they’re literally a copy paste. I don’t think there’s any sort of difference between them :sweat_smile: (the only difference I can find is the weapon being held, and the character class. That’s it) - I’m still convinced that it’s just a falsely assigned patrol path of some reason, and tbh I wish I can find the solution for this as soon as possible (or whatever the problem was…)

true, but apart from the particle and sound effects of a proper hit happening, no hit will be registered unless you’re like… insanely close (and somehow, even though the arrow was not pointing at you in the first place, they will still play at your position, as long as the enemy fired one)

that is also true (along with the patrolling issue)

Try a new literal copy paste, as this copy paste is broken… Not sure how, because quite literally copying a prefab and changing nothing, those prefabs should behave identically.

Privacy & Terms