pyxen.Entity
Entity는 경량 핸들 — 컴포넌트를 위한 컨테이너입니다.
엔티티-컴포넌트 시스템의 소개는 엔티티 및 컴포넌트를 참조하세요.
내장 컴포넌트
| 컴포넌트 | 접근 |
|---|---|
| Transform | e.x, e.y, e.rotation, e.scale |
| Sprite | e.sprite, e.color |
| Animation | e.animation |
| Sound | e.sound |
| Music | e.music |
| GridBody | e.body |
| GridMap | e.map |
| Node | e.parent, e.children |
| Layer | e.layer |
| Name | e.name |
| Camera | e.camera |
Transform
e.x = 100
e.y = 50
e.rotation = 45
e.scale = (2, 2)
scale을 읽으면 Vector2를 반환합니다:
s = e.scale
print(s.x, s.y)
Sprite
e.sprite = sprite
e.color = (1.0, 0.5, 0.5, 1.0)
color를 읽으면 Color를 반환합니다:
c = e.color
print(c.r, c.g, c.b, c.a)
스프라이트 애니메이션
animation 매개변수로 스프라이트를 생성하면 엔진이 자동으로 애니메이션을 재생합니다. e.animation을 통해 애니메이션 상태를 읽을 수 있습니다:
player.sprite = Sprite("hero", animation="walk")
속성
| 속성 | 타입 | 설명 |
|---|---|---|
name | str | 현재 애니메이션 클립의 이름 |
frame | int | 클립 내 현재 프레임 인덱스 |
playing | bool | 애니메이션이 재생 중이면 True |
finished | bool | 원샷 애니메이션이 완료되면 True |
anim = player.animation
if anim:
print(anim.name) # "walk"
print(anim.frame) # 2
print(anim.playing) # True
print(anim.finished) # False
애니메이션이 활성화되지 않은 경우 e.animation은 None을 반환합니다:
player.sprite = Sprite("hero", tile=(0, 0, 16, 16))
print(player.animation) # None
취소
e.animation에 None을 할당하여 재생을 중지합니다:
player.animation = None
새 스프라이트를 할당하면 기존 애니메이션이 자동으로 취소됩니다.
원샷 애니메이션
한 번만 재생되는 애니메이션에는 loop=False를 사용합니다:
player.sprite = Sprite("hero", animation="attack", loop=False)
def update():
if player.animation and player.animation.finished:
player.sprite = Sprite("hero", animation="idle")
Tweens
Tweens는 엔티티의 속성을 시간에 따라 부드럽게 애니메이션합니다. 엔진이 자동으로 보간을 처리하므로 프레임별 코드가 필요 없습니다.
entity.tween()
# 속성 이름 지정 형식
entity.tween("x", to=200, duration=1.0)
entity.tween("alpha", to=0, duration=0.5, ease="out_quad")
# 키워드 축약 형식 — 여러 속성을 동시에 애니메이션
entity.tween(x=200, y=100, duration=1.0, ease="out_quad")
| 매개변수 | 타입 | 기본값 | 설명 |
|---|---|---|---|
property | str | — | 속성 이름 (위치 인수 또는 키워드 형식) |
to | float | — | 목표 값 |
by | float | — | 상대 오프셋 (to의 대안) |
start | float | 현재 값 | 시작 값 (기본값은 현재 값) |
duration | float | 1.0 | 초 단위 지속 시간 |
ease | str | "linear" | 이징 함수 이름 |
to로 절대 목표를, by로 상대 오프셋을 지정합니다:
entity.tween("x", to=200, duration=1.0) # x=200으로 이동
entity.tween("x", by=50, duration=0.5) # 오른쪽으로 50픽셀 이동
start로 시작 값을 재지정합니다:
entity.tween("x", to=200, start=0, duration=1.0) # 항상 0에서 시작
애니메이션 가능한 속성
| 컴포넌트 | 속성 |
|---|---|
| Transform | x, y, rotation, sx, sy |
| Sprite | r, g, b, alpha |
| Camera | zoom |
이징 함수
| 이름 | 곡선 |
|---|---|
linear | 일정 속도 (기본값) |
in_quad | 느린 시작, 가속 |
out_quad | 빠른 시작, 감속 |
in_out_quad | 느린 시작과 끝 |
in_cubic | quad보다 느린 시작 |
out_cubic | quad보다 빠른 시작 |
in_out_cubic | 부드러운 시작과 끝 |
in_expo | 매우 느린 시작, 폭발적 가속 |
in_out_expo | 폭발적 중간 |
out_expo | 매우 빠른 시작, 부드러운 정지 |
in_back | 전진하기 전에 뒤로 당김 |
out_back | 목표를 초과한 후 안정 |
in_out_back | 뒤로 당김과 초과 |
in_elastic | 탄성 감기 |
out_elastic | 탄성 초과와 바운스 |
in_out_elastic | 양쪽 끝에서 탄성 |
entity.tweens.cancel()
엔티티의 트윈을 취소합니다. 속성 이름을 전달하면 하나의 트윈을 취소하고, 인수 없이 호출하면 모든 트윈을 취소합니다. 값은 현재 위치에서 고정됩니다.
entity.tweens.cancel("x") # 하나의 속성 취소
entity.tweens.cancel() # 모두 취소
충돌 해결
같은 엔티티와 속성에 새로운 트윈을 시작하면 이전 것이 교체됩니다. 서로 다른 속성의 트윈은 독립적으로 실행됩니다.
entity.tween("x", to=100, duration=1.0)
entity.tween("x", to=200, duration=0.5) # 첫 번째 트윈을 교체
entity.tween("y", to=50, duration=1.0) # x 트윈과 함께 실행
파괴된 엔티티의 트윈은 자동으로 정리됩니다.
속성에서 animate()를 시작하면 해당 속성의 활성 tween()이 취소되며, 그 반대도 마찬가지입니다.
애니메이션
무한히 실행되는 연속 애니메이션 — 사인파 진동(펄스, 호흡, 흔들림)과 일정 속도 회전.
entity.animate() — 진동
사인파로 중심 값 주위로 속성을 진동시킵니다.
entity.animate("sx", "sy", by=0.15, speed=6.0) # coin pulse
entity.animate("alpha", by=0.3, center=0.7, speed=4.0) # flicker 0.4–1.0
값은 다음을 따릅니다: center + sin(elapsed * speed) * by
entity.animate() — 스핀
일정한 속도로 값을 누적합니다. 스핀 모드를 사용하려면 by=를 생략합니다.
entity.animate("rotation", speed=180) # 180°/sec clockwise
매개변수
| 매개변수 | 타입 | 기본값 | 설명 |
|---|---|---|---|
*props | str | — | 하나 이상의 속성 이름 (위치 인수) |
by | float | — | 진폭 (진동 모드). 스핀의 경우 생략. |
center | float | 현재값 | 진동의 중심 값 |
speed | float | 1.0 | 각 주파수 (진동) 또는 단위/초 (스핀) |
트윈과 동일한 10개 속성: x, y, rotation, sx, sy, r, g, b, alpha, zoom.
entity.animations.cancel()
엔티티의 애니메이션을 취소합니다. 속성 이름을 전달하면 하나의 애니메이션을 취소하고, 인수 없이 호출하면 모든 애니메이션을 취소합니다.
entity.animations.cancel("rotation") # 하나의 속성 취소
entity.animations.cancel() # 모두 취소
부모 / 자식
child.parent = parent
자식에 접근:
for c in parent.children:
print(c.name)
자식 수 확인:
parent.children.count
커스텀 컴포넌트
컴포넌트는 할당으로 생성됩니다:
- 딕셔너리 — 구조화된 컴포넌트
- 불리언 — 마커 컴포넌트
e.cell = {
'row': 0,
'col': 0
}
첫 번째 할당이 스키마를 생성합니다. 필드 타입이 추론되며 스키마가 고정됩니다 — 이후에 새 필드를 추가할 수 없습니다.
시스템에서 컴포넌트 사용
for cell in world.all("cell"):
...
마커 컴포넌트
e.alive = True
마커는 데이터를 저장하지 않습니다 — 존재만 나타냅니다. 일반 컴포넌트처럼 쿼리합니다:
for e in world.all("alive"):
...
컴포넌트 필드 접근
e.cell.row
e.cell.col
e.cell.row = 5
지원되는 필드 타입
| Python 타입 | 저장 형태 |
|---|---|
int | 32비트 정수 |
float | 32비트 부동소수점 |
bool | 불리언 |
str | 고정 문자열 (최대 31자) |
Entity | 엔티티 참조 |
잘못된 타입은 에러를 발생시킵니다:
TypeError: invalid value type in dict
스키마 규칙
- 최대 컴포넌트 크기: 256바이트
- 문자열 최대: 31자
- 딕셔너리 키는 문자열이어야 함
- 스키마 생성 후 새 필드 추가 불가
e.cell = {'row': 0}
e.cell = {'row': 0, 'new_field': 5}
ValueError: cannot create new fields after schema is complete
이후 할당에서 필드를 생략하는 것은 유효합니다 — 0, 0.0, False, "" 등으로 기본값이 설정됩니다.
컴포넌트 확인
e.has("cell")
엔티티에 해당 컴포넌트가 있으면 True, 없으면 False를 반환합니다.
커스텀과 내장 컴포넌트 이름 모두 작동합니다:
e.has("sprite") # 엔티티에 스프라이트가 있으면 True
e.has("body") # 엔티티에 그리드 바디가 있으면 True
자손 쿼리
entity.all(*components, without=(), include_self=False)
주어진 컴포넌트 필터와 일치하는 엔티티의 모든 자손 (씬 그래프 계층을 통한 깊이 우선)에 대한 이터레이터를 반환합니다.
world.all()과 동일하게 작동하지만, 전체 월드 대신 하위 트리로 범위가 제한됩니다.
# "enemy" 컴포넌트를 가진 모든 자손
for e in parent.all("enemy"):
...
# 제외 조건 포함
for e in parent.all("enemy", without=("boss",)):
...
# 부모 엔티티 자체도 포함
for e in parent.all("tag", include_self=True):
...
| 매개변수 | 타입 | 기본값 | 설명 |
|---|---|---|---|
*components | str | — | 필요한 컴포넌트 이름 |
without | tuple | () | 제외할 컴포넌트 이름 |
include_self | bool | False | 엔티티 자체를 포함할지 여부 |
내장 및 커스텀 컴포넌트 이름 모두 지원됩니다.
컴포넌트 제거
del 또는 None 할당으로 엔티티에서 컴포넌트를 제거합니다:
# 둘 다 동일합니다:
del entity.health
entity.health = None
제거 후, entity.has("health")는 False를 반환하며 해당 엔티티는 더 이상 해당 컴포넌트에 대한 쿼리에 매칭되지 않습니다.
e = world.spawn()
e.health = {'hp': 100}
del e.health
e.has("health") # False
# world.all("health")에 e가 포함되지 않음
제거 후 컴포넌트를 다시 추가할 수 있습니다:
del e.health
e.health = {'hp': 50} # 정상 작동
내장 컴포넌트
모든 내장 컴포넌트를 제거할 수 있습니다:
del e.sprite # 스프라이트 데이터 초기화
del e.body # 물리 바디 초기화
del e.camera # 카메라 제거
del e.text # 텍스트 제거
del e.sound # 사운드 제거
del e.music # 음악 제거
del e.map # 그리드 맵 제거
스프라이트와 바디는 0으로 초기화됩니다 (엔티티는 여전히 저장소를 가지지만 데이터는 비어 있습니다). 카메라, 텍스트, 사운드, 음악, 맵은 완전히 삭제됩니다.
마커
e.enemy = True
del e.enemy # 또는: e.enemy = None
e.has("enemy") # False
엔티티 생명주기
world.spawn()으로 생성- 컴포넌트 할당으로 수정
del또는= None으로 컴포넌트 제거world.destroy()로 제거