내일배움캠프-언리얼

TIL - 80

이시보 2026. 5. 27. 21:27

버스트 스킬 시스템 구현 계획

책임 분리

GA_Burst (얇음)
  └─ 매칭 / 슬롯 소비 / 몽타주 재생 / 컴포넌트에 신호

PlayerCombatComponent (적중 감지 허브)
  └─ 노티파이 수신 / AttackType 분기 / 트레이스·오버랩 / GE 인플릭트

월드/투사체 액터 BP (자기 콜리전 책임)
  └─ 자기 overlap 감지 → PlayerCombatComponent에 적중 보고
책임 담당
ElementPattern 매칭 GA_Burst::FindMatchingCombination
게이지 슬롯 소비 GA_Burst → ElementGaugeComponent
몽타주 재생 GA_Burst::PlayMontageAndWait
AttackType 분기 PlayerCombatComponent::OnBurstHit
Cleave/Dash/AoE 트레이스 PlayerCombatComponent
Projectile 적중 BP_Projectile_* 액터 자체 콜리전
GroundEruption 적중 BP_* 월드 액터 자체 콜리전
데미지/상태 GE 인플릭트 PlayerCombatComponent::ApplyHitToTarget
원소 반응 별도 시스템 (Step 7)

Step 1 — FSkillCombination 확장

목표: 명세 10개 스킬을 데이터 한 행으로 표현 가능하게 만든다.

산출물:

  • Source/Retrieve/Data/RetrieveDataTableTypes.h
    • EBurstAttackType enum
    • EBurstHitSource enum
    • FBurstHitInstance struct
    • FSkillCombination 재구성

작업:

  1. 기존 7개 필드 중 PrimaryEffect, SecondaryEffect, DamageMultiplier, EffectDuration 제거
  2. enum 2종 + FBurstHitInstance 신규 추가
  3. FSkillCombination에 메타/공격형태/HitSequence/연출 섹션으로 재정렬
  4. forward declaration 추가 (UNiagaraSystem, USoundBase)

완료 조건:

  • 컴파일 성공
  • 에디터에서 DT_SkillCombination 행 추가 시 모든 필드 노출
  • EditCondition이 AttackType에 따라 부속 필드 가시성 제어

검증: FFF 한 행을 입력해서 ElementPattern={Fire:3}으로 매칭되는지 기존 FindMatchingCombination 호출


Step 2 — AnimNotify_BurstHit 신설

목표: 몽타주 노티파이가 HitSequence[HitIndex]를 발동시키는 컨벤션 확립.

산출물:

  • Source/Retrieve/Animation/AnimNotify_BurstHit.h/.cpp

작업:

UCLASS(DisplayName = "Burst Hit")
class UAnimNotify_BurstHit : public UAnimNotify
{
public:
    UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Burst")
    int32 HitIndex = 0;

    virtual void Notify(USkeletalMeshComponent* MeshComp, ...) override;
};

Notify 안에서:

  1. Owner Actor 검증
  2. PlayerCombatComponent 가져오기
  3. Combat->OnBurstHit(HitIndex) 호출

완료 조건:

  • 컴파일 성공
  • 임의 몽타주에 노티파이 박고 UE_LOG로 발동 확인

검증: 몽타주 트랙에 BurstHit(0), BurstHit(1) 두 개 박고 로그 두 번 찍히는지


Step 3 — PlayerCombatComponent 뼈대

목표: 적중 감지 허브의 외형만 먼저 만들어둔다 (분기 로직은 Step 5).

산출물:

  • Source/Retrieve/Components/PlayerCombatComponent.h/.cpp

작업:

class UPlayerCombatComponent : public UActorComponent
{
    // 현재 진행 중인 스킬 컨텍스트
    UPROPERTY()
    const FSkillCombination* ActiveSkill = nullptr;

    UFUNCTION(BlueprintCallable)
    void BeginBurstSkill(const FSkillCombination* Row);

    UFUNCTION(BlueprintCallable)
    void EndBurstSkill();

    UFUNCTION(BlueprintCallable)
    void OnBurstHit(int32 HitIndex);

private:
    // AttackType별 분기 (Step 5에서 채움)
    void DoCleaveHit(const FBurstHitInstance& Hit);
    void DoProjectileHit(const FBurstHitInstance& Hit);
    void DoGroundEruptionHit(const FBurstHitInstance& Hit);
    void DoDashHit(const FBurstHitInstance& Hit);
    void DoAoEHit(const FBurstHitInstance& Hit);

    // 공통 인플릭트
    void ApplyHitToTarget(AActor* Target, const FBurstHitInstance& Hit);

    // GroundEruption 전용
    TWeakObjectPtr<AActor> SpawnedWorldActor;
};

완료 조건:

  • 컴파일 성공
  • 플레이어 캐릭터 BP에 컴포넌트 추가 가능
  • OnBurstHit 호출 시 UE_LOG로 HitIndex/AttackType 출력

검증: Step 2의 노티파이가 컴포넌트에 도달하는지 로그 확인


Step 4 — GA_Burst 리팩토링

목표: GA는 매칭/슬롯/몽타주/컴포넌트 위임까지만 책임.

작업:

  • GA_Burst.cpp::ActivateAbility 안의 주석 (미구현) 부분을 다음으로 교체:
    1. Avatar->FindComponentByClass<UPlayerCombatComponent>() 검증
    2. Combat->BeginBurstSkill(MatchedRow) 호출
    3. PlayMontageAndWait 어빌리티 태스크로 MatchedRow->AttackMontage 재생
    4. OnBlendOut/OnCompleted/OnInterrupted 콜백에 Combat->EndBurstSkill + EndAbility 연결

완료 조건:

  • 게이지 3스택 → 버스트 발동 → 몽타주 재생 → 정상 종료

검증: FFF 게이지 채우고 버스트 키 입력 → 몽타주 끝까지 재생 + 로그 정상


Step 5 — AttackType별 분기 구현

각 sub-step은 하나의 PR 단위. FFF 부터 시작해서 가장 단순한 케이스로 검증한 뒤 복잡한 것으로 진행.

Step 5-1. Cleave (FFF, FFW)

작업:

  1. PlayerCombatComponentActiveHitboxComp : USphereComponent 멤버 추가 (적과 대칭)
  2. DoCleaveHit 구현:
    • HitSource에 따라 attach 본 결정 (Sword → Weapon_R 소켓 / Shield → 방패 소켓 / Body → root)
    • SphereOverlap으로 적 검출
    • 적마다 ApplyHitToTarget 호출
  3. ApplyHitToTarget 구현:
    • DamageEffect에 SetByCaller로 Data.Damage = AttackPower * Hit.DamageMultiplier 전달
    • Hit.StatusEffects 순회하며 ApplyGameplayEffectToTarget

검증: FFF로 적 한 마리 죽이기 / FFW로 1타 화상 + 2타 추위 부여 확인

Step 5-2. AreaOfEffect (FWA)

작업:

  1. DoAoEHit 구현:
    • 캐릭터 위치 기준 AoeRadius 구체 오버랩
    • 적마다 ApplyHitToTarget 호출 (StatusEffects = [GE_Vulnerable])

검증: FWA로 주변 적 다수에게 취약 부여

Step 5-3. Dash (AAF, AAW)

작업:

  1. BeginBurstSkill에서 AttackType=Dash면 캐릭터 전방 DashDistance 만큼 LaunchCharacter
  2. 돌진 중 캡슐 overlap 리스닝 (OnComponentBeginOverlap 바인딩)
  3. 처음 닿은 적에게 ApplyHitToTarget (중복 방지)

검증: AAF로 전방 돌진 + 적 적중 시 화상 / AAW 동일하게 추위

Step 5-4. Projectile (AAA, FFA, WWA)

작업:

  1. 액터 BP 3종 생성:
    • BP_Projectile_SwordSlash (AAA용)
    • BP_Projectile_FireSlash (FFA용)
    • BP_Projectile_WaterSlash (WWA용)
  2. 액터 공통 인터페이스:
    void Initialize(const FBurstHitInstance& HitData, AActor* InInstigator);
  3. 액터의 OnComponentBeginOverlap:
    • InInstigatorPlayerCombatComponent->ApplyHitToTarget(OverlappedActor, HitData)
  4. DoProjectileHit 구현:
    • MatchedRow->ProjectileClass 액터 스폰
    • Initialize 호출하며 HitData 전달
    • 캐릭터 전방으로 발사

검증: AAA로 검기 3발 발사 + 적 적중 시 데미지 / FFA로 화상 적용 / WWA로 추위 적용

Step 5-5. GroundEruption (WWW, WWF)

작업:

  1. 액터 BP 2종 생성:
    • BP_WaterPillar (WWW용, 일정 시간 지속)
    • BP_HotWater (WWF용)
  2. 액터는 자기 콜리전 + Niagara + 자체 LifeTime (self-destroy) 보유
  3. DoGroundEruptionHit 구현:
    • HitIndex=0일 때: WorldSpawnActorClass를 캐릭터 전방 WorldSpawnDistance에 스폰 → SpawnedWorldActor 저장
    • HitIndex>0일 때: SpawnedWorldActor의 overlap 영역 내 적에게 ApplyHitToTarget
  4. EndBurstSkill에서 SpawnedWorldActor 참조 해제 (액터는 자체 LifeTime으로 사라짐)

검증:

  • WWW 발동 → 물기둥 1개 솟음 → 안에 있는 적이 5회 데미지 + 추위 지속시간 갱신
  • WWF 발동 → 열탕 솟음 → 1회 데미지 + 추위→화상 순차 부여 → 반응 트리거(Step 7 이후)

Step 6 — 상태 GE 작성

목표: 화상/추위/취약 GE를 통일된 정책으로 만든다.

산출물:

  • GE_StatusBase (BP, 부모)
  • GE_Burn (BP)
  • GE_Cold (BP)
  • GE_Vulnerable (BP)
  • GE_BurstDamage (BP, SetByCaller Data.Damage)

GE_StatusBase 정책 (모든 상태 GE 공통):

Duration Policy                   : Has Duration (5.0s)
Stacking Type                     : Aggregate by Target
Stacking Limit Count              : 1
Stacking Duration Refresh Policy  : Refresh on Successful Application
Stacking Period Reset Policy      : Reset on Successful Application

각 GE 별 효과:

  • GE_Burn: 0.5초 주기 고정 피해 (Modifier on Period)
  • GE_Cold: MoveSpeedMultiplier × 0.8 (Infinite-style Modifier)
  • GE_Vulnerable: IncomingDamageMultiplier × 1.15

완료 조건:

  • 적 캐릭터에 GE 적용 시 5초 동안 효과 발동
  • 재적용 시 스택 없이 지속시간만 갱신

검증: 디버그 명령으로 적에게 GE_Burn 두 번 적용 → 스택 1 유지 + 두 번째 적용 시점부터 5초 다시 카운트


Step 7 — 원소 반응 시스템

목표: 화상↔추위 두 반응을 데이터 + 코드로 구현.

산출물:

  • Source/Retrieve/Data/RetrieveDataTableTypes.hFElementReactionRow 추가
  • DT_ElementReaction 데이터 테이블
  • UElementReactionComponent 또는 ASC 이벤트 리스너 로직

반응 정의:
| 기존 상태 | 들어온 상태 | 결과 |
|---|---|---|
| Burn | Cold | 추가 데미지 발동 + Burn 제거 |
| Cold | Burn | 이동속도 -40% 5초 + Cold 제거 |

구현 패턴:

  1. ASC의 OnGameplayEffectAppliedToTarget 또는 OnActiveGameplayEffectAdded 리스너
  2. 적용된 GE의 태그가 Status.X일 때, 대상이 이미 Status.Y를 가지고 있으면 매칭되는 반응 행 찾기
  3. 매칭되면 ReactionEffect 적용 + StatusesToRemove 제거

완료 조건:

  • WWF로 추위→화상 부여 시 자동으로 이동속도 추가 감속
  • FFW의 1타 화상→2타 추위 부여 시 추가 데미지

작업 외 약속 (팀룰 / 컨벤션)

데이터 영역 외에서 잡혀야 할 약속들:

  1. Status GE 정책GE_StatusBase 상속 강제
  2. World 액터 BP 규약 — 콜리전 + VFX + 자체 LifeTime (self-destroy)
  3. 노티파이 컨벤션AnimNotify_BurstHit(HitIndex)HitSequence[i] 발동
  4. Projectile 인터페이스Initialize(FBurstHitInstance, AActor* Instigator) 패턴 통일
  5. DamageEffect SetByCaller 키Data.Damage 태그로 통일
  6. 무기 데이터 PartName 컨벤션 — 검은 "Sword", 방패는 "Shield"
  7. 반응 시스템 트리거 위치 — Status GE가 적용되는 ASC 이벤트 리스너 (Step 7)

진행 순서 요약 (PR 단위)

Step 작업 의존성 예상 PR 수
1 FSkillCombination 확장 - 1
2 AnimNotify_BurstHit 1 1
3 PlayerCombatComponent 뼈대 1 1
4 GA_Burst 리팩토링 1, 3 1
5-1 Cleave 구현 (FFF, FFW) 2, 3, 4, 6 1
5-2 AoE 구현 (FWA) 5-1 1
5-3 Dash 구현 (AAF, AAW) 5-1 1
5-4 Projectile 구현 (AAA, FFA, WWA) 5-1 1
5-5 GroundEruption 구현 (WWW, WWF) 5-1 1
6 상태 GE 작성 - (Step 5와 병행 가능) 1
7 반응 시스템 5, 6 완료 후  

 

'내일배움캠프-언리얼' 카테고리의 다른 글

TIL - 82  (0) 2026.06.01
TIL - 81  (0) 2026.05.29
TIL - 79  (0) 2026.05.26
TIL - 78  (0) 2026.05.21
TIL - 77  (0) 2026.05.20