게임 루프
Pyxen은 고정 초당 30프레임으로 게임을 실행합니다. 메인 루프를 작성할 필요가 없습니다 — 두 개의 함수를 정의하면 Pyxen이 호출합니다.
start()와 update()
def start():
# 시작 시 한 번 호출됩니다
pass
def update():
# 매 프레임, 초당 30회 호출됩니다
pass
start()는 월드를 설정하는 곳입니다: 엔티티를 생성하고, 배치하고, 씬을 구성합니다.
update()는 게임 로직이 실행되는 곳입니다: 입력을 읽고, 엔티티를 이동하고, 조건을 확인하고, 상태를 업데이트합니다. 각 update() 호출 후 엔진이 프레임을 렌더링합니다.
고정 프레임 레이트
모든 프레임은 정확히 같은 시간을 차지합니다: 1/30초 (~33ms). 이것은 다음을 의미합니다:
- 게임 로직은 결정론적입니다 — 같은 입력이 같은 결과를 만듭니다
- 가변 델타 타임 대신 프레임을 직접 카운트(
time.frame)할 수 있습니다 - 타임 스크러버와 프레임 인스펙터는 각 프레임이 깨끗한 스냅샷이므로 안정적으로 작동합니다
시간 기반 움직임을 선호하면 time.dt(델타 타임)를 사용할 수도 있지만, 항상 1/30입니다.
프레임 순서
매 프레임 Pyxen은 이 단계를 순서대로 실행합니다:
- 입력 — 키보드, 마우스, 터치, 게임패드 상태 읽기
- update() — 코드 실행
- 충돌 — 엔진이 그리드 바디를 해결하고 충돌 콜백 호출
- 렌더 — 엔진이 모든 보이는 엔티티를 그림
이는 update()에서 입력을 읽을 때 값이 최신임을 의미합니다. 엔티티를 이동하면 충돌 해결이 바로 뒤에 일어납니다.
충돌 콜백
최상위에서 on_collision이나 on_tile_collision을 정의하면, 엔진이 충돌 단계에서 호출합니다:
def on_collision(entity, other):
# 두 그리드 바디가 겹쳤습니다
pass
def on_tile_collision(entity, tile):
# 그리드 바디가 타일맵 타일에 부딪혔습니다
pass
이것은 update() 이후, 렌더링 이전에 실행됩니다.
타이밍
pyxen.time 모듈이 제공하는 것:
| 속성 | 값 | 용도 |
|---|---|---|
time.frame | 0, 1, 2, ... | 시작 이후 프레임 카운터 |
time.dt | 0.0333... | 델타 타임 (항상 1/30) |
time.t | 0.0, 0.033, 0.066, ... | 경과 시간(초) |
time.fps | 30 | 초당 프레임 |
시간 기반 이벤트에는 두 가지 접근이 가능합니다:
# 프레임 기반: 60프레임(2초)마다 발사
if time.frame % 60 == 0:
fire()
# 시간 기반: 같은 동작
if int(time.t * 30) % 60 == 0:
fire()
프레임 카운팅이 일반적으로 더 간단하고 예측 가능합니다.