[UE4] 발사체를 궤적에 따라 날려보자 (Simulating a Projectile and Trajectory)

#언리얼4 공부중 / UE4 버젼: 4.22.3 / 피드백 환영

발사체(Projectile)을 쏘는 방식에 대해 알아보았다.  

메뉴얼을 뒤적거려보니 어떤 물체를 이동시키기 위해서 사용되는 컴포넌트들이 3가지로 정리되어 있었다. (메뉴얼 링크)

  • 캐릭터 무브먼트 컴포넌트 (CharacterMovementComponent)
  • 프로젝타일 무브먼트 컴포넌트 (ProjectileMovementComponent)
  • 로테이팅 무브먼트 컴포넌트 (RotatingMovementComponent)

관련 있는 컴포넌트는 ProjectileMovementComponent로 보였다.  이 컴포넌트를 보면 기능이 많이 붙어 있다.  발사체를 다양한 요구사항으로 날려보내려니 이렇게 기능이 복잡해진 듯 하다.  충격 이후의 탄성, 유도탄 같은 호밍 기능, 마찰력, 속도 제한, 물리 시물레이션 조절, 인터폴레이션 조절등이 있었다.  여기에는 타겟 좌표를 설정하는 곳은 없다.  Inital Speed나 Velocity를 지정하는 것이 존재하는 것으로 보아 타겟 좌표를 바로 지정해서 쏘는 것 까지는 안되는 모양이다.  사실 그것까지 기능을 포함하면 컴포넌트라고 불리기엔 무리가 있지 싶다.  속도만 지정하는 것이고 물리가 적용된 발사체라면 AddImpluse로 날려도 효과는 (단순히 타겟으로 날린다는 가정하에) 비슷해 보인다.

그럼 타겟 지점으로 보내기 위한 속도를 구하려면 어떻게 해야 할까.UGameplayStatics::SuggestProjectileVelocity를 사용하면 Velocity를 구할 수 있다.  Static 함수이고 이름 그대로 발사체의 속도를 제안해준다.  인자가 12개나 되지만 하나씩 대입해주면 원하는 Velocity를 얻을 수있다.  특이한 점은 함수 인자중에 bool bHighArc 가 있는데 높은 궤적과 낮은 궤적 두개 중 하나를 제안해 준다.  두개의 궤적이 같은 타겟 지점이라는 게 약간 뭔 소린가 해서 포물선 궤적에 대해 찾아보았다.

타겟이 5m 지점이라고 한다면, 노랗게 표시된 두개의 궤적이 타겟 지점에 동일하게 도착하게 된다.  그래서 bHighArc가 true이면 위의 경우 높은 포물선을 그릴 수 있는 Velocity를 얻을 수 있고, false일 경우는 낮은 포물선을 그리는 Velocity를 얻을 수 있다.  

하지만 다양한 궤적을 표현하고 싶다면 위의 두개만으로는 부족하다.  발사체라 하면 다양한 궤적으로 날릴 수 있어야 좀 더 게임이 다이나믹해 질 수 있다.  혹시나 해서 찾아봤더니 UGameplayStatics::SuggestProjectileVelocity_CustomArc 라는 함수가 보였다.  CustomArc라니 역시 언리얼은 이런 것도 만들어 놨구나.  인자도 5개로 단순하다.  

  • SuggestProjectileVelocity_CustomArc 인자
    • OutLaunchVelocity : 결과 Velocity
    • StartPos: 발사 지점
    • EndPos: 타겟 지점
    • OverrideGravityZ: (선택) World 중력 Z를 override 할 수 있다.
    • ArcParam: 0.0 – 1.0 사이의 값으로 Arc를 선택.  0은 제일 높은 포물선을, 1은 포물선없이 직접 조준.

ArcParam을 조절하여 원하는 궤적을 만들고 그에 따른 Velocity를 얻어 발사체를 쏘아 올리면 될 것 같다.  그런데, 이 함수는 tracing을 제공하지 않기 때문에 궤적에 그릴 수가 없다.  실제로 내가 정한 Arc대로 날아가는지 알아보려면 궤적이 그려져야 할 텐데 말이다. 

궤적은 어떻게 그릴까.  

UGameplayStatics::PredictProjectilePath 를 검색하다 찾아냈다.  친절하게도 projectile의 path도 알려주고, tracing도 제공해 준다고 한다.  struct FPredictProjectilePathParams를 인자로 잘 넘기면 원하는 결과를 얻을 수 있다고 하는데, 역시 이 구조체가 가지고 있는 변수도 10개 남짓하게 많다.  아무튼 이제 필요한 API는 다 찾았으니, 필수 정보들만으로 구현을 한번 해보자.

FVector startLoc = FVector::ZeroVector;      // 발사 지점
FVector targetLoc = FVector(0, 1000, 1000);  // 타겟 지점.
float arcValue = 0.5f;                       // ArcParam (0.0-1.0)
FVector outVelocity = FVector::ZeroVector;   // 결과 Velocity
if (UGameplayStatics::SuggestProjectileVelocity_CustomArc(this, outVelocity, startLoc, targetLoc, GetWorld()->GetGravityZ(), arcValue))
{
     FPredictProjectilePathParams predictParams(20.0f, startLoc, outVelocity, 15.0f);   // 20: tracing 보여질 프로젝타일 크기, 15: 시물레이션되는 Max 시간(초)
     predictParams.DrawDebugTime = 15.0f;     //디버그 라인 보여지는 시간 (초)
     predictParams.DrawDebugType = EDrawDebugTrace::Type::ForDuration;  // DrawDebugTime 을 지정하면 EDrawDebugTrace::Type::ForDuration 필요.
     predictParams.OverrideGravityZ = GetWorld()->GetGravityZ();
     FPredictProjectilePathResult result;
     UGameplayStatics::PredictProjectilePath(this, predictParams, result);
}

위와 같이 구현 후, arcValue의 변화를 주면 다양한 궤적을 표현 할 수 있다.  각 arcValue에 따른 궤적의 차이를 한번 보자.

arcValue가 0.5
arcValue가 0.3
arcValue가 0.8

ArcParam값에 따라 궤적이 잘 그려지는 것을 확인 했다.  이제 물리가 적용된 물체를 날려보자.  AddImpluse만 추가하면 된다. 물리가 적용된 물체의 경우 Physics의 Linear Damping 값은 0이어야 궤적에 따라 발사체를 날릴 수 있다.  Linear Damping이 0보다 클 경우 날아가는 동안 속도가 줄어들어 궤적보다 짧은 지점에 도달하게 된다.

FVector startLoc = FVector::ZeroVector;      // 발사 지점
FVector targetLoc = FVector(0, 1000, 1000);  // 타겟 지점.
float arcValue = 0.5f;                       // ArcParam (0.0-1.0)
FVector outVelocity = FVector::ZeroVector;   // 결과 Velocity
if (UGameplayStatics::SuggestProjectileVelocity_CustomArc(this, outVelocity, startLoc, targetLoc, GetWorld()->GetGravityZ(), arcValue))
{
     FPredictProjectilePathParams predictParams(20.0f, startLoc, outVelocity, 15.0f);   // 20: tracing 보여질 프로젝타일 크기, 15: 시물레이션되는 Max 시간(초)
     predictParams.DrawDebugTime = 15.0f;     //디버그 라인 보여지는 시간 (초)
     predictParams.DrawDebugType = EDrawDebugTrace::Type::ForDuration;  // DrawDebugTime 을 지정하면 EDrawDebugTrace::Type::ForDuration 필요.
     predictParams.OverrideGravityZ = GetWorld()->GetGravityZ();
     FPredictProjectilePathResult result;
     UGameplayStatics::PredictProjectilePath(this, predictParams, result);

     objectToSend->AddImpluse(outVelocity); // objectToSend는 발사체
}

FPredictProjectilePathParams안에 trace과 관련한 여러 변수들을 세팅할 수 있어서 필요한 경우에 따라 좀 더 기능을 확장해 볼 수도 있을 것 같다. 



Leave a Reply

Your email address will not be published. Required fields are marked *