충돌
Pyxen은 그리드 기반 충돌 시스템을 사용합니다. 충돌 바디는 공간 그리드 위에 존재하며, 엔진이 매 프레임 바디 간의 겹침을 감지합니다. 이를 통해 충돌 감지가 단순하고, 빠르며, 결정적입니다.
그리드 맵과 그리드 바디
GridMap은 공간적 월드를 정의합니다 — 지정된 셀 크기를 가진 타일 그리드. GridBody는 그 그리드 안에 존재하는 충돌 사각형입니다.
GridMap을 가진 엔티티는 GridBody를 가진 엔티티의 부모여야 합니다. 엔진은 단일 GridMap 내의 모든 충돌을 독립적으로 처리하므로, 같은 월드에서 여러 다른 GridMap을 실행해도 서로 간섭하지 않습니다.
level = world.spawn(
map=GridMap(rows=20, columns=20, size=(16, 16), image="tileset")
)
player = world.spawn(
parent=level,
body=GridBody(pos=(32, 32), size=(16, 16), tag=1, mask=(2,))
)
엔티티 위치
엔티티의 위치 (x, y)는 GridMap 충돌 처리에서 자동으로 파생됩니다. 엔진이 바디 이동을 처리할 때, 엔티티의 트랜스폼을 바디의 처리된 위치와 일치하도록 업데이트합니다. GridBody가 있는 엔티티에서 x/y를 수동으로 설정하지 마세요 — 충돌 시스템이 위치를 소유합니다.
바디 이동
바디가 있는 엔티티를 이동하려면 바디의 move 속성을 설정하세요. 엔진이 이동을 시도하고 충돌을 처리합니다:
def update():
player.body.move = (0, 0)
if input.keyboard.right.down:
player.body.move = (2, 0)
엔진이 바디를 이동하고, 충돌을 확인하고, 최종 위치를 조정합니다.
태그와 마스크
태그와 마스크는 어떤 바디가 서로 충돌할 수 있는지를 제어합니다.
- 태그 — 이 바디가 무엇인지 식별하는 숫자 (예: 1 = 플레이어, 2 = 적, 3 = 벽)
- 마스크 — 이 바디가 충돌해야 할 태그
이동하는 바디가 마스크에 포함된 태그를 가진 다른 바디나 타일을 만나면, 통과하지 않고 미끄러집니다. 이를 통해 벽과 장애물을 따라 부드럽게 이동하는 단단한 충돌을 제공합니다.
player = world.spawn(
parent=level,
body=GridBody(pos=(0, 0), size=(16, 16), tag=1, mask=(2, 3))
)
enemy = world.spawn(
parent=level,
body=GridBody(pos=(0, 0), size=(16, 16), tag=2, mask=(1,))
)
플레이어는 태그 2와 3 (적과 벽)과 충돌합니다. 적은 태그 1 (플레이어)과 충돌합니다. 태그가 0이거나 일치하는 마스크가 없는 바디는 서로를 통과합니다.
충돌 콜백
바디가 타일에 부딪히면 엔진이 on_hit_tile을 호출합니다:
def on_hit_tile(entity, tile):
# 엔티티가 단단한 타일에 부딪힘
pass
두 바디가 겹치면 엔진이 on_hit_body를 호출합니다:
def on_hit_body(entity, other):
if entity == player:
entity.health.value -= 1
이들은 충돌 단계에서, update() 이후 렌더링 전에 실행됩니다.
타일맵 충돌
GridMap은 타일 그리드를 정의하며, 타일 태그를 사용하여 타일을 단단하게 표시할 수 있습니다:
level.map.set(row=0, column=5, tile=(1, 0), tag=2)
마스크에 해당 태그가 포함된 바디는 그 타일에 미끄러집니다.
작동 방식
충돌 시스템은 그리드 기반이며, 픽셀 단위가 아닙니다. 바디는 그리드 내에서 이동하는 축 정렬 사각형입니다. 시뮬레이션은 GridMap별로 격리됩니다 — 각 GridMap 엔티티가 자체적으로 독립적인 충돌 처리를 실행합니다. 이를 통해 충돌이:
- 결정적 — 같은 입력은 항상 같은 충돌을 생성
- 빠름 — 그리드 조회는 상수 시간
- 예측 가능 — 부동소수점 엣지 케이스 없음
- 격리됨 — 여러 GridMap이 서로 간섭하지 않음