# DS-전투시스템 종합분석 ## 문서 개요 본 문서는 언리얼 엔진 에셋(DataTable, AnimMontage, Blueprint, CurveTable)을 JSON으로 익스포트하여 분석한 전투 로직 시스템에 대한 기술 문서입니다. **분석 대상 스토커**: Hilda, Urud, Nave, Baran, Rio, Clad, Rene, Sinobu, Lian, Cazimord (10명) **작성 일자**: 2025-10-23 **익스포트 데이터**: `DS-전투밸런스_분석자료/20251023_114317/` --- ## 목차 1. [개요 및 Asset Export 시스템](#1-개요-및-asset-export-시스템) 2. [DataTable 구조](#2-datatable-구조) 3. [AnimMontage 타이밍 시스템](#3-animmontage-타이밍-시스템) 4. [Blueprint Ability 및 캔슬 시스템](#4-blueprint-ability-및-캔슬-시스템) 5. [GameplayEffect 메커니즘](#5-gameplayeffect-메커니즘) 6. [DT_Skill 특수 케이스](#6-dt_skill-특수-케이스) 7. [코드-어셋 통합 흐름](#7-코드-어셋-통합-흐름) 8. [JSON 사용 가이드](#8-json-사용-가이드) --- ## 1. 개요 및 Asset Export 시스템 ### 1.1 Asset Export to JSON 기능 언리얼 엔진 에셋은 기본적으로 바이너리 형식으로 저장되어 LLM이나 외부 분석 도구가 직접 접근할 수 없습니다. 이를 해결하기 위해 **Asset Export to JSON** 에디터 확장 기능을 개발하였습니다. **참고 문서**: `DS-전투밸런스_분석자료/DS-Asset_Export_to_JSON.md` #### 주요 기능 - **지원 에셋 타입**: DataTable, Blueprint, AnimMontage, CurveTable - **프로젝트 설정 통합**: `편집 → 프로젝트 설정 → 플러그인 → Asset Export to JSON` - **타임스탬프 익스포트**: 익스포트 히스토리 보관을 위한 타임스탬프 폴더 생성 - **완전한 데이터 추출**: - DataTable: 모든 행/열 데이터 - Blueprint: 변수, 함수, 컴포넌트, 이벤트 그래프 노드 구조 - AnimMontage: 섹션, 노티파이, 커스텀 프로퍼티, 슬롯 애니메이션 - CurveTable: RichCurves 및 SimpleCurves 키 데이터 #### 익스포트 실행 ``` 툴 → WorldStalker → Export Assets to JSON ``` #### 출력 위치 ``` Content/Exports/20251023_114317/ ├── DataTable.json (1,151 에셋) ├── Blueprint.json (1,151 에셋) ├── AnimMontage.json (809 에셋) └── CurveTable.json (8 에셋) ``` ### 1.2 전투 로직 분석 범위 본 문서에서는 익스포트된 JSON 데이터를 기반으로 다음 전투 시스템을 분석합니다: #### 스토커별 전투 데이터 | 스토커 | 클래스 | 주요 특징 | |--------|--------|-----------| | **Hilda** | 전사 | Counter 스킬, 방패 방어 | | **Urud** | 원거리 | Reload 시스템, 궁극기 'Explosion' 범위 피해 | | **Nave** | 마법사 | 캐스팅 스킬, 궁극기 'Liberation' (DT_Skill 피해) | | **Baran** | 전사 | 중무장 탱커, Pulling 체인 메커니즘 | | **Rio** | 암살자 | Chain Score 시스템 (최대 3 스택) | | **Clad** | 성직자 | 힐링 및 언데드 특효 | | **Rene** | 소환사 | Spirit 소환, Lifesteal 효과 | | **Sinobu** | 닌자 | Shuriken 충전 시스템, 'Swap' 인술 (텔레포트) | | **Lian** | 레인저 | Reload 시스템, Precision Aim (조준 시스템) | | **Cazimord** | 전사 | Flash 스킬 스택, Parrying 시스템 | #### 분석 계층 ``` 스토커 전투 시스템 │ ├── DataTable Layer (밸런스 데이터) │ ├── DT_CharacterStat (기본 스탯, 스킬 목록) │ ├── DT_Skill (스킬 데이터, 피해 배율, 쿨타임) │ └── DT_CharacterAbility (기본 공격 어빌리티 매핑) │ ├── AnimMontage Layer (타이밍 데이터) │ ├── 기본 공격 몽타주 (AM_PC_[Stalker]_B_Attack_*) │ ├── 스킬 몽타주 (스킬별 애니메이션) │ └── 노티파이 시스템 │ ├── ANS_AttackState_C (공격 상태, 피해 배율) │ ├── AnimNotifyState_AttackWithEquip (히트 판정 타이밍) │ ├── ANS_SkillCancel_C (스킬 캔슬 윈도우) │ └── AN_SetAutoTarget_C (자동 타게팅) │ ├── Blueprint Ability Layer (로직 구현) │ ├── GA_Attack (기본 공격) │ ├── GA_Skill_* (스토커별 스킬 구현) │ ├── Activation Order Group (우선순위 캔슬 시스템) │ └── 특수 시스템 (Reload, Chain Score, Parrying 등) │ └── GameplayEffect Layer (효과 적용) ├── GE_Attack_* (피해 효과) ├── GE_Skill_* (스킬 효과) └── GE_Buff/Debuff_* (버프/디버프) ``` ### 1.3 JSON 참조 규칙 **중요**: JSON 파일은 에디터에서 익스포트한 결과물로, 소스 코드처럼 고정된 라인 번호가 없습니다. #### 올바른 참조 방법 ✅ **Asset 이름으로 참조** ``` DT_CharacterStat 어셋의 "hilda" 행 AM_PC_Hilda_B_Skill_SwordStrike 몽타주 GA_Skill_Hilda_SwordStrike_C 블루프린트 ``` ❌ **라인 번호로 참조하지 않음** ``` DataTable.json:358143-358192 (X) ``` #### JSON 구조 예시 **DataTable.json**: ```json { "AssetName": "DT_CharacterStat", "RowStructure": "CharacterStatData", "Rows": [ { "RowName": "hilda", "Data": { "name": "힐다", "defaultSkills": ["SK100201", "SK100202", "SK100204"], "subSkill": "SK100101", "ultimateSkill": "SK100301" } } ] } ``` **AnimMontage.json**: ```json { "AssetName": "AM_PC_Hilda_B_Skill_SwordStrike", "SequenceLength": 1.8, "AnimNotifies": [ { "NotifyStateClass": "ANS_SkillCancel_C", "TriggerTime": 1.3, "Duration": 0.5 } ] } ``` --- ## 2. DataTable 구조 ### 2.1 DT_CharacterStat (스토커 기본 스탯 및 스킬 매핑) DT_CharacterStat은 각 스토커의 기본 스탯과 사용 가능한 스킬 목록을 정의합니다. #### 구조 | 필드 | 타입 | 설명 | |------|------|------| | `name` | String | 스토커 이름 (한글) | | `jobName` | String | 직업 (전사, 원거리, 마법사 등) | | `str` | Int | 힘 (물리 공격력 영향) | | `dex` | Int | 민첩 (공격 속도, 크리티컬 영향) | | `int` | Int | 지능 (마법 공격력 영향) | | `con` | Int | 체력 (HP, 방어력 영향) | | `wis` | Int | 지혜 (마나, 마법 방어력 영향) | | `hP` | Float | 기본 HP | | `mP` | Float | 기본 MP | | `manaRegen` | Float | 마나 재생 속도 | | `defaultSkills` | Array | 기본 스킬 ID 목록 (3-4개) | | `subSkill` | String | 서브 스킬 ID (특수 스킬, 쿨타임 0초) | | `ultimateSkill` | String | 궁극기 스킬 ID | #### 스토커별 스탯 분포 **전체 스탯 합계**: 75 포인트 (모든 스토커 동일) ##### 힘 중심 스토커 (STR 20+) **Hilda** (전사): ``` STR: 20, DEX: 15, INT: 10, CON: 20, WIS: 10 HP: 100, MP: 50, Mana Regen: 0.2 Skills: SK100201 (Sword Strike), SK100202 (Counter), SK100204 (Provoke) Sub: SK100101 (Blocking) Ultimate: SK100301 (Blood Moon) ``` **Baran** (전사): ``` STR: 25, DEX: 10, INT: 5, CON: 25, WIS: 10 HP: 100, MP: 50, Mana Regen: 0.2 Skills: SK130204 (Pulling), SK130203 (Smash), SK130206 (Sword Stab) Sub: SK130101 (Blocking) Ultimate: SK130301 (Rock Breaker) ``` ##### 민첩 중심 스토커 (DEX 20+) **Urud** (원거리): ``` STR: 15, DEX: 20, INT: 10, CON: 15, WIS: 15 HP: 100, MP: 50, Mana Regen: 0.2 Skills: SK110205 (Multi Shot), SK110204 (Poison Arrow), SK110201 (Make Trap), SK110207 (Reload) Sub: SK110101 (Arrow Attack) Ultimate: SK110301 (Explosion) ``` **Rio** (암살자): ``` STR: 15, DEX: 25, INT: 10, CON: 15, WIS: 10 HP: 100, MP: 50, Mana Regen: 0.2 Skills: SK140201 (Rapid Stab), SK140205 (Approach), SK140202 (Throwing Dagger) Sub: SK140101 (Dropping Attack) Ultimate: SK140301 (Sensitive) ``` **Sinobu** (닌자): ``` STR: 10, DEX: 25, INT: 10, CON: 15, WIS: 15 HP: 100, MP: 50, Mana Regen: 0.2 Skills: SK180202 (Bomb Talisman), SK180203 (Thunder Kick), SK180205 (Ninpo Change) Sub: SK180101 (Shuriken) Ultimate: SK180301 (Deflect) ``` **Cazimord** (전사): ``` STR: 15, DEX: 25, INT: 10, CON: 15, WIS: 10 HP: 100, MP: 50, Mana Regen: 0.2 Skills: SK170201 (Flash), SK170202 (Blade Storm), SK170203 (Burn) Sub: SK170101 (Parrying) Ultimate: SK170301 (Mana Stone Burn) ``` **Lian** (레인저): ``` STR: 10, DEX: 20, INT: 10, CON: 15, WIS: 20 HP: 100, MP: 50, Mana Regen: 0.2 Skills: SK190207 (Rapid Shot), SK190205 (Back Step), SK190201 (Dark Souls), SK190209 (Reload) Sub: SK190101 (Charging Bow) Ultimate: SK190301 (Manastone Silence) ``` ##### 지능 중심 스토커 (INT 20+) **Nave** (마법사): ``` STR: 10, DEX: 10, INT: 25, CON: 10, WIS: 20 HP: 100, MP: 50, Mana Regen: 0.2 Skills: SK120201 (Magic Missile), SK120202 (Fire Wall), SK120206 (Wind Force) Sub: SK120101 (Mana Restore) Ultimate: SK120301 (Escape 4) ``` **Rene** (소환사): ``` STR: 10, DEX: 10, INT: 20, CON: 10, WIS: 25 HP: 100, MP: 50, Mana Regen: 0.2 Skills: SK160202 (Summon Ifrit), SK160206 (Summon Shiva), SK160203 (Poison Gas) Sub: SK160101 (Scratching) Ultimate: SK160301 (Mana Stone Carnival) ``` ##### 균형 스토커 **Clad** (성직자): ``` STR: 15, DEX: 10, INT: 10, CON: 20, WIS: 20 HP: 100, MP: 50, Mana Regen: 0.2 Skills: SK150206 (Holy Cure), SK150201 (Turn Undead), SK150202 (Holy Light) Sub: SK150101 (Block) Ultimate: SK150301 (Gold) ``` ### 2.2 DT_Skill (스킬 상세 데이터) DT_Skill은 각 스킬의 피해 배율, 쿨타임, 원소 타입, 사용 몽타주, 연결된 Ability 클래스 등을 정의합니다. #### 구조 | 필드 | 타입 | 설명 | |------|------|------| | `RowName` | String | 스킬 ID (SK + 6자리 숫자) | | `stalkerName` | String | 소유 스토커 이름 (소문자) | | `bIsUltimate` | Bool | 궁극기 여부 | | `skillName` | String | 스킬 이름 | | `skillDamageRate` | Float | 기본 피해 배율 (1.0 = 100%) | | `skillManaCost` | Float | 마나 소모량 | | `coolTime` | Float | 쿨타임 (초) | | `skillAttackType` | Enum | 공격 타입 (PhysicalSkill, MagicalSkill, Normal) | | `skillElementType` | Enum | 원소 타입 (None, Fire, Lightning, Poison, Water, Holy, Dark) | | `useMontages` | Array | 사용할 몽타주 경로 목록 | | `abilityClass` | String | 연결된 GA_* 블루프린트 경로 | | `gameplayEffectSet` | Array | 적용할 GameplayEffect 목록 | #### 스킬 타입 분류 ##### PhysicalSkill (물리 스킬) - 힘(STR) 또는 민첩(DEX) 스탯의 영향을 받음 - 방어력(Defense)에 의해 감소 - 예시: Hilda의 Sword Strike, Rio의 Rapid Stab, Cazimord의 Flash ##### MagicalSkill (마법 스킬) - 지능(INT) 또는 지혜(WIS) 스탯의 영향을 받음 - 마법 저항력(MagicalResistancePer)에 의해 감소 - 원소 타입에 따라 추가 저항력 적용 (Fire, Lightning, Poison 등) - 예시: Nave의 Magic Missile, Rene의 Summon Ifrit, Clad의 Turn Undead ##### Normal (일반/유틸리티) - 피해를 주지 않거나 특수 메커니즘을 가진 스킬 - 스탯 배율 없이 고정 효과 또는 0 피해 - 예시: Hilda의 Blocking, Urud의 Reload, Rio의 Approach #### 원소 타입별 특성 | 원소 | 저항 스탯 | 주요 사용 스토커 | |------|-----------|------------------| | **None** | - | 대부분 (범용 스킬) | | **Lightning** | lightningResistancePer | Hilda (Sword Strike), Sinobu (Thunder Kick) | | **Fire** | fireResistancePer | Nave (Fire Wall) | | **Poison** | poisonResistancePer | Urud (Poison Arrow), Rene (Poison Gas) | | **Water** | waterResistancePer | - | | **Holy** | holyResistancePer | Clad (Turn Undead), Lian (Dark Souls) | | **Dark** | darkResistancePer | Rene (Poison Gas - Dark 속성도 보유) | #### 스킬 예시 **Hilda - SK100201 (Sword Strike)**: ```json { "RowName": "SK100201", "stalkerName": "hilda", "skillName": "Sword Strike", "skillDamageRate": 1.3, "coolTime": 6.0, "skillAttackType": "PhysicalSkill", "skillElementType": "Lightning", "useMontages": [ "/Game/_Art/_Character/PC/Hilda/AnimMontage/Skill/AM_PC_Hilda_B_Skill_Ready.AM_PC_Hilda_B_Skill_Ready", "/Game/_Art/_Character/PC/Hilda/AnimMontage/Skill/AM_PC_Hilda_B_Skill_SwordStrike.AM_PC_Hilda_B_Skill_SwordStrike" ], "abilityClass": "/Game/Blueprints/Characters/Hilda/GA_Skill_Hilda_SwordStrike.GA_Skill_Hilda_SwordStrike_C" } ``` **Nave - SK120301 (Escape 4 / Liberation 궁극기)**: ```json { "RowName": "SK120301", "stalkerName": "nave", "bIsUltimate": true, "skillName": "Escape 4", "skillDamageRate": 1.0, "coolTime": 0.0, "skillAttackType": "MagicalSkill", "skillElementType": "None", "useMontages": [ "/Game/_Art/_Character/PC/Nave/AnimMontage/Ultimate/AM_PC_Nave_B_Skill_Escape4.AM_PC_Nave_B_Skill_Escape4" ], "abilityClass": "/Game/Blueprints/Characters/Nave/GA_Skill_Nave_Escape4.GA_Skill_Nave_Escape4_C" } ``` **특수 사항**: Nave의 궁극기는 AnimMontage의 노티파이가 아닌 **DT_Skill의 skillDamageRate와 gameplayEffectSet**에서 직접 피해를 정의합니다. (Section 6 참조) **Urud - SK110301 (Explosion 궁극기)**: ```json { "RowName": "SK110301", "stalkerName": "urud", "bIsUltimate": true, "skillName": "Explosion", "skillDamageRate": 1.0, "coolTime": 0.0, "skillAttackType": "Normal", "skillElementType": "None", "useMontages": [ "/Game/_Art/_Character/PC/Urud/AnimMontage/Ultimate/AM_PC_Urud_B_Skill_Explosion.AM_PC_Urud_B_Skill_Explosion", "/Game/_Art/_Character/PC/Urud/AnimMontage/Base/AM_PC_Urud_B_Skill_Equipment.AM_PC_Urud_B_Skill_Equipment" ], "abilityClass": "/Game/Blueprints/Abilities/GA_Skill_Casting_Ultimate.GA_Skill_Casting_Ultimate_C" } ``` **특수 사항**: Urud의 궁극기 'Explosion'은 투척한 돌이 적중 시 범위 피해를 줍니다. 피해는 AnimMontage의 노티파이 또는 GA_Skill Blueprint에서 처리됩니다. ### 2.3 DT_CharacterAbility (기본 공격 어빌리티 매핑) DT_CharacterAbility는 각 스토커의 기본 공격(좌클릭)에 사용되는 Ability 클래스를 매핑합니다. #### 구조 | 필드 | 타입 | 설명 | |------|------|------| | `characterName` | String | 스토커 이름 | | `abilityClass` | String | 기본 공격 Ability 경로 | 대부분의 스토커는 공통 `GA_Attack` 클래스를 사용하지만, 일부 스토커는 커스텀 공격 로직을 가집니다. #### 공통 기본 공격 **GA_Attack** 사용 스토커: - Hilda, Baran, Rio, Clad, Rene, Nave, Sinobu, Cazimord 기본 공격 로직: 1. 장착한 무기의 `montages` 배열에서 몽타주 선택 2. 콤보 시스템: 연속 입력 시 다음 몽타주로 체인 3. AnimNotifyState_AttackWithEquip 노티파이에서 히트 판정 4. ANS_AttackState_C 노티파이에서 피해 배율 적용 #### 특수 기본 공격 **Urud, Lian** (원거리 스토커): - Ability: `GA_Attack_Firearm` (총기형 무기 전용) - 특징: - 탄약 시스템 (GameplayTag 기반 탄약 카운트) - 탄약 소진 시 자동으로 Reload 스킬 트리거 - 조준 중 이동 속도 감소 - Reload 스킬(SK110207, SK190209)로 재장전 ### 2.4 스토커별 스킬 매핑 요약 | 스토커 | 기본 스킬 (3-4개) | 서브 스킬 (쿨타임 0) | 궁극기 | 특수 시스템 | |--------|------------------|---------------------|--------|-------------| | **Hilda** | Sword Strike, Counter, Provoke | Blocking | Blood Moon | Counter 반격, 방패 방어 | | **Urud** | Multi Shot, Poison Arrow, Make Trap, Reload | Arrow Attack | Explosion | Reload 시스템, 범위 피해 궁극기 | | **Nave** | Magic Missile, Fire Wall, Wind Force | Mana Restore | Escape 4 | 캐스팅 스킬, Liberation 피해(DT_Skill) | | **Baran** | Pulling, Smash, Sword Stab | Blocking | Rock Breaker | Pulling 체인, 중력 조작 | | **Rio** | Rapid Stab, Approach, Throwing Dagger | Dropping Attack | Sensitive | Chain Score (3 스택) | | **Clad** | Holy Cure, Turn Undead, Holy Light | Block | Gold | 힐링, 언데드 특효 | | **Rene** | Summon Ifrit, Summon Shiva, Poison Gas | Scratching | Mana Stone Carnival | Spirit 소환, Lifesteal | | **Sinobu** | Bomb Talisman, Thunder Kick, Ninpo Change | Shuriken | Deflect | Shuriken 충전, Swap 인술 | | **Lian** | Rapid Shot, Back Step, Dark Souls, Reload | Charging Bow | Manastone Silence | Reload 시스템, Precision Aim, 충전 3스택 | | **Cazimord** | Flash, Blade Storm, Burn | Parrying | Mana Stone Burn | Flash 스택, Parrying 반격 | --- ## 3. AnimMontage 타이밍 시스템 ### 3.1 AnimMontage 노티파이 시스템 개요 AnimMontage는 애니메이션 재생 중 특정 타이밍에 게임 로직을 트리거하는 노티파이(Notify) 시스템을 제공합니다. 전투 시스템에서는 노티파이를 통해 다음을 구현합니다: - **공격 판정 타이밍**: 무기가 적을 타격하는 정확한 프레임 - **피해 배율 조정**: 공격 구간별 피해량 증감 - **스킬 캔슬 윈도우**: 스킬을 중단하고 다음 행동으로 전환 가능한 시간대 - **자동 타게팅**: 적을 자동으로 추적하는 시점 - **이동 제한**: 스킬 사용 중 이동 속도 조절 - **벽 충돌 체크**: 근거리 공격 시 벽에 막히는지 검사 #### 노티파이 타입 | 타입 | 설명 | 예시 | |------|------|------| | **Notify** | 특정 시점에 한 번 실행 | AN_SetAutoTarget_C (자동 타게팅 활성화) | | **NotifyState** | 시작~종료 구간 동안 지속 | ANS_AttackState_C (공격 상태 유지) | ### 3.2 주요 노티파이 클래스 #### ANS_AttackState_C (공격 상태 및 피해 배율) **타입**: NotifyState (구간 지속) **역할**: - 공격 판정이 가능한 구간 정의 - 피해 배율 조정 (`AddNormalAttackPer` 프로퍼티) **프로퍼티**: | 프로퍼티 | 타입 | 설명 | |----------|------|------| | `TriggerTime` | Float | 시작 시간 (초) | | `Duration` | Float | 지속 시간 (초) | | `AddNormalAttackPer` | Float | 피해 배율 증감 (%) | **예시** - Baran 기본 공격 1타: ```json { "NotifyStateClass": "ANS_AttackState_C", "TriggerTime": 0.0, "Duration": 1.17, "CustomProperties": { "AddNormalAttackPer": "30.0" } } ``` → 0.0초부터 1.17초까지 공격 상태이며, 피해량이 +30% 증가 #### AnimNotifyState_AttackWithEquip (히트 판정 타이밍) **타입**: NotifyState (구간 지속) **역할**: - 무기의 궤적을 따라 히트 판정 수행 - 다중 히트 지점 정의 (TimeArray, LocationArray, RotationArray) - 공격 타입 태그 지정 (Event.Attack.Normal, Event.Attack.Skill 등) **프로퍼티**: | 프로퍼티 | 타입 | 설명 | |----------|------|------| | `TriggerTime` | Float | 시작 시간 (초) | | `Duration` | Float | 지속 시간 (초) | | `AttackTag` | GameplayTag | 공격 타입 태그 | | `TimeArray` | Array | 다중 히트 시간 배열 | | `LocationArray` | Array | 히트 위치 배열 | | `RotationArray` | Array | 히트 방향 배열 | **예시** - Baran 기본 공격 1타 히트 판정: ```json { "NotifyStateClass": "AnimNotifyState_AttackWithEquip", "TriggerTime": 0.8, "Duration": 0.12, "CustomProperties": { "AttackTag": "(TagName=\"Event.Attack.Normal\")", "TimeArray": ["0.0", "0.033333", "0.066667", "0.1"], "LocationArray": ["(X=0,Y=0,Z=0)", "(X=20,Y=10,Z=5)", ...] } } ``` → 0.8초부터 0.92초까지 4개의 히트 포인트에서 판정 (0.033초 간격) #### ANS_SkillCancel_C (스킬 캔슬 윈도우) **타입**: NotifyState (구간 지속) **역할**: - 스킬의 자연스러운 종료 타이밍 정의 - 이 구간에서 **같거나 낮은 Activation Order Group** 스킬로 캔슬 가능 - DPS 최적화에 중요한 역할 **프로퍼티**: | 프로퍼티 | 타입 | 설명 | |----------|------|------| | `TriggerTime` | Float | 캔슬 윈도우 시작 시간 (초) | | `Duration` | Float | 캔슬 윈도우 지속 시간 (초) | **예시** - Hilda Sword Strike 스킬: ```json { "NotifyStateClass": "ANS_SkillCancel_C", "TriggerTime": 1.3, "Duration": 0.5 } ``` → 1.3초부터 1.8초까지 캔슬 가능 (스킬 종료 구간) **중요**: ANS_SkillCancel_C는 **Activation Order Group 시스템과 독립적**으로 작동합니다 (OR 조건). 자세한 내용은 Section 4.2 참조. #### AN_SetAutoTarget_C (자동 타게팅) **타입**: Notify (순간 실행) **역할**: - 특정 시점에 가장 가까운 적을 자동으로 타게팅 - 캐릭터 회전 및 카메라 방향 조정 **예시**: ```json { "NotifyClass": "AN_SetAutoTarget_C", "TriggerTime": 0.1 } ``` → 0.1초 시점에 자동 타게팅 활성화 #### ANS_WeaponWallCheck_C (벽 충돌 검사) **타입**: NotifyState (구간 지속) **역할**: - 근거리 무기 공격 시 벽과의 충돌 검사 - 벽에 막히면 공격 판정 무효화 #### ANS_DisableBlockingState_C (방어 불가 구간) **타입**: NotifyState (구간 지속) **역할**: - 특정 구간에서 방어(Blocking) 불가 상태로 만듦 - 공격 모션 중 방어 전환 방지 ### 3.3 스토커별 기본 공격 타이밍 분석 기본 공격은 좌클릭으로 실행되며, 대부분 3타 콤보로 구성됩니다. 각 타의 AnimMontage에서 피해 배율과 히트 판정 타이밍을 추출했습니다. #### 3타 콤보 총 소요 시간 (공격 속도 랭킹) | 순위 | 스토커 | 1타 | 2타 | 3타 | 합계 | 특징 | |------|--------|-----|-----|-----|------|------| | 1 | **Rio** | 1.200s | 1.300s | 1.367s | **3.867s** | 최고속 암살자 | | 2 | **Sinobu** | 1.367s | 1.333s | 1.334s | **4.034s** | 빠른 공격 | | 3 | **Hilda** | 1.500s | 1.500s | 1.567s | **4.567s** | 중간 속도 | | 4 | **Nave** | 1.733s | 1.667s | 1.767s | **5.167s** | 캐스터 타입 | | 5 | **Cazimord** | 1.667s | 1.900s | 1.867s | **5.434s** | 듀얼 소드 | | 6 | **Baran** | 1.900s | 1.700s | 1.966s | **5.566s** | 무거운 공격 | | 7 | **Rene** | 2.000s | 1.900s | 2.000s | **5.900s** | 느린 소환사 | | 8 | **Clad** | 2.000s | 2.000s | 2.000s | **6.000s** | 가장 느림 | **참고**: - Urud, Lian: 원거리 총기형 공격 (발사 속도는 별도, Reload 필요) #### 기본 공격 피해 배율 (AddNormalAttackPer) | 스토커 | 1타 | 2타 | 3타 | 특징 | |--------|-----|-----|-----|------| | **Baran** | +30% | +35% | +30% | 가장 높은 피해 (탱커 타입) | | **Clad** | +30% | +50% | +30% | 2타 강화 (성직자 평타) | | **Hilda** | +30% | +30% | +30% | 일정한 피해 | | **Cazimord** | -5% | +15% | +20% | 3타 강화 (듀얼 소드) | | **Sinobu** | -30% | +10% | -30% | 속도 우선, 낮은 피해 | | **Rio** | -30% | -20% | -15% | 낮은 피해 (속도로 보완) | | **Nave** | 0% | 0% | 0% | 배율 없음 (마법 공격) | | **Rene** | 0% | 0% | 0% | 배율 없음 (소환수 의존) | **해석**: - **Baran, Clad**: 느리지만 강력한 공격 (STR/CON 스토커) - **Rio, Sinobu**: 빠르지만 약한 공격 (DEX 스토커, 스킬 의존) - **Nave, Rene**: INT/WIS 스토커로 평타보다 스킬 중심 #### 기본 공격 몽타주 예시 **Hilda - AM_PC_Hilda_B_Attack_W01_01** (1타): ``` Duration: 1.5초 ANS_AttackState_C: 0.0~1.133초, AddNormalAttackPer: +30% AnimNotifyState_AttackWithEquip: 0.733~0.867초 (히트 판정) AN_SetAutoTarget_C: 0.1초 (자동 타게팅) ``` **Rio - AM_PC_Rio_B_Attack_W01_01** (1타): ``` Duration: 1.2초 ANS_AttackState_C: 0.0~0.9초, AddNormalAttackPer: -30% AnimNotifyState_AttackWithEquip: 0.4~0.533초 (빠른 히트) ``` **Baran - AM_PC_Baran_B_Attack_W01_01** (1타): ``` Duration: 1.9초 ANS_AttackState_C: 0.0~1.17초, AddNormalAttackPer: +30% AnimNotifyState_AttackWithEquip: 0.8~0.92초 (느린 히트) ``` **Cazimord - AM_PC_Cazimord_B_Attack_W01_01** (1타): ``` Duration: 1.667초 ANS_AttackState_C: 0.0~0.900초, AddNormalAttackPer: -5% AnimNotifyState_AttackWithEquip: 0.592~0.733초, 0.710~0.891초 (듀얼 소드 더블 히트) ``` ### 3.4 스토커별 스킬 타이밍 분석 각 스토커의 주요 스킬 몽타주에서 타이밍 데이터를 추출했습니다. #### Hilda (전사) **SK100201 - Sword Strike** (AM_PC_Hilda_B_Skill_SwordStrike): ``` Duration: 1.8초 ANS_SkillCancel_C: 1.3~1.8초 (캔슬 윈도우 0.5초) ANS_AttackState_C: 0.0~1.533초, AddNormalAttackPer: +100% AnimNotifyState_AttackWithEquip: 0.6~0.8초 ``` **SK100202 - Counter** (AM_PC_Hilda_B_Skill_Counter): ``` Duration: 2.567초 ANS_SkillCancel_C: 없음 (캔슬 불가) 특수: 반격 판정 노티파이 포함 ``` **SK100204 - Provoke** (AM_PC_Hilda_B_Skill_Provoke): ``` Duration: 3.0초 ANS_SkillCancel_C: 없음 특수: 도발 범위 설정 노티파이 ``` #### Urud (원거리) **SK110205 - Multi Shot** (AM_PC_Urud_B_Skill_MultiArrow): ``` Duration: 2.0초 ANS_SkillCancel_C: 없음 AnimNotifyState_AttackWithEquip: 다중 화살 발사 타이밍 ``` **SK110204 - Poison Arrow** (AM_PC_Urud_B_Skill_PoisonArrow): ``` Duration: 1.8초 ANS_SkillCancel_C: 없음 특수: Poison 상태 이상 적용 ``` **SK110207 - Reload** (AM_PC_Urud_B_Skill_Reload): ``` Duration: 2.0초 ANS_SkillCancel_C: 없음 특수: 재장전 완료 노티파이 (탄약 충전) ``` #### Nave (마법사) **SK120201 - Magic Missile** (AM_PC_Nave_B_Skill_MagicMissile): ``` Duration: 1.5초 ANS_SkillCancel_C: 없음 특수: 캐스팅 타입 (CanMove_CanRelease) ``` **SK120202 - Fire Wall** (AM_PC_Nave_B_Skill_FireWall): ``` Duration: 1.8초 ANS_SkillCancel_C: 없음 특수: 범위 화염 장판 생성 ``` **SK120301 - Escape 4 (Liberation 궁극기)** (AM_PC_Nave_B_Skill_Escape4): ``` Duration: 3.5초 ANS_SkillCancel_C: 없음 특수: DT_Skill에서 피해 정의 (AnimMontage 노티파이 없음) ``` #### Baran (전사) **SK130204 - Pulling** (AM_PC_Baran_B_Skill_Pulling): ``` Duration: 1.7초 ANS_SkillCancel_C: 1.276~1.700초 (캔슬 윈도우 0.424초) 특수: Pulling 체인 판정 (적 끌어당김) ``` **SK130203 - Smash** (AM_PC_Baran_B_Skill_Smash): ``` Duration: 2.0초 ANS_SkillCancel_C: 없음 AnimNotifyState_AttackWithEquip: 1.0~1.2초 (강타 판정) ``` **SK130206 - Sword Stab** (AM_PC_Baran_B_Skill_SwordStab): ``` Duration: 1.733초 ANS_SkillCancel_C: 1.660~1.733초 (캔슬 윈도우 0.073초, 매우 짧음) AnimNotifyState_AttackWithEquip: 0.8~1.0초 ``` #### Rio (암살자) **SK140201 - Rapid Stab** (AM_PC_Rio_B_Skill_RapidStab): ``` Duration: 1.833초 ANS_SkillCancel_C: 1.513~1.833초 (캔슬 윈도우 0.32초) 특수: 빠른 연속 찌르기 (다중 히트) ``` **SK140205 - Approach** (AM_PC_Rio_B_Skill_Approach): ``` Duration: 1.5초 ANS_SkillCancel_C: 없음 특수: 돌진 이동 스킬 (피해 없음) ``` **SK140202 - Throwing Dagger** (AM_PC_Rio_B_Skill_ThrowingDagger): ``` Duration: 1.2초 ANS_SkillCancel_C: 없음 특수: 원거리 투척 공격 ``` #### Clad (성직자) **SK150206 - Holy Cure** (AM_PC_Clad_B_Skill_HolyCure): ``` Duration: 2.0초 ANS_SkillCancel_C: 없음 특수: 힐링 효과 (피해 없음) ``` **SK150201 - Turn Undead** (AM_PC_Clad_B_Skill_TurnUndead): ``` Duration: 2.5초 ANS_SkillCancel_C: 없음 특수: 언데드 특효 피해 ``` **SK150202 - Holy Light** (AM_PC_Clad_B_Skill_HolyLight): ``` Duration: 2.0초 ANS_SkillCancel_C: 없음 특수: 범위 버프 효과 ``` #### Rene (소환사) **SK160202 - Summon Ifrit** (AM_PC_Rene_B_Skill_SummonIfrit): ``` Duration: 2.5초 ANS_SkillCancel_C: 없음 특수: Ifrit 소환수 생성 ``` **SK160206 - Summon Shiva** (AM_PC_Rene_B_Skill_SummonShiva): ``` Duration: 2.5초 ANS_SkillCancel_C: 없음 특수: Shiva 소환수 생성 ``` **SK160203 - Poison Gas** (AM_PC_Rene_B_Skill_PoisonGas): ``` Duration: 2.0초 ANS_SkillCancel_C: 없음 특수: 독 구름 장판 생성 ``` #### Sinobu (닌자) **SK180202 - Bomb Talisman** (AM_PC_Sinobu_B_Skill_BombTalisman): ``` Duration: 1.5초 ANS_SkillCancel_C: 없음 특수: 폭탄 부적 투척 ``` **SK180203 - Thunder Kick** (AM_PC_Sinobu_B_Skill_ThunderKick): ``` Duration: 1.8초 ANS_SkillCancel_C: 없음 AnimNotifyState_AttackWithEquip: 0.6~0.8초 특수: 번개 속성 발차기 ``` **SK180205 - Ninpo Change (Swap)** (AM_PC_Sinobu_B_Skill_NinpoChange): ``` Duration: 1.0초 ANS_SkillCancel_C: 없음 특수: 텔레포트 인술 (위치 교환) ``` #### Lian (레인저) **SK190207 - Rapid Shot** (AM_PC_Lian_B_Skill_RapidShot1): ``` Duration: 2.626초 ANS_SkillCancel_C: 2.333~2.626초 (캔슬 윈도우 0.293초) 특수: 연속 사격 (6발) ``` **SK190205 - Back Step Bow Attack** (AM_PC_Lian_B_Skill_BackStepBowAttack): ``` Duration: 1.701초 ANS_SkillCancel_C: 1.492~1.701초 (캔슬 윈도우 0.209초) 특수: 백스텝 + 사격 (회피 기능) ``` **SK190201 - Dark Souls** (AM_PC_Lian_B_Skill_DarkSouls_NoCasting): ``` Duration: 2.0초 ANS_SkillCancel_C: 없음 특수: Holy 속성 관통 공격 ``` **SK190209 - Reload** (AM_PC_Lian_B_Skill_Reload): ``` Duration: 2.0초 ANS_SkillCancel_C: 없음 특수: 재장전 (탄약 충전) ``` #### Cazimord (전사) **SK170201 - Flash** (AM_PC_Cazimord_B_Skill_Flash, AM_PC_Cazimord_B_Skill_Flash_Active): ``` Flash (준비): Duration: 1.0초 Flash_Active (발동): Duration: 1.733초 ANS_SkillCancel_C: 1.001~1.733초 (Active 몽타주, 캔슬 윈도우 0.732초) 특수: Flash 스택 시스템 (최대 3스택) ``` **SK170202 - Blade Storm** (AM_PC_Cazimord_B_Skill_BladeStorm): ``` Duration: 3.0초 ANS_SkillCancel_C: 2.5~3.0초 (캔슬 윈도우 0.5초) 특수: 회전 범위 공격 ``` **SK170203 - Burn** (AM_PC_Cazimord_B_Skill_Burn): ``` Duration: 2.5초 ANS_SkillCancel_C: 없음 특수: Fire 지속 피해 ``` **SK170101 - Parrying (서브 스킬)** (AM_PC_Cazimord_B_Skill_Parrying): ``` Duration: 1.5초 ANS_SkillCancel_C: 없음 특수: 패링 성공 시 반격 + Flash 스택 충전 ``` ### 3.5 ANS_SkillCancel_C 보유 스킬 요약 전체 스킬 중 **ANS_SkillCancel_C 노티파이를 가진 스킬은 7개뿐**입니다. 기본 공격은 모두 캔슬 윈도우가 없습니다. | 스토커 | 스킬 ID | 스킬 이름 | 캔슬 시작 | 캔슬 종료 | 윈도우 크기 | |--------|---------|-----------|-----------|-----------|-------------| | **Hilda** | SK100201 | Sword Strike | 1.300s | 1.800s | 0.500s | | **Baran** | SK130204 | Pulling | 1.276s | 1.700s | 0.424s | | **Baran** | SK130206 | Sword Stab | 1.660s | 1.733s | 0.073s | | **Rio** | SK140201 | Rapid Stab | 1.513s | 1.833s | 0.320s | | **Lian** | SK190207 | Rapid Shot | 2.333s | 2.626s | 0.293s | | **Lian** | SK190205 | Back Step | 1.492s | 1.701s | 0.209s | | **Cazimord** | SK170202 | Blade Storm | 2.500s | 3.000s | 0.500s | | **Cazimord** | SK170201 | Flash (Active) | 1.001s | 1.733s | 0.732s | **캔슬 윈도우가 없는 스토커** (5명): - Urud, Nave, Clad, Rene, Sinobu 이들은 **Activation Order Group 시스템**에만 의존하여 스킬 캔슬을 처리합니다. (Section 4.2 참조) **캔슬 윈도우 크기 특징**: - **가장 긴 윈도우**: Cazimord Flash_Active (0.732초) - 스택 충전 시스템에 유리 - **가장 짧은 윈도우**: Baran Sword Stab (0.073초) - 정밀한 타이밍 요구 - **평균 윈도우**: 약 0.4초 --- ## 4. Blueprint Ability 및 캔슬 시스템 ### 4.1 GA_* Blueprint Ability 구조 모든 스킬과 기본 공격은 **UWSGameplayAbility**를 상속한 Blueprint 클래스로 구현됩니다. 이 클래스들은 `GA_`로 시작하며 (GameplayAbility), 스킬 로직을 정의합니다. #### UWSGameplayAbility 베이스 클래스 **소스 위치**: `WorldStalker/Source/WorldStalker/AbilitySystem/WSGameplayAbility.h` **주요 프로퍼티**: ```cpp // 0: 그룹 없음, 1이상: 높을 수록 아래 상태를 무시하고, 같은 값이면 서로 교체됨 UPROPERTY(EditDefaultsOnly, Category = "WorldStalker") uint8 ActivationOrderGroup = 0; UPROPERTY(EditDefaultsOnly, Category = "WorldStalker") bool bDisableOrderGroup = false; UPROPERTY(EditDefaultsOnly, Category = "WorldStalker") bool bCanSkillCancel = false; UPROPERTY(EditDefaultsOnly, Category = "WorldStalker") EWSAbilityActivationTrigger ActivationTrigger = EWSAbilityActivationTrigger::OnceInput; ``` #### Ability 클래스 분류 **1. 공통 Ability** (여러 스토커가 공유): | Ability 클래스 | 사용 스토커 | 용도 | |----------------|-------------|------| | `GA_Attack` | Hilda, Baran, Rio, Clad, Rene, Nave, Sinobu, Cazimord | 기본 공격 (좌클릭) | | `GA_Attack_Firearm` | Urud, Lian | 원거리 총기형 기본 공격 (탄약 시스템) | | `GA_Attack_Firearm_Reload` | Urud, Lian | 재장전 (Reload 스킬) | | `GA_Skill_Common_Blocking` | Hilda, Baran, Clad | 방패 방어 (서브 스킬) | | `GA_Skill_Casting_CanMove_CanRelease` | Nave (3개), Clad (3개), Rene (3개), Sinobu, Cazimord | 이동 가능 캐스팅 스킬 | | `GA_Skill_Casting_CantMove_CanRelease` | Urud (Make Trap) | 이동 불가 캐스팅 스킬 | | `GA_Skill_Casting_Ultimate` | Hilda, Urud, Clad, Rio, Rene, Lian | 궁극기 (공통 로직) | | `GA_Skill_Ultimate_Base` | Sinobu | 궁극기 베이스 클래스 | **2. 스토커 전용 Ability** (특수 로직 구현): 각 스토커는 고유한 GA_Skill_[Stalker]_[SkillName] 클래스를 가집니다. #### Ability 상속 계층 ``` UGameplayAbility (언리얼 엔진 기본 클래스) └─ UWSGameplayAbility (C++ 베이스 클래스) ├─ GA_WSGameplayAbilityBase_C (Blueprint 베이스) │ ├─ GA_Attack_C (기본 공격) │ ├─ GA_Attack_Firearm_C (총기 공격) │ ├─ GA_Skill_Common_Blocking_C (방어) │ ├─ GA_Skill_Casting_* (캐스팅 스킬들) │ └─ GA_Skill_[Stalker]_[Skill]_C (스토커별 스킬) └─ ... ``` ### 4.2 Activation Order Group 시스템 **Activation Order Group**은 스킬 활성화 우선순위를 정의하는 **주요 캔슬 메커니즘**입니다. #### 작동 원리 **우선순위 규칙**: 1. **높은 그룹이 낮은 그룹을 캔슬**: Group 3 스킬은 Group 1, 2 스킬을 언제든지 중단 가능 2. **같은 그룹은 서로 교체 가능**: Group 2 스킬끼리는 자유롭게 전환 가능 3. **낮은 그룹은 높은 그룹을 캔슬 불가**: Group 1 스킬은 Group 2 실행 중 발동 불가 4. **Group 0은 우선순위 없음**: 다른 스킬에 의해 자유롭게 캔슬됨 **C++ 코드 구현**: ```cpp // WSGameplayAbility.h:189-191 uint8 GetActivationOrderGroup() const { return ActivationOrderGroup; } void SetActivationOrderGroup(uint8 NewActivationOrderGroup) { ActivationOrderGroup = NewActivationOrderGroup; } bool IsDisableOrderGroup() const { return bDisableOrderGroup; } ``` #### 우선순위 예시 ``` 상황: Hilda가 Counter (Group 3) 실행 중 ✅ 가능한 행동: - Ultimate (Group 4) - 높은 그룹이므로 Counter 캔슬하고 발동 - Counter (Group 3) - 같은 그룹이므로 재사용 가능 ❌ 불가능한 행동: - Sword Strike (Group 2) - 낮은 그룹이므로 Counter 종료 전까지 대기 - 기본 공격 (Group 1) - 낮은 그룹이므로 Counter 종료 전까지 대기 ``` #### 일반적인 그룹 할당 패턴 | Group | 용도 | 예시 | |-------|------|------| | **0** | 우선순위 없음 | 패시브, 버프, 기본 행동 | | **2** | 이동/지원 스킬 | Approach, Sway | | **3** | 방어/반격 스킬 | Counter, Blocking, Parrying, Flash | | **4** | 일반 스킬 | 대부분의 공격 스킬 | | **5** | 기본 공격 | 좌클릭 평타, 특수 공격 | | **9** | 궁극기 | Ultimate 스킬 | #### 스토커별 Activation Order Group 실제 값 다음은 Blueprint.json에서 추출한 10명 스토커의 실제 ActivationOrderGroup 값입니다. **Hilda** (전사): - Group 4: Bash, SwordStrike - Group 0: BloodMoon_Active, SteelBlocking **Urud** (궁수): - Group 5: ArrowAttack, MultiShot_Quick, PoisonArrow - Group 0: CampFire, Explosion_Active, MakeTrap, MultiShot, PierceShot, PoisonArrow_Active, SpeedUp **Nave** (마법사): - Group 9: Escape4 (궁극기) - Group 4: ManaCharge_Casting - Group 0: Escape_Active, FireWall_Active, Invisible, MagicMissile_Active, MagicShield, TurnOff, WindForce **Baran** (기사): - Group 9: Decision (궁극기) - Group 5: Pulling - Group 4: RockBreaker, Smash, SwordStab - Group 0: Prepare, Slasher **Rio** (암살자): - Group 5: DroppingAttack - Group 4: Flashbang, RapidStab, ThrowingDagger - Group 2: Approach - Group 0: CatEyes, CorrosionDagger, Sensitive **Clad** (성직자): - Group 0: Gold, HolyCure, HolyLight, HolyShield, HolyWall, Stigma, TurnUndead (모두 지원/버프 스킬) **Rene** (소환사): - Group 5: Scratching - Group 0: BloodChange, BloodChange_Active, BloodSword_Active, ManaStoneCarnival, PoisonGas_Active, Slow_Active, SummonIfrit_Active, SummonShiva_Active **Sinobu** (닌자): - Group 9: Silence (궁극기) - Group 5: Shuriken - Group 4: BombTalisman, MarkingSting, ThunderKick - Group 3: NinpoGecko_Casting - Group 0: Deflect, NinpoChange, NinpoFlame, SIlence_Active **Lian** (레인저): - Group 5: BackStepBowAttack, ChargingBow, RapidShot - Group 4: DarkSouls - Group 0: CallingRat, MakeWoodCover, ManaStoneSilence_Active, MoreArrow, OneAim **Cazimord** (검사): - Group 9: WingCutter (궁극기) - Group 4: BladeStorm, Burn, FrontKick, Parrying - Group 3: Flash - Group 2: BladeStorm_Perk, Sway, Sway_Perk - Group 0: Burn_Active **주요 발견**: - **궁극기 (Group 9)**: Nave, Baran, Sinobu, Cazimord만 보유 - **기본 공격 (Group 5)**: Urud, Baran, Rio, Rene, Sinobu, Lian이 사용 - **Clad와 Rene**: 대부분 Group 0 (패시브/지원) 스킬로 구성 - **Cazimord**: 가장 다양한 Group 분포 (0, 2, 3, 4, 9) ### 4.3 통합 캔슬 시스템 (OR 조건) 스킬 캔슬은 **두 개의 독립적인 메커니즘**으로 작동합니다. 둘 중 **하나만 만족**하면 캔슬이 가능합니다 (OR 조건). #### 메커니즘 1: Activation Order Group (주요 시스템) **정의 위치**: GA_* Blueprint의 Class Defaults (C++ 프로퍼티) **작동 시점**: 언제든지 (애니메이션 재생 중 어느 구간이든) **조건**: ``` IF (새 스킬의 ActivationOrderGroup >= 현재 스킬의 ActivationOrderGroup) THEN 캔슬 가능 ``` **특징**: - 우선순위 기반 시스템 - 프레임 단위 정밀 제어 불필요 - 대부분의 캔슬 처리 담당 #### 메커니즘 2: ANS_SkillCancel_C (보조 시스템) **정의 위치**: AnimMontage의 Notify State **작동 시점**: 노티파이가 활성화된 시간 구간 (TriggerTime ~ TriggerTime+Duration) **조건**: ``` IF (현재 시간이 ANS_SkillCancel_C 구간 내) AND (새 스킬의 ActivationOrderGroup >= 현재 스킬의 ActivationOrderGroup) THEN 캔슬 가능 ``` **특징**: - 타이밍 기반 윈도우 - 스킬의 "자연스러운 종료" 구간 정의 - 같은 우선순위 스킬도 이 구간에서는 캔슬 가능 #### 통합 로직 (의사 코드) ```cpp bool CanCancelCurrentSkill(GA_NewSkill, GA_CurrentSkill, CurrentTime) { // 조건 1: Activation Order Group 우선순위 체크 bool bHigherPriority = (GA_NewSkill.ActivationOrderGroup > GA_CurrentSkill.ActivationOrderGroup); // 조건 2: ANS_SkillCancel_C 윈도우 체크 bool bInCancelWindow = false; if (GA_CurrentSkill.CurrentMontage->HasSkillCancelNotify()) { float CancelStart = SkillCancelNotify.TriggerTime; float CancelEnd = CancelStart + SkillCancelNotify.Duration; bInCancelWindow = (CurrentTime >= CancelStart && CurrentTime <= CancelEnd); // 캔슬 윈도우 내에서는 같은 우선순위도 허용 if (bInCancelWindow && GA_NewSkill.ActivationOrderGroup >= GA_CurrentSkill.ActivationOrderGroup) return true; } // OR 조건: 둘 중 하나만 만족하면 캔슬 가능 return bHigherPriority || bInCancelWindow; } ``` #### 실전 예시 **예시 1: Hilda - Sword Strike 스킬 사용 중 (Group 2)** | 상황 | 시간 | 새 입력 | 캔슬 가능? | 이유 | |------|------|---------|-----------|------| | 스킬 시작 | 0.5초 | 기본 공격 (Group 1) | ❌ | 낮은 우선순위 | | 스킬 시작 | 0.5초 | Counter (Group 3) | ✅ | 높은 우선순위 (메커니즘 1) | | 캔슬 윈도우 | 1.4초 | 다른 스킬 (Group 2) | ✅ | ANS_SkillCancel_C 구간 (메커니즘 2) | | 캔슬 윈도우 | 1.4초 | 기본 공격 (Group 1) | ❌ | 여전히 낮은 우선순위 | | 스킬 종료 | 1.9초 | 모든 스킬 | ✅ | 스킬 완전 종료 | **예시 2: Urud - Multi Shot 스킬 (ANS_SkillCancel_C 없음)** | 상황 | 새 입력 | 캔슬 가능? | 이유 | |------|---------|-----------|------| | 스킬 실행 중 | 일반 스킬 (같은 Group) | ❌ | ANS_SkillCancel_C 없음, 같은 우선순위 | | 스킬 실행 중 | Ultimate (Group 4) | ✅ | 높은 우선순위 (메커니즘 1만 작동) | ### 4.4 주요 Ability 구현 분석 이전에 추출한 Blueprint.json 데이터를 기반으로 주요 Ability의 구조를 분석합니다. #### GA_Attack (기본 공격) **사용 스토커**: Hilda, Baran, Rio, Clad, Rene, Nave, Sinobu, Cazimord **부모 클래스**: GA_WSGameplayAbilityBase_C **EventGraph 노드 수**: 70개 (중간 복잡도) **주요 변수**: | 변수명 | 타입 | 설명 | |--------|------|------| | `ComboIndex` | Int | 현재 콤보 인덱스 (0~2) | | `MaxComboCount` | Int | 최대 콤보 수 (기본 3) | | `ComboInputBuffer` | Bool | 콤보 입력 버퍼 (선입력 지원) | **로직**: 1. 장착한 무기의 `montages` 배열에서 ComboIndex에 해당하는 몽타주 재생 2. 콤보 윈도우 내에 재입력 시 다음 콤보로 체인 3. 최대 콤보 도달 또는 일정 시간 경과 시 ComboIndex 리셋 #### GA_Attack_Firearm (총기형 기본 공격) **사용 스토커**: Urud, Lian **부모 클래스**: GA_Attack_C **EventGraph 노드 수**: 85개 (높은 복잡도) **주요 변수**: | 변수명 | 타입 | 설명 | |--------|------|------| | `AmmoTag` | GameplayTag | 탄약 카운트 태그 (Character.State.Ammo) | | `MaxAmmo` | Int | 최대 탄약 수 | | `bAutoReload` | Bool | 탄약 소진 시 자동 재장전 | **특수 로직**: 1. 공격 시 AmmoTag 값을 1 감소 2. 탄약이 0이면 공격 불가, 자동으로 Reload 스킬 트리거 3. Reload 스킬 완료 시 AmmoTag를 MaxAmmo로 복구 **예시 - Urud**: ``` 초기 탄약: 6발 발사 1~6발: AmmoTag = 6 → 5 → 4 → 3 → 2 → 1 → 0 탄약 소진: GA_Attack_Firearm_Reload 자동 실행 재장전 완료: AmmoTag = 6으로 복구 ``` #### GA_Skill_Common_Blocking (방패 방어) **사용 스토커**: Hilda, Baran, Clad **EventGraph 노드 수**: 112개 (매우 높은 복잡도) **주요 변수**: | 변수명 | 타입 | 설명 | |--------|------|------| | `BlockingStaminaCost` | Float | 방어 시 스태미나 소모량 | | `BlockingDamageReduction` | Float | 피해 감소율 (%) | | `bPerfectBlockWindow` | Bool | 완벽 방어 윈도우 활성화 | **특수 로직**: 1. 버튼을 누르고 있는 동안 방어 상태 유지 (WhileInput 트리거) 2. 피격 시 BlockingDamageReduction만큼 피해 감소 3. 스태미나가 0이면 방어 해제 4. 완벽 방어 타이밍 성공 시 스태미나 소모 없음 #### GA_Skill_Casting_CanMove_CanRelease (이동 가능 캐스팅) **사용 스토커**: Nave (3개 스킬), Clad (3개 스킬), Rene (3개 스킬), Sinobu, Cazimord **EventGraph 노드 수**: 68개 **주요 변수**: | 변수명 | 타입 | 설명 | |--------|------|------| | `CastingTime` | Float | 캐스팅 시간 (DT_Skill에서 가져옴) | | `bCanMoveWhileCasting` | Bool | 캐스팅 중 이동 가능 (true) | | `bCanRelease` | Bool | 버튼을 떼면 즉시 발동 (차징) | **특수 로직**: 1. 버튼 누름: 캐스팅 시작 (몽타주 재생) 2. 캐스팅 중 이동 속도 감소 (GE_WalkSpeed 적용) 3. 버튼을 떼거나 CastingTime 경과 시 스킬 발동 4. 캐스팅 중 다른 높은 우선순위 스킬로 캔슬 가능 ### 4.5 스토커별 특수 시스템 구현 #### Hilda - Counter (반격 시스템) **Ability**: GA_Skill_Knight_Counter_C **메커니즘**: 1. Counter 스킬 발동 시 짧은 시간 동안 "반격 판정" 상태 2. 이 상태에서 피격 시 피해 무효화 + 자동 반격 3. 반격 성공 시 추가 피해 (DT_Skill의 skillDamageRate 배율 적용) **AnimMontage**: AM_PC_Hilda_B_Skill_Counter (Duration: 2.567초) - 반격 판정 구간: 0.3~0.8초 (0.5초 윈도우) - ANS_SkillCancel_C 없음 (강제로 끝까지 재생) #### Urud & Lian - Reload 시스템 **Ability**: GA_Attack_Firearm_Reload_C **메커니즘**: 1. Reload 스킬 사용 시 재장전 몽타주 재생 2. 재장전 완료 노티파이에서 AmmoTag 복구 3. 재장전 중에는 공격 불가 (Blocking 상태) **특수 사항**: - **Urud**: 탄약 6발, Reload 2.0초 - **Lian**: 탄약 6발, Reload 2.0초 + Precision Aim (조준 시스템) **Lian의 Precision Aim**: - 조준 버튼 누르면 줌 + 이동 속도 대폭 감소 - 조준 중 명중률 증가 (히트박스 확대) #### Rio - Chain Score 시스템 (3 스택) **Ability**: GA_Skill_Rio_DroppingAttack_C (서브 스킬) **EventGraph 노드 수**: 42개 **주요 변수**: | 변수명 | 타입 | 설명 | |--------|------|------| | `ChainScoreTag` | GameplayTag | 체인 스코어 태그 (Character.State.ChainScore) | | `MaxChainScore` | Int | 최대 스택 (3) | | `ChainScoreDamageBonus` | Float | 스택당 피해 증가율 | **메커니즘**: 1. Dropping Attack (공중 낙하 공격) 성공 시 ChainScore +1 2. 스택이 쌓일수록 다음 공격의 피해량 증가 3. 최대 3스택 유지, 일정 시간 경과 또는 피격 시 리셋 **예시**: ``` Dropping Attack 성공 1회: ChainScore = 1, 다음 공격 피해 +10% Dropping Attack 성공 2회: ChainScore = 2, 다음 공격 피해 +20% Dropping Attack 성공 3회: ChainScore = 3, 다음 공격 피해 +30% (최대) ``` #### Rene - Spirit 소환 및 Lifesteal **Ability**: GA_Skill_Rene_SummonIfrit_C, GA_Skill_Rene_SummonShiva_C **EventGraph 노드 수**: Summon Ifrit (58개), Summon Shiva (55개) **메커니즘**: 1. 소환 스킬 사용 시 AI 제어 소환수 스폰 2. 소환수는 독립적으로 적 공격 3. Rene의 공격에 Lifesteal 효과 (GE_Lifesteal 적용) **Lifesteal**: - 피해량의 일정 %를 HP로 회복 - GameplayEffect로 구현 (Section 5 참조) #### Sinobu - Shuriken 충전 시스템 **Ability**: GA_Skill_Sinobu_Shuriken_C (서브 스킬) **주요 변수**: | 변수명 | 타입 | 설명 | |--------|------|------| | `ShurikenChargeTag` | GameplayTag | 수리검 충전 태그 | | `MaxShurikenCharge` | Int | 최대 충전 수 (3) | **메커니즘**: 1. 수리검 발사 시 ChargeTag -1 2. 자동으로 시간 경과에 따라 충전 (1초당 1개) 3. 충전이 0이면 발사 불가 **Swap 인술** (SK180205 - Ninpo Change): - 적 위치와 자신의 위치를 순간 교환 (텔레포트) - 중력 조작 (Gravity = 0) 구간 존재 #### Lian - Charging Bow (3스택 충전) **Ability**: GA_Skill_Lian_ChargingBow_C (서브 스킬) **EventGraph 노드 수**: 116개 (가장 복잡한 서브 스킬) **주요 변수**: | 변수명 | 타입 | 설명 | |--------|------|------| | `ChargeLevel` | Int | 충전 레벨 (0~3) | | `ChargeTime` | Float | 레벨당 충전 시간 | | `ChargeDamageMultiplier` | Array | 레벨별 피해 배율 | **메커니즘**: 1. 버튼 누름: 충전 시작 2. 0.5초마다 ChargeLevel +1 (최대 3) 3. 버튼 떼기: 현재 ChargeLevel에 해당하는 피해로 화살 발사 4. ChargeLevel 3 (만충전): 피해 2배 + 관통 효과 **피해 배율**: ``` Level 0: 0.7x (즉발) Level 1: 1.0x Level 2: 1.5x Level 3: 2.0x + 관통 ``` #### Cazimord - Flash 스택 & Parrying **Flash 시스템** (SK170201): **Ability**: GA_Skill_Cazimord_Flash_C **주요 변수**: | 변수명 | 타입 | 설명 | |--------|------|------| | `FlashStackTag` | GameplayTag | Flash 스택 태그 | | `MaxFlashStack` | Int | 최대 스택 (3) | **메커니즘**: 1. Flash 스킬 사용 시 준비 몽타주 재생 (1.0초) 2. 스택이 있으면 Flash_Active 몽타주 재생 (1.733초) 3. Flash_Active는 빠른 돌진 + 피해 4. 스택은 Parrying 성공 시 충전 **ANS_SkillCancel_C**: Flash_Active에만 존재 (1.001~1.733초, 0.732초 윈도우) **Parrying 시스템** (SK170101): **Ability**: GA_Skill_Cazimord_Parrying_C **메커니즘**: 1. Parrying 발동 시 짧은 패링 판정 구간 (0.2초) 2. 패링 성공 시: - 적 피해 무효화 - 자동 반격 (높은 피해) - FlashStackTag +1 - Parrying 스킬 쿨타임 즉시 리셋 (자기 자신 쿨타임 감소) **특수 사항**: - Parrying은 쿨타임 감소 로직을 가진 유일한 스킬 - 패링 실패 시에도 쿨타임 적용 (리스크-리워드) --- ## 5. GameplayEffect 메커니즘 ### 5.1 GameplayEffect 개요 **GameplayEffect**는 언리얼 엔진의 Gameplay Ability System에서 캐릭터의 Attribute (HP, MP, 공격력 등)를 수정하는 메커니즘입니다. 모든 피해, 회복, 버프, 디버프는 GameplayEffect로 구현됩니다. #### 주요 GameplayEffect 타입 | 타입 | 지속 시간 | 용도 | 예시 | |------|-----------|------|------| | **Instant** | 즉시 적용 | 피해, 즉시 회복 | GE_Attack_Physical (공격 피해) | | **Duration** | 정해진 시간 | 버프/디버프 | GE_Buff_AttackSpeed (5초간 공격 속도 증가) | | **Infinite** | 영구 (수동 제거) | 장비 효과, 패시브 | GE_Equip_Weapon (무기 장착 보너스) | ### 5.2 주요 GameplayEffect 클래스 #### GE_Attack_* (공격 피해) **DT_Skill의 gameplayEffectSet 또는 GA_* Blueprint에서 적용** **예시 - GE_Attack_Physical**: - **타입**: Instant - **Modifier**: HP -= (공격력 × skillDamageRate × 속성 배율) - **적용 시점**: AnimNotifyState_AttackWithEquip 히트 판정 성공 시 **피해 계산 공식** (일반적): ``` 최종 피해 = 기본 공격력 × skillDamageRate × AddNormalAttackPer × (1 - 방어율) × 크리티컬 배율 × 속성 저항 ``` #### GE_Skill_* (스킬 효과) 스킬별로 고유한 GameplayEffect를 가질 수 있습니다. **예시 - Hilda의 Sword Strike (SK100201)**: - GE_Skill_Hilda_SwordStrike: Lightning 속성 피해 - Lightning 저항 적용: `FinalDamage × (1 - Target.LightningResistancePer)` **예시 - Rene의 Lifesteal**: - GE_Lifesteal_Rene: Duration 타입, 일정 시간 동안 모든 공격에 흡혈 효과 - Modifier: HP += (DamageDealt × LifestealRate) #### GE_Buff/Debuff_* (버프/디버프) **예시 - GE_Buff_AttackSpeed**: - **타입**: Duration (5초) - **Modifier**: AttackSpeed += 20% - **시각 효과**: 버프 아이콘 표시 **예시 - GE_Debuff_Poison** (Urud의 Poison Arrow): - **타입**: Duration (10초) - **Modifier**: HP -= 10 (매 1초마다, Periodic) - **시각 효과**: 독 이펙트 파티클 #### GE_Cooldown_* (쿨타임) DT_Skill의 `coolTime` 값을 기반으로 자동 생성됩니다. **메커니즘**: 1. 스킬 사용 시 GE_Cooldown_[SkillID] 적용 2. GameplayTag: `Cooldown.Skill.[SkillID]` 부여 3. coolTime 초 경과 후 자동 제거 4. 태그가 있는 동안 해당 스킬 사용 불가 **쿨타임 감소**: - WSGameplayAbility의 `ReduceSkillCoolTime` 함수 사용 - 특정 룬/장비 효과로 쿨타임 % 감소 가능 #### GE_ManaCost_* (마나 소모) DT_Skill의 `skillManaCost` 값을 기반으로 적용됩니다. **메커니즘**: 1. 스킬 CommitAbility 단계에서 마나 체크 2. 마나 부족 시 스킬 발동 실패 3. 마나 충분 시 GE_ManaCost 적용하여 MP 감소 ### 5.3 GameplayEffectSet 구조 DT_Skill의 `gameplayEffectSet` 배열은 여러 GameplayEffect를 순차적으로 적용합니다. **구조**: ```json "gameplayEffectSet": [ { "applyTrigger": "OnActivation", "gameplayEffectClass": "/Game/Blueprints/Effects/GE_Skill_Buff.GE_Skill_Buff_C", "targetType": "Self" }, { "applyTrigger": "OnHit", "gameplayEffectClass": "/Game/Blueprints/Effects/GE_Attack_Damage.GE_Attack_Damage_C", "targetType": "Target" } ] ``` **applyTrigger 타입**: - `OnActivation`: 스킬 활성화 시 즉시 - `OnHit`: 타격 성공 시 - `OnEnd`: 스킬 종료 시 - `Periodic`: 주기적으로 **targetType**: - `Self`: 시전자에게 적용 - `Target`: 타격 대상에게 적용 - `Area`: 범위 내 모든 대상 ### 5.4 특수 GameplayEffect 예시 #### Nave의 Fire Wall (지속 피해 장판) **메커니즘**: 1. Fire Wall 스킬 사용 시 바닥에 화염 장판 생성 2. 장판 범위 내 적에게 GE_Debuff_FireWall_Periodic 적용 3. Periodic 효과: 매 0.5초마다 Fire 속성 피해 **GameplayEffect**: - **타입**: Duration (5초) - **Period**: 0.5초 - **Modifier**: HP -= (BaseDamage × 0.2) #### Baran의 Pulling (중력 조작) **메커니즘**: 1. Pulling 스킬 적중 시 GE_Pulling_Gravity 적용 2. 적의 Gravity를 음수로 변경 (끌어당김) 3. 0.5초 동안 적을 Baran 방향으로 이동 **GameplayEffect**: - **타입**: Duration (0.5초) - **Modifier**: Gravity = -1000 (강제 이동) - **Physics**: AddImpulse (Baran 방향) #### Clad의 Holy Cure (힐링) **메커니즘**: 1. Holy Cure 스킬 사용 시 GE_Heal_Holy 적용 2. 자신 또는 아군의 HP 회복 **GameplayEffect**: - **타입**: Instant - **Modifier**: HP += (HealAmount × WIS) - **targetType**: Self 또는 FriendlyTarget --- ## 6. DT_Skill 특수 케이스 ### 6.1 Nave의 궁극기 - Liberation (Escape 4) **스킬 ID**: SK120301 **특수 사항**: 대부분의 스킬은 AnimMontage의 노티파이(AnimNotifyState_AttackWithEquip)에서 피해를 처리하지만, Nave의 궁극기 'Liberation'은 **DT_Skill의 데이터에서 직접 피해를 정의**합니다. #### 일반 스킬의 피해 처리 (일반적 케이스) **흐름**: ``` 1. DT_Skill에서 skillDamageRate 정의 (예: 1.3) 2. AnimMontage의 AnimNotifyState_AttackWithEquip 노티파이 실행 3. 노티파이에서 AttackTag (Event.Attack.Skill) 트리거 4. GA_* Blueprint에서 skillDamageRate를 곱하여 피해 계산 5. GE_Attack_* GameplayEffect 적용 ``` **예시 - Hilda Sword Strike**: ``` DT_Skill: skillDamageRate = 1.3 AnimMontage: AnimNotifyState_AttackWithEquip (TriggerTime: 0.6초) Blueprint: ApplyAttackEffectToTarget 함수 호출 GameplayEffect: GE_Attack_Lightning 적용 최종 피해 = 기본 공격력 × 1.3 × Lightning 배율 ``` #### Nave 궁극기의 피해 처리 (특수 케이스) **흐름**: ``` 1. DT_Skill의 gameplayEffectSet에 직접 피해 GameplayEffect 정의 2. AnimMontage에는 피해 노티파이 없음 (시각 효과만) 3. GA_Skill_Nave_Escape4 Blueprint에서 gameplayEffectSet 직접 적용 4. 범위 내 모든 적에게 피해 (Area 타겟) ``` **DT_Skill 데이터 (SK120301)**: ```json { "RowName": "SK120301", "stalkerName": "nave", "bIsUltimate": true, "skillName": "Escape 4", "skillDamageRate": 1.0, "skillAttackType": "MagicalSkill", "skillElementType": "None", "useMontages": [ "/Game/_Art/_Character/PC/Nave/AnimMontage/Ultimate/AM_PC_Nave_B_Skill_Escape4.AM_PC_Nave_B_Skill_Escape4" ], "abilityClass": "/Game/Blueprints/Characters/Nave/GA_Skill_Nave_Escape4.GA_Skill_Nave_Escape4_C", "gameplayEffectSet": [ { "applyTrigger": "OnActivation", "gameplayEffectClass": "/Game/Blueprints/Effects/GE_Skill_Nave_Liberation.GE_Skill_Nave_Liberation_C", "targetType": "Area" } ] } ``` **GE_Skill_Nave_Liberation**: - **타입**: Instant - **범위**: 반경 500 units - **Modifier**: HP -= (MagicPower × 1.0) × 4회 (4번의 텔레포트 각각 피해) - **특징**: 텔레포트 이동 경로에 있는 모든 적 피해 **AnimMontage (AM_PC_Nave_B_Skill_Escape4)**: - Duration: 3.5초 - ANS_SkillCancel_C: 없음 - 피해 판정 노티파이: 없음 (GameplayEffect에서 직접 처리) - 시각 효과 노티파이만 존재 (4회 텔레포트 이펙트) #### 왜 이렇게 구현했는가? **일반 스킬 방식의 한계**: - AnimNotifyState_AttackWithEquip은 무기 궤적 기반 히트 판정 - Nave의 궁극기는 텔레포트 경로 전체가 피해 범위 - 무기가 없고, 정해진 궤적이 없음 **gameplayEffectSet 직접 사용 장점**: - 복잡한 범위 판정을 GameplayEffect 로직으로 처리 - 4회 텔레포트의 각 경로마다 별도 피해 적용 가능 - AnimMontage와 피해 로직 분리로 유연성 확보 ### 6.2 Urud의 궁극기 - Explosion (범위 피해) **스킬 ID**: SK110301 **특수 사항**: 투척한 돌이 지면 또는 적 적중 시 폭발하여 범위 피해를 줍니다. **메커니즘**: 1. 궁극기 활성화 시 투사체(돌) 스폰 2. 투사체 충돌 시 Explosion Actor 생성 3. Explosion Actor의 Overlap 이벤트에서 범위 내 적 검색 4. 각 적에게 GE_Skill_Urud_Explosion 적용 **특징**: - 피해는 AnimMontage가 아닌 투사체 충돌 이벤트에서 처리 - DT_Skill의 skillDamageRate는 기본 배율로 사용 - 범위 중심부일수록 높은 피해 (거리 기반 감쇠) --- ## 7. 코드-어셋 통합 흐름 ### 7.1 스킬 실행 전체 흐름 스킬이 입력부터 피해 적용까지 거치는 전체 과정을 정리합니다. ``` [1] 입력 처리 ↓ [2] DT_CharacterStat에서 스킬 ID 조회 ↓ [3] DT_Skill에서 스킬 데이터 로드 ├─ skillDamageRate ├─ coolTime ├─ skillManaCost ├─ useMontages └─ abilityClass ↓ [4] GA_* Blueprint Ability 실행 ├─ CanActivateAbility: 쿨타임/마나 체크 ├─ ActivateAbility: 스킬 시작 │ ├─ Activation Order Group 비교 │ └─ 현재 스킬 캔슬 여부 결정 ├─ CommitAbility: 마나/쿨타임 소모 확정 │ ├─ GE_ManaCost 적용 │ └─ GE_Cooldown 적용 └─ PlayMontage: AnimMontage 재생 ↓ [5] AnimMontage 재생 ├─ ANS_AttackState_C: 공격 상태 활성화 ├─ AnimNotifyState_AttackWithEquip: 히트 판정 │ ├─ AttackTag 기반 이벤트 트리거 │ └─ 범위 내 적 검색 ├─ ANS_SkillCancel_C: 캔슬 윈도우 활성화 └─ AN_SetAutoTarget_C: 자동 타게팅 ↓ [6] 피해 계산 (GA_* Blueprint) ├─ FindSkillDataRow: DT_Skill 데이터 재조회 ├─ CalculateSkillRate: 최종 피해 배율 계산 │ ├─ skillDamageRate (기본) │ ├─ AddNormalAttackPer (몽타주) │ ├─ 장비 보정 (EquipSkillModify) │ └─ 룬 보정 └─ ApplyAttackEffectToTarget: GameplayEffect 적용 ↓ [7] GameplayEffect 적용 ├─ GE_Attack_* 또는 GE_Skill_* ├─ Attribute Modifier 실행 │ ├─ Target HP 감소 │ ├─ 속성 저항 계산 │ └─ 크리티컬 판정 └─ 시각/사운드 효과 재생 ↓ [8] 스킬 종료 ├─ EndAbility: Ability 정리 │ ├─ ActiveGameplayEffectHandle 제거 │ └─ 카메라 모드 해제 └─ 다음 입력 대기 ``` ### 7.2 캔슬 판정 상세 흐름 스킬 실행 중 새로운 입력이 들어왔을 때의 판정 과정: ``` 새 스킬 입력 (예: Sword Strike → Counter) ↓ [1] 현재 실행 중인 Ability 확인 ├─ CurrentAbility: GA_Skill_Hilda_SwordStrike ├─ CurrentAbility.ActivationOrderGroup: 2 └─ CurrentTime: 0.5초 (몽타주 재생 중) ↓ [2] 새 Ability 정보 확인 ├─ NewAbility: GA_Skill_Knight_Counter └─ NewAbility.ActivationOrderGroup: 3 ↓ [3] 캔슬 가능 여부 판정 (OR 조건) ├─ 조건 A: Activation Order Group 비교 │ └─ 3 > 2 → TRUE (높은 우선순위) ├─ 조건 B: ANS_SkillCancel_C 윈도우 체크 │ ├─ CurrentMontage: AM_PC_Hilda_B_Skill_SwordStrike │ ├─ SkillCancel 구간: 1.3~1.8초 │ └─ CurrentTime 0.5초 < 1.3초 → FALSE (윈도우 밖) └─ 최종 결과: TRUE (조건 A 만족) ↓ [4] 캔슬 실행 ├─ CurrentAbility.EndAbility(bWasCancelled=true) ├─ NewAbility.ActivateAbility() └─ Counter 몽타주 재생 시작 ``` ### 7.3 C++ 코드와 어셋의 연동 지점 | C++ 클래스 | 어셋 | 연동 방법 | |------------|------|-----------| | `UWSGameplayAbility` | GA_* Blueprint | Blueprint이 C++ 클래스 상속 | | `ActivationOrderGroup` (C++ 변수) | GA_* Class Defaults | Blueprint 에디터에서 값 설정 | | `FSkillDataRow` (C++ 구조체) | DT_Skill DataTable | DataTable의 RowStructure로 사용 | | `ApplyAttackEffectToTarget` (C++ 함수) | AnimNotifyState_AttackWithEquip | 노티파이에서 함수 호출 | | `UGameplayEffect` | GE_* Blueprint | GameplayEffect 서브클래스 | **핵심 연동 함수**: ```cpp // WSGameplayAbility.cpp FSkillDataRow* UWSGameplayAbility::FindSkillDataRow(FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActorInfo* ActorInfo) const { // DT_Skill에서 현재 스킬의 행 데이터 조회 // abilityClass 경로로 역참조 } void UWSGameplayAbility::ApplyAttackEffectToTarget(FGameplayAbilityTargetDataHandle TargetData, FGameplayTag InAttackTag, bool& bAttackFailMotion) { // AnimMontage 노티파이에서 호출 // DT_Skill 데이터 + 장비 보정 + 룬 보정 통합 계산 // 최종 GameplayEffect 적용 } ``` --- ## 8. JSON 사용 가이드 ### 8.1 JSON 파일 구조 이해 익스포트된 JSON 파일들은 다음과 같은 구조를 가집니다: **DataTable.json**: ```json [ { "AssetName": "DT_CharacterStat", "AssetPath": "/Game/Blueprints/DataTable/DT_CharacterStat.DT_CharacterStat", "RowStructure": "CharacterStatData", "Rows": [ { "RowName": "hilda", "Data": { ... } } ] } ] ``` **AnimMontage.json**: ```json [ { "AssetName": "AM_PC_Hilda_B_Skill_SwordStrike", "AssetPath": "/Game/_Art/_Character/PC/Hilda/AnimMontage/Skill/AM_PC_Hilda_B_Skill_SwordStrike.AM_PC_Hilda_B_Skill_SwordStrike", "SequenceLength": 1.8, "Sections": [...], "AnimNotifies": [...] } ] ``` **Blueprint.json**: ```json [ { "AssetName": "GA_Skill_Hilda_SwordStrike", "AssetPath": "/Game/Blueprints/Characters/Hilda/GA_Skill_Hilda_SwordStrike.GA_Skill_Hilda_SwordStrike", "ParentClass": "GA_WSGameplayAbilityBase_C", "Variables": [...], "Functions": [...], "EventGraphs": [...] } ] ``` ### 8.2 데이터 추출 방법 #### Python을 사용한 JSON 파싱 예시 ```python import json # 1. DataTable에서 특정 스토커의 스킬 목록 조회 with open('DataTable.json', 'r', encoding='utf-8') as f: datatables = json.load(f) # DT_CharacterStat 찾기 dt_char_stat = next((dt for dt in datatables if dt['AssetName'] == 'DT_CharacterStat'), None) # Hilda의 스킬 목록 hilda_data = next((row for row in dt_char_stat['Rows'] if row['RowName'] == 'hilda'), None) default_skills = hilda_data['Data']['defaultSkills'] # ["SK100201", "SK100202", "SK100204"] # 2. DT_Skill에서 스킬 상세 정보 조회 dt_skill = next((dt for dt in datatables if dt['AssetName'] == 'DT_Skill'), None) for skill_id in default_skills: skill_data = next((row for row in dt_skill['Rows'] if row['RowName'] == skill_id), None) print(f"{skill_id}: {skill_data['Data']['skillName']}, Damage: {skill_data['Data']['skillDamageRate']}") ``` #### 특정 노티파이를 가진 AnimMontage 검색 ```python with open('AnimMontage.json', 'r', encoding='utf-8') as f: montages = json.load(f) # ANS_SkillCancel_C를 가진 모든 몽타주 찾기 cancel_montages = [] for montage in montages: for notify in montage.get('AnimNotifies', []): if notify.get('NotifyStateClass') == 'ANS_SkillCancel_C': cancel_montages.append({ 'montage': montage['AssetName'], 'start': notify['TriggerTime'], 'duration': notify['Duration'] }) for item in cancel_montages: print(f"{item['montage']}: {item['start']}~{item['start']+item['duration']}초") ``` ### 8.3 크로스 레퍼런스 (어셋 간 연결 추적) 스킬 하나의 전체 정보를 추적하는 방법: ```python # 1. DT_CharacterStat에서 스토커 → 스킬 ID stalker_name = 'hilda' skill_id = 'SK100201' # DT_CharacterStat.hilda.defaultSkills[0] # 2. DT_Skill에서 스킬 ID → 몽타주 경로, Ability 경로 skill_row = find_skill_by_id(skill_id) montage_paths = skill_row['Data']['useMontages'] ability_path = skill_row['Data']['abilityClass'] # 3. AnimMontage.json에서 몽타주 경로 → 타이밍 데이터 montage_asset_name = montage_paths[1].split('.')[-1] # "AM_PC_Hilda_B_Skill_SwordStrike" montage_data = find_montage_by_name(montage_asset_name) # ANS_SkillCancel_C 추출 cancel_notify = next((n for n in montage_data['AnimNotifies'] if n.get('NotifyStateClass') == 'ANS_SkillCancel_C'), None) # 4. Blueprint.json에서 Ability 경로 → 로직 구조 ability_asset_name = ability_path.split('/')[-1].split('.')[0] # "GA_Skill_Hilda_SwordStrike" ability_data = find_blueprint_by_name(ability_asset_name) # 최종 통합 정보 skill_info = { 'id': skill_id, 'name': skill_row['Data']['skillName'], 'damage_rate': skill_row['Data']['skillDamageRate'], 'cooldown': skill_row['Data']['coolTime'], 'montage_duration': montage_data['SequenceLength'], 'cancel_window': f"{cancel_notify['TriggerTime']}~{cancel_notify['TriggerTime']+cancel_notify['Duration']}" if cancel_notify else "없음", 'ability_complexity': len(ability_data['EventGraphs'][0]['Nodes']) } ``` ### 8.4 주의사항 **1. 라인 번호 참조 금지**: - JSON 파일은 익스포트할 때마다 순서가 바뀔 수 있음 - 항상 AssetName, RowName으로 검색 **2. 경로 형식 이해**: ``` 언리얼 경로: /Game/Blueprints/Abilities/GA_Attack.GA_Attack_C ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 에셋 경로 ^^^^^^^^^^^ 클래스 이름 JSON에서 찾을 때: "GA_Attack" (확장자 제외) ``` **3. Blueprint Class Defaults 값 (ActivationOrderGroup) 포함**: - EventGraph 노드 구조: JSON 포함 - Class Defaults 값 (ActivationOrderGroup): JSON에 **포함됨** (Variables 배열 내) - 추출 방법: Blueprint의 Variables 배열에서 "Name": "ActivationOrderGroup" 검색하여 DefaultValue 확인 **4. 몽타주 경로 추출**: ```python # DT_Skill.useMontages 배열 useMontages = [ "/Script/Engine.AnimMontage'/Game/_Art/_Character/PC/Hilda/AnimMontage/Skill/AM_PC_Hilda_B_Skill_Ready.AM_PC_Hilda_B_Skill_Ready'", "/Script/Engine.AnimMontage'/Game/_Art/_Character/PC/Hilda/AnimMontage/Skill/AM_PC_Hilda_B_Skill_SwordStrike.AM_PC_Hilda_B_Skill_SwordStrike'" ] # 몽타주 이름 추출 montage_names = [path.split('/')[-1].split('.')[0] for path in useMontages] # ["AM_PC_Hilda_B_Skill_Ready", "AM_PC_Hilda_B_Skill_SwordStrike"] ``` ### 8.5 유용한 분석 패턴 **패턴 1: 모든 스킬의 평균 쿨타임 계산** ```python skills = [row for dt in datatables if dt['AssetName'] == 'DT_Skill' for row in dt['Rows']] avg_cooldown = sum(skill['Data']['coolTime'] for skill in skills) / len(skills) ``` **패턴 2: 특정 스토커의 총 EventGraph 노드 수 (복잡도)** ```python stalker_abilities = [bp for bp in blueprints if 'Hilda' in bp['AssetName']] total_nodes = sum(len(eg['Nodes']) for bp in stalker_abilities for eg in bp['EventGraphs']) ``` **패턴 3: ANS_SkillCancel_C 보유율** ```python total_montages = len(montages) cancel_montages = len([m for m in montages if any(n.get('NotifyStateClass') == 'ANS_SkillCancel_C' for n in m.get('AnimNotifies', []))]) cancel_rate = (cancel_montages / total_montages) * 100 ``` --- ## 문서 종료 본 문서는 언리얼 엔진 에셋(DataTable, AnimMontage, Blueprint)을 JSON으로 익스포트하여 분석한 WorldStalker 전투 로직 시스템의 기술 문서입니다. **작성 정보**: - **작성일**: 2025-10-23 - **익스포트 데이터**: `DS-전투밸런스_분석자료/20251023_114317/` - **분석 대상**: 10명의 스토커 (Hilda, Urud, Nave, Baran, Rio, Clad, Rene, Sinobu, Lian, Cazimord) **추가 참고 문서**: - `DS-기획_메뉴얼-전투_로직.md`: 전투 로직 기획 문서 - `DS-Asset_Export_to_JSON.md`: Asset Export 시스템 가이드 **문의**: jinilkim@oneunivrs.com