I suggest we keep the rotation while attacking in the FreeLookState - this gives the player more control as to what he is trying to attack. Since we are adding a forward force to each attack, it’s very easy to “miss” the enemy if you cant rotate mid-attack. Think of God of War chains attacks. Those have a large radius and require the player to target its direction. This is not an issue in the target lock state as we will always move towards the enemy. Here is a video of how this looks and how I implemented it
I just copied the rotation and movement functions from FreeLookState and I’m using them in the AttackingState as well. We also check to see if we are targeting or if our movement is zero, we ignore the rotation function.
This is my PlayerAttackingState.cs
(this also incorporated my other suggestion for here UPDATED - Target State - Press & Release same Button)
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerAttackingState : PlayerBaseState
{
private float _previousFrameTime;
private Attack _attack;
private bool _alreadyAppliedForce;
public PlayerAttackingState(PlayerStateMachine stateMachine, int attackIndex) : base(stateMachine)
{
_attack = stateMachine.Attacks[attackIndex];
}
public override void Enter()
{
stateMachine.Animator.CrossFadeInFixedTime(_attack.AnimationName,_attack.TransitionDuration);
}
public override void Tick(float deltaTime)
{
Move(deltaTime);
FaceTarget();
float normalizedTime = GetNormalizedAtime();
Vector3 movement = CalculateMovement();
if (normalizedTime >= _previousFrameTime && normalizedTime < 1f)
{
if (normalizedTime >= _attack.ForceTime)
{
TryApplyForce();
}
if (stateMachine.InputReader.IsAttacking)
{
TryComboAttack(normalizedTime);
//We check to see if we have a target or are in the targeting state - if we are , we ignore the rotation
if (stateMachine.Targeter.CurrentTarget != null && stateMachine.InputReader.IsTargeting){return;}
//We check to see if we are standing still and our input is zero - if we are , we ignore the rotation
if (movement == Vector3.zero) {return;}
//we trigger the rotation based on player input
FaceDirection(movement, Time.deltaTime);
}
}
else
{
//we check to see if we have a target and we are pressing the targeting button
if (stateMachine.Targeter.CurrentTarget != null && stateMachine.InputReader.IsTargeting)
{
stateMachine.SwitchState(new PlayerTargetingState(stateMachine));
}
else
{
//we check to see if we pressed the targeting button while we are attacking
if (stateMachine.InputReader.IsTargeting)
{
//if we pressed the targeting button while we were attacking, we select a target and enter targeting state
stateMachine.Targeter.SelectTarget();
stateMachine.SwitchState(new PlayerTargetingState(stateMachine));
}
else
{
//if we release the targeting button while we still have a CurrentTarget we remove it before going back to free look
stateMachine.Targeter.Cancel();
stateMachine.SwitchState(new PlayerFreeLookState(stateMachine));
}
}
}
_previousFrameTime = normalizedTime;
}
public override void Exit()
{
}
private void TryComboAttack(float normalizedTime)
{
if (_attack.ComboStateIndex == -1) {return;}
if(normalizedTime < _attack.ComboAttackTime) {return;}
stateMachine.SwitchState(new PlayerAttackingState(stateMachine,_attack.ComboStateIndex));
}
private void TryApplyForce()
{
if (_alreadyAppliedForce) {return;}
stateMachine.ForceReceiver.AddForce(stateMachine.transform.forward*_attack.Force);
_alreadyAppliedForce = true;
}
private float GetNormalizedAtime()
{
AnimatorStateInfo currentAnimatorStateInfo = stateMachine.Animator.GetCurrentAnimatorStateInfo(0);
AnimatorStateInfo nextAnimatorStateInfo = stateMachine.Animator.GetNextAnimatorStateInfo(0);
if (stateMachine.Animator.IsInTransition(0) && nextAnimatorStateInfo.IsTag("Attack"))
{
return nextAnimatorStateInfo.normalizedTime;
}
if (!stateMachine.Animator.IsInTransition(0) && currentAnimatorStateInfo.IsTag("Attack"))
{
return currentAnimatorStateInfo.normalizedTime;
}
return 0f;
}
private Vector3 CalculateMovement()
{
Vector3 forward = stateMachine.MainCameraTransform.forward;
Vector3 right = stateMachine.MainCameraTransform.right;
forward.y = 0f;
right.y = 0f;
forward.Normalize();
right.Normalize();
return forward*stateMachine.InputReader.MovementValue.y + right*stateMachine.InputReader.MovementValue.x;
}
private void FaceDirection(Vector3 movement,float deltaTime)
{
stateMachine.transform.rotation = Quaternion.Lerp(stateMachine.transform.rotation,
Quaternion.LookRotation(movement),
deltaTime*stateMachine.RotationDamping);
}
}