Unreal/C++ 과제 7 - 도전 구현2 - 중력 및 낙하 구현
Unreal/C++ 과제 7(2) - 중력 및 낙하 구현
과제 소개 및 목표
- 인공 중력 구현
- Tick 함수를 통해 매 프레임 중력 가속도를 직접 계산
- 적절한 중력 상수 (예: -980 cm/s²)를 사용하여 낙하 속도를 구현
- LineTrace 또는 SweepTrace를 사용하여 지면 충돌을 감지
- 착지 시 Z축 속도를 0으로 초기화합니다.
- Tick 함수를 통해 매 프레임 중력 가속도를 직접 계산
- 에어컨트롤 구현 (공중 WASD 제어)
- 공중에서는 지상 이동속도의 30~50% 정도로 제한
- 지상/공중 상태에 따라 이동 로직을 구분하여 자연스러운 움직임을 구현
- 공중에서는 지상 이동속도의 30~50% 정도로 제한
구현 기능
- 인공 중력 구현
- Tick + 임의의 중력 상수 사용
- LineTrace를 통하여 지면 충돌을 감지
- Tick + 임의의 중력 상수 사용
- 에어 컨트롤 및 궁중 이동 관성 구현
- JumpVelocity를 별도로 사용하여 기존 movement에 반영
- JumpVelocity를 별도로 사용하여 기존 movement에 반영
클래스 설명
- 추가 구현 클래스
TaskCharacter (APawn 상속)
- 기존 기능 + Jump 및 인공 중력 상수 추가
- LineTrace를 이용하여 바닥 도착 판정 추가
TaskPlayerController
- Jump Action 추가
코드 구현부
void ATaskCharacter::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
if (MoveVec.Size() > 0)
{
double JumpScale = bJump ? 0.3 : 1.0;
FVector TempMoveVec = MoveVec.GetSafeNormal() * MoveSpeed * DeltaTime * JumpScale;
AddActorLocalOffset(TempMoveVec);
}
if (JumpVec.Size() > 0)
{
JumpVec.X += MoveVec.X * MoveSpeed * DeltaTime;
JumpVec.Y += MoveVec.Y * MoveSpeed * DeltaTime;
JumpVec.Z += GravityScale * DeltaTime;
if (UWorld* World = GetWorld())
{
FVector Start = GetActorLocation();
FVector End = GetActorLocation() + GetActorUpVector() * -TraceHeight;
FHitResult HitResult;
FCollisionQueryParams Params;
Params.AddIgnoredActor(this);
bool bLand = World->LineTraceSingleByChannel(
HitResult, Start, End, ECollisionChannel::ECC_Visibility, Params);
if (bLand)
{
SetActorLocation(Start + GetActorUpVector() * CapsuleComponent->GetScaledCapsuleHalfHeight() / 2);
bJump = false;
JumpVec = FVector::Zero();
}
}
FVector TempJumpVec = JumpVec * DeltaTime;
AddActorLocalOffset(TempJumpVec);
}
if (FMath::IsNearlyZero(RotateR.Yaw) == false)
{
AddActorLocalRotation(FRotator(0.0,RotateR.Yaw * RotateSpeed * DeltaTime,0.0));
}
if (FMath::IsNearlyZero(RotateR.Pitch) == false)
{
FRotator SpringRot = SpringArmComp->GetRelativeRotation() + FRotator(RotateR.Pitch * RotateSpeed * DeltaTime,0.0 , 0.0);
SpringRot.Pitch = FMath::Clamp(SpringRot.Pitch, -40.0, 60.0);
SpringArmComp->SetRelativeRotation(SpringRot);
}
MoveVec = FVector::Zero();
RotateR = FRotator::ZeroRotator;
}
---
void ATaskCharacter::Jump(const FInputActionValue& value)
{
if (value.Get<bool>() &&
bJump == false)
{
bJump = true;
JumpVec = FVector(MoveVec.X * MoveSpeed, MoveVec.Y * MoveSpeed, JumpSpeed);
}
}
-
Jump에서 JumpVec에 MoveVec 와 JumpSpeed 를 통해
점프 상태에서 적용할 관성용 벡터값 저장 -
bool 변수를 통하여 점프 중, 이동에 대한 속도 저하
(JumpScale)
트러블 슈팅 1 - 점프 이후, 내려오지 않는다…?
점프 이후 ‘상승’만 하고
캐릭터가 내려오지를 않는 상황
JumpVec.Z -= GravityScale * DeltaTime;
코드를 보았을때는 ‘중력 상수’ 만큼 빼주고 있는데 왜이러지? 싶었다
그런데 알고보니…
GravityScale = -980.0f;
값을 음수로… 잡아두고 있었다
JumpVec.Z += GravityScale * DeltaTime;
이렇게 수정하니 적당히 올라간 후
땅으로 떨어지게 되었다
트러블 슈팅 2 - 점프 순간의 관성이 유지되지 않는다?
궁중에서 손을 ‘놓’으니
그대로 ‘툭’하고 떨어진다!
문제점이 무엇인가 확인하니
사실상 JumpVec가 ‘방향’을 저장하는 역할 밖에 없었으며
그것도 매우 미약하기에 Jump의 ‘관성’적인 역할은 거의 없었다
// TICK
if (JumpVec.Size() > 0)
{
FVector TempJumpVec = JumpVec.GetSafeNormal() * MoveSpeed * DeltaTime;
AddActorLocalOffset(TempJumpVec);
if (UWorld* World = GetWorld())
{
FVector Start = GetActorLocation();
FVector End = GetActorLocation() + GetActorUpVector() * -TraceHeight;
FHitResult HitResult;
FCollisionQueryParams Params;
Params.AddIgnoredActor(this);
bool bLand = World->LineTraceSingleByChannel(
HitResult, Start, End, ECollisionChannel::ECC_Visibility, Params);
if (bLand)
{
SetActorLocation(Start + GetActorUpVector() * CapsuleComponent->GetScaledCapsuleHalfHeight() / 2);
bJump = false;
JumpVec = FVector::Zero();
}
}
}
---
void ATaskCharacter::Jump(const FInputActionValue& value)
{
if (value.Get<bool>() &&
bJump == false)
{
bJump = true;
JumpVec = FVector(MoveVec.X, MoveVec.Y, JumpSpeed);
}
}
- 문제는 Normalize를 통해
JumpVec 자체가 ‘방향’처럼 쓰인다는 점…
내가 원하는 건 점프 시점에서
그 점프의 관성을 유지하는 것이기에
Normalize를 제거하고
대신 미리 Jump 시점에서 그 ‘관성’을 이용하기로 하였다
void ATaskCharacter::Jump(const FInputActionValue& value)
{
if (value.Get<bool>() &&
bJump == false)
{
bJump = true;
JumpVec = FVector(MoveVec.X * MoveSpeed, MoveVec.Y * MoveSpeed, JumpSpeed);
}
}
다만 이렇게 되니
궁중 ‘이동’ 중에서
‘손’을 놓는 경우
다시 JumpVec가 저장한 방향대로 가기에
역시 어색한 면이 존재하였다
그렇기에
JumpVec가 MoveVec에 영향을 받도록 수정하여
관성과 ‘움직임’ 모두를 챙기려고 하였다
if (JumpVec.Size() > 0)
{
JumpVec.X += MoveVec.X * MoveSpeed * DeltaTime;
JumpVec.Y += MoveVec.Y * MoveSpeed * DeltaTime;
JumpVec.Z += GravityScale * DeltaTime;
if (UWorld* World = GetWorld())
{
FVector Start = GetActorLocation();
FVector End = GetActorLocation() + GetActorUpVector() * -TraceHeight;
FHitResult HitResult;
FCollisionQueryParams Params;
Params.AddIgnoredActor(this);
bool bLand = World->LineTraceSingleByChannel(
HitResult, Start, End, ECollisionChannel::ECC_Visibility, Params);
if (bLand)
{
SetActorLocation(Start + GetActorUpVector() * CapsuleComponent->GetScaledCapsuleHalfHeight() / 2);
bJump = false;
JumpVec = FVector::Zero();
}
}
FVector TempJumpVec = JumpVec * DeltaTime;
AddActorLocalOffset(TempJumpVec);
}
이제는 영상처럼 잘 동작한다!
댓글남기기