标签:unreal engine c++ 继承 源代码
官方商店里有个PlatformerGame整个免费的游戏,是一个卷轴类的跑酷游戏。整个项目的角色控制器很有意思,可以跑、跳、滑行,很酷。这里来分析下它具体是如何实现的。
UCharacterMovementComponent这个类实现了角色控制器的大部分功能,PlatformerGame实现了一个自定义的MovmentComponent,叫UPlatformerPlayerMovementComp,它从标准的UCharacterMovementComponent继承,并扩展了它的行为。
UPlatformerPlayerMovementComp::UPlatformerPlayerMovementComp(const class FPostConstructInitializeProperties& PCIP)
: Super(PCIP)
{
MaxAcceleration = 200.0f;
BrakingDecelerationWalking = MaxAcceleration;
MaxWalkSpeed = 900.0f;
SlideVelocityReduction = 30.0f;
SlideHeight = 60.0f;
SlideMeshRelativeLocationOffset = FVector(0.0f, 0.0f, 34.0f);
bWantsSlideMeshRelativeLocationOffset = true;
MinSlideSpeed = 200.0f;
MaxSlideSpeed = MaxWalkSpeed + 200.0f;
ModSpeedObstacleHit = 0.0f;
ModSpeedLedgeGrab = 0.8f;
}void UPlatformerPlayerMovementComp::StartFalling(int32 Iterations, float remainingTime, float timeTick, const FVector& Delta, const FVector& subLoc)
{
Super::StartFalling(Iterations, remainingTime, timeTick, Delta, subLoc);
if (MovementMode == MOVE_Falling && IsSliding())
{
TryToEndSlide();
}
}FVector UPlatformerPlayerMovementComp::ScaleInputAcceleration(const FVector& InputAcceleration) const
{
FVector NewAccel = InputAcceleration;
APlatformerGameMode* GI = GetWorld()->GetAuthGameMode<APlatformerGameMode>();
if (GI && GI->IsRoundInProgress())
{
NewAccel.X = 1.0f;
}
return Super::ScaleInputAcceleration(NewAccel);
}游戏一开始,角色就会一直朝右开始跑,玩家只需要控制在合适的时候跳和滑行就行了。那怎么实现不用输入,就一直朝右跑呢?一直给角色一个加速的输入即可。这里判断当前是不是在游戏过程中,是的话,就始终给原始的InputAcceleration参数一个X方向的输入。就好像你一直按着方向键一样。void UPlatformerPlayerMovementComp::PhysWalking(float deltaTime, int32 Iterations)
{
APlatformerCharacter* MyPawn = Cast<APlatformerCharacter>(PawnOwner);
if (MyPawn)
{
const bool bWantsToSlide = MyPawn->WantsToSlide();
if (IsSliding())
{
CalcCurrentSlideVelocityReduction(deltaTime);
CalcSlideVelocity(Velocity);
const float CurrentSpeedSq = Velocity.SizeSquared();
if (CurrentSpeedSq <= FMath::Square(MinSlideSpeed))
{
// slide has min speed - try to end it
TryToEndSlide();
}
}
else if (bWantsToSlide)
{
if (!IsFlying() &&
Velocity.Size() > MinSlideSpeed * 2.0f) // make sure pawn has some velocity
{
StartSlide();
}
}
}
Super::PhysWalking(deltaTime, Iterations);
}void UPlatformerPlayerMovementComp::CalcCurrentSlideVelocityReduction(float DeltaTime)
{
float ReductionCoef = 0.0f;
const float FloorDotVelocity = FVector::DotProduct(CurrentFloor.HitResult.ImpactNormal, Velocity.SafeNormal());
const bool bNeedsSlopeAdjustment = (FloorDotVelocity != 0.0f);
if (bNeedsSlopeAdjustment)
{
const float Multiplier = 1.0f + FMath::Abs<float>(FloorDotVelocity);
if (FloorDotVelocity > 0.0f)
{
ReductionCoef += SlideVelocityReduction * Multiplier; // increasing speed when sliding down a slope
}
else
{
ReductionCoef -= SlideVelocityReduction * Multiplier; // reducing speed when sliding up a slope
}+
}
else
{
ReductionCoef -= SlideVelocityReduction; // reducing speed on flat ground
}
float TimeDilation = GetWorld()->GetWorldSettings()->GetEffectiveTimeDilation();
CurrentSlideVelocityReduction += (ReductionCoef * TimeDilation * DeltaTime);
}const float FloorDotVelocity = FVector::DotProduct(CurrentFloor.HitResult.ImpactNormal, Velocity.SafeNormal());
void UPlatformerPlayerMovementComp::CalcSlideVelocity(FVector& OutVelocity) const
{
const FVector VelocityDir = Velocity.SafeNormal();
FVector NewVelocity = Velocity + CurrentSlideVelocityReduction * VelocityDir;
const float NewSpeedSq = NewVelocity.SizeSquared();
if (NewSpeedSq > FMath::Square(MaxSlideSpeed))
{
NewVelocity = VelocityDir * MaxSlideSpeed;
}
else if (NewSpeedSq < FMath::Square(MinSlideSpeed))
{
NewVelocity = VelocityDir * MinSlideSpeed;
}
OutVelocity = NewVelocity;
}void UPlatformerPlayerMovementComp::StartSlide()
{
if (!bInSlide)
{
bInSlide = true;
CurrentSlideVelocityReduction = 0.0f;
SetSlideCollisionHeight();
APlatformerCharacter* MyOwner = Cast<APlatformerCharacter>(PawnOwner);
if (MyOwner)
{
MyOwner->PlaySlideStarted();
}
}
}void UPlatformerPlayerMovementComp::TryToEndSlide()
{
// end slide if collisions allow
if (bInSlide)
{
if (RestoreCollisionHeightAfterSlide())
{
bInSlide = false;
APlatformerCharacter* MyOwner = Cast<APlatformerCharacter>(PawnOwner);
if (MyOwner)
{
MyOwner->PlaySlideFinished();
}
}
}
}void UPlatformerPlayerMovementComp::SetSlideCollisionHeight()
{
if (!CharacterOwner || SlideHeight <= 0.0f)
{
return;
}
// Do not perform if collision is already at desired size.
if (CharacterOwner->CapsuleComponent->GetUnscaledCapsuleHalfHeight() == SlideHeight)
{
return;
}
// Change collision size to new value
CharacterOwner->CapsuleComponent->SetCapsuleSize(CharacterOwner->CapsuleComponent->GetUnscaledCapsuleRadius(), SlideHeight);
// applying correction to PawnOwner mesh relative location
if (bWantsSlideMeshRelativeLocationOffset)
{
ACharacter* DefCharacter = CharacterOwner->GetClass()->GetDefaultObject<ACharacter>();
const FVector Correction = DefCharacter->Mesh->RelativeLocation + SlideMeshRelativeLocationOffset;
CharacterOwner->Mesh->SetRelativeLocation(Correction);
}
}bool UPlatformerPlayerMovementComp::RestoreCollisionHeightAfterSlide()
{
ACharacter* CharacterOwner = Cast<ACharacter>(PawnOwner);
if (!CharacterOwner)
{
return false;
}
ACharacter* DefCharacter = CharacterOwner->GetClass()->GetDefaultObject<ACharacter>();
const float DefHalfHeight = DefCharacter->CapsuleComponent->GetUnscaledCapsuleHalfHeight();
const float DefRadius = DefCharacter->CapsuleComponent->GetUnscaledCapsuleRadius();
// Do not perform if collision is already at desired size.
if (CharacterOwner->CapsuleComponent->GetUnscaledCapsuleHalfHeight() == DefHalfHeight)
{
return true;
}
const float HeightAdjust = DefHalfHeight - CharacterOwner->CapsuleComponent->GetUnscaledCapsuleHalfHeight();
const FVector NewLocation = CharacterOwner->GetActorLocation() + FVector(0.0f, 0.0f, HeightAdjust);
// check if there is enough space for default capsule size
FCollisionQueryParams TraceParams(TEXT("FinishSlide"), false, CharacterOwner);
FCollisionResponseParams ResponseParam;
InitCollisionParams(TraceParams, ResponseParam);
const bool bBlocked = GetWorld()->OverlapTest(NewLocation, FQuat::Identity, UpdatedComponent->GetCollisionObjectType(), FCollisionShape::MakeCapsule(DefRadius, DefHalfHeight), TraceParams);
if (bBlocked)
{
return false;
}
// restore capsule size and move up to adjusted location
CharacterOwner->TeleportTo(NewLocation, CharacterOwner->GetActorRotation(), false, true);
CharacterOwner->CapsuleComponent->SetCapsuleSize(DefRadius, DefHalfHeight);
// restoring original PawnOwner mesh relative location
if (bWantsSlideMeshRelativeLocationOffset)
{
CharacterOwner->Mesh->SetRelativeLocation(DefCharacter->Mesh->RelativeLocation);
}
return true;
}const bool bBlocked = GetWorld()->OverlapTest(NewLocation, FQuat::Identity, UpdatedComponent->GetCollisionObjectType(), FCollisionShape::MakeCapsule(DefRadius, DefHalfHeight), TraceParams);
void UPlatformerPlayerMovementComp::PauseMovementForObstacleHit()
{
SavedSpeed = Velocity.Size() * ModSpeedObstacleHit;
StopMovementImmediately();
DisableMovement();
TryToEndSlide();
}
void UPlatformerPlayerMovementComp::PauseMovementForLedgeGrab()
{
SavedSpeed = Velocity.Size() * ModSpeedLedgeGrab;
StopMovementImmediately();
DisableMovement();
TryToEndSlide();
}PauseMovementForObstacleHit()处理当遇到障碍时的情况。void UPlatformerPlayerMovementComp::RestoreMovement()
{
SetMovementMode(MOVE_Walking);
if (SavedSpeed > 0)
{
Velocity = PawnOwner->GetActorRotation().Vector() * SavedSpeed;
}
}Unreal Engine 4 C++ PlatformerGame自定义角色控制器源代码分析
标签:unreal engine c++ 继承 源代码
原文地址:http://blog.csdn.net/shangguanwaner/article/details/41952219