GAMEPLAY / 구조 노트 / DCB

밧줄 다리의 물리 흔들림: 발 접지·바람과 충돌면·플레이어 이동 연결

네 감쇠 진동자로 밧줄 다리를 변형하고 발 접지·하중·횡풍을 입력으로 연결했다. 메시와 충돌면의 불일치, 횡이동 실림과 양끝 걸림을 고친 과정 및 현재 설정을 설명한다.

다리 위에서 서고 걸을 때 서로 다른 힘이 들어간다

DCB의 밧줄 다리는 올라선 위치의 하중으로 처지고, 발이 닿을 때마다 추가 충격을 받으며, 옆바람과 돌풍에 횡으로 밀리고 비틀리는 구조다. 하중과 바람이 사라져도 남은 진동은 감쇠한 뒤 원래 모양으로 돌아온다.

다리만 흔들리는 화면으로 끝나지 않도록 바닥 충돌면도 움직이고, 플레이어의 접지 이동에 다리 표면의 속도를 더한다. 난간은 높이에 따른 횡 변위를 더 크게 표현한다. 고정부와 양끝은 움직임을 줄여 지형과의 연결을 유지한다.

2026년 10월 19일 RopeBridgeSway, 플레이어 접지 코드, Bridge_00과 신사 구간의 중첩 프리팹, 발 접지·검증 도구 및 커밋 이력을 대조했다. 날짜는 충돌면·플레이어 이동·접지 충격을 연결한 9월 21일의 작업 작성일이다. 이번에는 Unity 컴파일·실제 보행·바람 재생·성능 측정을 새로 실행하지 않았다.

네 진동자의 합으로 모양을 만든다

다리의 모든 판자와 밧줄에 Rigidbody·Joint를 붙인 방식은 아니다. 수직 1차·수직 2차·횡 1차·롤 1차의 네 감쇠 진동자를 계산하고, 그 값을 위치별 형상 함수에 곱해 정점을 변형한다. 게임의 하중과 접지에 반응하는 물리 기반 표현이며 실제 교량의 구조 해석이나 밧줄 장력 해석을 수행하지 않는다.

다리 길이 방향 위치를 s∈[0,1]s\in[0,1]로 정규화하면 두 기본 형상은 다음과 같다.

ϕ1(s)=sin⁡(πs),ϕ2(s)=sin⁡(2πs)\phi_1(s)=\sin(\pi s),\qquad \phi_2(s)=\sin(2\pi s)

1차는 중앙에서 가장 크고 양끝에서 0이다. 2차는 앞뒤 절반의 부호가 달라 S자 변형을 만든다. 여기에 양끝 페이드 가중치를 곱한다. 현재 페이드 설정은 길이의 0.05 비율이며 고정 대상으로 지정한 렌더러는 변형 수집에서 제외한다.

네 모드는 각각 다음 식으로 목표를 따라간다. FF는 바람 난류처럼 계속 주는 가진 가속도이며, 발걸음은 별도로 속도에 충격을 더한다.

x¨=ω2(xtarget−x)−2ζωx˙+F,ω=2πf\ddot x = \omega^2(x_\text{target}-x)-2\zeta\omega\dot x+F, \qquad \omega=2\pi f

진동수·감쇠비를 실행 중 바꾸면 기존 위치·속도를 유지하고 계수만 다시 읽는다. 힘을 바꾸는 튜닝과 상태 자체를 초기화하는 일을 구분했다.

서 있는 하중과 발 접지 충격을 분리한다

플레이어는 자동으로 하중 1로 등록한다. 현재 이 값은 지게 짐 무게에 따라 자동 증가하지 않는다. 다른 대상은 AddLoad()로 등록할 수 있지만, 이 API가 있다고 모든 몬스터가 자동으로 다리 하중에 참여하는 것은 아니다.

하중 인정은 다리 로컬 길이·폭·높이 범위로 판단한다. Trigger에 들어왔다는 사실만 쓰거나, 발이 정확히 접촉한 한 점을 하중 위치로 쓰는 구조는 아니다. 하중 위치의 형상값만큼 정적 처짐 목표를 더한다.

발 충격은 FootstepAudioPlayer.FootContact를 구독한다. 이 이벤트는 보행 애니메이션의 발 접지 위상에서 나오며, 실제 물리 충돌의 힘을 측정한 값은 아니다. 누적 접지 수를 다음 시뮬레이션에서 소비하고 걷기·달리기·질주 배율을 적용한다. 좌우 충격의 부호는 번갈아 준다.

이벤트 제공자가 없는 등록 하중만 이동 거리와 보폭으로 접지를 대신한다. 이벤트 경로와 거리 경로를 동시에 더하지 않는다. 발 접지 제공자 자체의 기본 이동 속도 게이트는 0.5m/s, 재발동 간격은 0.12초다. 제자리에서 다리가 흔들린다고 발걸음 충격을 계속 만드는 것을 막는 데 관련되는 조건이다.

바람은 다리 옆 방향으로 투영한다

다리 중앙에서 WindField를 샘플링하고 바람 방향을 다리의 횡축에 투영한다. 지속 바람에 양의 돌풍 성분을 배율 1.5로 더한 값이 횡 변위·롤 목표에 들어간다. 별도의 노이즈는 잔진동 입력으로 사용한다. 다리 길이를 따라 부는 바람과 옆에서 부는 바람을 같은 횡 충격으로 취급하지 않는다.

정점 변위에는 난간 높이와 좌우 위치도 들어간다. 난간이 데크보다 크게 옆으로 움직이고 롤에 따라 양쪽 높이가 달라지는 이유다. 바람 세기를 곧바로 실측 풍속이나 실제 재료의 탄성 계수로 해석하지 않는다.

flowchart TD
    A[위치별 하중] --> D[네 감쇠 진동자]
    B[발 접지 이벤트와 보행 종류] --> D
    C[횡풍과 돌풍] --> D
    D --> E[위치별 변위와 기울기]
    E --> F[Burst 잡의 정점 변형]
    E --> G[판자와 난간 충돌면 추종]
    D --> H[접지 지점의 표면 속도]
    H --> I[플레이어 이동 속도에 합산]

메시만 처져 캐릭터가 떠 보이던 문제

9월 19일 초기 구현은 화면의 메시를 변형하고 기존 충돌면은 그대로 뒀다. 그 결과 다리 중앙이 처져도 캐릭터는 원래 충돌면 위에 서 있어 판자에서 떠 보일 수 있었다.

9월 21일에는 콜라이더마다 길이 방향 위치, 기준 중심과 자세를 기록하고 그 구간의 변위·기울기를 적용하도록 바꿨다. 변형된 메시로 MeshCollider를 매 프레임 다시 굽는 대신 충돌체 오브젝트를 옮기는 방식이다.

이후 큰 데크·밧줄 MeshCollider를 끄고, 판자와 난간을 작은 BoxCollider들로 나눴다. 밧줄 전체를 중앙 변위만큼 통째로 옮기면 양끝 근처 플레이어까지 밀어낼 수 있었기 때문이다. 현재 대표 프리팹에는 이름 기준 판자 상자 26개와 난간 상자 52개가 있다. 과거 커밋의 68개·80개 구성과 다르므로 과거 추종 개수와 오차를 현재 검사 결과로 재사용하지 않는다.

판자 콜라이더의 앞뒤 기울기 추종은 켜져 있고 롤 회전 추종은 꺼져 있다. 넓은 플레이어 캡슐 밑에서 판자가 좌우로 회전하면 몸을 들어 올리거나 밀 수 있었기 때문이다. 롤이 만드는 위치 변위까지 없앤 것은 아니다. 각 상자는 대표 지점의 움직임을 따라가므로 변형된 표면 전체와 정확히 일치하는 충돌 메시도 아니다.

Rigidbody를 추가하지 않은 선택은 기존 StepUp·Climb·SideStep의 표면 판정과도 관련된다. 현재 연결을 유지하기 위한 방식이며 모든 동적 플랫폼에 적용할 일반 해법으로 소개하지 않는다.

다리의 횡이동에 플레이어도 실리게 한다

바닥 높이를 맞춘 것만으로는 다리가 옆으로 움직일 때 캐릭터가 함께 이동하지 않는다. 이를 위해 IGroundCarrier.TryGetSurfaceVelocity()로 발밑 지점의 표면 속도를 제공한다. 변위에 사용한 형상과 같은 방식으로 네 모드의 속도를 합성한다.

플레이어 컨트롤러는 발밑 Collider가 바뀔 때 부모의 IGroundCarrier를 찾는다. 접지 이동과 루트 모션 속도에 이 표면 속도를 더한다. 플랫폼의 자식으로 플레이어를 재부모화하는 처리는 아니다.

걸음 속도를 계측할 때는 실려 간 몫을 뺀다. 다리 위에 가만히 서서 좌우로 밀리는 이동까지 보행으로 읽으면, 걸음 애니메이션·발 접지 충격이 다리 움직임에 다시 반응할 수 있기 때문이다. 표면 이동과 플레이어가 스스로 움직인 양을 구분한 연결이다.

양끝에서 내려설 때 걸리던 홈을 메운다

작업 기록에는 마지막 판자 다음 지형이 낮아 한 번 떨어졌다 다시 올라가던 문제가 남아 있다. 초기화 때 끝 판자의 바깥 윗모서리부터 바깥 방향으로 지면을 탐색하고, 판자와 지면을 잇는 고정 착지 상자를 만들도록 보완했다.

현재 탐색은 0.25m 간격으로 최대 1.5m, 높이 일치 허용은 5cm다. 높이가 맞지 않아도 마지막으로 찾은 지면이 0~0.5m 아래라면 내리막 상자를 만들 수 있다. 더 깊은 절벽이나 유효한 지면이 없는 경우에는 만들지 않는다. 두 착지 상자가 모든 배치에서 반드시 생성되는 기능은 아니다.

상자는 실행 중 생성되며 씬에 저장된 지형 자체를 평평하게 바꾸지 않는다. 이번에는 현재 구간에서 생성 결과와 보행 연결을 다시 확인하지 않았다.

현재 저장값은 과거 기본 튜닝과 다르다

아래는 현재 Bridge_00의 저장값이다. 신사 구간의 중첩 인스턴스에서는 이 흔들림 수치를 덮어쓴 설정이 없음을 확인했다. 다른 배치나 에디터의 미저장 실행 상태까지 같은 값이라고 단정하지 않는다.

항목현재 저장값의미
수직 1차1Hz / 감쇠 0.2중앙 처짐과 발 충격의 주된 상하 운동
수직 2차2.6Hz / 감쇠 0.25S자 성분
횡 / 롤1Hz·0.05 / 1.2Hz·0.18좌우 움직임과 비틀림
정적 처짐 목표 계수 / 2차 비율0.5m / 0.2하중 위치별 목표 계산. 실제 중앙 처짐을 0.5m로 보장하지 않음
발 접지 수직 / 횡 충격0.8 / 0.15m/s모드 속도에 더하는 기준
걷기 / 달리기 / 질주1.6 / 2 / 3배발 충격 배율
난간 횡 증폭 / 기준 높이1 / 1.1m해당 높이에서 횡 기본 성분을 2배로 표현
모드 제한수직 ±0.35m / 횡 ±0.45m / 롤 ±0.2rad각 진동자 상태의 제한. 합성 표면 전체의 단일 상한은 아님

튜닝 문서의 정적 처짐 4.5cm·수직 1.6Hz를 현재 적용값으로 옮기지 않았다. 예를 들어 중앙 하중 1의 1차 목표는 −0.5m지만 상태는 −0.35m에서 제한된다. 중앙 보행 한 번의 수직 1차 속도 충격은 −0.8×1.6=−1.28-0.8\times1.6=-1.28m/s다. 이 계산은 코드·저장값의 예이며 화면에서 관찰한 진폭은 아니다. 현재 설정의 반복적인 제한 도달과 보행 안정성은 실제 실행에서 확인해야 한다.

갱신 비용과 시간 간격의 한계

변형용 메시와 정점별 형상 계수는 초기화 때 만든다. Burst 잡으로 정점 위치를 계산한 뒤 Complete()로 완료를 기다리고 메시를 갱신한다. 작업 예약만으로 메인 스레드와 완전히 비동기라고 설명하지 않는다. 병렬 인덱스 작업과 완료 대기의 기본 사용은 Unity Parallel jobs 문서와 대조했다. 파괴 때 메시와 NativeArray를 정리하는 경로도 확인했다.

하중·바람·남은 진동이 없으면 원형으로 복원하고 휴면한다. 현재 코드 기본값의 카메라 경계 거리 80m 밖이면서 하중이 없으면 모드 계산은 계속하고 정점·콜라이더 갱신만 건너뛴다. 가까워지면 현재 모드 상태로 맞춘다. 화면 밖이라는 이유만으로 즉시 전체 계산을 끄는 방식은 아니다. 이번에 CPU 시간이나 프레임 개선량을 측정하지 않았다.

시간 적분은 목표 간격 1/50초, 최대 8회로 나누지만 긴 프레임에는 전체 시간을 그 8회에 나눈다. 따라서 0.16초를 넘으면 간격이 목표보다 커질 수 있다. 지게 짐의 긴 프레임 수정처럼 남는 시간을 버리는 방식까지 적용돼 있다고 설명하지 않는다. 제한값 클램프와 별개로 긴 프레임의 복귀·움직임 품질을 재확인할 필요가 있다.

작업 기록과 검증 범위

작성일커밋문제와 변경
2026-09-199594498a8하중·바람을 받는 네 모드와 메시 변형 추가
2026-09-21a0076cedf메시만 처지고 충돌면은 남는 문제에 구간별 콜라이더 추종 추가
2026-09-21ce5cc0dd4횡이동 실림, 표면 이동을 뺀 보행 계측, 판자·난간 상자로 교체
2026-09-214cdb05dca이동 거리 추정에서 발 접지 이벤트로 전환하고 보행별 충격 연결
2026-09-215271b7e94판자 끝과 지형 사이 홈을 착지 상자로 연결
2026-10-025f7a18d73멀고 하중이 없는 다리의 정점·콜라이더 갱신 생략

담당한 부분은 다리의 시각 변형을 기존 지면 판정·플레이어 이동·발 접지·바람 시스템에 연결하고, 서로 어긋나던 접촉과 이동을 보완하는 구조다. 코드와 도구 작성에는 AI 보조를 사용했고 관련 커밋에도 공동 작성 기록이 있다. 기존 다리 모델·텍스처의 원본 제작을 직접 담당한 성과로 포함하지 않는다.

당시 커밋에는 편집·플레이 모드의 콜라이더 추종, 횡풍 실림, 발 접지 수와 충격 수, 하차 후 복원 결과가 기록돼 있다. 이번에는 그 원본 로그·영상을 재확인하지 않았다. 현재 검증 도구가 해당 조건을 계측한다는 소스 확인과 과거 커밋에 적힌 실행 결과를 구분한다. 특히 이후 콜라이더 수와 튜닝이 달라졌으므로 과거 통과 기록을 현재 프리팹의 재검증으로 간주하지 않는다.

같은 조건으로 확인할 실험과 판정

  1. 무풍에서 중앙에 서기와 걷기를 비교한다. 중앙에서는 1차 형상값이 1, 2차가 0이므로 정적 처짐과 발 충격의 차이를 보기 쉽다. 현재 목표와 제한값을 함께 기록하고 0.5m가 내려가지 않았다는 이유만으로 오류라고 판정하지 않는다.
  2. 서 있는 상태로 옆바람을 주고 이동 입력 없이 촬영한다. 몸이 표면과 함께 이동하는지, 실려 간 이동 때문에 보행이 발생하는지, 난간 충돌로 밀리는지 분리한다. 추종 오차·발밑 틈·자체 보행 속도를 함께 계측한다.
  3. 다리 진입·이탈, 달리기·질주, 긴 프레임을 나눠 확인한다. 발 접지 수와 소비한 충격 수, 양끝 착지 상자 유무·보행 단차, 하차 후 복원과 원거리 갱신 재개를 기록한다. 현재 검사 도구의 허용 범위가 강한 튜닝에도 적합한지 먼저 확인한다.

실제 게임 카메라에서의 가독성, 판자·발의 접촉 품질, 강한 바람의 캡슐 안정성, 여러 하중과 Player 빌드 성능은 새 실행·촬영 전까지 미검증 항목으로 남긴다.

DCB 개발 기록

  1. 01IK 트레버설 구현: StepUp, Climb, SideStep
  2. 02Samurai의 그룹 전투: 포위·압박·공격 패턴
  3. 032D·3D 장비 파츠 조합 구현
  4. 04물 캐시 재생과 메시 업로드
  5. 05불 이펙트의 레이어와 품질 단계
  6. 06AI를 사용한 구현과 검증 과정
  7. 07지게 운반의 이동 속도와 스태미너 계산 구조
  8. 08병렬 AI 세션을 위한 공통 지침과 Git 워크트리 운영
  9. 09Unity Behavior 그래프를 코드 기반 FSM·DSL로 바꾼 과정
  10. 10장비 조합 UI의 불티·연기와 코일 연출
  11. 11DCB의 장애물 이동: 턱 오르기·벽 타기·옆걸음
  12. 12DCB의 장비 조합: 파츠 선택부터 완성 무기 보관까지
  13. 13DCB의 급류: 수면·포말·물보라를 함께 재생하는 방식
  14. 14DCB의 환경 화염: 모양·연기·잔불과 화면 크기별 표현
  15. 15몬스터 행동 DSL·그룹 제어의 문제 해결 과정
  16. 16DCB의 시네마틱: 구간 진입에서 매복 전투로 이어지는 흐름
  17. 17지게 짐의 출렁임: 받침 가속도와 감쇠 스프링으로 만든 2차 운동
  18. 18밧줄 다리의 물리 흔들림: 발 접지·바람과 충돌면·플레이어 이동 연결
  19. 19DCB의 게임플레이 밸런싱: 이동·스태미너·행동 제어
  20. 20플레이어 밸런싱 도구 제작: 값 편집·결과 예측·플레이 중 확인
  21. 21걷기·달리기·질주 전환과 스태미너 고갈 후 재입력 제어
  22. 22공격·구르기·패리의 스태미너 비용과 회복 지연·재개
  23. 23인벤토리 무게·갈증·물속 감속이 이동 속도에 중첩되는 방식
← 글 모아보기