Hello everyone; I hope you are doing fantastic; I’m just using the lessons from Unity 3rd Person Combat & Traversal as starter points to get my game going.
I have had a fantastic experience adding new features so far.
I was looking for a solution to determine whether the enemy is attacking from the front or the back so if the player is blocking and in a vulnerable position is going to receive damage and transition to the Impact state and so on.
So I wanted to share with you the code I used to achieve that.
Not completely sure If creating two variants from one state, for example, as I did for PlayerBlockingState and PlayerTargetingBlockingState is the right approach to handle this, but in my case worked like a charm, and I can still figure out what it is doing at first glance.
My PlayerBlockingState Looks like this:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerBlockingState : PlayerBaseState
{
private readonly int BlockHash = Animator.StringToHash("Block");
private const float CrossFadeDuration = 0.1f;
private const float blockCoverageAngle = 20f;
public PlayerBlockingState(PlayerStateMachine stateMachine) : base(stateMachine) { }
public override void Enter()
{
stateMachine.Health.SetInvulnerable(true);
stateMachine.Animator.CrossFadeInFixedTime(BlockHash, CrossFadeDuration);
}
private bool AttackComingFromFront(Transform otherTransform)
{
if (!stateMachine.EnemyTransform)
{
return false;
}
// Get the angle between the enemy and the player
// playerDirection is pointing from the player's position to the position of the enemy.
Vector3 playerDirection = stateMachine.EnemyTransform.position - stateMachine.transform.position;
playerDirection.y = 0f;
float angle = Quaternion.Angle(
Quaternion.LookRotation(stateMachine.transform.forward),
Quaternion.LookRotation(playerDirection));
// Debug.Log("Angle between enemy and player: " + angle);
// Debug.Log("Block coverage angle: " + blockCoverageAngle);
// Debug.Log(otherTransform.name + " is attacking from the front: " + (angle > blockCoverageAngle));
return angle <= blockCoverageAngle;
}
public override void Tick(float deltaTime)
{
Move(deltaTime);
Move(deltaTime);
if (!stateMachine.InputReader.IsBlocking)
{
stateMachine.SwitchState(new PlayerFreeLookState(stateMachine));
return;
}
// Check if the enemy is attacking from the back
bool attackFromBack = !AttackComingFromFront(stateMachine.EnemyTransform);
if (attackFromBack)
{
// Set invulnerability to false if the enemy is attacking from the back
stateMachine.Health.SetInvulnerable(false);
}
}
public override void Exit()
{
stateMachine.Health.SetInvulnerable(false);
}
}
Also, since I wanted to block without the need to be in the targeting state
that way, I could block in the PlayerFreeLookState
I created a new state: PlayerTargetingBlockingState
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerTargetingBlockingState : PlayerBaseState
{
private readonly int BlockHash = Animator.StringToHash("Block");
private const float CrossFadeDuration = 0.1f;
public PlayerTargetingBlockingState(PlayerStateMachine stateMachine) : base(stateMachine) { }
public override void Enter()
{
stateMachine.Health.SetInvulnerable(true);
stateMachine.Animator.CrossFadeInFixedTime(BlockHash, CrossFadeDuration);
}
public override void Tick(float deltaTime)
{
Move(deltaTime);
if (!stateMachine.InputReader.IsBlocking)
{ stateMachine.SwitchState(new PlayerTargetingState(stateMachine));
return;
}
}
public override void Exit()
{
stateMachine.Health.SetInvulnerable(false);
}
}