Third Person Combat Course Issues, Part 3/3

And here is my last problem:

  1. the first 2 states of my combo attack deal absolutely no damage, and the third one outputs only a damage value of 10 (I had to turn off the enemy’s weapon box collider to see this work), regardless of what value I throw into it… This was a past value, and now it deals absolutely no damage

(Before we start, to be able to get this kind of data, I had to either slow my enemy’s attack animation speed down to an absolute zero, or turn his weapon’s Box Collider (or it’s trigger) off)

So for this one, to keep this as simple and direct as possible, my first 2 strikes of the combo attack play the animations perfectly fine, BUT… they deal absolutely no damage, and the third animation hits a weird value of 10 (why is it weird? Because there’s literally no numerical number of 10 in any of my state indexes in my latest update, not in knockbacks, force receivers, impulses or any other values… as you can see in the screenshot below):

I’ll attach my ‘PlayerAttackingState.cs’ script below, as I believe it is potentially responsible for the error below:

public class PlayerAttackingState : PlayerBaseState
{

    private float previousFrameTime;
    private bool alreadyAppliedForce;
    private Attack attack;

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

    public override void Enter()
    {
        stateMachine.Weapon.SetAttack(attack.Damage, attack.Knockback);
        // stateMachine.Shield.SetAttack(attack.Damage);
        stateMachine.Animator.CrossFadeInFixedTime(attack.AnimationName, attack.TransitionDuration);
    }

    public override void Tick(float deltaTime)
    {

        Move(deltaTime);
        FaceTarget();

        // guard to stop us from accidentally getting data from the last animation frame:
        float normalizedTime = GetNormalizedTime(stateMachine.Animator, "Attack");

        if (normalizedTime >= previousFrameTime && normalizedTime < 1f) {            
        // if (normalizedTime < 1f) {
            if (normalizedTime >= attack.ForceTime) TryApplyForce();
            if (stateMachine.InputReader.IsAttacking) {
                TryComboAttack(normalizedTime);
            }
        }

        // if the player isn't holding down the mouse, return to whatever state he was previously in:
        else {
            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 we don't have a combo, or just finished the last one (ComboStateIndex = -1), return:
        if (attack.ComboStateIndex == -1) return;

        // if we are not ready to combo attack, return
        if (normalizedTime < attack.ComboAttackTime) {
            return;
        }

        // Do the combo attack:
        stateMachine.SwitchState (
            new PlayerAttackingState(
                stateMachine,
                attack.ComboStateIndex
            )
        );
    }

    private void TryApplyForce() {
        if (alreadyAppliedForce) return;
        stateMachine.ForceReceiver.AddForce(stateMachine.transform.forward * attack.Force);
        alreadyAppliedForce = true;
    }

}

These are hard to track down because first, we have to determine if the hit is occurring in the first place. For that, we need to add some Debug.Logs to your WeaponDamage.cs in the OnTriggerEnter method.

We also need to determine if the WeaponDamage is getting the correct information from the Attack class (as it is passed through from the WeaponHandler class. Again, adding a Debug.Log to the WeaponHandler.SetAttack() will confirm that this is the case.

Finally, for the shield attack (the second one), as I mentioned in the Udemy forums, you are never actually sending the damage data to the shield, only the Weapon. Therefore the shield has no attack data.

The debugger we created in part 2/3 states that no damage is dealt… quite literally, at all, in any attack, as per the Debug we created in this answer forum (Part 2/3 of my issues)

For that one, I’m still trying to figure out how did I activate it before, because… I literally forgot how :sweat_smile:

As for the values of the weapon damage, they seem to be fine, as through this debugger I implemented in ‘WeaponDamage.SetAttack()’:

public void SetAttack(int damage, float knockback) {
        this.damage = damage;
        this.knockback = knockback;
        Debug.Log($"Weapon Damage: {damage}, Weapon Knockback: {knockback}");
    }

and the results of the debugger, along with the inspector inputs, as shown below:

As you can see, the damage the weapon claims to deal in the debugger matches the values in the inspector (and the first one being a repetition is not an error. I clicked it twice, 7 seconds apart, on purpose. I’m sure you’d have noticed that, but I’m mentioning it here, just in case)

And when I place the same debugger in ‘PlayerAttackingState.Enter()’, as follows:

public override void Enter()
    {
        stateMachine.Weapon.SetAttack(attack.Damage, attack.Knockback);
        // stateMachine.ShieldDamage.SetAttack(attack.Damage, attack.Knockback);
        Debug.Log($"Weapon damage: {attack.Damage}, Weapon Knockback: {attack.Knockback}");
        stateMachine.Animator.CrossFadeInFixedTime(attack.AnimationName, attack.TransitionDuration);
    }

we get the exact same value, so something else is completely wrong, as the value transfer seems to be fine

(I also noticed the Impact reaction force played on my player after he does his first ever in-game combo is, unless there’s an enemy in my radius, it’s harmless otherwise, I.E: it does him no damage, from the debugger as well)

(I believe you meant WeaponDamage, not WeaponHandler.cs, as WeaponHandler.cs handles the enabling and disabling of the collider of the weapons)

I did a bit further investigation a few moments later, and realized my enemy doesn’t have a capsule collider around him to begin with. I added that, but it seemed to do absolutely no benefit in getting my player to be capable of hurting him

I mentioned debugging logs in the other thread, but I see you’ve done that for this case…
It’s almost like your trigger is happening on a different copy of WeaponDamage…

Humor me… Add this line to PlayerStateMachine.Start()

foreach(WeaponDamage wd in GetComponentsInChildren<WeaponDamage>())
{
    Debug.Log($"Weapon Damage found {wd.name} child of {wd.transform.parent.gameObject.name}");
}

Good day Brian. That debugger gives out no results (I’m assuming it should’ve given us something before the player goes to FreeLookState when the game starts). Here is the PlayerStateMachine.Start() function:

// Start is called before the first frame update:
    private void Start()
    {
        // locking and hiding the cursor from the players during gameplay:
        Cursor.lockState = CursorLockMode.Locked;
        Cursor.visible = false;

        // Debugging WeaponDamage:
        foreach (WeaponDamage wd in GetComponentsInChildren<WeaponDamage>()) {
            Debug.Log($"Weapon Damage found {wd.name}, child of {wd.transform.parent.gameObject.name}");
        }

        // Search the scene for the gameObject tagged "Camera" (your camera), and assign its transform to your 'MainCameraTransform':
        MainCameraTransform = Camera.main.transform;
        SwitchState(new PlayerFreeLookState(this));
    }

Try it this way (my bad, it’s disabled)

foreach(WeaponDamage wd in GetComponentsInChildren<WeaponDamage>(true))

Works perfectly fine, here is the message the debugger we placed in “PlayerStateMachine.Start()” returns (when the game starts):

Weapon Damage found WeaponLogic, child of Hand_L
UnityEngine.Debug:Log (object)
PlayerStateMachine:Start () (at Assets/Scripts/StateMachines/Player/PlayerStateMachine.cs:46)

So just the one… Which makes zero sense. The WeaponDamage is getting the data, but then promptly forgetting it??

If your enemy has a CharacterController on it, a CharacterController is a child class of CapsuleCollider.

Are you logging the OnTriggerEnter in WeaponDamage? (I thought I had you put one in, but I could be mistaken).
Additionally, log the methods in WeaponHandler that enable and disable the weapons.

Yup, I am, in both ‘WeaponDamage.SetAttack()’ and ‘WeaponDamage.OnTriggerEnter()’:

By all means, here is the full ‘WeaponDamage.cs’ script:

using System.Collections.Generic;
using UnityEditor.Experimental.GraphView;
using UnityEngine;

public class WeaponDamage : MonoBehaviour
{

    [SerializeField] private Collider myCollider;

    // REVERSE THESE THREE VARIABLES TO PRIVATE:
    public List<Collider> alreadyCollidedWith = new List<Collider>();
    public int damage;
    public float knockback;

    private void OnEnable() {
        alreadyCollidedWith.Clear();
    }

    private void OnTriggerEnter(Collider other) {
        
        if (other == myCollider) return;

        if (alreadyCollidedWith.Contains(other)) return;
        alreadyCollidedWith.Add(other);

        if (other.TryGetComponent<Health>(out Health health)) {
            health.DealDamage(damage);
        }

        if (other.TryGetComponent<ForceReceiver>(out ForceReceiver forceReceiver)) {
            Vector3 direction = (other.transform.position - myCollider.transform.position).normalized;
            Vector3 force = direction * knockback;
            Debug.Log($"Applying {force} force to {forceReceiver.gameObject.name}");
            forceReceiver.AddForce(force);
        }

    }

    public void SetAttack(int damage, float knockback) {
        this.damage = damage;
        this.knockback = knockback;
        Debug.Log($"Setting {myCollider.gameObject.name}'s Weapon Damage on {name}. Damage = {damage}, knockback = {knockback}");
    }

}

Let’s try this:

if(alreadyCollidedWith.Contains(other))
{
     Debug.Log($"Not hitting {other} because we already done so");
     return;
}
else 
{
    Debug.Log($"Adding {other} to alreadyCollidedWith");
    alreadyCollidedWith.Add(other);
}

This one is quite interesting… the cube I threw in there, which has a target.cs script on it, keeps notifying me every few seconds that we have already collided with it (the first time, it was added to the list of colliders), so we won’t attack it again, and it’s referring to a Sphere Collider (I didn’t attack it or anything, it just keeps saying that)

Wait, it’s notifying you that it’s already hit the cube even when you’re not attacking? (And I presume nowhere near the cube?)

Yes, that’s right (I don’t remember how I got it to work before, but now it doesn’t anymore… I apologize for the major confusion, I’m just as confused as you are, if not more)

Nope, I’m near the cube

If you’re getting it while not attacking, then the Weapon collider isn’t getting turned on and off with the animations…

Now that my ledge system is more stable, I can focus more on this issue before we start the major transition to the RPG Project :stuck_out_tongue_winking_eye:

I genuinely don’t know what on Earth is going on here, but somehow… After I assigned my shield to act as a sort of weapon, it worked perfectly fine in assigning damage to my enemy. The problem is, I can’t say the same about my main weapon

If it helps, this is my ‘ShieldDamage.cs’ script:

using System.Collections.Generic;
using UnityEngine;

public class ShieldDamage : MonoBehaviour
{

    [SerializeField] private Collider myCollider;

    private List<Collider> alreadyCollidedWith = new List<Collider>();

    private int damage;
    private float knockback;

    private void OnEnable()
    {
        alreadyCollidedWith.Clear();
    }

    private void OnTriggerEnter(Collider other)
    {

        if (other == myCollider) return;

        if (alreadyCollidedWith.Contains(other)) return;
        alreadyCollidedWith.Add(other);

        if (other.TryGetComponent<Health>(out Health health))
        {
            health.DealDamage(damage);
        }

        if (other.TryGetComponent<ForceReceiver>(out ForceReceiver forceReceiver))
        {
            Vector3 direction = (other.transform.position - myCollider.transform.position).normalized;
            Vector3 force = direction * knockback;
            Debug.Log($"Applying {force} force to {forceReceiver.gameObject.name}");
            forceReceiver.AddForce(force);
        }

    }

    public void SetAttack(int damage, float knockback) {
        this.damage = damage;
        this.knockback = knockback;
    }

}

and this is my ‘WeaponDamage.cs’ script:

using System.Collections.Generic;
using UnityEngine;

public class WeaponDamage : MonoBehaviour
{

    [SerializeField] private Collider myCollider;

    // REVERSE THESE THREE VARIABLES TO PRIVATE:
    public List<Collider> alreadyCollidedWith = new List<Collider>();
    public int damage;
    public float knockback;

    private void OnEnable() {
        alreadyCollidedWith.Clear();
    }

    private void OnTriggerEnter(Collider other) {
        
        if (other == myCollider) return;

        if (alreadyCollidedWith.Contains(other)) {
            Debug.Log($"Not hitting {other} because we already did that");
            return;
        }
        else {
            Debug.Log($"Adding {other} to alreadyCollidedWith");
            alreadyCollidedWith.Add(other);
        }
        if (other.TryGetComponent<Health>(out Health health)) {
            health.DealDamage(damage);
        }

        if (other.TryGetComponent<ForceReceiver>(out ForceReceiver forceReceiver)) {
            Vector3 direction = (other.transform.position - myCollider.transform.position).normalized;
            Vector3 force = direction * knockback;
            Debug.Log($"Applying {force} force to {forceReceiver.gameObject.name}");
            forceReceiver.AddForce(force);
        }

    }

    public void SetAttack(int damage, float knockback) {
        this.damage = damage;
        this.knockback = knockback;
        Debug.Log($"Setting {myCollider.gameObject.name}'s Weapon Damage on {name}. Damage = {damage}, knockback = {knockback}");
    }

}

Did I miss out on something? (The only other difference I can find, is that the shield has a capsule collider, whereas my weapon has a box collider)

Are both colliders Triggers?

Yup, both colliders are triggers. I have cross-checked the settings many many times as well in the inspector, I literally can’t find a single difference that justifies why my weapon won’t work, but my shield, and my enemies’ weapon work perfectly fine

I have also changed my ‘PlayerAttackingState.Enter()’ a little bit, to include the shield damage as well, in case this makes a difference:

public override void Enter()
    {
        stateMachine.Weapon.SetAttack(attack.Damage, attack.Knockback);
        stateMachine.Shield.SetAttack(attack.Damage, attack.Knockback);
        Debug.Log($"Attack Index: {attack}, Weapon damage: {attack.Damage}, Weapon Knockback: {attack.Knockback}");
        stateMachine.Animator.CrossFadeInFixedTime(attack.AnimationName, attack.TransitionDuration);
    }

Are you getting any messages about animation events not being responded to?

Nope, neither, only this one (IGNORE IT) and it’s usually fixed when I restart Unity:

<db8a1c04fd814f0db971268716e408b0>:0)
UnityEditor.Graphs.AnimationStateMachine.Graph.GetEdgeInfo (UnityEditor.Graphs.Edge edge) (at <db8a1c04fd814f0db971268716e408b0>:0)
UnityEditor.Graphs.AnimationStateMachine.EdgeGUI.DoEdges () (at <db8a1c04fd814f0db971268716e408b0>:0)
UnityEditor.Graphs.AnimationStateMachine.GraphGUI.OnGraphGUI () (at <db8a1c04fd814f0db971268716e408b0>:0)
UnityEditor.Graphs.AnimatorControllerTool.StateMachineView (UnityEngine.Rect position, System.Single zoomLevel) (at <db8a1c04fd814f0db971268716e408b0>:0)
UnityEditor.Graphs.AnimatorControllerTool.DoGraph (UnityEngine.Rect graphRect, System.Single zoomLevel) (at <db8a1c04fd814f0db971268716e408b0>:0)
UnityEditor.Graphs.AnimatorControllerTool.<SetupGUI>b__143_12 () (at <db8a1c04fd814f0db971268716e408b0>:0)
UnityEditor.Graphs.AnimatorControllerTool.ScopedOnGUI (System.Action onGUIHandler) (at <db8a1c04fd814f0db971268716e408b0>:0)
UnityEditor.Graphs.AnimatorControllerTool.<SetupGUI>b__143_10 () (at <db8a1c04fd814f0db971268716e408b0>:0)
UnityEngine.UIElements.IMGUIContainer.DoOnGUI (UnityEngine.Event evt, UnityEngine.Matrix4x4 parentTransform, UnityEngine.Rect clippingRect, System.Boolean isComputingLayout, UnityEngine.Rect layoutSize, System.Action onGUIHandler, System.Boolean canAffectFocus) (at <c42523c5e6f741739559ce608c30429a>:0)
UnityEngine.UIElements.IMGUIContainer.HandleIMGUIEvent (UnityEngine.Event e, UnityEngine.Matrix4x4 worldTransform, UnityEngine.Rect clippingRect, System.Action onGUIHandler, System.Boolean canAffectFocus) (at <c42523c5e6f741739559ce608c30429a>:0)
UnityEngine.UIElements.IMGUIContainer.DoIMGUIRepaint () (at <c42523c5e6f741739559ce608c30429a>:0)
UnityEngine.UIElements.UIR.RenderChainCommand.ExecuteNonDrawMesh (UnityEngine.UIElements.UIR.DrawParams drawParams, System.Single pixelsPerPoint, System.Exception& immediateException) (at <c42523c5e6f741739559ce608c30429a>:0)
Rethrow as ImmediateModeException
UnityEngine.UIElements.UIR.RenderChain.Render () (at <c42523c5e6f741739559ce608c30429a>:0)
UnityEngine.UIElements.UIRRepaintUpdater.Update () (at <c42523c5e6f741739559ce608c30429a>:0)
UnityEngine.UIElements.VisualTreeUpdater.UpdateVisualTreePhase (UnityEngine.UIElements.VisualTreeUpdatePhase phase) (at <c42523c5e6f741739559ce608c30429a>:0)
UnityEngine.UIElements.Panel.UpdateForRepaint () (at <c42523c5e6f741739559ce608c30429a>:0)
UnityEngine.UIElements.Panel.Repaint (UnityEngine.Event e) (at <c42523c5e6f741739559ce608c30429a>:0)
UnityEngine.UIElements.UIElementsUtility.DoDispatch (UnityEngine.UIElements.BaseVisualElementPanel panel) (at <c42523c5e6f741739559ce608c30429a>:0)
UnityEngine.UIElements.UIElementsUtility.UnityEngine.UIElements.IUIElementsUtility.ProcessEvent (System.Int32 instanceID, System.IntPtr nativeEventPtr, System.Boolean& eventHandled) (at <c42523c5e6f741739559ce608c30429a>:0)
UnityEngine.UIElements.UIEventRegistration.ProcessEvent (System.Int32 instanceID, System.IntPtr nativeEventPtr) (at <c42523c5e6f741739559ce608c30429a>:0)
UnityEngine.UIElements.UIEventRegistration+<>c.<.cctor>b__1_2 (System.Int32 i, System.IntPtr ptr) (at <c42523c5e6f741739559ce608c30429a>:0)
UnityEngine.GUIUtility.ProcessEvent (System.Int32 instanceID, System.IntPtr nativeEventPtr, System.Boolean& result) (at <ff6b5db041e141f9a771d6b39c070602>:0)

Privacy & Terms