在tick中不断执行更新角色旋转
UpdateGroundedRotation()
UpdateInAirRotation()
核心函数
void ACwlCharacter::SmoothCharacterRotation(const FRotator &Target, float TargetInterpSpeed, float ActorInterpSpeed, float DeltaTime)
{
TargetRotator = UKismetMathLibrary::RInterpTo_Constant(TargetRotator, Target, DeltaTime, TargetInterpSpeed);
FRotator NewRotation = UKismetMathLibrary::RInterpTo(GetActorRotation(), TargetRotator, DeltaTime, ActorInterpSpeed);
SetActorRotation(NewRotation);
}
TargetRotator是一个角色身上一个我自己记录的角色旋转值
Target是调用函数时候,我们希望角色的旋转是Target。这时候TargetRotator一直往Target插值过度
同时当前角色的实际旋转GetActorRotation()也朝向TargetRotator插值过度
所以这里有两个插值速度。
void ACwlCharacter::UpdateGroundedRotation()
{
// 这里做的事就是 character movement 的使用控制器旋转做的事情
float DeltaTime = GetWorld()->GetDeltaSeconds();
if (GetMovementAction() == ECwlMovementAction::Rolling)
{
if (EssentialInfomation.bHasMovementInput)
{
FRotator YawRotation;
YawRotation.Roll = 0.f;
YawRotation.Pitch = 0.f;
YawRotation.Yaw = LastMovementInputRotation.Yaw;
SmoothCharacterRotation(YawRotation, 0.f, 2.f, DeltaTime);
}
return ;
}
if (GetMovementAction() != ECwlMovementAction::None)
{
return ;
}
if (CanUpdateMovingRotation())
{
// 让角色转向查看方向旋转的实现
if (GetRotationMode() == ECwlRotationMode::LookingDirection)
{
if (GetGait() == ECwlGait::Walking || GetGait() == ECwlGait::Running)
{
FRotator YawRotation;
YawRotation.Roll = 0.f;
YawRotation.Pitch = 0.f;
YawRotation.Yaw = GetControlRotation().Yaw + GetAnimCurveValue(TEXT("YawOffset"));
SmoothCharacterRotation(YawRotation, 500, CalculateGroundedRotationRate(), DeltaTime);
}
else if (GetGait() == ECwlGait::Sprinting)
{
FRotator YawRotation;
YawRotation.Roll = 0.f;
YawRotation.Pitch = 0.f;
YawRotation.Yaw = LastVelocityRotation.Yaw;
SmoothCharacterRotation(YawRotation, 500, CalculateGroundedRotationRate(), DeltaTime);
}
}
else if (GetRotationMode() == ECwlRotationMode::VelocityDirection)
{
FRotator YawRotation;
YawRotation.Roll = 0.f;
YawRotation.Pitch = 0.f;
YawRotation.Yaw = LastVelocityRotation.Yaw;
SmoothCharacterRotation(YawRotation, 700, CalculateGroundedRotationRate(), DeltaTime);
}
else if (GetRotationMode() == ECwlRotationMode::Aiming)
{
FRotator YawRotation;
YawRotation.Roll = 0.f;
YawRotation.Pitch = 0.f;
YawRotation.Yaw = GetControlRotation().Yaw;
SmoothCharacterRotation(YawRotation, 1000.f, 20.f, DeltaTime);
}
}
else
{
if (GetViewMode() == ECwlViewMode::ThirdPerson && GetRotationMode() == ECwlRotationMode::Aiming || GetViewMode() == ECwlViewMode::FirstPerson)
{
LimitRotation(-100, 100, 20);
}
const float RotAmountCurve = GetAnimCurveValue(TEXT("RotationAmount"));
if (FMath::Abs(RotAmountCurve) > 0.001)
{
float Yaw = RotAmountCurve * DeltaTime * 30;
AddActorWorldRotation(UKismetMathLibrary::MakeRotator(0, 0, Yaw));
}
TargetRotator = GetActorRotation();
}
}
void ACwlCharacter::UpdateInAirRotation()
{
float DeltaTime = GetWorld()->GetDeltaSeconds();
if (GetRotationMode() == ECwlRotationMode::VelocityDirection || GetRotationMode() == ECwlRotationMode::LookingDirection)
{
SmoothCharacterRotation(UKismetMathLibrary::MakeRotator(0.f, 0.f, EssentialInfomation.InAirRotation.Yaw), 0.f, 5.f, DeltaTime);
}
else if (GetRotationMode() == ECwlRotationMode::Aiming)
{
SmoothCharacterRotation(UKismetMathLibrary::MakeRotator(0.f, 0.f, GetControlRotation().Yaw), 0.f, 15.f, DeltaTime);
EssentialInfomation.InAirRotation = GetControlRotation();
}
}
bool ACwlCharacter::CanUpdateMovingRotation()
{
return (EssentialInfomation.bIsMoving && EssentialInfomation.bHasMovementInput || EssentialInfomation.Speed > 150.f) && !HasAnyRootMotion();
}
void ACwlCharacter::LimitRotation(float AimYawMin, float AimYawMax, float InterpSpeed)
{
float DeltaTime = GetWorld()->GetDeltaSeconds();
float DeltaYaw = UKismetMathLibrary::NormalizedDeltaRotator(GetControlRotation(), GetActorRotation()).Yaw;
if (!UKismetMathLibrary::InRange_FloatFloat(DeltaYaw, AimYawMin, AimYawMax))
{
float Yaw = UKismetMathLibrary::SelectFloat(GetControlRotation().Yaw + AimYawMin, GetControlRotation().Yaw + AimYawMax, DeltaYaw > 0);
FRotator Target = UKismetMathLibrary::MakeRotator(0, 0, Yaw);
SmoothCharacterRotation(Target, 0, InterpSpeed, DeltaTime);
}
}
除了这个地方,我觉得代码都比较简单
if (CanUpdateMovingRotation())
{
// 让角色转向查看方向旋转的实现
if (GetRotationMode() == ECwlRotationMode::LookingDirection)
{
if (GetGait() == ECwlGait::Walking || GetGait() == ECwlGait::Running)
{
FRotator YawRotation;
YawRotation.Roll = 0.f;
YawRotation.Pitch = 0.f;
YawRotation.Yaw = GetControlRotation().Yaw + GetAnimCurveValue(TEXT("YawOffset"));
SmoothCharacterRotation(YawRotation, 500, CalculateGroundedRotationRate(), DeltaTime);
}
意图是旋转模式LookingDirection下,视角转型某个方向,自动旋转过去。
一个是这条曲线是干嘛的
YawRotation.Yaw = GetControlRotation().Yaw + GetAnimCurveValue(TEXT("YawOffset"));
在动画蓝图中。根据速度方向和控制方向的夹角。读取曲线,计算出前后左右4个Yaw。
void UCwlBaseAnimIns::UpdateRotationValues()
{
CalculateMovementDirection();
float Yaw = UKismetMathLibrary::NormalizedDeltaRotator(UKismetMathLibrary::MakeRotFromX(Velocity), Character->GetControlRotation()).Yaw;
FVector VectorFbYawOffset = AlsAnimCurveDataAsset->YawOffsetFb->GetVectorValue(Yaw);
FVector VectorLrYawOffset = AlsAnimCurveDataAsset->YawOffsetLr->GetVectorValue(Yaw);
FYaw = VectorFbYawOffset.X;
BYaw = VectorLrYawOffset.Y;
LYaw = VectorLrYawOffset.X;
RYaw = VectorLrYawOffset.Y;
}
然后在动画的八向移动状态机里面 modify curve。前是FYaw,后是BYaw,左边两个是LYaw,右边两个是RYaw
然后曲线值又被角色读取来修改移动目标。
打开曲线,意图是不同的旋转角度有不同的插值速度?