Hi,
I have been struggling with TryApplyForce
in my implementation of combat that uses mouse click attacks rather than click and hold logic.
The issues is: when I set the forceTime
to values below 0.5f its working correctly, however when that values is set to something like 0.8 then the force is added after the whole animation ends rather than at the specified time.
Spent hours and wasn’t able to find details.
Player Attacking State:
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerAttackingState : PlayerBaseState
{
private Attack attack;
private float previousFrameTime;
private float normalizedTime;
private bool comboFailed;
private bool alreadyAppliedForce;
public PlayerAttackingState(
PlayerStateMachine stateMachine,
int AttackIndex
) :
base(stateMachine)
{
attack = stateMachine.Attacks[AttackIndex];
}
public override void Enter()
{
Attack();
stateMachine.InputReader.AttackEvent += TryComboAttack;
}
private void Attack()
{
stateMachine
.Animator
.CrossFadeInFixedTime(attack.AnimationName,
attack.TransitionDuration);
}
public override void Tick(float deltaTime)
{
Move (deltaTime);
FaceTarget();
normalizedTime = GetNormalizedTime();
if (normalizedTime < 1f)
{
if (normalizedTime >= attack.ForceTime)
{
TryApplyForce();
}
}
else
{
SetPreviousState();
}
}
private void TryComboAttack()
{
if (comboFailed) return;
if (attack.ComboStateIndex == -1)
{
return;
}
if (normalizedTime < attack.ComboAttackTime)
{
comboFailed = true;
return;
}
if (
normalizedTime > attack.ComboAttackTime &&
normalizedTime < attack.ComboAttackTime + attack.ComboWindow
)
{
stateMachine
.SwitchState(new PlayerAttackingState(stateMachine,
attack.ComboStateIndex));
}
else
{
comboFailed = true;
}
}
private void TryApplyForce()
{
if (alreadyAppliedForce)
{
return;
}
alreadyAppliedForce = true;
stateMachine
.ForceReceiver
.AddForce(stateMachine.transform.forward * attack.Force);
}
private void SetPreviousState()
{
if (stateMachine.Targeter.CurrentTarget != null)
{
stateMachine.SwitchState(new PlayerTargetingState(stateMachine));
}
else
{
stateMachine.SwitchState(new PlayerFreeLookState(stateMachine));
}
}
public override void Exit()
{
stateMachine.InputReader.AttackEvent -= TryComboAttack;
}
private float GetNormalizedTime()
{
AnimatorStateInfo currentInfo =
stateMachine.Animator.GetCurrentAnimatorStateInfo(0);
AnimatorStateInfo nextInfo =
stateMachine.Animator.GetNextAnimatorStateInfo(0);
if (stateMachine.Animator.IsInTransition(0) && nextInfo.IsTag("Attack"))
{
return nextInfo.normalizedTime;
}
else if (
!stateMachine.Animator.IsInTransition(0) &&
currentInfo.IsTag("Attack")
)
{
return currentInfo.normalizedTime;
}
else
{
return 0;
}
}
}
Force Receiver
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ForceReceiver : MonoBehaviour
{
private float verticalVelocity;
private Vector3 impact;
private Vector3 dampingVelocity;
[SerializeField]
private CharacterController controller;
[SerializeField]
private float drag = 0.3f;
public Vector3 Movement => impact + Vector3.up * verticalVelocity;
private void Update()
{
if (verticalVelocity < 0 && controller.isGrounded)
{
verticalVelocity = Physics.gravity.y * Time.deltaTime;
}
else
{
verticalVelocity += Physics.gravity.y * Time.deltaTime;
}
impact =
Vector3.SmoothDamp(impact, Vector3.zero, ref dampingVelocity, drag);
}
public void AddForce(Vector3 force)
{
impact += force;
}
}
PlayerBaseStats
using UnityEngine;
public abstract class PlayerBaseState : State
{
protected PlayerStateMachine stateMachine;
public PlayerBaseState(PlayerStateMachine stateMachine)
{
this.stateMachine = stateMachine;
}
protected void Move(Vector3 motion, float deltaTime)
{
stateMachine
.CharacterController
.Move((motion + stateMachine.ForceReceiver.Movement) * deltaTime);
}
protected void Move(float deltaTime)
{
Move(Vector3.zero, deltaTime);
}
protected void FaceTarget()
{
if (stateMachine.Targeter.CurrentTarget == null) return;
Vector3 lookPosition =
stateMachine.Targeter.CurrentTarget.transform.position -
stateMachine.transform.position;
lookPosition.y = 0f;
stateMachine.transform.rotation = Quaternion.LookRotation(lookPosition);
}
}
Video: