Files
maplecontest/docs/superpowers/specs/2026-06-11-combat-feel-design.md
gahusb 858f9727dd docs(combat-feel): P3 전투 연출 설계+계획 (드래그 타겟·공격 이펙트·개별 차례·팝업)
probe 완료: ScreenTouchEvent/ScreenToUIPosition 실측, UITouchReceiveComponent 드래그 이벤트 확인.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-11 03:18:10 +09:00

5.4 KiB
Raw Blame History

전투 연출 (P3) — 설계

  • 날짜: 2026-06-11
  • 대상: tools/deck/gen-slaydeck.mjs(카드 드래그·연출·적 턴 시퀀스), 생성물
  • 상태: 승인됨(사용자 사전 위임 — P2~P5 일괄 진행 지시). 로드맵 P3/5.

1. 목표 (사용자 요구)

  1. 카드 드래그→몬스터 지정: 카드를 끌어 특정 몬스터에 놓아 사용(클릭 대체).
  2. 공격 모션 후 데미지: 공격 카드 사용 시 연출(스킬 이펙트) 후 몬스터가 피해.
  3. 몬스터 개별 차례: 적 턴에 몬스터가 한 마리씩 순서대로 행동(행동자 표시).
  4. 데미지 숫자 표시·사망 연출 등 게임필 보강.

2. 타당성 (probe 완료)

  • maker_mouse_input down → ScreenTouchEvent 발화 + ScreenToUIPosition 변환 실측 일치(카드2 위치).
  • MOD.Core.UITouchReceiveComponent: UI 엔티티에 부착 시 UITouchBeginDragEvent/UITouchDragEvent/UITouchEndDragEvent/UITouchDownEvent/UITouchUpEvent 제공(공식, Client). 드래그는 이걸 사용.
  • 몬스터 world→screen(_UILogic:WorldToScreenPosition)은 P1에서 검증됨 — 드롭 판정에 재사용.

3. 설계

3.1 카드 드래그 타겟팅

  • 손패 Card1~5 엔티티에 MOD.Core.UITouchReceiveComponent 추가(생성기 componentNames+컴포넌트).
  • 컨트롤러 상태: DragSlot(0=없음), DragOrigin(원위치 Vector2), DragMoved(boolean).
  • BindButtons에서 카드별로 connect:
    • UITouchBeginDragEvent → CombatOver 아니고 손패에 카드 있으면 DragSlot=i, 원위치 저장(CARD_XS[i] 상수로 복원 가능하므로 저장은 단순화 가능 — 원위치 = (CARD_XS[i], 0)).
    • UITouchDragEvent → 카드 anchoredPosition = ScreenToUIPosition(TouchPoint) - CardHandOffset (CardHand 부모 중심의 UI 좌표 보정값은 런타임 계산: 카드 부모 CardHand의 화면상 중심 = UI(0, -360) → 보정 상수로 굽기).
    • UITouchEndDragEventResolveCardDrop(i, TouchPoint) 후 카드 위치 복원.
  • ResolveCardDrop(slot, screenPoint):
    • 카드 kind 조회. Attack: 생존 몬스터 중 화면 거리(몬스터 world→screen vs screenPoint) 최소이고 임계(예: 200px) 이내인 몬스터 → SetTarget(그 몬스터)PlayCard(slot). 임계 밖이면 취소(복귀만).
    • Skill: 드롭 위치가 손패 위(화면 y 기준 카드 영역 위쪽, 예: screen y > 화면 40%)면 PlayCard(slot), 아니면 취소.
  • 기존 카드 ButtonComponent 클릭 PlayCard 바인딩 제거(드래그와 충돌 방지, 사용은 드래그로 일원화 — STS 방식). 몬스터 슬롯 클릭 SetTarget은 유지(타겟만 바꾸는 보조 수단).

3.2 공격 연출 → 데미지 (PlayCard Attack 시퀀스)

  • CombatHud/SkillFx 엔티티 1개(96×96 이미지 스프라이트, 평소 숨김).
  • PlayCard(Attack) 흐름 변경: 에너지 차감·손패 제거·렌더는 즉시, 데미지는 지연:
    1. ShowSkillFx(targetIndex, c.image): 타겟 몬스터 world→screen 위치에 SkillFx 표시(ImageRUID=카드 이미지).
    2. 0.35s 타이머 → SkillFx 숨김 + DealDamageToTarget(damage) + 데미지 팝업 + RenderCombat + CheckCombatEnd.
  • 연출 중 입력 보호: FxBusy=true 동안 PlayCard/EndPlayerTurn 무시(0.35s).

3.3 데미지 숫자 팝업

  • MonsterSlot{i}/DmgPop(텍스트, 숨김 기본): ShowDmgPop(slot, amount) — "-N" 표시 → 0.6s 후 숨김(타이머; 위치 고정 단순화).
  • PlayerPanel/DmgPop 동일(적 공격 시 "-N", 방어 흡수로 0이면 "막음").

3.4 적 개별 차례 (EnemyTurn 시퀀스화)

  • EnemyTurn → 비동기 체인으로 재작성:
    • EnemyActIndex=0; EnemyActStep(): 다음 생존 몬스터 찾기 → 없으면 FinishEnemyTurn().
    • 행동 몬스터 슬롯에 ActFrame(적색 하이라이트 — TargetFrame과 별도 자식, 156×108 적색 a0.3) 표시 → 0.45s 타이머 → 의도 적용(Attack: DealDamageToPlayer+플레이어 DmgPop / Defend: block+슬롯 의도 갱신) → ActFrame 숨김 → 다음 EnemyActStep() (0.15s 간격).
    • 플레이어 사망 시 즉시 FinishEnemyTurn().
  • FinishEnemyTurn(): CheckCombatEnd 후 미종료면 0.45s 뒤 StartPlayerTurn(기존 EndPlayerTurn 후반부 이동).
  • EndPlayerTurn: 손패 버림+렌더 후 EnemyTurn() 호출로 종료(후속 로직은 FinishEnemyTurn으로 이동). TurnBusy=true로 적 턴 중 입력 차단(FxBusy와 함께 가드).

3.5 사망 연출

  • KillMonster: 즉시 SetVisible(false) → 0.4s 지연으로 변경(DmgPop과 겹쳐 보이게), 슬롯 비활성은 즉시 유지.

4. 검증

  • 생성 결정성·dup 0·sim 14/14(규칙 불변 — 연출 지연만 추가, 데미지 계산 동일).
  • 메이커: ①카드를 몬스터2에 드래그→타겟 변경+이펙트→데미지 팝업→HP 감소 ②Skill 카드 위로 드래그→방어 ③드롭 취소(빈 곳) ④턴 종료→적들이 한 마리씩 순차 행동(ActFrame 이동)+플레이어 팝업 ⑤전체 처치 승리 정상.

5. 리스크

  • UITouchReceiveComponent와 ButtonComponent 공존(슬롯 클릭/드래그 간섭) — 카드에서 Button 제거하므로 카드는 안전; 몬스터 슬롯은 Button 유지(드래그 없음).
  • UITouchDragEvent 빈도/좌표계 — 구현 후 메이커 검증(§4①). 드래그 좌표 보정 상수는 실측 튜닝.
  • 비동기 체인 중 상태 변화(연출 중 사망 등) — FxBusy/TurnBusy 가드 + 각 스텝에서 alive/CombatOver 재확인.