看了一下终于发现了跳跃的关键代码
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); }