pyxen.Entity

Entity는 경량 핸들 — 컴포넌트를 위한 컨테이너입니다.

엔티티-컴포넌트 시스템의 소개는 엔티티 및 컴포넌트를 참조하세요.


내장 컴포넌트

컴포넌트접근
Transforme.x, e.y, e.rotation, e.scale
Spritee.sprite, e.color
Animatione.animation
Sounde.sound
Musice.music
Meshe.mesh
GridBodye.body
GridMape.map
Nodee.parent, e.children
Layere.layer
Namee.name
Camerae.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")

속성

속성타입설명
namestr현재 애니메이션 클립의 이름
frameint클립 내 현재 프레임 인덱스
playingbool애니메이션이 재생 중이면 True
finishedbool원샷 애니메이션이 완료되면 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")
매개변수타입기본값설명
propertystr—속성 이름 (위치 인수 또는 키워드 형식)
tofloat—목표 값
byfloat—상대 오프셋 (to의 대안)
startfloat현재 값시작 값 (기본값은 현재 값)
durationfloat1.0초 단위 지속 시간
easestr"linear"이징 함수 이름
delayfloat0.0시작 전 대기 시간(초)

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에서 시작

delay로 트윈을 나중에 시작할 수 있습니다. 기다린 뒤 그 순간의 속성 값에서 시작합니다(by도 거기서부터 셉니다). 시차를 둔 낙하, 팝, 카드 나눠주기가 반복문 하나로 됩니다:

for i, card in enumerate(hand):
    card.tween("y", to=120, duration=0.3, delay=i * 0.08, ease="out_back")

애니메이션 가능한 속성

컴포넌트속성
Transformx, y, rotation, sx, sy
Spriter, g, b, alpha
Textr, g, b, alpha
Camerazoom

이미지 없이 텍스트를 표시하는 엔티티에서는 r, g, b, alpha가 Text의 색입니다. 튀어나온 점수는 label.tween("alpha", to=0, duration=0.4)로 사라집니다.

이징 함수

이름곡선
linear일정 속도 (기본값)
in_quad느린 시작, 가속
out_quad빠른 시작, 감속
in_out_quad느린 시작과 끝
in_cubicquad보다 느린 시작
out_cubicquad보다 빠른 시작
in_out_cubic부드러운 시작과 끝
in_expo매우 느린 시작, 폭발적 가속
in_out_expo폭발적 중간
out_expo매우 빠른 시작, 부드러운 정지
in_back전진하기 전에 뒤로 당김
out_back목표를 초과한 후 안정
in_out_back뒤로 당김과 초과
in_elastic탄성 감기
out_elastic탄성 초과와 바운스
in_out_elastic양쪽 끝에서 탄성
in_bounce출발 전에 튕김
out_bounce떨어져 튕기다 멈춤
in_out_bounce양쪽 끝에서 튕김

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 트윈과 함께 실행

delay가 있는 트윈은 차례를 기다립니다. 그 속성에서 실행 중인 트윈은 지연된 트윈이 시작될 때까지 계속되다가 그때 교체됩니다. 팝한 뒤 되돌아오는 예:

entity.tween("sx", to=1.3, duration=0.1)
entity.tween("sx", to=1.0, duration=0.2, delay=0.1)

파괴된 엔티티의 트윈은 자동으로 정리됩니다.

속성에서 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

매개변수

매개변수타입기본값설명
*propsstr—하나 이상의 속성 이름 (위치 인수)
byfloat—진폭 (진동 모드). 스핀의 경우 생략.
centerfloat현재값진동의 중심 값
speedfloat1.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"):
    ...

참조: pyxen.world.all()


마커 컴포넌트

e.alive = True

마커는 데이터를 저장하지 않습니다 — 존재만 나타냅니다. 일반 컴포넌트처럼 쿼리합니다:

for e in world.all("alive"):
    ...

컴포넌트 필드 접근

e.cell.row
e.cell.col
e.cell.row = 5

지원되는 필드 타입

Python 타입저장 형태
int32비트 정수
float32비트 부동소수점
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()

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):
    ...
매개변수타입기본값설명
*componentsstr—필요한 컴포넌트 이름
withouttuple()제외할 컴포넌트 이름
include_selfboolFalse엔티티 자체를 포함할지 여부

내장 및 커스텀 컴포넌트 이름 모두 지원됩니다.


컴포넌트 제거

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()로 제거

참조: pyxen.world.spawn() | pyxen.world.destroy()