看Ue4角色代码——跳跃与实现二段跳

看了一下终于发现了跳跃的关键代码

bool UCharacterMovementComponent::DoJump(bool bReplayingMoves)
{
    if ( CharacterOwner && CharacterOwner->CanJump() )
    {
        // Don't jump if we can't move up/down.
        if (!bConstrainToPlane || FMath::Abs(PlaneConstraintNormal.Z) != 1.f)
        {
            Velocity.Z = JumpZVelocity;
            SetMovementMode(MOVE_Falling);
            return true;
        }
    }
    
    return false;
}

这里跳跃就和JumpZVelocity联系在一起了,同时运动状态改成了Falling(我认为这里设置Falling是不对的,因为在空中有上升还有下落两个状态),不过MovementComponent有判断停止下落的函数,可以在状态机里直接用。

当然判断是否可以跳跃就是另一回事了,你也可以自己写一个跳跃函数。

首先是CharactorMovementComponent中的DoJump(),里面检测一下能不能跳跃,(即是否允许角色Z方向的移动,因为要兼容别的类型的游戏。
),之后在ACharacter中DoJump()中与CanJump()进行与运算,CanJump返回CanJumpInternal(),CanJumpInternal()是个事件,所以我们需要
去看CanJumpInternal_Implementation(),果然CanJumpInternal_Implementation是个虚函数,所以修改跳跃逻辑就修改这个函数就好了。

看一下CanJumpInternal_Implementation的逻辑

bool ACharacter::CanJumpInternal_Implementation() const
{
    const bool bCanHoldToJumpHigher = (GetJumpMaxHoldTime() > 0.0f) && IsJumpProvidingForce();

    return !bIsCrouched && CharacterMovement && (CharacterMovement->IsMovingOnGround() || bCanHoldToJumpHigher) && CharacterMovement->IsJumpAllowed() && !CharacterMovement->bWantsToCrouch;
}

!bIsCrouched角色的运行模式不能为蹲

CharacterMovement->IsMovingOnGround() 确定角色是否还处于走路状态

bCanHoldToJumpHigher的是判断当按下跳跃键的时候,是否还能到达最高点

CharacterMovement->IsJumpAllowed() 角色运动组件是否可以跳

!CharacterMovement->bWantsToCrouch不能正在做下蹲动作

另外补充一下相关代码

float UCharacterMovementComponent::GetMaxJumpHeight() const
{
    const float Gravity = GetGravityZ();
    if (FMath::Abs(Gravity) > KINDA_SMALL_NUMBER)
    {
        return FMath::Square(JumpZVelocity) / (-2.f * Gravity);
    }
    else
    {
        return 0.f;
    }
}

一个重力下落公式

void UCharacterMovementComponent::JumpOff(AActor* MovementBaseActor)
{
    if ( !bPerformingJumpOff )
    {
        bPerformingJumpOff = true;
        if ( CharacterOwner )
        {
            const float MaxSpeed = GetMaxSpeed() * 0.85f;
            Velocity += MaxSpeed * GetBestDirectionOffActor(MovementBaseActor);
            if ( Velocity.Size2D() > MaxSpeed )
            {
                Velocity = MaxSpeed * Velocity.GetSafeNormal();
            }
            Velocity.Z = JumpOffJumpZFactor * JumpZVelocity;
            SetMovementMode(MOVE_Falling);
        }
        bPerformingJumpOff = false;
    }
}

 ----------------------------------------------------------------------------------------------

如何实现:

首先在你的角色类的头文件中加入

virtual bool CanJumpInternal_Implementation() const override;

覆盖CanJumpInternal事件

之后在Cpp文件中加入

bool AThirdPersonCharacter::CanJumpInternal_Implementation() const
{
    const bool bCanHoldToJumpHigher = (GetJumpMaxHoldTime() > 0.0f) && IsJumpProvidingForce();

    //return !bIsCrouched && CharacterMovement && (CharacterMovement->IsMovingOnGround() || bCanHoldToJumpHigher) && CharacterMovement->IsJumpAllowed() && !CharacterMovement->bWantsToCrouch;
  
    return !bIsCrouched && CharacterMovement  && CharacterMovement->IsJumpAllowed() && !CharacterMovement->bWantsToCrouch;
}

我把(CharacterMovement->IsMovingOnGround() || bCanHoldToJumpHigher)删掉了,这里就替换上你的二段跳逻辑即可

在头文件中加入2个用于判断二段跳的变量

//最大跳跃次数
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Jump")
int32 MaxJumpNum = 2;

//当前跳跃次数
int32 JumpNum=2;

当然把JumNum在构造函数中赋值才是正确选择 

覆盖事件

    //virtual void OnMovementModeChanged(EMovementMode PrevMovementMode, uint8 PreviousCustomMode = 0) override;

    virtual void Landed(const FHitResult& Hit) override;

2个都可以用,第一个功能更加强大

然后在Cpp文件里:

void AThirdPersonCharacter::Jump()
{
    if (JumpNum>0)
    {
        bPressedJump = true;
        JumpKeyHoldTime = 0.0f;
        JumpNum = JumpNum - 1;
    }
}

void AThirdPersonCharacter::Landed(const FHitResult& Hit)
{
    Super::Landed(Hit);
    JumpNum = MaxJumpNum;
}

当然之前的按键事件绑定也需要修改一下

void AThirdPersonCharacter::SetupPlayerInputComponent(class UInputComponent* InputComponent)
{
    // Set up gameplay key bindings
    check(InputComponent);
  //把这个Jump改为你自己刚才定义的Jump InputComponent
->BindAction("Jump", IE_Pressed, this, &AThirdPersonCharacter::Jump); InputComponent->BindAction("Jump", IE_Released, this, &ACharacter::StopJumping); InputComponent->BindAxis("MoveForward", this, &AThirdPersonCharacter::MoveForward); InputComponent->BindAxis("MoveRight", this, &AThirdPersonCharacter::MoveRight); // We have 2 versions of the rotation bindings to handle different kinds of devices differently // "turn" handles devices that provide an absolute delta, such as a mouse. // "turnrate" is for devices that we choose to treat as a rate of change, such as an analog joystick InputComponent->BindAxis("Turn", this, &APawn::AddControllerYawInput); InputComponent->BindAxis("TurnRate", this, &AThirdPersonCharacter::TurnAtRate); InputComponent->BindAxis("LookUp", this, &APawn::AddControllerPitchInput); InputComponent->BindAxis("LookUpRate", this, &AThirdPersonCharacter::LookUpAtRate); // handle touch devices InputComponent->BindTouch(IE_Pressed, this, &AThirdPersonCharacter::TouchStarted); InputComponent->BindTouch(IE_Released, this, &AThirdPersonCharacter::TouchStopped); }