게임 프로그래밍 충돌 판정 오류 해결법 가이드 2026
충돌 판정이 틀어지는 첫 번째 원인: 좌표계 불일치
월드 좌표와 로컬 좌표를 섞으면 생기는 증상
캐릭터가 벽을 통과하거나, 총알이 분명히 맞았는데 판정이 나지 않거나, 특정 각도에서만 충돌이 튀는 문제가 있다면 먼저 좌표계 불일치를 의심해야 합니다. 게임 프로그래밍에서 충돌 판정은 렌더링보다 훨씬 엄격합니다. 화면에 보이는 위치가 맞아 보여도 실제 물리 계산에 쓰는 좌표가 로컬 기준인지, 월드 기준인지, 부모 오브젝트 기준인지가 다르면 결과는 바로 어긋납니다.
특히 포트폴리오용 엔진이나 직접 만든 math 라이브러리에서 Transform, Matrix, Vector를 분리해 설계한 경우 이 문제가 자주 발생합니다. 렌더링 파이프라인은 모델 행렬을 거쳐 화면에 정상 출력되지만, 충돌 시스템은 회전 전 AABB를 그대로 쓰거나 스케일이 반영되지 않은 콜라이더를 참조할 수 있습니다. 이때 디버그 로그만 보면 좌표값이 크게 이상하지 않아 원인을 놓치기 쉽습니다.
- 월드 좌표 변환 누락: 자식 오브젝트의 콜라이더가 부모 Transform을 반영하지 않아 실제 위치와 판정 위치가 달라집니다.
- 스케일 반영 누락: 캐릭터를 1.2배 키웠지만 충돌 박스는 원래 크기로 남아 피격 판정이 작게 잡힙니다.
- 회전 처리 오류: OBB가 필요한 상황에서 AABB만 사용하면 대각선 벽이나 회전 플랫폼에서 판정이 부정확해집니다.
해결 순서: 충돌 데이터를 화면에 그려 보세요
가장 빠른 해결법은 충돌 데이터를 실제 렌더링 결과 위에 디버그 드로우로 표시하는 것입니다. 텍스트 로그보다 시각화가 훨씬 강력합니다. 캐릭터 메시, 콜라이더, 속도 벡터, 접촉 노멀을 동시에 표시하면 좌표 변환이 어느 단계에서 누락됐는지 바로 보입니다.
- 모든 콜라이더의 중심점과 크기를 월드 좌표로 변환해 표시합니다.
- 렌더링 메시와 콜라이더가 같은 Transform 체인을 사용하는지 확인합니다.
- 부모-자식 구조가 있는 오브젝트는 최종 월드 행렬을 기준으로 충돌 데이터를 갱신합니다.
- 스케일, 회전, 위치를 각각 따로 바꿔 보며 어느 값에서 오차가 생기는지 분리합니다.
팁: 충돌 판정 버그를 고칠 때는 먼저 수학을 의심하기보다, 어떤 좌표계를 입력으로 넣고 어떤 좌표계를 출력으로 기대하는지 표로 적어 보세요. Vector 연산 자체보다 데이터 흐름이 틀린 경우가 더 많습니다.
빠른 물체가 통과하는 문제: 터널링을 잡는 법
프레임 기반 판정의 한계
총알, 레이저, 빠른 대시 캐릭터, 낙하하는 작은 아이템이 벽을 통과한다면 터널링 문제일 가능성이 큽니다. 일반적인 프레임 기반 충돌 판정은 현재 프레임의 위치와 다음 프레임의 위치만 비교합니다. 물체가 한 프레임 안에 벽 두께보다 더 먼 거리를 이동하면, 두 위치 모두 벽 바깥으로 계산되어 충돌이 없다고 판단할 수 있습니다.
2026년 기준으로도 이 문제는 초보자뿐 아니라 커스텀 엔진을 만드는 개발자에게 자주 발생합니다. 고성능 PC에서는 문제가 없어 보이다가 모바일, 웹 빌드, 저사양 환경에서 프레임 타임이 흔들리면 갑자기 재현됩니다. 그래서 게임 프로그래밍에서는 단순히 FPS가 높게 나오는지보다 delta time이 흔들릴 때 물리 판정이 안정적인지를 반드시 확인해야 합니다.
- 총알형 오브젝트: 프레임 사이 이동 경로 전체를 검사하는 raycast 또는 sweep test가 필요합니다.
- 플레이어 대시: 순간 이동처럼 보이는 이동에는 여러 sub-step을 적용하거나 이동 경로를 캡슐 형태로 검사합니다.
- 낙하 물체: 중력 가속으로 속도가 계속 커지므로 최대 속도 제한과 연속 충돌 검사를 함께 고려합니다.
CCD와 서브스텝을 언제 써야 할까
해결책은 크게 두 가지입니다. 첫째는 연속 충돌 감지, 즉 CCD를 사용하는 방식입니다. 이전 위치에서 현재 위치까지의 경로를 선분, 캡슐, swept AABB 등으로 검사해 중간에 충돌한 지점을 찾습니다. 둘째는 물리 업데이트를 더 작은 시간 단위로 나누는 서브스텝 방식입니다. 프레임 하나를 2~8회로 쪼개면 터널링 가능성이 줄어듭니다.
다만 모든 오브젝트에 CCD를 적용하면 비용이 커집니다. 실제 프로젝트에서는 빠른 투사체, 플레이어, 중요한 상호작용 오브젝트에만 우선 적용하는 것이 합리적입니다. 기본 개념과 구현 흐름은 C/C++ 게임 프로그래밍 관련 서적처럼 엔진 구현을 다루는 자료와 함께 보면 구조를 잡기 쉽습니다.
- 이동 거리와 콜라이더 두께를 비교해 터널링 위험 오브젝트를 분류합니다.
- 투사체는 raycast, 캐릭터는 capsule sweep, 박스형 오브젝트는 swept AABB를 우선 검토합니다.
- delta time이 큰 프레임에서는 물리 업데이트를 고정 시간 간격으로 여러 번 수행합니다.
- CCD 적용 대상을 제한해 성능 비용을 관리합니다.
충돌 후 튕김과 떨림: 보정 순서가 핵심입니다
침투 보정이 반복되면 떨림이 생깁니다
캐릭터가 바닥 위에서 미세하게 떨리거나, 벽 모서리에 닿았을 때 위아래로 튕기거나, 플랫폼 위에 서 있는데 계속 밀려나는 현상은 대개 충돌 해결 단계의 문제입니다. 충돌을 감지하는 것과 충돌을 해결하는 것은 다른 일입니다. 감지는 겹쳤다는 사실을 알려 주지만, 해결은 어느 방향으로 얼마나 밀어낼지 결정합니다.
초보 구현에서는 충돌이 발생하면 단순히 이전 위치로 되돌리는 방식을 많이 씁니다. 이 방식은 작은 게임잼 프로토타입에서는 빠르게 동작하지만, 경사면, 움직이는 플랫폼, 여러 벽이 동시에 닿는 상황에서는 쉽게 깨집니다. 특히 X축을 먼저 보정하고 Y축을 나중에 보정하는 구조에서는 모서리에서 의도하지 않은 슬라이딩이나 끼임이 생길 수 있습니다.
- 이전 위치 복귀: 구현은 쉽지만 빠른 이동과 다중 충돌에서 부자연스럽습니다.
- 최소 이동 벡터 보정: 겹친 깊이가 가장 작은 방향으로 밀어내 자연스러운 결과를 만들 수 있습니다.
- 접촉 노멀 기반 반응: 벽, 바닥, 경사면을 구분해 속도 성분을 제거하거나 반사시킬 수 있습니다.
해결 단계 체크리스트
떨림을 줄이려면 위치 보정, 속도 보정, 상태 갱신 순서를 명확히 해야 합니다. 예를 들어 바닥 충돌이 확인되면 먼저 침투 깊이만큼 캐릭터를 위로 보정하고, 그다음 수직 속도를 0으로 만들며, 마지막으로 grounded 상태를 true로 바꿔야 합니다. 순서가 바뀌면 다음 프레임에서 중력이 다시 적용되어 바닥 안으로 파고들고, 다시 밀려나며 떨림이 반복됩니다.
또 하나 중요한 점은 작은 오차를 허용하는 것입니다. 부동소수점 계산에서는 완벽히 0인 접촉을 기대하면 안 됩니다. 그래서 skin width, slop, epsilon 같은 작은 여유 값을 둡니다. 이 값은 너무 크면 캐릭터가 떠 보이고, 너무 작으면 떨림이 남습니다. 일반적으로 월드 단위와 캐릭터 크기에 맞춰 실험적으로 조정해야 합니다.
- 충돌 감지 결과에서 접촉 노멀과 침투 깊이를 함께 저장합니다.
- 가장 깊은 침투부터 처리할지, 가장 작은 보정부터 처리할지 규칙을 정합니다.
- 위치 보정 후 해당 방향의 속도 성분을 제거합니다.
- 바닥, 벽, 천장 상태를 접촉 노멀 기준으로 분류합니다.
- epsilon 값을 도입해 미세한 겹침은 안정적으로 무시합니다.
전문가 조언: 캐릭터 컨트롤러의 품질은 충돌 감지 알고리즘보다 보정 정책에서 갈리는 경우가 많습니다. 플레이어가 느끼는 것은 수학적 정답이 아니라 입력 후 움직임의 일관성입니다.
수학 라이브러리에서 자주 나는 실수: Vector와 Matrix 점검
정규화, 내적, 외적을 대충 넘기면 안 됩니다
Will Perone 같은 개발자 포트폴리오 사이트에서 math 라이브러리와 게임 프로그래밍을 함께 다룬다면, 충돌 판정 오류는 단순한 버그가 아니라 코드 설계 역량을 보여 주는 주제입니다. 충돌 시스템은 Vector, Matrix, Quaternion, Transform의 품질을 그대로 드러냅니다. 작은 수학 함수 하나가 잘못되어도 전체 물리 반응이 이상하게 보일 수 있습니다.
예를 들어 길이가 0에 가까운 벡터를 정규화하면 NaN이 퍼질 수 있습니다. 내적 결과를 acos에 넣을 때 -1과 1 범위를 벗어나면 각도 계산이 깨집니다. 외적의 방향을 반대로 쓰면 노멀 방향이 뒤집혀 캐릭터가 벽 안쪽으로 밀려 들어갈 수 있습니다. 이런 문제는 렌더링보다 충돌 판정에서 더 빨리 드러납니다.
- Normalize 안전 처리: 벡터 길이가 epsilon보다 작으면 기본 방향을 반환하거나 정규화를 건너뜁니다.
- Dot clamp: 각도 계산 전에 dot 값을 -1~1 범위로 제한해 NaN을 방지합니다.
- 행렬 곱 순서: row-major, column-major, 좌표계 규칙을 프로젝트 전체에서 통일합니다.
- 단위 테스트: 벡터 반사, 투영, 거리 계산, AABB 교차 테스트는 작은 테스트 케이스로 검증합니다.
테스트 케이스는 작고 단단해야 합니다
수학 코드는 눈으로 확인하기 어렵기 때문에 테스트가 중요합니다. 하지만 거대한 통합 테스트부터 만들 필요는 없습니다. 점 하나와 박스 하나, 선분 하나와 원 하나처럼 가장 작은 입력으로 시작하세요. 충돌 판정 함수가 true 또는 false를 반환하는지뿐 아니라, 충돌 지점, 노멀, 침투 깊이까지 함께 검증하면 회귀를 빠르게 잡을 수 있습니다.
게임 개발 콘퍼런스와 업계 지식 흐름을 살펴보면, GDC 같은 행사는 엔진 구조와 개발 워크플로를 폭넓게 다룹니다. 용어 배경이 궁금하다면 GDC 지식백과 설명을 참고해도 좋습니다. 다만 실제 구현에서는 발표 자료보다 자신의 엔진 좌표계와 테스트 케이스가 더 직접적인 기준이 됩니다.
- Vector2, Vector3의 기본 연산을 독립 테스트로 분리합니다.
- AABB, Circle, Ray, Capsule 교차 판정을 각각 최소 사례로 검증합니다.
- 경계값 테스트를 추가해 정확히 닿는 경우와 아주 조금 떨어진 경우를 나눕니다.
- NaN, Infinity, 0 길이 벡터 입력에 대한 방어 코드를 넣습니다.
디버깅 루틴: 재현, 시각화, 기록으로 좁히기
감으로 고치면 같은 버그가 돌아옵니다
충돌 판정 오류는 감으로 수정하면 잠시 좋아졌다가 다른 맵에서 다시 터지는 경우가 많습니다. 그래서 문제 해결 가이드의 핵심은 재현 가능한 최소 상황을 만드는 것입니다. 특정 맵 전체를 켜 놓고 디버깅하지 말고, 벽 하나, 캐릭터 하나, 속도 하나만 남긴 테스트 장면을 별도로 구성하세요.
이때 기록할 값은 생각보다 많지 않습니다. 프레임 번호, delta time, 이전 위치, 현재 위치, 속도, 콜라이더 bounds, 충돌 노멀, 침투 깊이만 있으면 대부분의 문제를 좁힐 수 있습니다. 중요한 것은 같은 입력을 넣었을 때 같은 결과가 나오는지입니다. 재현성이 없으면 수정 효과도 검증할 수 없습니다.
- 재현 장면 분리: 문제 오브젝트만 남긴 테스트 씬을 만들어 수정 시간을 줄입니다.
- 디버그 오버레이: 콜라이더, 이동 경로, 접촉점, 노멀 화살표를 화면에 표시합니다.
- 프레임 로그: 충돌 발생 전후 10프레임 정도만 저장해 로그 과부하를 막습니다.
- 입력 고정: 키보드 입력 대신 기록된 이동 벡터를 재생해 같은 상황을 반복합니다.
개발자와 기획자의 언어를 맞추는 것도 중요합니다
충돌 판정은 엔지니어만의 문제가 아닙니다. 기획자는 “점프가 답답하다”, “벽에 걸린다”, “피격이 억울하다”처럼 체감 언어로 문제를 전달합니다. 개발자는 이를 수치로 바꿔야 합니다. 예를 들어 “벽에 잘 걸린다”는 표현은 모서리 스냅, 마찰값, 캡슐 반지름, 이동 보정 순서 중 하나일 수 있습니다.
협업 관점에서 기획자의 역할을 이해하려면 기획자 지식백과 항목처럼 직무 정의를 참고할 수 있습니다. 실제 팀에서는 체감 표현을 버그 리포트 템플릿으로 바꾸는 것이 효과적입니다. “어느 맵에서, 어떤 입력으로, 몇 번 중 몇 번 재현되는지”를 적게 하면 충돌 시스템 문제를 훨씬 빠르게 좁힐 수 있습니다.
- 버그 리포트에 좌표, 맵 이름, 입력 순서, 재현 확률을 포함합니다.
- 플레이어 체감 표현을 속도, 가속도, 판정 크기, 보정 방향 같은 수치 항목으로 변환합니다.
- 수정 전후 영상을 나란히 비교해 체감 개선 여부를 확인합니다.
- 해결된 케이스는 회귀 테스트 장면으로 남겨 다음 수정 때 다시 실행합니다.
이것만은 꼭 기억하세요: 충돌 판정 점검표
문제별 빠른 진단표
충돌 판정은 한 번에 완벽하게 만들기보다, 문제가 생겼을 때 빠르게 원인을 좁히는 능력이 중요합니다. 아래 표처럼 증상과 원인을 연결해 두면 다음 버그에서 시간을 크게 줄일 수 있습니다. 특히 직접 만든 엔진, 포트폴리오 프로젝트, 게임잼 이후 리팩터링 단계에서는 이 점검표가 실질적인 작업 순서가 됩니다.
2026년에도 게임 프로그래밍의 핵심은 화려한 기능보다 기본기의 일관성입니다. 충돌 시스템은 입력, 이동, 애니메이션, 렌더링, 레벨 디자인과 모두 연결됩니다. 그러니 판정이 틀어졌을 때 단일 함수만 보지 말고, 데이터가 생성되고 변환되고 소비되는 전체 경로를 확인해야 합니다.
- 벽을 통과함: delta time 급증, 이동 거리 과다, CCD 누락, 콜라이더 두께 부족을 확인합니다.
- 바닥에서 떨림: 중력 적용 순서, grounded 갱신 시점, epsilon 값, 침투 보정 방향을 확인합니다.
- 피격 판정이 억울함: 렌더 메시와 히트박스 불일치, 애니메이션 프레임별 콜라이더 갱신 누락을 확인합니다.
- 모서리에 걸림: 캡슐 콜라이더 사용 여부, 슬라이딩 벡터 계산, 마찰 처리 방식을 확인합니다.
- 특정 플랫폼에서만 튐: 움직이는 오브젝트의 속도 전달, 부모 Transform 반영, 업데이트 순서를 확인합니다.
작업 순서로 바로 쓰는 7단계
실제 현장에서 바로 적용하려면 다음 순서를 추천합니다. 먼저 버그를 재현하고, 콜라이더를 시각화하고, 좌표계를 확인합니다. 그다음 이동 경로와 delta time을 기록하고, 충돌 감지와 충돌 해결을 분리해 테스트합니다. 마지막으로 수정한 케이스를 회귀 테스트로 남깁니다.
이 과정을 반복하면 충돌 판정은 점점 예측 가능한 시스템이 됩니다. 포트폴리오에 올릴 때도 “충돌 판정을 구현했습니다”보다 “터널링 방지를 위해 swept test를 적용했고, 디버그 오버레이와 회귀 테스트를 구축했습니다”라고 설명하는 편이 훨씬 설득력 있습니다. Will Perone 사이트의 주제인 developer, game programming, math, portfolio와도 잘 맞는 접근입니다.
- 문제가 발생하는 최소 장면을 분리합니다.
- 렌더링 메시와 콜라이더를 동시에 표시합니다.
- 월드 좌표, 로컬 좌표, 부모 Transform 반영 여부를 확인합니다.
- 프레임별 위치, 속도, delta time을 기록합니다.
- 빠른 물체에는 raycast, sweep test, sub-step 중 하나를 적용합니다.
- 침투 보정과 속도 보정 순서를 고정합니다.
- 고친 사례를 자동 테스트 또는 디버그 씬으로 보관합니다.
체크 포인트: 충돌 판정 버그를 줄이는 가장 현실적인 방법은 더 복잡한 알고리즘을 바로 넣는 것이 아니라, 현재 시스템이 어떤 좌표와 시간 간격으로 계산되는지 투명하게 보이게 만드는 것입니다.

- 다음글여름 게임잼 게임 프로그래밍 프로토타입 가이드 26.07.23
등록된 댓글이 없습니다.
