msw-combat-system

Installation
SKILL.md

msw-combat-system

MSW 전투 파이프라인 전체. 2D 장르 불문 공통 전투 레이어 중 MSW 네이티브 API 지원 항목만 정리. 수식·이론 제외. 전부 default-local-workspace/Environment/NativeScripts/**/*.d.mlua 원본 재검증 완료 (2026-04-21).

의심/예외는 docs/engine-source-index.md 캐시 먼저 확인.


0. 커버리지 매트릭스

# 레이어 네이티브 커스텀 필요
1 Attack Resolution AttackComponent + HitComponent (Box/Circle/Polygon) Capsule/Cone/Ray, 관통 카운트
2 Damage Model CalcDamage/CalcCritical/GetCriticalDamageRate/GetDisplayHitCount 훅 + HitEvent.Extra:any 속성 상성, 복합 공식
3 Hit Reaction Body별 넉백 API, IsHitTarget 기반 i-frame 경직 레벨, 상태이상
4 Game Feel 6개 전부 네이티브 (Hit Stop, Shake, Zoom, Flash, VFX, SFX)
5 Combat State StateComponent + DeadEvent/ReviveEvent, PlayerComponent HP/부활 MP/Stamina/Rage, 어그로
6 Event Bus HitEvent/AttackEvent/StateChangeEvent/PlayerActionEvent + 커스텀 @Event OnKill/OnBlocked
7 AI FSM StateComponent, _UserService.UserEntities BehaviourTree, Threat Table
+ Damage Skin DamageSkin* 3종 + DamageSkinService
+ Hit Effect HitEffectSpawnerComponent (auto)
+ Avatar Motion AvatarStateAnimationComponent (State→MapleAvatarBodyActionState)

1. Attack Resolution

1-1. Shape & Attack 발동

HitComponent.ColliderType = Box / Circle / Polygon 만 지원. 기타 형태는 조합으로 근사.

AttackComponent:
  Attack(Vector2 size, Vector2 offset, string attackInfo, CollisionGroup? cg)    → table<Component>
  Attack(Shape shape, string attackInfo, CollisionGroup? cg)                     → table<Component>
  AttackFast(Shape shape, string attackInfo, CollisionGroup? cg)                 → void   (대량 판정용, 탄막)
  AttackFrom(Vector2 size, Vector2 position, string attackInfo, CollisionGroup? cg) → table<Component>
  emitter EmitAttackEvent(AttackEvent)
  • Shape: RectangleShape(center, size) / CircleShape(center, radius) / BoxShape(pos, size, angle)
  • Polygon 피격면: HitComponent.PolygonPoints: SyncList<Vector2>
  • AttackFast 는 히트 테이블 미생성 → 탄막·대량 판정 성능 유리

1-2. 타겟 필터

지점 오버라이드 용도
공격자 AttackComponent:IsAttackTarget(defender, attackInfo) → boolean 진영·거리·상태
피격자 HitComponent:IsHitTarget(attackInfo) → boolean 무적·면역

한쪽이라도 false → 히트 제외. super 호출은 __base:IsAttackTarget(...) (mlua 고유).

  • HitComponent.CollisionGroup 기본 CollisionGroups.HitBox. Attack(..., cg) 마지막 인자로 타겟 그룹 지정
  • 중복 히트 방지 / 관통 / 최대 히트 수: 네이티브 없음. Attack 반환 테이블 + table<Entity, boolean> 캐시로 스크립트 관리

1-3. attackInfo 태깅

CalcDamage/IsHitTarget/GetDisplayHitCount 로 전파되는 string 확장 지점. 값 규약은 프로젝트 자율. "melee.light", "dot.poison" 식 네임스페이스 권장.

1-4. ⚠️ IsLegacy

HitComponent.IsLegacy = false 에서만 ColliderType/ColliderOffset/PolygonPoints 유효. BoxOffset/ColliderName deprecated.

1-5. 공격 형태별 Shape 매핑

형태 Shape 구성
근접 전방 Box BoxShape(pos + LookDirectionX*offset, size, 0) — DefaultPlayer PlayerAttack 참조
원형 AoE CircleShape(self.WorldPos, radius)
투사체 Body 없는 모델(Sprite+Transform만) 스폰 + OnUpdate(delta)에서 TransformComponent:Translate(speed*delta, 0) + 거리 기반 적중 판정 + _EntityService:Destroy. 이동 규칙 §1-6, 전체 구현(모델·스크립트·U도·변형 패턴) → references/projectile.md

MSW 전용 투사체 시스템 없음 — 엔티티 + AttackComponent 조합으로 구현.

1-6. 연속 이동 — 투사체·몬스터·AI 공통 규칙

연속 이동(추적·비행·자동 이동)은 매 프레임 OnUpdate(delta) 기반. 타이머(SetTimerRepeat(0.1~0.15s))로 이동시키면 6~10Hz 순간이동으로 보여 끊긴다.

대상별 권장 API

대상 Body 유무 이동 API 근거
몬스터 / NPC / AI 있음 (맵 타입 Body + MovementComponent) MovementComponent:MoveToDirection(dir, 0) + MovementComponent.InputSpeed MovementComponent.d.mlua:1 — Rigid/Kinematic/Sideview 셋 다 제어. InputSpeed는 MovementComponent 소속(.d.mlua:7)으로 Player 전용 아님. 두 번째 인자 0 — deltaTime은 사다리에서만 적용(.d.mlua:32). BT 공식 예제 ActionFollow/ActionMoveRandom0 사용
투사체 / 젬 / 드롭 아이템 / 이펙트 없음 (Sprite+Transform+Trigger) self.Entity.TransformComponent:Translate(speed*delta, 0) 매 프레임 Body 없으면 Transform 직접 조작 안전. 공식 "Create a Long-Range Projectile" 튜토리얼 패턴
Rigidbody 엔티티 직접 제어 (고급) 있음 body:AddForce(...) — 지속 가속/임펄스 RigidbodyComponent.d.mlua:71MoveVelocity는 "mainly controlled by MovementComponent" 이므로 직접 쓰기보다 MovementComponent 경유 권장

MovementComponent.InputSpeed 맵 타입별 실제 velocity 환산은 msw-general/references/platform.md §10 참조 (MapleTile=×1, RectTile=÷1.2, SideView=×1.5).

금지 패턴

사유
_TimerService:SetTimerRepeat(move, 0.1~0.15) 이동 6~10Hz teleport, 프레임 보간 없음 → 뚝뚝 끊김
OnUpdatebody:SetPosition(...) / MovementComponent:SetPosition(...) 둘 다 텔레포트 메서드 (MovementComponent.d.mlua:37, 각 Body .d.mluaSetPosition). 연속 이동에 쓰면 끊김. 1회성 스폰·리스폰·스냅에만
self.Entity.TransformComponent.Position = newPos (Body 활성 엔티티) 다음 프레임 물리엔진이 덮어쓰고 네트워크 동기화 차단
Translate(0.009, 0) 같이 delta 없이 상수 이동 프레임률 의존. 60FPS·30FPS에서 속도 다름

⚠ 공식 msw-search 안티패턴 주의

mlua_Document_Retriever에 "Entity Movement Control Using MovementComponent" 문서가 높은 점수로 잡히지만, 본문은 OnUpdate 안에서 MovementComponent:SetPosition(...)으로 매 프레임 이동하는 안티패턴이다 — 이 문서는 무시하고 위 표의 MoveToDirection / Translate로. FlappyFish Remake·Stopping the Taxi·Making a Moving Foothold도 delta 누락 또는 Position 직접 대입을 보여주므로 참조 시 주의.

MovementComponent 부착 — 몬스터/NPC

몬스터 모델에 기본 포함 안 됨. .model에 다음 컴포넌트가 모두 있어야 한다.

컴포넌트 비고
MOD.Core.TransformComponent 기본
MOD.Core.SpriteRendererComponent 렌더
Body (맵 타입) RigidbodyComponent(MapleTile) / KinematicbodyComponent(RectTile) / SideviewbodyComponent(SideViewRectTile)
MOD.Core.MovementComponent InputSpeed = 2.0 등 — 이동 API용

교차 참조


2. Damage Model

AttackComponent:
  method integer CalcDamage(attacker, defender, attackInfo)        -- default 1
  method boolean CalcCritical(attacker, defender, attackInfo)      -- default false
  method float   GetCriticalDamageRate()                           -- default 2.0
  method int32   GetDisplayHitCount(attackInfo)                    -- default 1
  method void    OnAttack(defender)

HitComponent:
  method void OnHit(Entity attacker, integer damage, boolean isCritical, string attackInfo, int32 hitCount)
  emitter EmitHitEvent(HitEvent)

2-1. HitEvent 페이로드

AttackCenter:   Vector2
AttackerEntity: Entity (nilable)
Damages:        List<integer>    -- 멀티히트 분할
Extra:          any              -- ★ 확장 슬롯 (knockback/stun/element/tags)
IsCritical:     boolean
TotalDamage:    integer
FeedbackAction: HitFeedbackAction  -- ⚠ 전체 enum deprecated

부가 정보(넉백 벡터·경직 시간·속성)는 Extra 테이블로 실어 전달.

2-2. AttackEvent 페이로드

DefenderEntity: Entity 하나. 공격자는 핸들러 self.


3. Hit Reaction

3-1. 넉백 — Body별 API

Body (맵 타입) 구현
Rigidbody (MapleTile) body:AddForce(Vector2(dir*5, 3)) ★권장 · SetForce · JustJump(Vector2(0, 4)) (수직)
Kinematicbody (RectTile/탑다운) body.MoveVelocity = Vector2(dir*5, 0) — AddForce 없음
Sideviewbody (SideViewRectTile) body.MoveVelocity + body.JumpSpeed
  • Rigidbody 는 엔진이 자동 감쇠. Kinematic/Sideview 는 OnUpdate 에서 수동 감쇠 (MoveVelocity *= 0.9)
  • 벽 튕김: FootholdCollisionEvent 구독 후 velocity 반전
  • 넉백은 1-shot 임펄스 — 연속 이동(추적·비행)과 혼동 금지. 연속 이동은 §1-6
  • ⚠ 금지: Body 활성 엔티티에 TransformComponent.Position 직접 대입 → 네트워크 동기화 차단. body:SetPosition(...) 은 텔레포트 메서드이므로 OnUpdate 루프에서 호출 금지 (§1-6)

3-2. i-frame

표준 패턴: _UtilLogic.ElapsedSeconds 기반 deadline 체크 + HitComponent:IsHitTarget 에서 false. DefaultPlayer 기본 PlayerHit.mlua 가 이 패턴을 그대로 제공 (§9-4).

대안: 무적 중 HitComponent.CollisionGroup 을 별도 그룹으로 교체 → 판정 자체 제외. 프레임 단위 정확도에 유리.

3-3. 상태이상 (Buff/Debuff)

네이티브 부재. @Component BuffComponent 직접 구현 + _TimerService:SetTimerRepeat 틱 + 커스텀 StatusAppliedEvent/StatusExpiredEvent 브로드캐스트.

단순 스턴 1종이면 StateComponent:ChangeState("STUN") + 입력/AI 차단 플래그로 충분.


4. Game Feel — 전부 네이티브

요소 API ExecSpace
Hit Stop (전역) _UtilLogic:SetClientTimeScale(float) — 0~100 ClientOnly
Hit Stop (개별) renderer.PlayRate = 0 (Sprite/Skeleton/Avatar) @Sync
Slow Motion _UtilLogic:SetClientTimeScale(0.3) + 타이머 복귀 ClientOnly
Camera Shake cameraComp:ShakeCamera(intensity, duration, targetUserId?) Client
Camera Zoom cameraComp:SetZoomTo(percent, duration, targetUserId?) · 단 IsAllowZoomInOut=true 선행 Client
Hit Flash spriteRenderer.Color = Color(r,g,b,a) → 타이머 복귀 @Sync
Color HDR 오버브라이트 Color.HSVToRGB(h, s, v, hdr=true) — 1.0 초과 허용
VFX 고정 _EffectService:PlayEffect(clipRUID, instigator, pos, zRot, scale, isLoop?, options?) → serial
VFX 부착 _EffectService:PlayEffectAttached(clipRUID, parent, localPos, localZRot, localScale, isLoop?, options?)
VFX 제거 _EffectService:RemoveEffect(serial)
SFX 2D _SoundService:PlaySound(id, volume, targetUserId?) Client
SFX 3D _SoundService:PlaySoundAtPos(id, pos, listener, volume) Client
SFX 루프 PlayLoopSound / PlayLoopSoundAtPos Client
SFX 부착 SoundComponent:Play() · 피치 랜덤은 Pitch 0~3 Client
BGM _SoundService:PlayBGM(id, volume) / StopBGM(immediately) Client
프리로드 _SoundService:LoadSound(id) ClientOnly

PlayEffect options 키: FlipX, FlipY, SortingLayer, OrderInLayer, Alpha, StartFrameIndex, EndFrameIndex, PlayRate, SyncFlip, Color, MaterialID, IgnoreMapLayerCheck, LitMode

현재 카메라 획득: _CameraService:GetCurrentCameraComponent().

ParticleService — 내장 파티클

RUID 없이 enum 값만으로 범용 파티클 연출. 3종류:

-- BasicParticle: 범용 프리셋 (RUID 불필요)
integer _ParticleService:PlayBasicParticle(BasicParticleType, Entity instigator, Vector3 pos, number zRot, Vector3 scale, boolean isLoop, Dictionary options)
integer _ParticleService:PlayBasicParticleAttached(BasicParticleType, Entity parent, Vector3 localPos, number localZRot, Vector3 localScale, boolean isLoop, Dictionary options)

-- SpriteParticle: 커스텀 스프라이트를 파티클로 (spriteRUID 필요)
integer _ParticleService:PlaySpriteParticle(SpriteParticleType, string spriteRUID, Entity instigator, Vector3 pos, number zRot, Vector3 scale, boolean isLoop, Dictionary options)
integer _ParticleService:PlaySpriteParticleAttached(SpriteParticleType, string spriteRUID, Entity parent, Vector3 localPos, number localZRot, Vector3 localScale, boolean isLoop, Dictionary options)

-- AreaParticle: 넓은 영역 환경 파티클 (areaSize 추가)
integer _ParticleService:PlayAreaParticle(AreaParticleType, Vector2 areaSize, Entity instigator, Vector3 pos, number zRot, Vector3 scale, boolean isLoop, Dictionary options)

void _ParticleService:RemoveParticle(integer serial)

options 키: Color, SortingLayer, OrderInLayer, ParticleSize, ParticleCount

루프 파티클(isLoop=true)은 반드시 RemoveParticle(serial)로 정리. serial을 self._T에 저장해두어야 나중에 제거 가능.

BasicParticleType 전체 목록

계열 이름 설명
폭발/충격 SparkExplosion 스파크 (1회) — 범용 피격
SparkLoop 연속 스파크
SparkRadialExplosion 원형으로 튀는 스파크
SmallExplosion 작은 폭발 + 연기
BigExplosion 큰 폭발 + 연기
TinyExplosion 아주 작은 폭발 (Color 옵션 무효)
DustExplosion 원형 파동 + 연기 (Color 옵션 무효)
EnergyExplosion 원형 파동 후 중심 수렴
CircleBurst 원형 빛 폭발
PillarBurst 원형 빛 폭발 + 방향성 빛
불/화염 FireField 만화풍 불꽃
FireFieldIntense 강한 만화풍 불꽃
FireBall 한 곳에 화염 생성
FlameThrower 화염 방출
LargeFlames 바닥에서 큰 화염
MediumFlames 바닥에서 중간 화염
TinyFlames 바닥에서 작은 화염
WildFire 거대한 화염 기둥 (Color 옵션 무효)
번개/전기 LightningOrbSharp 구형 전기 파티클
LightningStrikeSharp 번개
LightningStrikeSharpTall 긴 번개
LightningOrbSoft 전기파 방출
LightningBlast 주기적 전기파
LightningStrike 주기적 번개
LightningStrikeTall 주기적 긴 번개
버프/마법 Aura 바닥에서 오로라 빛
Buff 바닥에서 강한 빛 상승
Charge 큰 파티클이 한 점으로 수렴
ChargeOrb 파티클이 한 점으로 수렴
Enchant 큰 빛 주변에 빛과 파티클
SpinField 회전하는 원 주변에 파티클
StarVortex 별빛이 중심으로 수렴
Nova 넓은 원형 파동
UpperCylinder 바닥에서 기둥 형태 상승
기타 Firework 불꽃놀이
FireworkCluster 여러 폭죽 동시
FireFlies 반딧불이
GoopSpray 옆으로 액체 분출
GoopSprayEffect 바닥으로 액체 분출
DustStorm 넓은 모래 폭풍
RisingSteam 바닥에서 흰 안개 상승
BigSplash 큰 물보라
Shower 한 곳에 물 뿌림

SpriteParticleType 전체 목록 (8종)

이름 설명
BurstBig 스프라이트가 원형으로 퍼지며 등장
SpawnField 파티클+스프라이트가 원형 영역에 등장
BurstNova 파티클+스프라이트가 원형으로 폭발
SimpleSpawn 파티클+스프라이트 단순 등장
Burst 파티클+스프라이트가 퍼짐
Stream 특정 방향으로 이동하며 생성
StreamSharp 가느다란 선으로 특정 방향 이동
AdditiveColor 스프라이트에 색상 효과 적용

AreaParticleType 전체 목록 (12종)

이름 설명
Rain
Snow
FogCalm 안개
FogHeavy 짙은 내려오는 안개
FogLively 올라오는 안개
CalmStarField 올라오는 별 무리
StarFieldSimple 반짝이는 별 무리
StarFog 별+성운 파티클 (제자리)
StarFogFlow 별+성운 파티클 (상승)
Windlines 가는 선
WindlinesBig 가는 선 + 굵은 선
WindlinesSpeedy 빠른 직선

EffectService vs ParticleService 선택

상황 권장
메이플스토리 스킬/피격 애니메이션 (구체적 이미지) EffectService (RUID 지정)
범용 피격·폭발 (빠른 구현) ParticleService.BasicParticle
커스텀 이미지를 파티클로 흩뿌리기 ParticleService.SpriteParticle
비·눈·안개 등 환경 연출 ParticleService.AreaParticle
버프 오라 등 지속 효과 둘 다 isLoop=true 가능
풍성한 연출 EffectService + ParticleService 동시 조합

서버 이벤트 → 클라이언트 이펙트 표준 패턴: @Sync 프로퍼티 변경 → OnSyncProperty(ClientOnly) 감지 → EffectService/ParticleService 호출.


5. 사망 / 부활

이벤트 발행 조건 페이로드
DeadEvent StateComponent:ChangeState("DEAD") 시 자동 없음
ReviveEvent 부활 시 자동 없음
StateChangeEvent 모든 상태 전환 CurrentStateName, PrevStateName

킬러 추적: DeadEvent 페이로드 없음 → HandleHitEvent 에서 self.LastAttacker = event.AttackerEntity 캐시 후 HandleDeadEvent 에서 사용.

수동 발행: StateComponent:EmitDeadEvent(DeadEvent) / EmitReviveEvent(ReviveEvent) / EmitStateChangeEvent(StateChangeEvent) 가능.

플레이어 전용 사망·부활은 §9-1 PlayerComponent.Respawn/ProcessDead/ProcessRevive 우선 사용.


6. Event Bus

논리 이벤트 MSW 구현
OnAttackStart OnAttack 훅 또는 커스텀 AttackStartEvent
OnAttackHit / OnDamageTaken 네이티브 HitEvent
OnAttackMiss 커스텀 — IsAttackTarget false 시 SendEvent
OnCriticalHit HitEvent.IsCritical 플래그로 커버
OnDeath / OnRevive 네이티브 DeadEvent/ReviveEvent
OnStateChange 네이티브 StateChangeEvent
OnKill / OnBlocked / OnParry / OnStatusApplied 커스텀 @Event

6-1. 커스텀 이벤트 규칙

  • 정의: @Event script XxxEvent extends EventType + property
  • 수신: handler 키워드 (method 아님), @EventSender("Self" | "Service","XxxService" | "Logic","XxxLogic")
  • 연결/해제: entity:ConnectEvent(XxxEvent, self.Handler) / OnEndPlay 에서 반드시 DisconnectEvent (엔진 자동 해제 없음)
  • 전역: @Logic CombatEventBusLogic 싱글턴 + @EventSender("Logic","CombatEventBusLogic")

7. AI FSM — StateComponent

readonly @Sync property string CurrentStateName = "IDLE"

method boolean AddState(string stateName, Type stateType)
method boolean AddCondition(string stateName, string nextStateName, boolean reverseResult = false)
method boolean ChangeState(string stateName)
method void    RemoveState(string name)
method void    RemoveCondition(string stateName, string nextStateName)

emitter EmitDeadEvent / EmitReviveEvent / EmitStateChangeEvent
  • BehaviourTree 네이티브 부재 (BehaviourTreeStatus enum 만 존재)
  • 플레이어 조회: _UserService.UserEntities: ReadOnlyDictionary<string, Entity>
  • 거리·시야: (target.WorldPosition - self.WorldPosition):Length() + LookDirectionX 부호
  • 쿨다운: _TimerService:SetTimerOnce 또는 _UtilLogic.ElapsedSeconds deadline
  • 보스 페이즈: AddCondition + HP% 체크 자동 전이, 또는 스크립트 ChangeState("PHASE2") 강제
  • Threat/Aggro Table 은 커스텀 table<Entity, float> 필요

몬스터 엔티티 전체 구성 (9컴포넌트 조립 + ActionSheet 5상태 + 히트박스 + AI 선택 + MonsterAI 스크립트 부착) → references/monster-setup.md

본 SKILL.md는 전투 특화(ATTACK/HIT/DEAD + DeadEvent/ReviveEvent)만 다룬다. 일반 mlua 상태 머신/스크립팅 패턴은 msw-scripting 참조.


8. UI 네이티브

UI API
HP 바 (화면 고정) SliderComponent (MinValue/MaxValue/Value/FillRectColor/FillRectImageRUID/Direction/UseHandle) + SliderValueChangedEvent. ⚠ UI 엔티티 전용
데미지 숫자 DamageSkin* 3종 + DamageSkinService — §11
크로스헤어 .uiSpriteGUIRendererComponent
콤보 카운터 / 버프 아이콘 TextComponent + SpriteGUIRendererComponent

월드스페이스 HP바 (머리 위): 네이티브 부재. 자식 엔티티 SpriteRendererComponentLocalScale.x = hp/maxHp 또는 TiledSize.x 조정 (SpriteDrawMode.Tiled 전제).

PixelRendererComponent 기반 체력바 (@Sync HP + Lazy Init + 색상 변화) 전체 구현 → references/hp-gauge.md


9. DefaultPlayer 전투 네이티브

플레이어 엔티티는 HP·부활·입력을 네이티브로 갖는다. 커스텀 Hp/MaxHp 프로퍼티 만들지 말 것PlayerComponent 사용.

PlayerComponent / PlayerControllerComponent 전체 프로퍼티·메서드 표는 msw-defaultplayer/SKILL.md 참조. 여기서는 전투 핵심만.

9-1. 전투 핵심 API

항목 사용법
HP 감소 self.Entity.PlayerComponent.Hp -= event.TotalDamage
사망 판정 PlayerComponent:IsDead()
부활 PlayerComponent:Respawn()RespawnPosition → SpawnLocation → 맵 진입점. DeadEvent/ReviveEvent 자동 발행
클라 전용 사망 처리 @ExecSpace("Client") ProcessDead(targetUserId?) / ProcessRevive(targetUserId?)
방향 판정 ★ PlayerControllerComponent.LookDirectionX (+1 우, -1 좌). TransformComponent.Scale.x 사용 금지
액션 훅 오버라이드 ActionAttack / ActionJump / ActionInteraction(key, isKeyDown)
액션 이벤트 수신 EmitPlayerActionEvent(PlayerActionEvent) → §9-3

9-3. PlayerActionEvent

property string ActionName       -- "Attack" / "Jump" / "Crouch" / ...
property Entity PlayerEntity

PlayerAttack extends AttackComponent 에서 @EventSender("Self") handler HandlePlayerActionEvent(...) 로 수신 후 event.ActionName == "Attack" 분기가 기본 패턴.

9-4. 기본 제공 템플릿 (RootDesk/MyDesk/)

수정 없이 복붙 가능. 필요 시 오버라이드:

파일 역할 핵심 포인트
PlayerAttack.mlua 전방 Box 공격 LookDirectionX 로 방향, AttackFast + CollisionGroups.Monster, CalcDamage=50, 30% 크리
PlayerHit.mlua i-frame ImmuneCooldown 프로퍼티, _UtilLogic.ElapsedSeconds deadline, IsHitTarget 오버라이드
Monster.mlua 몬스터 HP 커스텀 @Sync Hp (PlayerComponent 없음), HandleHitEventDead/Respawn
MonsterAttack.mlua sprite 크기 기반 근접 IsAttackTarget 에서 isvalid(defender.PlayerComponent) + __base:IsAttackTarget(...) super

9-5. 시간 기준

_UtilLogic.ElapsedSeconds 권장 (월드 기준, 일시정지/리스토어 일관). os.clock() 금지.

9-6. 표준 CollisionGroup

상수 용도
CollisionGroups.Player 몬스터 → 플레이어 공격
CollisionGroups.Monster 플레이어 → 몬스터 공격
CollisionGroups.HitBox HitComponent.CollisionGroup 기본

10. 아바타 모션 — AvatarStateAnimationComponent

StateComponent 전환을 아바타 애니메이션에 자동 연결.

@Sync property SyncDictionary<string, AvatarBodyActionElement> StateToAvatarBodyActionSheet  -- IsLegacy=false
@Sync property SyncDictionary<string, string>                  ActionSheet                    -- IsLegacy=true (deprecated)

method void   SetActionSheet(string key, string animationClipRuid)
method void   RemoveActionSheet(string key)
method string StateStringToAnimationKey(string stateName)
emitter EmitBodyActionStateChangeEvent(BodyActionStateChangeEvent)
  • ChangeState("HIT") → 매핑된 MapleAvatarBodyActionState.Hit 자동 재생
  • 전투 관련 상태값: Attack=3, Hit=14, Dead=10, Alert=4, Heal=13
  • IsLegacy=false 고정, StateToAvatarBodyActionSheet 만 사용

Avatar 컴포넌트(AvatarRendererComponent 등) 전반은 msw-defaultplayer 참조. 본 섹션은 전투 모션 매핑만 다룬다.


11. 데미지 스킨 (숫자 표시)

기본 RUID

용도 RUID 사용처
타격 3271c3e79bf04ecba9a107d55495970d 공격자 DamageSkinSettingComponent.DamageSkinId 디폴트
피격 02c22d93421b4038b3c413b3e40b57ec 피격자 측 표시 — _DamageSkinService:Play 수동 호출
회복 d58b67cf0f3a4eaf9fe1ad87c0ffac8a 힐/포션 — _DamageSkinService:Play 수동 호출

11-1. 자동 모드 (컴포넌트 기반)

Attack/AttackFast 호출 시, 아래 3개가 모두 있으면 자동으로 데미지 숫자 표시:

위치 컴포넌트 역할
공격자 DamageSkinSettingComponent 어떤 스킨/스타일로 표시할지
피격자 DamageSkinSpawnerComponent 표시 위치 오프셋
피격자 DamageSkinComponent 데미지 숫자 본체(엔티티 위)

3개 모두 .model에 포함시켜두면 스크립트 코드 0줄로 데미지 숫자가 뜬다.

DamageSkinSettingComponent (공격자)

속성 타입 기본값 설명
DamageSkinId DataRef 타격 RUID (위 표) 데미지 숫자 스킨 RUID
DamageSkinScale Vector2 (1, 1) 숫자 크기
Alpha float 1 투명도
PlayRate float 1 재생 속도
DelayPerAttack float 0.05 멀티히트 간 딜레이(초)
TweenType DamageSkinTweenType Default 연출 타입
LitMode LitMode Default 조명 영향

DamageSkinTweenType: Default(팝업) / Volcano(부채꼴) / Blade(겹침) / 각 *Mini(75% 축소)

DamageSkinSpawnerComponent (피격자)

속성 타입 기본값
DamageSkinOffset Vector2 (0,0)

11-2. 수동 모드 — DamageSkinService

자동 모드로 잡히지 않는 케이스(힐, Miss/Guard, 비표준 데미지원)는 _DamageSkinService 직접 호출.

_DamageSkinService:Play(targetEntity, skinRuid, delay, damages:List<int>, tweenType, isCritical, offset, scale, playRate, alpha, litMode)
_DamageSkinService:PlayTextDamage(targetEntity, skinRuid, textType, tweenType)
_DamageSkinService:PreloadAsync(skinRuid, callback(success))    -- ClientOnly

DamageSkinTextType: Miss / Guard / Resist / Shot / Counter

_DamageSkinService:PlayClient 영역 — 서버 로직(HP 차감 등)에서 호출하려면 @ExecSpace("Client") 메서드로 래핑하거나 @Sync 프로퍼티 변경 후 OnSyncProperty에서 트리거.

11-3. 활용 레시피

(a) 크리티컬 강조 — 자동 모드 + 동적 스케일

자동 모드는 IsCritical=true면 빨강 폰트로 자동 처리. 추가 강조하려면 공격자 측 스케일을 일시 증대:

@ExecSpace("ServerOnly")
method int32 CalcDamage(Entity attacker, Entity defender, string attackInfo)
    return 100
end

@ExecSpace("ServerOnly")
method boolean CalcCritical(Entity attacker, Entity defender, string attackInfo)
    return math.random() < 0.3
end

method float GetCriticalDamageRate()
    return 2.5     -- 100 → 250
end

DamageSkinSettingComponent.TweenType = Volcano (부채꼴 흩뿌림) 또는 Blade (겹침)로 크리티컬 시각 차별화.

(b) 힐 / 회복 — 수동 호출

local HEAL_RUID = "d58b67cf0f3a4eaf9fe1ad87c0ffac8a"

@ExecSpace("Client")
method void ShowHeal(Entity target, integer amount)
    _DamageSkinService:Play(
        target, HEAL_RUID, 0,
        { amount },                            -- damages
        DamageSkinTweenType.Default,
        false,                                 -- isCritical
        Vector2(0, 0.5),                       -- offset (머리 위)
        Vector2(1, 1), 1.0, 1.0, LitMode.Default
    )
end

(c) Miss / Guard / Resist 텍스트

local HIT_RUID = "02c22d93421b4038b3c413b3e40b57ec"

@ExecSpace("Client")
method void ShowMiss(Entity target)
    _DamageSkinService:PlayTextDamage(
        target, HIT_RUID, DamageSkinTextType.Miss, DamageSkinTweenType.Default
    )
end

AttackComponent:IsAttackTarget이 false를 반환했을 때 호출하면 "miss 연출 + 데미지 0".

(d) 멀티히트 — 한 번 호출로 N개 분할

_DamageSkinService:Playdamages 인자에 List를 통째로 넘기면 DelayPerAttack(공격자 컴포넌트의 값) 간격으로 순차 표시:

_DamageSkinService:Play(target, ATTACK_RUID, 0, { 12, 8, 14, 11, 9 },
    DamageSkinTweenType.Default, false, Vector2(0,0), Vector2(1,1), 1, 1, LitMode.Default)

자동 모드도 HitEvent.Damages(List)로 동일 동작 — GetDisplayHitCount(attackInfo)를 오버라이드해 분할 수를 제어한다.

(e) Preload — 첫 표시 끊김 방지

스킨 RUID 첫 사용 시 텍스처 로드 지연이 있을 수 있다. 맵 진입 시 미리 로드:

@ExecSpace("ClientOnly")
method void OnBeginPlay()
    _DamageSkinService:PreloadAsync("3271c3e79bf04ecba9a107d55495970d", function(ok) end)
    _DamageSkinService:PreloadAsync("02c22d93421b4038b3c413b3e40b57ec", function(ok) end)
    _DamageSkinService:PreloadAsync("d58b67cf0f3a4eaf9fe1ad87c0ffac8a", function(ok) end)
end

(f) TweenType 사용 케이스

TweenType 추천 상황
Default 일반 타격
Volcano 크리티컬 / 광역 타격 (위로 흩뿌림)
Blade 연속 베기 / 콤보 (숫자 겹침)
*Mini DoT(독/화상) 같은 작은 데미지 — 화면 점유 줄임

(g) 진영별 스킨 차별화

플레이어 vs 적, PvP 진영 등에서 다른 스킨 RUID를 쓰려면 DamageSkinSettingComponent.DamageSkinId를 런타임에 교체:

self.Entity.DamageSkinSettingComponent.DamageSkinId = MY_TEAM_SKIN_RUID

12. 히트 이펙트 — HitEffectSpawnerComponent

피격자에 부착만 하면 HitEvent 발생 시 자동으로 히트 이펙트 재생. property 없음, .model 에 컴포넌트 추가만.


13. 완성형 전투 체크리스트

  • 공격자 모델: AttackComponent 상속 스크립트 (+ 선택: DamageSkinSettingComponent)
  • 피격자 모델: HitComponent + HitEffectSpawnerComponent + (선택: DamageSkinSpawnerComponent + DamageSkinComponent)
  • HitComponent: IsLegacy=false, ColliderType/BoxSize/CircleRadius 설정, CollisionGroup 설정
  • 상태 모션: StateComponent + AvatarStateAnimationComponent.StateToAvatarBodyActionSheetATTACK/HIT/DEAD 등록
  • HP 처리: 플레이어는 PlayerComponent.Hp, 몬스터는 커스텀 @Sync Hp
  • 방향 판정: LookDirectionX (Scale.x 금지)
  • 시간 기준: _UtilLogic.ElapsedSeconds (os.clock 금지)
  • 이벤트 정리: OnEndPlay 에서 DisconnectEvent 명시 해제
  • Body 규칙: Body 활성 엔티티에 TransformComponent.Position 직접 대입 금지

14. 커스텀 구현이 필수

Buff/Debuff · BehaviourTree · Aggro Table · 투사체 풀링 · 관통/최대 히트 수 · 경직 레벨 체계 · 자원(MP/Stamina/Rage) · 세계→스크린 좌표 변환 · 월드스페이스 HP바


비범위

Related skills

More from msw-git/msw-ai-coding-plugins-official

Installs
5
First Seen
2 days ago