C++攀岩(上下左右移动)

前面我们完成了组件的绑定和判断条件后,我们就可以开始写攀岩的具体功能了。

首先当我们攀岩时,character应该是处于一个缩着的状态,那我们的碰撞范围也要相应的变小。

重载我们的OnMovementModeChanged,从CharacterMovementComponent中调用,通知角色移动模式已经改变。

virtual	void OnMovementModeChanged(EMovementMode PreviousMovementMode, uint8 PreviousCustomMode) override; 
DECLARE_DELEGATE(FOnEnterClimbState)
DECLARE_DELEGATE(FOnExitClimbState)
FOnEnterClimbState OnEnterClimbStateDelegate;
FOnExitClimbState OnExitClimbStateDelegate;
void UCustomMovementComponent::OnMovementModeChanged(EMovementMode PreviousMovementMode, uint8 PreviousCustomMode)
{
	if (IsOrNotClimbingModel())
	{
		bOrientRotationToMovement = false;//将我们的旋转给取消(character的旋转取决于加速度的方向)
		CharacterOwner->GetCapsuleComponent()->SetCapsuleHalfHeight(48.f);//将我们碰撞胶囊体范围变为一半

		OnEnterClimbStateDelegate.ExecuteIfBound();
	}

	if (PreviousMovementMode == MOVE_Custom && PreviousCustomMode == ECustomMovementMode::MOVE_Climb)//当之前的模式为custom时
	{
		bOrientRotationToMovement = true;
		CharacterOwner->GetCapsuleComponent()->SetCapsuleHalfHeight(96.f);

		const FRotator DirtyRotation = UpdatedComponent->GetComponentRotation();//重置旋转
		const FRotator CleanStandRotation = FRotator(0.f, DirtyRotation.Yaw, 0.f);
		UpdatedComponent->SetRelativeRotation(CleanStandRotation);
		StopMovementImmediately();//立即停止运动

		OnExitClimbStateDelegate.ExecuteIfBound();
	}
	Super::OnMovementModeChanged(PreviousMovementMode, PreviousCustomMode);
}

重载运动更新

void UCustomMovementComponent::PhysCustom(float deltaTime, int32 Iterations)
{
	if (IsOrNotClimbingModel())
	{
		PhysicsClimb(deltaTime, Iterations);
	
	}
	Super::PhysCustom(deltaTime, Iterations);
}

当我们开始攀爬时,我们的物理模式理应也要进入到我们的攀爬状态,所以创建我们的物理状态,将我们的飞行物理状态复制下来进行修改

void PhysicsClimb(float deltaTime, int32 Iterations);
void UCustomMovementComponent::PhysicsClimb(float deltaTime, int32 Iterations)
{
	if (deltaTime < MIN_TICK_TIME)
	{
		return;
	}
	//Process all the Climbable surface info 
	TraceClimbableSurface();

	ProcessClimbableSurfaceInfo();//获取攀爬信息
	bool reachedFloor = CheckHasReachedFloor();
	 if (CheckShouldStopClimbing()|| CheckHasReachedFloor())//当character当地顶部或者接触到地面时
	 {
	 	StopClimbing();
	 }

	RestorePreAdditiveRootMotionVelocity();

	if (!HasAnimRootMotion() && !CurrentRootMotion.HasOverrideVelocity())
	{
		CalcVelocity(deltaTime, 0.f, true, MaxBreakClimbDeceleration);
	}

	ApplyRootMotionToVelocity(deltaTime);

	
	FVector OldLocation = UpdatedComponent->GetComponentLocation();
	const FVector Adjusted = Velocity * deltaTime;
	FHitResult Hit(1.f);

	//Handle climb rotation
	SafeMoveUpdatedComponent(Adjusted, GetClimbRotation(deltaTime), true, Hit);

	if (Hit.Time < 1.f)
	{
		//adjust and try again
		HandleImpact(Hit, deltaTime, Adjusted);
		SlideAlongSurface(Adjusted, (1.f - Hit.Time), Hit.Normal, Hit, true);
	}

	if (!HasAnimRootMotion() && !CurrentRootMotion.HasOverrideVelocity())
	{
		Velocity = (UpdatedComponent->GetComponentLocation() - OldLocation) / deltaTime;
	}

	SnapMovementToClimbableSurfaces(deltaTime);
}
void UCustomMovementComponent::ProcessClimbableSurfaceInfo()
{
	CurrentClimbableSurfaceLocation = FVector::ZeroVector;
	CurrentClimbableSurfaceNormal = FVector::ZeroVector;

	if (ClimbableSurfacesResults.IsEmpty()) return;

	for (const FHitResult& TracedHitResult : ClimbableSurfacesResults)
	{
		CurrentClimbableSurfaceLocation += TracedHitResult.ImpactPoint;//获取碰撞点
		CurrentClimbableSurfaceNormal += TracedHitResult.ImpactNormal;//获取碰撞点的法线
		
	}
	CurrentClimbableSurfaceLocation /= ClimbableSurfacesResults.Num();//从该数组中获取所以这些可能的位置
	CurrentClimbableSurfaceNormal = CurrentClimbableSurfaceNormal.GetSafeNormal();//归一化
}
FQuat UCustomMovementComponent::GetClimbRotation(float DeltaTime)
{
	const FQuat CurrentQuat = UpdatedComponent->GetComponentQuat();
	if (HasAnimRootMotion() || CurrentRootMotion.HasOverrideVelocity())//当我们有root驱动人物运动时
	{
		return CurrentQuat;
	}

	const FQuat TargetQuat = FRotationMatrix::MakeFromX(-CurrentClimbableSurfaceNormal).ToQuat();//控制我们的旋转始终对着墙壁
	return FMath::QInterpTo(CurrentQuat, TargetQuat, DeltaTime, 5.f);//从“当前”到“目标”的四元数插值。
}
void UCustomMovementComponent::SnapMovementToClimbableSurfaces(float DeltaTime)//控制character始终根据碰撞的方向进行移动
{
	const FVector ComponentForward = UpdatedComponent->GetForwardVector();
	const FVector ComponentLocation = UpdatedComponent->GetComponentLocation();
	const FVector ProjectedCharacterToSurface = 
		(CurrentClimbableSurfaceLocation - ComponentLocation).ProjectOnTo(ComponentForward);

	const FVector SnapVector = -CurrentClimbableSurfaceNormal * ProjectedCharacterToSurface.Length();
	UpdatedComponent->MoveComponent(SnapVector * DeltaTime * MaxClimbSpeed,UpdatedComponent->GetComponentQuat(),true);
}

当物体到达顶部和地面时,我们要让character停止攀爬

bool UCustomMovementComponent::CheckShouldStopClimbing()
{
	if (ClimbableSurfacesResults.IsEmpty()) return true;

	const float DotResult = FVector::DotProduct(CurrentClimbableSurfaceNormal, FVector::UpVector);
	const float DegreeDiff = FMath::RadiansToDegrees(FMath::Acos(DotResult));//获取点乘的角度

	Debug::Print(FString::SanitizeFloat(DegreeDiff), FColor::Cyan,1);

	if (DegreeDiff <= 60.f)
	{
		return true;
	}

	return false;
}

bool UCustomMovementComponent::CheckHasReachedFloor()
{
	const FVector DownVector = -UpdatedComponent->GetUpVector();
	const FVector StartOffset = DownVector * 50.f;
	const FVector Start = UpdatedComponent->GetComponentLocation()+StartOffset;
	const FVector End = Start + DownVector;
	TArray<FHitResult> PossibleFloorHits = GetTraceClimbableSurface(Start, End, true,false);

	if (PossibleFloorHits.IsEmpty()) return false;

	for (const FHitResult& PossibleFloorHit : PossibleFloorHits)
	{
		const bool bFloorReached = 
		FVector::Parallel(PossibleFloorHit.ImpactNormal, FVector::UpVector) &&
			GetUnrotatedClimbVelocity().Z < -10.f;//查看两个法向量是否几乎平行,这意味着它们之间的角度接近 0 度。
		
		if (bFloorReached)
		{
			return true;
		}
	
	}
	return false;
}
FVector UCustomMovementComponent::GetUnrotatedClimbVelocity() const
{
	return UKismetMathLibrary::Quat_UnrotateVector(UpdatedComponent->GetComponentQuat(),Velocity);
}

完成之后,我们设置我们回到我们的character文件里设置上下移动

void HandleGroundMovementInput(const FInputActionValue& Value);
	
void HandleClimbMovementInput(const FInputActionValue& Value);
void AClimbingSystemCharacter::Move(const FInputActionValue& Value)
{
	// input is a Vector2D
	if(!CustomMovementComponent) return;
	if(CustomMovementComponent->IsOrNotClimbingModel())//判断使用哪种模式
	{
		HandleClimbMovementInput(Value);

	}
	else
	{
		HandleGroundMovementInput(Value);
	}
}
void AClimbingSystemCharacter::HandleGroundMovementInput(const FInputActionValue& Value)
{
	const FVector2D MovementVector = Value.Get<FVector2D>();
 
	if (Controller != nullptr)
	{
		// find out which way is forward
		const FRotator Rotation = Controller->GetControlRotation();
		const FRotator YawRotation(0, Rotation.Yaw, 0);
 
		// get forward vector
		const FVector ForwardDirection = FRotationMatrix(YawRotation).GetUnitAxis(EAxis::X);
 
		// get right vector 
		const FVector RightDirection = FRotationMatrix(YawRotation).GetUnitAxis(EAxis::Y);
 
		// add movement 
		AddMovementInput(ForwardDirection, MovementVector.Y);
		AddMovementInput(RightDirection, MovementVector.X);
	}
}
 
void AClimbingSystemCharacter::HandleClimbMovementInput(const FInputActionValue& Value)
{
	const FVector2D MovementVector = Value.Get<FVector2D>();
	const FVector ForwardDirection = FVector::CrossProduct(
		-CustomMovementComponent->GetClimbableSurfaceNormal(),
		GetActorRightVector()
		);
	const FVector RightDirection = FVector::CrossProduct(
		-CustomMovementComponent->GetClimbableSurfaceNormal(),
		-GetActorUpVector()
	);
 
	// add movement 
	AddMovementInput(ForwardDirection, MovementVector.Y);
	AddMovementInput(RightDirection, MovementVector.X);
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值