본문 바로가기

물리 기반 캐릭터 제어

Character Movement의 이동·가속·마찰·중력·충돌 설정을 익히고 입력과 물리 반응이 충돌하지 않게 제어합니다.

표면의 물리 성질을 정했다면, 이제 그 규칙을 플레이어 캐릭터의 움직임으로 넓힐 차례입니다. 얼음 바닥에 PM_Ice를 지정했는데도 캐릭터가 전혀 미끄러지지 않는다면, 그것은 캐릭터가 일반 물리 물체와 다른 방식으로 움직이기 때문입니다. 언리얼 엔진의 캐릭터는 캐릭터 무브먼트 컴포넌트(Character Movement Component)라는 전용 이동 시스템으로 움직입니다.

이 글에서는 캐릭터 무브먼트 컴포넌트가 일반 물리 시뮬레이션과 어떻게 다른지 먼저 정리하고, 걷기와 점프, 충돌, 물리 상호작용에 관련된 주요 설정을 살펴봅니다. 그다음 블루프린트로 얼음 구간과 저중력 구간을 만들어, 표면이나 영역에 따라 캐릭터의 움직임을 바꾸는 방법을 익힙니다. 설정 항목이 많으므로, 각 값이 무엇을 바꾸는지 이해한 상태에서 조정하는 것이 사실감과 조작감을 함께 지키는 길입니다.


캐릭터는 어떻게 움직이나

언리얼 엔진의 Character 클래스는 플레이어 캐릭터든 AI 캐릭터든 기본으로 캐릭터 무브먼트 컴포넌트를 가지고 있습니다. 이 컴포넌트에는 3D 캐릭터를 움직이는 데 필요한 로직이 대부분 들어 있고, 물리 엔진과도 밀접하게 연결되어 있지만, 일반 강체(rigid body)의 물리와는 동작 방식이 다릅니다.

일반 물리 시뮬레이션은 Simulate Physics를 켠 물체의 질량과 중력, 받는 힘에 따라 엔진이 움직임을 모두 계산합니다. 그래서 부딪히면 튕겨 나가거나 굴러떨어지는 것처럼, 결과를 미리 정확히 예측하기 어려운 반응이 나타납니다.

반면 캐릭터 무브먼트 컴포넌트는 플레이어의 입력을 받아 걷기, 달리기, 점프, 떨어지기 같은 정해진 규칙에 따라 캐릭터를 움직이고, 충돌은 캡슐 컴포넌트(Capsule Component)로 처리합니다. 마찰 같은 물리의 영향을 받기는 하지만, 입력과 충돌 조건에 따라 예측할 수 있는 이동을 주는 데 중점을 둡니다. 벽에 닿으면 벽을 따라 미끄러지듯 걷고, 계단을 부드럽게 오르내리는 것 같은 복잡한 움직임도 알아서 처리합니다. 즉 일반 보행 캐릭터는 캡슐을 강체로 시뮬레이션하는 것이 아니며, 캐릭터가 물리 물체를 미는 기능과 캐릭터 자신의 충돌과 이동 처리는 서로 다른 것입니다.

물리 기반 제어가 필요한 경우

대부분의 게임에서는 캐릭터 무브먼트 컴포넌트만으로 충분합니다. 하지만 몇 가지 경우에는 물리 기반 제어를 이해하고 써야 합니다. 첫째는 완전한 래그돌 캐릭터나 차량처럼 물리 엔진이 직접 움직이는 캐릭터입니다. 둘째는 미끄러운 얼음 위를 걷거나 움직이는 발판 위에서 균형을 잡는 것처럼, 환경의 물리 성질이 캐릭터의 움직임에 직접 영향을 줘야 할 때입니다. 셋째는 중력이 뒤집히거나 한 지점에 매달리는 것처럼, 기본 캐릭터 무브먼트 컴포넌트로는 만들기 어려운 독특한 이동 방식입니다.


실습 전에 준비할 것

아래에서 말하는 PlayerCharacter는 플레이어가 조종하는 Character 블루프린트를 가리키는 예시 이름입니다. First Person이나 Third Person 템플릿으로 이미 캐릭터가 있다면 그 블루프린트를 그대로 쓰고, 뒤에 나오는 캐스트 노드도 실제 클래스 이름에 맞추면 됩니다. 에셋의 이름을 바꿀 필요는 없습니다.

1장에서 Blank 템플릿과 Blueprint로 시작했다면 아직 조종할 캐릭터가 없을 수 있습니다. 그렇다면 플레이어 컨트롤러, 폰 블루프린트에서 Pawn과 Character를 만들고 플레이어가 조종하게(possess) 하는 방법을, 게임 모드, 스테이트 블루프린트에서 게임 모드의 기본 클래스 설정을 먼저 확인합니다. 입력은 프로젝트 설정과 기본 옵션에서 본 것처럼, 프로젝트가 기존 입력 방식과 향상된 입력 가운데 무엇을 쓰는지 구분합니다.

이 글들은 클래스와 조종, 입력 설정의 관계를 설명할 뿐, 완성된 이동과 카메라 입력 그래프를 주지는 않습니다. 이 실습은 플레이어가 조종하는 캐릭터와 카메라, 이동 입력이 준비된 상태를 전제로 합니다. 아직 없다면 아래 설정의 의미를 먼저 읽어 두고, 캐릭터를 준비한 뒤 돌아와 실습합니다. 준비했다면 지금 레벨의 게임 모드와 Default Pawn Class, 그리고 실제로 조종하는 대상이 같은 캐릭터인지 확인합니다.

준비가 되었다면 PlayerCharacter 블루프린트를 열고 Character Movement (Inherited) 컴포넌트를 선택해 디테일 패널을 봅니다.


캐릭터 무브먼트의 주요 설정

걷기와 멈추기

Walkable Floor Angle은 캐릭터가 걸어 올라갈 수 있는 경사의 최대 각도입니다. 이보다 가파른 경사에서는 미끄러지거나 더 올라가지 못합니다.

Ground Friction은 캐릭터가 땅 위에서 움직일 때 적용되는 마찰입니다. 값이 클수록 방향을 빨리 바꿀 수 있고, 값을 낮추면 얼음 위처럼 미끄러지는 효과를 낼 수 있습니다. 또 Use Separate Braking Friction이 꺼져 있으면 이 값이 멈출 때의 제동에도 쓰여, Braking Deceleration Walking과 함께 멈추기까지의 거리를 정합니다. Braking Deceleration Walking은 걷다가 입력을 멈췄을 때 얼마나 빨리 감속할지를 정합니다.

점프와 낙하

Gravity Scale은 이 캐릭터에만 적용되는 중력의 배율입니다. 값을 바꾸면 캐릭터의 점프 높이와 떨어지는 빠르기가 달라집니다. 0.5처럼 작게 하면 중력이 약해져 더 높이 뛰고 천천히 떨어지고, 2.0처럼 크게 하면 중력이 강해져 낮게 뛰고 빨리 떨어집니다. Air Control은 공중에 있을 때 얼마나 움직임을 제어할 수 있는지로, 값이 클수록 공중에서 방향을 자유롭게 바꿀 수 있습니다.

충돌

캐릭터 무브먼트 컴포넌트는 자기만의 충돌 형상을 갖지 않고, 부모인 Character 클래스의 캡슐 컴포넌트를 충돌에 씁니다. 캡슐의 Collision Presets는 보통 Pawn을 씁니다. CharacterMesh는 캐릭터의 스켈레탈 메시에 쓰는 프리셋이므로 캡슐과 구분합니다.

환경 쪽에도 캐릭터와 관련된 설정이 있습니다. 환경 컴포넌트의 Can Character Step Up On은 부딪힌 캐릭터가 그 물체를 밟고 올라설 수 있는지를 정합니다. 이것은 충돌 채널이 아니며, 경사를 걸을 수 있는지는 캐릭터의 Walkable Floor Angle과 표면의 Walkable Slope Override로 따로 정합니다. 캐릭터 무브먼트 컴포넌트의 Nav Movement 항목에 있는 Movement Capabilities에서는 Can Crouch, Can Jump, Can Walk, Can Swim, Can Fly로 각 이동 방식을 허용할지 정합니다.

물리 물체와의 상호작용

캐릭터가 물리 시뮬레이션 중인 물체를 밀 때의 동작은 Physics Interaction 항목에서 정합니다. Enable Physics Interaction을 켜면 캐릭터가 물리 물체와 닿았을 때 그 물체를 밀어낼 수 있습니다. 이 옵션은 모든 충돌을 끄거나 물체를 끌어당기는 기능을 만드는 스위치가 아니며, 실제 기본값은 쓰고 있는 캐릭터 클래스에서 확인합니다.

Push Force Factor는 캐릭터가 물리 물체를 밀어낼 때 적용하는 힘의 배율입니다. Push Force Point Z Offset Factor는 물체의 높이를 기준으로 힘을 가할 위치를 조절하며, Push Force Using Z Offset을 쓸 때 적용됩니다.


블루프린트로 구간마다 움직임 바꾸기

캐릭터 무브먼트 컴포넌트의 설정을 블루프린트에서 실행 중에 바꾸면, 특정 구간에서만 다르게 움직이는 캐릭터를 만들 수 있습니다. 두 예제는 같은 틀을 씁니다. 구간을 트리거로 표시하고, 플레이어의 캡슐이 들어오면 값을 바꾸고, 나가면 처음에 저장해 둔 값으로 되돌립니다. 되돌릴 값을 미리 저장하지 않으면 구간을 나간 뒤에도 바뀐 값이 남거나, 원래 값을 짐작해 적은 숫자로 돌아가게 되므로 이 저장 단계를 빼지 않습니다.

미끄러운 얼음 구간

물리 재질 설정 및 최적화에서 본 것처럼, 바닥에 PM_Ice를 지정해도 캐릭터 무브먼트의 Ground Friction은 바뀌지 않습니다. 그래서 얼음 구간에서 캐릭터를 미끄러지게 하려면 Ground Friction을 직접 바꿔야 합니다.

먼저 PM_Ice(Friction 0.05)를 만들고, 단순 충돌을 가진 바닥 컴포넌트의 Phys Material Override에 지정합니다. 이것은 물리 물체가 미끄러지게 하는 설정이고, 캐릭터를 위해서는 같은 범위를 덮는 Box Collision 트리거를 따로 둡니다.

그다음 PlayerCharacter 블루프린트에서, 게임이 시작될 때 캐릭터의 Ground Friction과 제동 설정을 변수에 저장해 둡니다. 예를 들어 8.0은 예시값일 뿐이고, 실제로는 프로젝트의 값을 기준으로 합니다. 트리거와 플레이어 캡슐의 Overlap 설정을 맞추고, 겹친 이벤트에서는 Other Actor가 플레이어인지뿐 아니라 Other Comp가 플레이어의 캡슐인지도 확인합니다. 캐릭터의 다른 컴포넌트가 먼저 겹쳐 이벤트가 두 번 생기는 것을 막기 위해서입니다.

BeginOverlap에서는 Get Character Movement로 캐릭터 무브먼트의 참조를 얻어 Set Ground Friction으로 값을 낮춥니다. 별도의 제동 마찰을 켜 두었다면 그 값과 Braking Deceleration Walking도 따로 바꿔야 미끄러지는 느낌이 납니다. EndOverlap에서는 처음 저장해 둔 값으로 되돌립니다.

이 예는 얼음 구간이 하나라고 가정합니다. 구간이 여러 개 겹친다면, 지금 캐릭터가 들어가 있는 구간들을 모아 어느 구간의 값을 쓸지 우선순위를 정하고, 한 구간을 나갈 때 남은 구간의 값을 다시 적용해야 합니다. 그렇지 않으면 겹친 두 구간 가운데 하나만 나가도 원래 값으로 돌아가 버립니다.

저중력 구간

이번에는 들어가면 몸이 가벼워지는 구간을 만듭니다. Actor 블루프린트에 Box Collision이나 Sphere Collision 컴포넌트를 더하고, 커스텀 응답으로 Pawn에만 Overlap으로, 나머지에는 Ignore로 응답하게 한 뒤 양쪽의 Generate Overlap Events를 켭니다.

이 트리거 액터의 이벤트 그래프에서 OnComponentBeginOverlap 이벤트를 추가하고, Other Actor를 Cast To PlayerCharacter(또는 실제 캐릭터 블루프린트 클래스)에 연결합니다. 캐스트에 성공하면 Other Comp가 플레이어의 캡슐인지 확인하고, As PlayerCharacter 핀에서 Get Character Movement를 얻은 뒤 Set Gravity Scale로 원하는 배율을 정합니다. 예를 들어 0.2로 낮추면 점프가 훨씬 높아집니다. 이때 되돌릴 값으로는 게임 시작 때 저장해 둔 Gravity Scale을 씁니다.

같은 캡슐의 OnComponentEndOverlap에서는 저장해 둔 Gravity Scale로 되돌립니다. 구간이 겹치는 경우는 얼음 구간과 같은 우선순위 방식으로 처리합니다. 중력이 바뀔 때 화면의 색을 바꾸거나 HUD에 아이콘을 띄우고 소리를 내면, 플레이어가 변화를 바로 알아차리고 몰입감도 높아집니다.


주의할 점

먼저 캐릭터 무브먼트 컴포넌트가 맡는 범위를 이해합니다. 대부분의 경우 이 컴포넌트가 캐릭터의 움직임을 효율적으로 처리하므로, 물리 시뮬레이션을 완전히 켜는 것은 래그돌이나 차량 같은 특정 상황에만 쓰고, 일반적인 걷기와 점프에는 이 컴포넌트의 설정을 씁니다.

충돌 설정도 정확해야 합니다. 캐릭터의 캡슐과 환경 물체의 충돌이 맞아야 하며, 필요 없는 곳에 복잡 충돌을 쓰지 않습니다. 캐릭터가 예상치 못하게 미끄러지거나, 벽에 끼거나, 충돌이 일어나지 않는다면 캐릭터 무브먼트의 Ground Friction과 Walkable Floor Angle, 계단처럼 올라설 수 있는 최대 높이인 Max Step Height, 그리고 부딪히는 물체의 물리 재질과 충돌 프리셋을 꼼꼼히 확인합니다.

멀티플레이어 게임이라면 네트워크도 생각해야 합니다. 물리 기반 캐릭터 제어는 네트워크 지연(latency)에 민감할 수 있습니다. 캐릭터 무브먼트 컴포넌트는 네트워크 복제(replication)에 맞게 만들어져 있지만, 직접 만든 물리 로직은 여러 기기 사이의 동기화를 따로 고려해야 합니다.

성능 면에서 Set Ground Friction이나 Set Gravity Scale 같은 노드는 큰 부담을 주지 않습니다. 다만 매 프레임 복잡한 물리 계산을 강제하는 로직은 피합니다.

더 깊이: 캐릭터 무브먼트는 네트워크에서 어떻게 맞춰지나

캐릭터 무브먼트 컴포넌트의 네트워크 이동 문서에 따르면, 이 컴포넌트는 소유자의 네트워크 역할(network role)에 따라 다른 경로로 이동을 맞춥니다. 플레이어가 직접 조종하는 클라이언트 쪽 캐릭터(autonomous proxy)는 먼저 자기 기기에서 이동을 계산해 적용하고, 그 이동의 정보를 저장된 이동(saved move)으로 쌓아 둔 뒤 비슷한 것끼리 합쳐 ServerMove RPC로 서버에 보냅니다. 서버는 받은 이동을 같은 이동 계산으로 다시 재현하고, 결과 위치가 클라이언트가 보고한 위치와 같으면 이동이 유효하다고 알리고, 다르면 ClientAdjustPosition RPC로 보정을 보냅니다. 보정을 받은 클라이언트는 서버의 결과에서 출발해 아직 확인받지 못한 저장된 이동을 다시 적용해 새 위치를 구합니다.

다른 클라이언트의 화면에 보이는 캐릭터(simulated proxy)는 이 과정을 거치지 않고, 서버가 복제한 위치와 회전, 상태를 적용한 뒤 네트워크 스무딩(network smoothing)으로 움직임을 부드럽게 보이게 합니다. 이 구조에서 서버는 클라이언트의 이동을 자기 쪽의 값으로 다시 계산하므로, 얼음 구간의 Ground Friction처럼 이동에 영향을 주는 값을 클라이언트에서만 바꾸면 두 계산의 결과가 달라져 보정이 생길 수 있습니다. 그래서 멀티플레이어에서는 이동 값을 바꾸는 로직이 서버와 그 클라이언트에서 같은 값으로 실행되는지 확인합니다.


정리

  • 캐릭터는 강체 시뮬레이션이 아니라 캐릭터 무브먼트 컴포넌트가 입력과 정해진 규칙에 따라 움직이며, 충돌은 캡슐 컴포넌트로 처리합니다.
  • 실습에는 플레이어가 조종하는 캐릭터와 이동 입력이 필요하며, Blank 프로젝트라면 Pawn과 게임 모드부터 준비합니다.
  • Ground Friction은 방향 전환과 제동에, Gravity Scale과 Air Control은 점프와 공중 움직임에, Walkable Floor Angle은 오를 수 있는 경사에 영향을 줍니다.
  • 캡슐에는 보통 Pawn 프리셋을 쓰고, Can Character Step Up On은 충돌 채널이 아니라 밟고 올라설 수 있는지를 정하는 설정입니다.
  • 표면의 물리 재질은 캐릭터의 Ground Friction을 바꾸지 않으므로, 트리거 구간에서 Set Ground Friction이나 Set Gravity Scale로 직접 바꾸고 나갈 때 저장한 값으로 되돌립니다.
  • 겹친 상대는 Other Comp까지 확인하고, 구간이 겹치면 우선순위를 정해 남은 구간의 값을 다시 적용합니다.

연습 문제

1. 얼음 위에서 미끄러지지 않는다

바닥 메시의 Phys Material Override에 PM_Ice(Friction 0.05)를 지정했더니, 물리 큐브는 잘 미끄러지는데 플레이어 캐릭터는 평소처럼 걷습니다. 왜 그럴까요? 캐릭터도 미끄러지게 하려면 어떻게 해야 할까요?

해설 보기

물리 큐브는 강체 시뮬레이션이라 바닥의 물리 재질을 읽지만, 캐릭터는 캐릭터 무브먼트 컴포넌트의 규칙으로 움직이며, 그 Ground Friction은 바닥의 물리 재질을 자동으로 가져오지 않습니다.

캐릭터도 미끄러지게 하려면 얼음 범위를 덮는 트리거를 두고, 플레이어의 캡슐이 들어오면 Set Ground Friction으로 값을 낮추고, 별도 제동 마찰을 쓴다면 그 값과 Braking Deceleration Walking도 함께 바꿉니다. 나갈 때는 처음 저장해 둔 값으로 되돌립니다.

2. 두 구간이 겹칠 때

저중력 구간 A(Gravity Scale 0.5)와 B(Gravity Scale 0.2)가 일부 겹쳐 있습니다. 캐릭터가 A에 들어갔다가 B에도 들어간 뒤 A에서만 나왔는데, 아직 B 안에 있는데도 중력이 원래대로 돌아왔습니다. 왜 그럴까요? 어떻게 고칠 수 있을까요?

해설 보기

A의 EndOverlap이 저장해 둔 원래 Gravity Scale로 무조건 되돌렸기 때문입니다. 캐릭터가 아직 B 안에 있다는 정보를 고려하지 않았습니다.

캐릭터가 지금 들어가 있는 구간들의 목록을 관리하고, 구간마다 우선순위를 정합니다. 한 구간을 나갈 때는 목록에서 그 구간을 빼고, 남은 구간 가운데 우선순위가 가장 높은 구간의 값을 다시 적용하며, 남은 구간이 없을 때만 원래 값으로 되돌립니다.

3. 이벤트가 두 번 생긴다

얼음 트리거의 BeginOverlap에서 Other Actor가 플레이어인지만 확인했더니, 캐릭터가 한 번 들어갔는데 이벤트가 두 번 생기고, 나갈 때 값이 이상하게 되돌아갑니다. 무엇을 더 확인해야 할까요?

해설 보기

캐릭터에는 캡슐 말고도 스켈레탈 메시처럼 겹침을 만들 수 있는 컴포넌트가 있을 수 있어서, 같은 캐릭터라도 컴포넌트마다 이벤트가 생길 수 있습니다. Other Actor만 보면 이것을 구분할 수 없습니다.

Other Comp가 플레이어의 캡슐 컴포넌트인지도 함께 확인해, 캡슐이 들어오고 나갈 때만 값을 바꾸고 되돌리게 합니다. 그러면 들어갈 때와 나갈 때의 이벤트가 한 번씩만 처리됩니다.

캐릭터 무브먼트 컴포넌트의 속성 목록은 UCharacterMovementComponent 레퍼런스에서, 네트워크에서의 동작은 Epic의 캐릭터 무브먼트 네트워크 안내에서 확인할 수 있습니다.

다음 글 물리 시뮬레이션 성능 조정에서는 많은 물리 물체가 함께 움직일 때 생기는 비용을 측정하고, 잠들기와 충돌 형상, 서브스텝으로 줄이는 방법을 다룹니다.