내일배움캠프_Unreal/TIL

내일배움캠프_Unreal/TIL - Day 133 (260611)

하임 2026. 6. 11. 21:23

 

어빌리티 상호배제·타이밍 함정·데미지 축 설계


1. BlockAbilitiesWithTag ≠ 취소 (Block vs Cancel)


GAS 어빌리티가 발동될 때 다른 어빌리티에 주는 영향은 두 컨테이너로 나뉜다.
 
컨테이너  동작 
`BlockAbilitiesWithTag` 해당 태그 어빌리티의 신규 발동만 차단 (이미 활성인 건 그대로 둠)
`CancelAbilitiesWithTag` 해당 태그 어빌리티를 즉시 취소

❌ 카운터 발동 시 가드를 끝내려 `BlockAbilitiesWithTag(Guard)`만 넣음 → 가드는 계속 활성, GuardMontage가 카운터 몽타주와 충돌
`CancelAbilitiesWithTag(Guard)` 추가 → 가드가 실제로 취소되며 `bStopWhenAbilityEnds` 몽타주가 정리됨

> "발동 시 진행 중인 걸 끝내고 싶다" = Cancel. Block은 재발동 방지.


2. 타이밍 함정 — EndAbility 복구 vs 엔진 판정 순서


찍기 착지 시 ALS 낙법을 끄려고 *발동 시 끔 / EndAbility에서 복구* 했더니 안 먹힘.

착지 시 실행 순서:
`ACharacter::Landed()``LandedDelegate` → 내 `EndAbility`(복구=true) → *그 다음* ALS `NotifyLocomotionModeChanged`(낙법 판정)
→ 판정 시점엔 이미 true로 복구돼 낙법이 발동.

❌ 어빌리티 수명(EndAbility)에 의존해 복구
판정 함수 자체를 오버라이드해서, 판정 바로 그 순간에만 억제 + 1회성 플래그 소비

void ARetrieveAlsCharacter::NotifyLocomotionModeChanged(FGameplayTag Prev)
{
    const bool bLanding = GetLocomotionMode() == AlsLocomotionModeTags::Grounded
                       && Prev == AlsLocomotionModeTags::InAir;
    if (bLanding && bSuppressLandingRoll && Settings)
    {
        const bool bPrev = Settings->Rolling.bStartRollingOnLand;
        Settings->Rolling.bStartRollingOnLand = false;
        Super::NotifyLocomotionModeChanged(Prev);   // 낙법 판정이 false로 지나감
        Settings->Rolling.bStartRollingOnLand = bPrev;
        bSuppressLandingRoll = false;               // 1회 소비
        return;
    }
    Super::NotifyLocomotionModeChanged(Prev);
}
 

> 상태를 "잠깐 끄고 되돌리는" 로직은, 그 상태가 읽히는 정확한 지점에서 감싸야 함. 다른 콜백의 실행 순서를 가정하면 깨짐.

3. 데미지 3축 분리 + 약점 취약을 코드 없이 데이터로


전투 데미지는 3개 축이 직교함.
 
  소스  역할 
데미지 수치 `IncomingDamage` 메타 어트리뷰트 실제 체력 감산량
방어 분기 `Attack.Type.*` 태그  Parry/Guard 분기만 (무효·감소·관통)
피격 반응  `HitReact.Type.*` 태그  Flinch/Stagger/Knockdown 모션만

데미지 공식: `FinalDamage = AttackPower × Mul × IncomingDamageMultiplier`
여기서 `IncomingDamageMultiplier`피격 대상의 어트리뷰트를 ExecCalc가 실시간 캡처함.

→ "약점 취약(데미지 증가)"을 경직 GE가 대상의 `IncomingDamageMultiplier`를 올리도록 모디파이어만 추가해 해결. 경직 중 들어가는 모든 공격이 증폭됨. C++ 수정 0.

> 데미지 증가를 "공격자 버프"가 아니라 "피격 대상의 취약"으로 풀면, 기존 공식 그대로 데이터만으로 약점 시스템이 된다.


4. 형제(sibling) 태그로 인한 cancel/block 누락 → stuck active


변형 공격(Sprint/Jump/ParryCounter)이 각자 `Ability.Player.SprintAttack` 같은 형제 태그를 가지면, 기존에 `Ability.Player.Attack`만 기준으로 짠 cancel/block 로직에서 누락됨.

증상: 진행 중 변형 공격이 대시/피격으로 안 끊기면 종료되지 못하고 active로 남아 → 재발동 불가(stuck active)

- 임시: 관련 cancel 목록에 변형 태그를 명시적으로 추가
- 근본: 공통 부모 태그 또는 단일 공격 실행 파이프라인으로 통합

추가로, 진행 중 공격이 연타에 끊기는 문제는 공격 어빌리티가 `State.Player.Attacking`을 ActivationBlockedTags로 보유하게 해 "공격 중엔 새 공격 발동 불가"로 막음. (콤보 연결은 어빌리티 내부 입력 태스크가 처리)

5. 무한 패리 무적 → 행동이 아닌 "보상 창"에 쿨다운


가드 키 연타로 짧은 Parry 창이 반복 생성돼 사실상 무적이 되던 버그.

❌ 가드 자체를 쿨다운으로 막음 → 입력 씹힘, 컨트롤 답답함
가드는 항상 허용, Parry 창만 쿨다운 게이트

// GA_Guard::ActivateAbility
if (ParryWindowEffect && !ASC->HasMatchingGameplayTag(Cooldown_Player_Parry))
{
    ApplyGameplayEffectToOwner(... ParryWindowEffect ...);   // State.Player.Parrying (0.3s)
    ApplyGameplayEffectToOwner(... ParryCooldownEffect ...); // Cooldown.Player.Parry (0.5~0.8s)
}
 

> 연타 악용은 "행동"이 아니라 "그 행동의 보상 창"에 쿨다운을 걸어야 조작 감각을 안 망침. (소울류 패링 패턴)

비교 정리

 
상황  잘못된 접근 올바른 접근
진행 중 어빌리티 종료 `BlockAbilitiesWithTag` `CancelAbilitiesWithTag`
엔진 판정 직전 상태 토글 EndAbility에서 복구 판정 함수 내부에서 감싸기
약점 데미지 증가 공격자 측 보너스 계산 대상 `IncomingDamageMultiplier` ↑ (데이터)
무적 연타 차단 행동 자체 쿨다운 보상 창에만 쿨다운

핵심 요약

> - GAS `BlockAbilitiesWithTag`재발동 차단만, 진행 중 종료는 `CancelAbilitiesWithTag`.
> - "잠깐 끄고 복구"는 그 상태가 읽히는 함수 내부에서 감싸라 — 콜백 실행 순서 가정은 깨짐.
> - 데미지는 수치 / 방어분기(Attack.Type) / 피격모션(HitReact.Type) 3축이 직교. 약점 취약은 대상 IncomingDamageMultiplier로 데이터만에 해결.
> - 형제 태그 어빌리티는 cancel/block 누락으로 stuck active 되기 쉬움.
> - 연타 악용은 행동이 아니라 보상 창에 쿨다운.