SimulationBall
개요
SimulationBall은 공의 움직임을 미리 계산한 뒤 그 결과를 재생하는 오브젝트입니다. 일반적인 물리 공보다 궤적을 예측하기 쉽고, 여러 플레이어가 함께 보는 환경에서도 보다 안정적으로 같은 결과를 표현할 수 있습니다.
특히 다음과 같은 상황에서 유용합니다.
축구공, 농구공처럼 공의 궤적이 중요한 게임
벽이나 바닥에 튀는 공의 움직임을 연출해야 하는 경우
공의 이동 경로를 미리 확인하면서 게임 플레이를 조정하고 싶은 경우
사용 방법
SimulationBall 생성 및 충돌 대상 Trace Channel 설정

레벨 브라우저에서 SimulationBall을 생성한 뒤, 사용할 위치에 배치합니다.

SimulationBall이 어떤 오브젝트와 충돌할지는 Collision Profile과 StaticObjectTypes을 통해 정의합니다.
BallMeshCollisionProfile: SimulationBall 자체가 가지는 Collision Profile입니다. 다른 오브젝트가 SimulationBall과 충돌할 때 참고합니다. 다만 해당 Profile은 실제 시뮬레이션에서 사용되지 않습니다.StaticObjectTypes: SimulationBall이 충돌 시뮬레이션을 수행할 때 사용할 Object Type 채널입니다. 실제로 Simulation 할 때, 해당 채널들과의 충돌을 바탕으로 궤적을 찾아냅니다.
따라서 벽이나 바닥처럼 공이 충돌해야 하는 오브젝트들은, StaticObjectTypes 에 해당 ObjectType을 추가하여야 합니다.
에디터를 이용한 Simulation Preview

게임에서 시뮬레이션 볼에 필요한 적절한 물리 값을 찾으려면, 수차례 수치를 바꾸어 가며 테스트해야 합니다. 이를 돕기 위해 게임을 실제로 플레이하지 않고도 편집 단계에서 다양한 시뮬레이션을 실행해 볼 수 있는 프리뷰 기능을 제공합니다.
SimulationBall의 EditorBallSimParams에 여러 값을 넣어 보면서, 공이 어떤 궤적으로 날아가는지 에디터에서 바로 확인할 수 있습니다. 이때 EnablePathMarker를 켜 두면 계산된 궤적이 경로 마커로 표시되어 값을 비교하기 쉽습니다.
스크립트를 이용한 Simulation 조작
SimulationBall을 찾은 뒤, 시작 위치와 속도를 설정하고 시뮬레이션을 실행합니다.
Simulate()는 호출 직후 결과가 바로 완성되지 않습니다. 내부적으로 시뮬레이션을 비동기적으로 나누어 계산하기 때문에, FindNextBallBounce()나 GetCFrameAtTime()처럼 시뮬레이션 결과를 조회하는 API는 계산이 끝난 뒤에 호출해야 합니다. 반면 재생을 시작하는 Play()는 아래 예제처럼 Simulate() 직후에 이어서 호출해도 됩니다.
BallSimParams의 주요 속성
공의 움직임은 BallSimParams의 주요 설정값을 바꾸면서 조정할 수 있습니다.
InitialCFrame
공이 시작하는 위치와 초기 회전을 정합니다.
InitialSpeed
공이 발사되는 속력을 km/h 단위로 정합니다.
InitialDirection
공이 날아가는 방향을 단위 벡터로 정합니다.
InitialSpinAxis
공이 회전하는 축을 단위 벡터로 정의합니다.
InitialSpinSpeed
공의 회전 속도를 RPM(분당 회전수) 단위로 정의합니다(-12000~12000). 절댓값이 클수록 마그누스 힘이 커집니다.
Mass
공의 질량을 kg 단위로 정합니다. 충돌 임펄스와 회전 관성 계산에 사용됩니다.
BaseGravity
공이 아래로 떨어지는 정도(중력 가속도)를 cm/s² 단위로 조정합니다. 기본값 980이며, 0인 경우 무중력 상태이고, 음수가 되면 중력이 반대로 작동합니다.
Restitution
충돌 시 반발 계수를 정합니다(0~1). 1에 가까울수록 공이 잘 튀어 오릅니다.
Friction
지면과의 마찰 계수를 정합니다(0~1). 값이 클수록 지면 접촉 시 슬라이딩 속도가 빠르게 감소합니다.
RollingFriction
구름 저항 계수를 정합니다(0~1). 지면에서 구르는 동안 속도가 감소하는 정도를 결정합니다.
SpinMagnusWeight
공이 회전할 때 적용되는 마그누스 효과의 가중치를 정의합니다(0.0~0.1). 가중치가 클수록 궤적이 휘는 정도가 커지며, 축구공·골프공 수준의 자연스러운 효과는 약 0.01~0.015입니다.
Simsteps
시뮬레이션을 몇 단계로 나누어 계산할지 정합니다(1~14400). 값이 클수록 더 정밀해질 수 있지만 계산 비용도 늘어납니다.
StepsPerSecond
시뮬레이션 주파수를 Hz 단위(초당 스텝 수)로 정합니다(1~480). `Simsteps`와 함께 전체 시뮬레이션 길이(`Simsteps ÷ StepsPerSecond`)와 정밀도를 결정합니다.
예를 들어, 더 멀리 날리고 싶다면 InitialSpeed 값을 키우고, 더 높은 곳에서 시작하게 하려면 InitialCFrame의 높이 값을 올리면 됩니다.
SimulationBall 추가 기능 사용
SimulationBall은 일반적인 실시간 물리 시뮬레이션과 달리, 사전에 입력된 BallSimParams를 바탕으로 미리 공의 궤적과 충돌을 계산하고 그 결과를 Play하게 됩니다. 이를 통해 Play 전에 공의 이동 경로 또는 충돌 위치를 미리 알아낼 수 있습니다.
공의 다음 Bound 위치 알아내기
SimulationBall은 시뮬레이션이 끝난 뒤, 다음으로 튕길 지점의 정보를 미리 알아낼 수 있습니다.
이 기능은 다음과 같은 경우에 유용합니다.
공이 어느 벽에서 튈지 미리 확인하고 싶은 경우
다음 바운드 위치에 이펙트를 배치하고 싶은 경우
AI 또는 게임 로직에서 다음 충돌 지점을 예측하고 싶은 경우
위 코드에서는 FindNextBallBounce()를 사용해 다음 바운드 정보를 가져옵니다.
반환된 값 안에는 바운드가 일어나는 시간과 위치가 포함되어 있어, 공이 어디에서 튈지 미리 확인할 수 있습니다.
단, Simulate() 직후 바로 FindNextBallBounce()를 호출하면 아직 계산이 끝나지 않아 원하는 값을 얻지 못할 수 있습니다. 위 예제처럼 task.wait()로 잠시 대기한 뒤 호출해야 합니다. Simsteps가 커서 계산량이 많은 경우에는 한 프레임 대기로 부족할 수 있으므로, 유효한 값이 반환될 때까지 반복 확인하거나 충분한 시간을 두고 조회하세요.
N초 후 공의 물리 값 가져오기
SimulationBall은 특정 시간이 지난 뒤 공의 상태도 미리 가져올 수 있습니다.
이 기능은 다음과 같은 경우에 유용합니다.
N초 뒤 공의 위치를 알고 싶은 경우
공의 속도가 얼마나 되는지 확인하고 싶은 경우
회전 속도까지 포함해서 미래 상태를 예측하고 싶은 경우
위 예제에서는 2초 후 공의 물리 값을 미리 가져옵니다.
GetCFrameAtTime(): 해당 시점의 위치와 회전GetLinearVelocityAtTime(): 해당 시점의 이동 속도 방향과 크기GetSpeedAtTime(): 해당 시점의 속력만 숫자로 확인GetAngularVelocityAtTime(): 해당 시점의 회전 속도 확인
즉, SimulationBall은 단순히 공을 재생하는 것뿐 아니라, 미래 시점의 위치와 속도, 회전까지 미리 조회할 수 있습니다.
이때도 Simulate() 직후 바로 값을 가져오는 방식은 피하고, task.wait()로 잠시 대기한 뒤 조회하는 것이 좋습니다.
또한 CheckTime이 현재 시뮬레이션 범위를 넘어서면 기대한 값을 얻지 못할 수 있으므로, Simsteps ÷ StepsPerSecond가 조회하려는 시간보다 충분히 크도록 설정해야 합니다.
목표 위치로 공을 자동 조준하기
SimulateToTarget()을 사용하면 발사 속도와 방향을 직접 계산할 필요 없이, 목표 위치만 지정하면 해당 지점에 도달하는 속도와 방향을 자동으로 계산하여 시뮬레이션을 실행합니다.
이 기능은 다음과 같은 경우에 유용합니다.
특정 위치로 정확히 공을 던지는 트릭샷을 구현하고 싶은 경우
NPC나 AI가 플레이어 위치를 향해 공을 던지게 하고 싶은 경우
발사 속도/방향을 수동으로 계산하는 대신 목표 지점만으로 궤적을 구성하고 싶은 경우
SimulateToTarget()은 계산 결과를 BallSimTargetResult로 즉시 반환하므로, Simulate()와 달리 별도의 대기 없이 반환값을 바로 사용할 수 있습니다. bHit으로 목표에 도달하는 궤적을 찾았는지 확인하고, HitTime으로 도달 시각을, ActualSpeed/Direction으로 실제 사용된 속도와 방향을 확인할 수 있습니다.
충돌 이벤트 활용하기
SimulationBall은 재생 중 다른 오브젝트와 충돌하면 이벤트를 발생시킵니다. 골 판정, 사운드 재생, 이펙트 표시 같은 충돌 기반 로직을 구현할 때 사용합니다.
Touched: 공이 다른 파트와 충돌했을 때 호출됩니다.TouchEnded: 공이 파트와의 접촉에서 떨어져 나갈 때 호출됩니다.Bounded: 공이 파트에 바운스(충돌 반사) 했을 때만 호출됩니다. 슬라이딩 충돌은 포함되지 않습니다.
재생 제어하기
시뮬레이션 재생은 Play() 외에도 다양한 방법으로 제어할 수 있습니다.
Pause(): 재생을 일시 정지합니다.Play()를 다시 호출하면 정지한 지점부터 이어서 재생됩니다.Stop(): 재생을 정지합니다. 이후Play()를 호출하면 시뮬레이션 시작 시점부터 다시 재생됩니다.Play(bReset):bReset에true를 넘기면 재생 시간(PlaybackTime)을 초기화한 뒤 재생합니다. 생략하면 기본값false로 동작합니다.SetPlaybackTime(time): 재생 시간을 임의의 시점으로 이동합니다. 재생 중에도 호출할 수 있어 되감기나 특정 순간으로 건너뛰기에 사용할 수 있습니다.SlomoFactor: 재생 속도 배율입니다. 1.0이 정상 속도이며, 0.5면 절반 속도(슬로모션), 2.0이면 2배 속도로 재생됩니다.
비행 중 궤적 다시 계산하기 (ReSimulate)
재생 중인 공의 궤적을 특정 시점부터 새로 계산할 수도 있습니다. 패스를 가로채거나, 공중에서 방향을 바꾸는 연출 등에 활용할 수 있습니다.
ReSimulateWithDelay(): 현재 재생 시간에서 지정한 지연 시간 후, 새로운 방향·속도·스핀으로 궤적을 재계산합니다.ReSimulateToTargetWithDelay(): 지연 시간 후 목표 위치를 향하도록 궤적을 재계산합니다. 스핀은 현재 각속도를 기반으로 자동 계산됩니다.ReSimulateSpinToTargetWithDelay(): 지연 시간 후 지정한 스핀 축·스핀 속도를 사용해 목표 위치를 향하도록 궤적을 재계산합니다.
ReSimulateToTargetWithDelay()와 ReSimulateSpinToTargetWithDelay()는 SimulateToTarget()과 마찬가지로 BallSimTargetResult를 반환합니다.
주의사항
Simulate()는 즉시 완료되지 않으며 내부적으로 비동기적으로 처리됩니다.FindNextBallBounce(),GetCFrameAtTime()등 시뮬레이션 결과를 조회하기 전에task.wait()로 잠시 대기해야 하며, 계산량이 많은 경우 유효한 값이 반환될 때까지 반복 확인이 필요할 수 있습니다.공을 재생하기 전에 먼저 시뮬레이션을 실행해야 합니다.
StaticObjectTypes가 비어 있으면WorldStatic채널만 충돌 대상이 됩니다. 공이 튕겨야 할 오브젝트의 채널을 반드시 추가하세요.속도 값이 너무 작으면 공이 거의 움직이지 않는 것처럼 보일 수 있습니다.
미래 시점 조회 시
CheckTime이 전체 시뮬레이션 길이(Simsteps ÷ StepsPerSecond)를 넘지 않도록 설정해야 합니다.테스트 중에는
EnablePathMarker를 켜 두는 것이 확인에 도움이 됩니다.
참고 문서
SimulationBallBallSimParamsBallSimTargetResultBallBounceLast updated