JustMakeGame 8. 角色旋转

在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
然后曲线值又被角色读取来修改移动目标。

打开曲线,意图是不同的旋转角度有不同的插值速度?

上一篇
下一篇