Merging the Third Person Controller Course with the RPG Course

0 * 1 =ā€¦ :stuck_out_tongue:
So that leaves the question of why youā€™re hitting twiceā€¦ Do you have multiple TryHit animation events on the animation? Is there still a Hit animation event in your animation? Does your TryHit have multiple damage calls?

nope, weā€™re hitting twice because thereā€™s a ā€˜WeaponDamageā€™ and an ā€˜AttackForceā€™ both exerting forceā€¦ I like the weapon damage because of the easier name and that itā€™s directly accessible from when we are creating the weapon, but attack force is there as well, and I couldnā€™t be more baffled down the line :sweat_smile:

Anyway, Iā€™m still trying to figure out the sudden character generator bug before we get back into adding new stuff :slight_smile:

Force is what flows through all living beings, and can be characterized as light or dar-- erā€¦ sorry, wrong movie.

Damage is applied to health and doesā€¦ .damage
Force is applied to ForceReciever and should doā€¦ no damageā€¦

you really love classic movies, donā€™t you? May the 4th be with you :stuck_out_tongue:

OK so to recall before I go back to my annoying bug (which I am really clueless of how to solve, but Iā€™m tryingā€¦):

  • Attack Force is the force applied to the player, when they hit someone
  • Hit force is the reaction force to push them back

ā€¦ Right?

Thatā€™s correct

good day to you too kind sir :laughing:

Fair enough, for now Iā€™ll let it be. Still trying to figure out what went wrong with my Synty modelsā€¦ :sweat_smile: - link

did I miss out on something with the Impact statesā€¦? When both me and my enemy try to return to idle state after we received a hit, we both literally freeze pretty much foreverā€¦ As if the ā€˜HandleForceCompleted()ā€™ function never unsubscribed to begin withā€¦ (and yes, I made sure it was in both ā€˜Enterā€™ and ā€˜Exitā€™ states of both the Impact States)

In ā€˜EnemyImpactState.csā€™ (SPOILER), this script:

        using UnityEngine;

namespace RPG.States.Enemies {

    public class EnemyImpactState : EnemyBaseState
    {
        private readonly int ImpactHash = Animator.StringToHash("Impact");
        
        public EnemyImpactState(EnemyStateMachine stateMachine) : base(stateMachine) {}

        public override void Enter()
        {
            stateMachine.Animator.CrossFadeInFixedTime(ImpactHash, stateMachine.CrossFadeDuration);
            stateMachine.ForceReceiver.OnForceCompleted += HandleOnForceCompleted;
        }

        public override void Tick(float deltaTime)
        {
            Move(deltaTime);
        }

        public override void Exit()
        {
            stateMachine.ForceReceiver.OnForceCompleted -= HandleOnForceCompleted;
        }

        private void HandleOnForceCompleted() 
        {
            stateMachine.SwitchState(new EnemyIdleState(stateMachine));
        }
    }
}

and my ā€˜PlayerImpactState.csā€™ (ANOTHER SPOILER):

using UnityEngine;

namespace RPG.States.Player {

    public class PlayerImpactState : PlayerBaseState
    {
        private readonly int ImpactHash = Animator.StringToHash("Impact");
        
        public PlayerImpactState(PlayerStateMachine stateMachine) : base(stateMachine) {}

        public override void Enter()
        {
            stateMachine.Animator.CrossFadeInFixedTime(ImpactHash, stateMachine.CrossFadeDuration);
            stateMachine.ForceReceiver.OnForceCompleted += HandleOnForceCompleted;
        }

        public override void Tick(float deltaTime)
        {
            Move(deltaTime);
        }

        public override void Exit()
        {
            stateMachine.ForceReceiver.OnForceCompleted -= HandleOnForceCompleted;
        }

        private void HandleOnForceCompleted() 
        {
            SetLocomotionState();
        }

    }

}

Placing debugs around seems to work, butā€¦ they both still freeze. I went through the tutorial twice so far, I still canā€™t find where I went wrong (apart from placing ā€˜HandleForceAppliedā€™ in the StateMachine script (because the ā€˜Start()ā€™ function needs that), instead of the ā€˜ImpactStateā€™ scriptsā€¦

and I also have to mention that if we donā€™t give the ā€˜OnForceAppliedā€™ event in ā€˜ForceReceiver.csā€™ a type of Vector3, we will get errors, so this is how itā€™s written (since the force type using it in ā€˜AddForceā€™ is of type Vector3):

public event System.Action <Vector3> OnForceApplied;

without doing that, ā€˜Invokeā€™ wonā€™t take in the force Input :slight_smile: (the stuff bixarrio and you teach meā€¦ :laughing:)

It sounds like OnForceCompleted isnā€™t getting calledā€¦
Letā€™s take a look at your ForceReceiver.cs scriptā€¦

using UnityEngine;

namespace RPG.Movement
{
    public class ForceReceiver : MonoBehaviour
    {
        [SerializeField] private float drag = 0.3f;
        [SerializeField] private float minimumImpactVelocity = 0.1f;
        
        private CharacterController controller;
        private UnityEngine.AI.NavMeshAgent agent;

        public event System.Action <Vector3> OnForceApplied;
        public event System.Action OnForceCompleted;

        bool forceActive;

        private void Awake()
        {
            controller = GetComponent<CharacterController>();
            agent = GetComponent<UnityEngine.AI.NavMeshAgent>();
        }

        private float verticalVelocity;

        private Vector3 impact;
        private Vector3 dampingVelocity;

        public Vector3 Movement => impact + Vector3.up * verticalVelocity;

        private void Update()
        {
            if (verticalVelocity < 0f && controller.isGrounded)
            {
                verticalVelocity = Physics.gravity.y * Time.deltaTime;
            }
            else
            {
                verticalVelocity += Physics.gravity.y * Time.deltaTime;
            }
            
            impact = Vector3.SmoothDamp(impact, Vector3.zero, ref dampingVelocity, drag);

            // if the squared magnitude of the impact is below the 'minimumImpactVelocity',
            // reset the impact and enable the agent (i.e: he took the impact, now get back to normal)
            if (forceActive) 
            {
                if (impact.sqrMagnitude < minimumImpactVelocity) 
                {
                    impact = Vector3.zero;
                    OnForceCompleted?.Invoke();
                    forceActive = false;
                    if (agent) agent.enabled = false;
                }
            }
        }

        public void AddForce(Vector3 force, bool triggerKnockbackEvent = false)
        {
            impact += force;
            if (agent) agent.enabled = false;
            if (triggerKnockbackEvent) OnForceApplied?.Invoke(force);
        }
    }
}

        public void AddForce(Vector3 force, bool triggerKnockbackEvent = false)
        {
            impact += force;
            if (agent) agent.enabled = false;
            if(triggerKnockbackEvent) OnForceApplied?.Invoke(force);
            forceActive = true;
        }

We need to know f the force is active so we only invoke that OnForceCompleted once. Without it, the force never depletes and of course OnForceCompleted never invokes.

I have no idea how I missed that, but itā€™s fixed (for now, still testing it). Thank you again Brian for saving me :slight_smile:

By the way, considering the current state of the project, suppose I want to open it from another project name, computer or something else, what do I need to check for to get it to work? I just gave that a go (because I wanted to test something else), but that did not go as expectedā€¦ I canā€™t even get my controllers to work when the game runs for some reason (not on my main project, but on a copy I tried making out of it)

I imported all the necessary libraries for that (otherwise it wonā€™t run)

You could zip up the project folder (but remove the Library, obj and Temp folders from the zip) and unzip it in a new directory. (It should be that simple). Then you just add the project from disk in the Unity Hub.

Using source control, itā€™s even easier, you just clone the project using your git client, then add the project from disk in the Unity Hub.

SOOā€¦ Go to the unity project location, delete the temporary, library and object files and then unzip it wherever I want to take itā€¦ Got it!

Quick question by the way, for the randomization part of the script, when assigning ā€˜forceAmountā€™, did you mean ā€˜force.sqrMagnitudeā€™ on the Right Hand Side? Placing ā€˜forceAmountā€™ gives errors (for obvious reasonsā€¦)

Apart from that, the randomization isnā€™t working against the player, only the enemyā€¦ Iā€™m guessing it has to do with the mathematical formula, Iā€™m still trying to understand it :slight_smile:

  • ZIP the project
  • Remove the Library, obj, and temp folders from the zip
  • Go to a new directory
  • Unzip the project in that new directory

Yes, I think I might have typed the changes on that one in by hand rather than pasting from the repo.

1 Like

welp, will go investigate the randomizer a bit and see why my enemy canā€™t knock me back :slight_smile:

Not sure what went wrong, but my player literally never goes into impact state, but the enemy does so 100% of the timeā€¦ Any idea what went wrong?

Letā€™s see your Start() and HandleImpact in the PlayerStateMachineā€¦

void Start()
        {
            MainCameraTransform = Camera.main.transform;

            // 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 PlayerDeathState(this));
            else SwitchState(new PlayerFreeLookState(this));

            Health.onDie.AddListener(() => {
                SwitchState(new PlayerDeathState(this));
            });
            Health.onResurrection.AddListener(() => {
                SwitchState(new PlayerFreeLookState(this));
            });

            ForceReceiver.OnForceApplied += HandleForceApplied;
        }

        private void HandleForceApplied(Vector3 force)
        {
            if (Health.IsDead()) return;
            SwitchState(new PlayerImpactState(this));
        }

I removed the randomizer in ā€˜HandleForceApplied()ā€™ for the moment, to see what went wrongā€¦

Seeā€¦ youā€™re asking why it didnā€™t randomize, and the two things I wanted to see were if the method was subscribed to and what your randomize code looked like.

The code I provided was merely an example anyways.

lol ironically enough, even without the randomizer my player was not going into impact state for some reasonā€¦

Anyway, here is my tuned attempt with the randomizer, for both the player and the enemy state machine:

PlayerStateMachine:

        private void HandleForceApplied(Vector3 force)
        {
            if (Health.IsDead()) return;
            float forceAmount = Random.Range(0f, force.sqrMagnitude);
            if (forceAmount > Random.Range(0f, SkillStore.GetSkillLevel(Skill.Attack))) // I created a getter for the SkillStore...
{
            SwitchState(new PlayerImpactState(this));
            }
        }

EnemyStateMachine:

    private void HandleForceApplied(Vector3 force) 
    {
        if (Health.IsDead()) return;
        float forceAmount = Random.Range(0f, force.sqrMagnitude);
        if (forceAmount > Random.Range(0f, BaseStats.GetLevel())) {
        SwitchState(new EnemyImpactState(this));
        }
    }

Whatā€™s concerning me though, is that the impact state never gets entered for the player even without a randomizer, albeit the code is asking it to do soā€¦

Edit: Through code debugging, the player enters the Impact state, but the animation is not playing for the player for some reasonā€¦ I checked the hash, all seems wellā€¦

Edit 2: Entering and Exiting the Impact state work perfectly fineā€¦ now Iā€™m going nuts about why in the world is the animation not playing!

Edit 3: LOOOOOOOOOOOOOOOOL I had ZERO Hit force applied to my first attack with the 1H-Sword, NO WONDER IT WASNā€™T WORKING!

Privacy & Terms