일단 유튜브나 구글링을 해보아도 거의다 블루프린트로밖에 구현을 한 것이 없었다..
일단 블루프린트로 만든 여러 영상들을 보면서 c++코드로 나만의 방법으로 옮김.
파쿠르 구현
튜토리얼 1~3
간단한 방식? 영상
Vault영상
이 세가지를 참고했는데 다 BP임. 그래도 그나마 도움됬던 부분은 튜토리얼1~3식 영상이였음.
Vault를 구현하기 위해서 LineTrace를 세방 쏠것이다.
Jump키 (Space Bar)를 누를 때 특정거리에서 위에서 아래로 쏘는 LineTrace
HitResult가 true로 반환되면은 전방으로 쏘는 LineTrace
2번을 실행하고 나면은 2번 Trace의 끝 지점에서 Characater로 향하는 LineTrace
이 세가지 값을 이용해서
HighValt, LowThinVault, LowThickVault
높은 곳 올라가는 Vault인지, 얇은 Actor 지나갈 때 Vault인지, 낮은데 두꺼운 Actor 일 때 Vault인지 나눌 수 있다.
먼저 캐릭터의 길이(키) 만큼에서 부터 먼저 위에서 아래로 내리 꽂는 LinTrace를 발생시킨다.
이값에 따라 너무 높다면은 Vault할 수 없고, 또 너무 낮다면 할 수 없다.
일단 앞에있는 물체가 두껍든 아니든 높이부터 잰다.
이높이에 따라서 Actor가 일단 높이가 낮은지 높은지 분기한다.
높이가 낮다면
초록선을 GetActorLocation으로 부터 쏜다.
이것으로 부터 초록선의 HitResult의 HitResult.Location을 얻어온다.
지금 원하는 것은
파란 선 만큼의 길이를 원하기 때문에
반대쪽에서도 같은 방향으로
똑같은 길이 만큼 LineTrace를 쏴주면된다.
그리고
주황선의 Location - 초록선의 Location을 빼면은 길이가 나옴.
현재 좌표에서 좌표를 빼기 때문에 저 길이의 값은 캐릭터의 방향에 따라서 음수가 나올 수도 있어서 절대값을 넣어주어야한다.
AABMyCharacter::VaultMode AABMyCharacter::CanVault()
{
FHitResult HitResult;
FCollisionQueryParams Params(NAME_None, false, this);
Params.AddIgnoredActor(this);
FVector ForwardVector = GetActorLocation() + GetActorForwardVector() * 50.f;
float CapsuleHalfHeight = CapsuleComp->GetScaledCapsuleHalfHeight();
FVector Start = FVector(ForwardVector.X, ForwardVector.Y, ForwardVector.Z + CapsuleHalfHeight);
FVector End = FVector(ForwardVector.X, ForwardVector.Y, ForwardVector.Z - CapsuleHalfHeight);
bool bResult = GetWorld()->LineTraceSingleByChannel
(
OUT HitResult,
Start, End,
ECollisionChannel::ECC_GameTraceChannel1,
Params
);
FColor DrawColor;
if (bResult)
DrawColor = FColor::Green;
else
DrawColor = FColor::Red;
DrawDebugLine(GetWorld(), Start, End, DrawColor, false, 1.f);
if (bResult)
return CanVaultToHit(HitResult);
else
return VaultMode::CantVault;
}
아까말한 먼저 위에서 아래로 내리 꼽는 라인트레이스 이다.
bResult의 값이 true라는 것은 높이가 낮든 높든 일단 범위 안에 맞았다는 말이다.
이후 CanVaultToHit함수를 통해서 해당 Actor가 Vault할 수 있는지 없는지 본다.
높이가 어느정도 인지 확인
표면이 걸을 수 있는지 서있을 수 있는지 확인
오브젝트 위에 서있을 수 있는지
이 세가지를 확인하고 VaultMode를 반환하도록 한다.
AABMyCharacter::VaultMode AABMyCharacter::CanVaultToHit(FHitResult& HitResult)
{
// Vault할 수 있는 높이 인지
FVector HitLocation = HitResult.Location;
FVector HiTTraceEnd = HitResult.TraceEnd;
float Height = HitLocation.Z - HiTTraceEnd.Z;
ABLOG(Warning, TEXT("Object Height : %f"), Height);
ABLOG(Warning, TEXT("Object InRange : %d"), UKismetMathLibrary::InRange_FloatFloat(Height, MinHighVault, MaxHighVault));
if (UKismetMathLibrary::InRange_FloatFloat(Height, MinHighVault, MaxHighVault) == false && UKismetMathLibrary::InRange_FloatFloat(Height, MinLowVault, MaxLowVault) == false) return VaultMode::CantVault;
// 표면이 걸을 수 있는 높이? 경사로인지
if (CheckWalkable(HitResult.Normal.Z, CharacterMovementComp->GetWalkableFloorZ()) == false) return VaultMode::CantVault;
// 오브젝트의 표면 위에 서있을 수 있는지
// HighVault일 경우
if (UKismetMathLibrary::InRange_FloatFloat(Height, MinHighVault, MaxHighVault))
{
CheckCapsuleCollision(FVector(HitLocation.X, HitLocation.Y, HitLocation.Z + CapsuleComp->GetScaledCapsuleHalfHeight() + CapsuleComp->GetScaledCapsuleRadius()), CapsuleComp->GetScaledCapsuleHalfHeight(), CapsuleComp->GetScaledCapsuleRadius());
SetEndingLocation(FVector(HitLocation.X, HitLocation.Y, HitLocation.Z + CapsuleComp->GetScaledCapsuleHalfHeight()));
return VaultMode::HighVaulting;
}
// LowVault일 경우
if (CheckThinOrThick() == VaultMode::LowThickVaulting)
{
SetEndingLocation(FVector(HitLocation.X, HitLocation.Y, HitLocation.Z + CapsuleComp->GetScaledCapsuleHalfHeight()));
CheckCapsuleCollision(FVector(HitLocation.X, HitLocation.Y, HitLocation.Z + CapsuleComp->GetScaledCapsuleHalfHeight() + CapsuleComp->GetScaledCapsuleRadius()), CapsuleComp->GetScaledCapsuleHalfHeight(), CapsuleComp->GetScaledCapsuleRadius());
}
return CheckThinOrThick();
}
해당경우 다 통과 했다면은 CheckThinOrThick를 통해서 두깨를 확인을 해준다.
AABMyCharacter::VaultMode AABMyCharacter::CheckThinOrThick()
{
FHitResult HitResult;
FCollisionQueryParams Params(NAME_None, false, this);
Params.AddIgnoredActor(this);
FVector Start = FVector(GetActorLocation().X, GetActorLocation().Y, GetActorLocation().Z - ForLowVaultCheck);
FVector End = Start + GetActorForwardVector() * LowVaultRange;
bool bResult = GetWorld()->LineTraceSingleByChannel
(
OUT HitResult,
Start, End,
ECollisionChannel::ECC_GameTraceChannel1,
Params
);
FColor DrawColor;
if (bResult)
DrawColor = FColor::Green;
else
DrawColor = FColor::Red;
DrawDebugLine(GetWorld(), Start, End, DrawColor, false, 1.f, 10, 1.f);
// ################################################################
FHitResult HitResultOtherSide;
FCollisionQueryParams ParamsOtherSide(NAME_None, false, this);
ParamsOtherSide.AddIgnoredActor(this);
FVector StartOtherSide = HitResult.TraceEnd;
FVector EndOtherSide = StartOtherSide + (GetActorForwardVector() * -1) * LowVaultRange;
bool bResultOtherSide = GetWorld()->LineTraceSingleByChannel
(
OUT HitResultOtherSide,
StartOtherSide, EndOtherSide,
ECollisionChannel::ECC_GameTraceChannel1,
ParamsOtherSide
);
FColor DrawColorOtherSide;
if (bResultOtherSide)
DrawColorOtherSide = FColor::Orange;
else
DrawColorOtherSide = FColor::Blue;
DrawDebugLine(GetWorld(), StartOtherSide, EndOtherSide, DrawColorOtherSide, false, 1.f, 10, 1.f);
FVector Depth = HitResultOtherSide.Location - HitResult.Location;
float DepthX = FMath::Abs(Depth.X);
ABLOG(Warning, TEXT("Low Actor's Depth : %f"), FMath::Abs(Depth.X));
// Low Actor Thin
if (DepthX >= 10.f && DepthX <= MinLowDepth)
{
FVector OriginalPos = FVector(HitResultOtherSide.Location.X, HitResultOtherSide.Location.Y, HitResultOtherSide.Location.Z + ForLowVaultCheck);
FVector EndPos = OriginalPos + (GetActorForwardVector() * CapsuleComp->GetScaledCapsuleRadius() * 2);
SetEndingLocation(EndPos);
CheckCapsuleCollision(EndPos, CapsuleComp->GetScaledCapsuleHalfHeight(), CapsuleComp->GetScaledCapsuleRadius());
return VaultMode::LowThinVaulting;
}
// Low Actor Thick
else if (DepthX > MinLowDepth || DepthX < 10.f)
{
return VaultMode::LowThickVaulting;
}
return VaultMode::CantVault;
}
OtherSide가 주황선이다.
뭐 사실 방법은 많은거같다. 어떻게든
위에서 먼저 쏘고 높이 구하고
높이에 따라 낮은지 높은지 확인
높으면 그대로 파쿠르 해주고 낮다면은 그 낮은 Actor의 길이?(두깨)를 구해준다.
이 두깨에 따라서 계단 밟듯이 올라갈지 파쿠르해서 그냥 넘어갈지 결정
이렇게 구현을 해주면 된다.
그리고 중간에 애를 많이 먹었던 부분이
선형보간을 통해서 Character를 움직이게 하였는데 선형보간할 때
StartLocation이랑 EndingLocation을 잡는데 시간을 많이 잡아 먹음.
float AABMyCharacter::VaultTick(float DeltaTime)
{
Progress += DeltaTime / VaultSpeed;
Progress = FMath::Clamp(Progress, 0.f, 1.f);
if (Progress >= 1.f)
{
SetVaultEnd();
}
return Progress;
}
일단 선형보간할 때 진행상태를 반환하는 VaultTick함수인데 Progress에 따라 SetActorLocation을 해줄 것이다.
void AABMyCharacter::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
switch (VaultState)
{
case VaultMode::HighVaulting:
{
SetActorLocation(FMath::Lerp(StartingLocation, EndingLocation, VaultTick(DeltaTime)));
}
break;
case VaultMode::LowThinVaulting:
{
SetActorLocation(FMath::Lerp(StartingLocation, EndingLocation, VaultTick(DeltaTime)));
}
break;
case VaultMode::LowThickVaulting:
{
SetActorLocation(FMath::Lerp(StartingLocation, EndingLocation, VaultTick(DeltaTime)));
}
break;
}
}
그러면 어떤 Vault상태인지 상관없이 선형보간한 값으로 일정하게 쭉 진행이 된다.
이것도 선형보간 방식으로 구현을 해도 되고 자기만의 방식으로 마음대로 캐릭터를 움직이게 해도된다.
근데 유튜브나 구글링했을 때는 거의 대부분
Vault할 경우 Capsulecomp를 가져와서
CapsuleComp->SetCollisionEnabled(ECollisionEnabled::NoCollision);
CharacterMovementComp->SetMovementMode(EMovementMode::MOVE_Flying);
이런식으로 했다가 Delay를 준다음에 이동이 끝나고 나면은
ECollisionEnabled::CollsiionPhysicsAndQuery
MovementMode::MOVE_Walking);
이렇게 다시 돌려놓는? 식이였는데 본인은
이 영상 참고함.