어빌리티 상호배제·타이밍 함정·데미지 축 설계
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 되기 쉬움.
> - 연타 악용은 행동이 아니라 보상 창에 쿨다운.
'내일배움캠프_Unreal > TIL' 카테고리의 다른 글
| 내일배움캠프_Unreal/TIL - Day 137 (260617) (0) | 2026.06.17 |
|---|---|
| 내일배움캠프_Unreal/TIL - Day 135 (260615) (0) | 2026.06.15 |
| 내일배움캠프_Unreal/TIL - Day 130 (260608) (0) | 2026.06.08 |
| 내일배움캠프_Unreal/TIL - Day 128 (260604) (0) | 2026.06.04 |
| 내일배움캠프_Unreal/TIL - Day 127 (260602) (0) | 2026.06.02 |