[UE4] 커브볼이나 감아차기처럼 발사체(Projectile)에 회전을 넣어봤다
#언리얼4 공부중 / UE4 버젼: 4.22.3 / 피드백 환영
이전에 글에서 발사체를 궤적에 따라 날려봤으니 이제 그 발사체에 스핀을 걸어보고 싶었다. 그럼 야구에서 커브볼같은 효과나 축구에서 감아차기같은 효과를 낼 수 있을 것이다. 그런 효과를 마그너스 효과(Magnus Effect)라고 부르더라. 간단히 설명하자면 회전하는 공으로 인해 공기의 흐름이 한쪽으로 몰려 그 지점에 힘이 가해져서 공의 방향이 휘어지는 효과이다.
그 효과를 구현하기 위해 구글을 열심히 검색 해보았다. 하지만 뭔가 뚜렷한 답을 찾기 어려웠다. 스핀을 주는 컴포넌트가 따로 존재하는지도 모르겠고, 특별히 그 효과를 위해 어떠한 요소가 있는 것 같지 않았다. 여러 커뮤니티에서 구현 방법에 대해 나름 자신들의 방식을 설명하는 글들은 꽤 많았다. 일단 주어들은 것들을 종합해서 구현을 해보기로 했다.
대략 방법을 구상해보면 이렇다.
- 발사체를 날릴 때, 회전하도록 각속도(angular velocity)를 준다.
- 매 tick마다 회전하는 방향과 날아가는 방향을 구하여, 공의 방향을 바꿔줄 방향을 구한다.
- 매 tick마다 최종 구해진 방향으로 일정한 힘을 가한다.
1번처럼 각속도(angular velocity)를 주려면 AddAngularImpulseInDegrees를 이용하면 될 것 같았다. 그런데 어느 축으로 회전을 주어야 하는가. 첫번째 인자인 AngularImpulse는 어떻게 찾아야 하는가. 메뉴얼에는 Direction is axis of rotation이라고 되어 있다. 회전 축의 방향 벡터가 필요하다는 얘기이다. 그럼 회전 축의 방향을 구해야 한다.
위의 그림에서 처럼 빨간 화살표(A)가 공이 날아가는 방향이라고 하면, 회전하고자 하는 축(C)을 녹색이라고 하자. 그럼 공이 날아가는 동안 보라색 화살표 방향으로 회전하며 날아갈 것이다. A 벡터는 날리는 방향이니 이미 구해진 벡터이다. 그럼 C 벡터는 B 벡터를 구한후 Cross Product를 통해 얻을 수 있을 것이다. B 벡터를 구해보자. A 벡터의 오른쪽 벡터이지만 그냥 FVector::RightVector로 구할 수는 없다. A 벡터가 향하는 방향으로 부터 오른쪽 벡터를 구하려면 ToOrientationQuat()를 사용하면 된다. 코드를 짜보자.
FVector AVector = A.GetSafeNormal();
FVector BVector = AVector.ToOrientationQuat().GetRightVector();
FVector CVector = FVector::CrossProduct(AVector, BVector);
CVector를 구했으니 이제 각속도를 줄 수 있다.
AddAngularImpulseInDegrees(CVector);
2,3번으로 넘어가서 매 tick마다 회전 벡터와 날아가는 방향 벡터를 구하여 그 Cross Product를 구하면 그 방향으로 원하는 만큼의 힘을 주면 매 틱마다 방향을 틀어줄 수 있을 것이다. 아래 그림처럼 파란색 방향의 벡터로 힘을 매 틱마다 주면 공이 자연스럽게 틀어져 날아가는 효과를 낼 수 있다.
코드를 짜보자.
void TestActor::Tick(float DeltaSeconds)
{
FVector angularVelocityDelta = GetPhysicsAngularVelocityInDegrees(); // 각속도 얻기
FVector compVelocityDelta = GetComponentVelocity(); // 날아가는 속도 얻기
angularVelocityDelta.Normalize();
compVelocityDelta.Normalize();
FVector crossPrdt = FVector::CrossProduct(angularVelocityDelta, compVelocityDelta); // 휘어지는 방향
AddForce(crossPrdt); // 원하는 힘 만큼 multiplier하면 더 휘어지는 효과를 줄 수 있다.
}
이렇게 하면 발사체나 공이 날아가는 방향으로 부터 바깥쪽으로 휘어지게 되는 데, 안쪽으로 휘어지도록 하려면 AddAngularImpulseInDegrees에서 AngularImpulse를 위에 정의한 것과 반대방향으로 주면 된다.
(추가 수정. 2019.11.21)
CrossProduct를 구할 때 두번째 벡터로 compVelocityDelta를 사용했었는 데, 포물선이 높이 차가 큰 경우, 떨어질 때 compVelocityDelta의 방향이 아래를 향하게 되어 CrossProduct의 결과가 반대로 나오는 경우가 발생한다. 그래서, 날아가는 방향 (최초 A 벡터)를 사용하면 포물선의 형태와 관계없이 깔끔한 커브를 그릴 수 있다. 즉, FVector crossPrdt = FVector::CrossProduct(angularVelocityDelta, <strong>AVector</strong>);
이렇게 바꾸면 된다.
ToOrientationQuat()의 블루프린트 노드는 혹시 뭘까요?
To Quaternion 이라는 노드가 있네요. 🙂
https://docs.unrealengine.com/5.0/en-US/BlueprintAPI/Math/Conversions/ToQuaternion_Vector4/