내일배움캠프_Unreal/TIL

내일배움캠프_Unreal/TIL - Day 148 (260702)

하임 2026. 7. 2. 23:12

스태프 확장 - 부채꼴 판정 · 이벤트 착지 슬램 · 돌진 관통 · 원소 손 VFX


1. 이벤트 기반 착지 슬램 - Tick 폴링이 아니라 LandedDelegate

공중 버스트(WindBurst)가 바닥에 닿는 순간을 매 프레임 검사(Tick)로 잡지 않고, ACharacter::LandedDelegate에 바인딩해 콜백으로 받음.

// ActivateAbility — 착지 슬램 버스트만 바인딩
if (bLandingImpactEnabled)
{
    AlsChar->SetSuppressLandingRoll(true);                 // §2
    LandChar->LandedDelegate.AddDynamic(this, &ThisClass::HandleLanded);
    BoundLandedCharacter = LandChar;
}

// HandleLanded — 착지 순간 3가지
MoveComp->StopMovementImmediately();                       // (1) 잔여 다이브 속도 제거 → 그 자리 정지
ASC->CurrentMontageJumpToSection(CachedLandingSection);   // (2) 착지 섹션 점프
CachedBurstComp->ApplyLandingImpact(Center, Radius, ...); // (3) AoE 데미지 + 방사 넉백(보스 제외)
  • (1)의 이유: 다이브 낙하 속도가 남으면 착지 후 앞으로 미끄러짐 → StopMovementImmediately()로 벽 앞이든 어디든 딱 그 자리에 멈춤.
  • 1회성 플래그 누수 방지 🐛: bLandingHandled로 중복 방지 + CleanupBurst()에서 착지 억제 플래그가 소비 안 된 채 종료되면 강제 해제 + UnbindLanded()로 델리게이트 구독 해제 → 다음 착지로 새지 않게. 이벤트 바인딩은 반드시 대칭 해제.

2. ALS 착지 연출 억제 - Roll만으론 부족, Ragdoll도

"쿵 하고 그냥 착지" 연출을 위해 ALS 착지 자동 연출을 이 착지 1회만 끔.

❌ Before — Roll만 억제 → 착지 래그돌이 남아 캐릭터가 픽 쓰러짐

Settings->Rolling.bStartRollingOnLand = false;   // Roll만

✅ After — Roll + Ragdoll 둘 다 억제 후 원복

const bool bPrevRoll    = Settings->Rolling.bStartRollingOnLand;
const bool bPrevRagdoll = Settings->Ragdolling.bStartRagdollingOnLand;
Settings->Rolling.bStartRollingOnLand       = false;
Settings->Ragdolling.bStartRagdollingOnLand = false;
Super::NotifyLocomotionModeChanged(PreviousLocomotionMode);
Settings->Rolling.bStartRollingOnLand       = bPrevRoll;   // 원복
Settings->Ragdolling.bStartRagdollingOnLand = bPrevRagdoll;
bSuppressLandingRoll = false; // 1회 소비

패턴: 공유 세팅(ALS는 세팅을 전역으로 공유)을 잠깐 바꿔야 하면 [저장 → 끄기 → Super 호출 → 원복] 스코프로 감싼다. 영구 변경하면 다른 상황의 착지까지 망가짐.


3. 부채꼴(Cone) 판정 - 구체 오버랩 + 내적 각도 컷

전용 콘 셰이프를 만들지 않고, 구체 오버랩으로 후보를 모은 뒤 내적으로 각도를 자름. 저렴하고 간단함.

const FVector Forward = Owner->GetActorForwardVector().GetSafeNormal2D();  // 수평 투영
const float CosHalf = FMath::Cos(FMath::DegreesToRadians(ConeHalfAngleDeg));
// ...구체 오버랩으로 Overlaps 수집 후:
const FVector ToTarget = (Target->GetActorLocation() - Origin).GetSafeNormal2D();
if (FVector::DotProduct(Forward, ToTarget) < CosHalf) continue;  // 반각 밖 → 스킵
  • 핵심: Dot(전방, 대상방향) ≥ cos(반각) 이면 부채꼴 안. acos 없이 코사인 비교로 처리.
  • GetSafeNormal2D = 높이차 무시하고 수평 부채꼴.
  • 두 곳에서 재사용: 버스트/강공 공용(EAttackExecutionType::ConeDoConeHit)과 콤보 스텝 광역(GA_Attack::ApplyStepDamage).

콤보 스텝 콘 - 블레이드 판정 + 콘 광역을 합산

콤보 스텝에 bUseCone를 켜면 무기 블레이드 트레이스 결과에 콘 오버랩 결과를 더함(마지막 타 광역 등). bWeaponTrace=false면 블레이드 없이 콘만(왼손 방출 스텝).

// UE5: FHitResult는 액터를 HitObjectHandle로 들고 있음 → GetActor()가 유효하려면 명시 세팅해야 함.
FHitResult ConeHit(ForceInit);
ConeHit.HitObjectHandle = FActorInstanceHandle(ConeTarget);   // ⚠️ 이거 없으면 후단에서 GetActor()==nullptr
ConeHit.ImpactPoint = ConeTarget->GetActorLocation();
ConeHit.bBlockingHit = true;
AllHits.Add(ConeHit);   // 블레이드 히트 + 콘 히트 한 배열로 합쳐 뒤에서 일괄 데미지
  • 중복 방지: PerHitHitActors[HitIndex] 집합으로 노티 매 프레임 호출에도 대상당 1회.

4. 돌진 중 적(Pawn) 관통 -콜리전을 끄지 말고 "채널 응답"만 토글

돌진(Dash) 공격이 적을 밀치며 통과하되 벽은 막혀야 함. 콜리전을 통째로 끄면(SetActorEnableCollision) 벽도 통과 → 대신 Pawn 채널 응답만 Ignore.

// RetrieveGameplayAbility (공통 베이스 헬퍼 — Burst/Heavy 공용)
void URetrieveGameplayAbility::SetAvatarPawnCollisionIgnored(bool bIgnore) const
{
    if (UCapsuleComponent* Capsule = Character->GetCapsuleComponent())
        // 벽(WorldStatic/Dynamic)은 그대로 Block, 적(Pawn)만 통과.
        Capsule->SetCollisionResponseToChannel(ECC_Pawn, bIgnore ? ECR_Ignore : ECR_Block);
}
// GA_Burst::ActivateAbility / GA_HeavyAttack::RunExecutorPath — 돌진 타입만 켜고
if (AttackType == EAttackExecutionType::Dash) SetAvatarPawnCollisionIgnored(true);
// CleanupBurst / EndAbility — 반드시 복구(비돌진이면 no-op)
SetAvatarPawnCollisionIgnored(false);

교훈: "특정 대상만 통과"는 콜리전 on/off가 아니라 채널별 Response 전환. 켜기/복구는 GA 진입·종료에서 대칭으로, 공용 로직은 베이스 클래스 헬퍼로 뺀다.


5. 메테오 대각선 낙하 · 지면 스냅 · 도착 임팩트 VFX

낙하 소환물(ABurstWorldActorBase)이 수직으로만 떨어지던 걸 확장.

기능 필드 / 처리
대각선 낙하 SpawnHorizontalOffset(FVector2D, 로컬 X전후/Y좌우)를 SpawnHeightOffset(Z)과 합쳐 액터 회전 기준 회전벡터로 시작점 산출
지면 스냅 bWorldSnapToGround → 전방 스폰 지점 아래로 라인트레이스 → 착지 Z를 지면에 맞춘 뒤 WorldSpawnHeightOffset만큼 위에서 스폰
판정 반경 WorldActorRadius 데이터 우선, 0이면 컴포넌트 기본값 폴백
도착 임팩트 지면 도착(모션 완료) 순간 ImpactVFX 1회 스폰(bImpactSpawned 중복 방지) — BP 배선 없이 데이터로
// 대각선: 로컬 오프셋을 액터 회전으로 돌려 월드 시작점
const FVector LocalStartOffset(SpawnHorizontalOffset.X, SpawnHorizontalOffset.Y, SpawnHeightOffset);
StartLocation = TargetLocation + GetActorRotation().RotateVector(LocalStartOffset);

// 지면 스냅: 시전자 높이가 아니라 착지 지점 지면 기준
if (ActiveSpec.bWorldSnapToGround && World->LineTraceSingleByChannel(GroundHit, Up, Down, ECC_Visibility, P))
    SpawnLocation.Z = GroundHit.ImpactPoint.Z;
SpawnLocation.Z += ActiveSpec.WorldSpawnHeightOffset;

6. 스태프 왼손 원소 VFX - 이벤트 구독 + diff 기반 재스폰 회피

현재 원소(Fire/Water/Wind)에 맞는 Niagara를 왼손 소켓에 상시 부착, 원소가 바뀔 때만 스왑. URetrieveCosmeticDataTMap<원소태그, Niagara> 를 두어 데이터 주도.

// 원소 변경 이벤트(GameplayEvent.Element.ModeChange) 구독 → RefreshCosmeticState → RefreshHandVFX
// 원하는 VFX: Staff 장착 + 현재 원소 매핑이 있을 때만
UNiagaraSystem* DesiredVFX = (Staff && Element.IsValid())
    ? CosmeticData->HandVFXByElement.Find(CurrentElementTag)->LoadSynchronous() : nullptr;

// ⭐ diff: 이미 같은 시스템이면 재스폰 안 함 (원소/무기 그대로면 유지 → 깜빡임·누수 방지)
if (DesiredVFX == (SpawnedHandVFX ? SpawnedHandVFX->GetAsset() : nullptr)) return;

// 교체: 기존 Destroy → 새 원소 VFX를 손 소켓에 bAutoDestroy=false로 부착
SpawnedHandVFX = UNiagaraFunctionLibrary::SpawnSystemAttached(DesiredVFX, Mesh, HandVFXSocket, ...);
  • 소켓 부재 방어: 소켓이 스켈레톤에 없으면 루트에 붙어 하반신을 덮음 → DoesSocketExist 체크 후 경고 로그.
  • 갱신 트리거: 무기 변경/원소 변경/장착 모두 RefreshCosmeticState()로 수렴 → 한 경로에서 스왑.

비교 정리

구분 ❌ Before ✅ After
착지 감지 Tick 폴링 경향 LandedDelegate 이벤트 1회(+대칭 해제)
착지 후 이동 잔여 속도로 미끄러짐 StopMovementImmediately()
착지 연출 억제 Roll만 Roll + Ragdoll (1회 후 원복)
부채꼴 판정 (없음) 구체 오버랩 + Dot ≥ cos(반각)
콤보 마지막 광역 블레이드만 블레이드 히트 + 콘 히트 합산
UE5 합성 히트 GetActor() null HitObjectHandle = FActorInstanceHandle(...) 명시
돌진 관통 콜리전 on/off (벽도 통과) Pawn 채널 응답Ignore, 벽은 Block
소환물 낙하 순수 수직 대각선 + 지면 스냅 + 도착 임팩트 VFX
손 원소 VFX (없음/BP) 이벤트 구독 + diff 재스폰 회피(데이터 맵)

핵심 요약

  • 착지 훅은 Tick 폴링 말고 LandedDelegate — 착지 순간 StopMovementImmediately로 미끄러짐 제거, 이벤트 바인딩·1회성 플래그는 정리 경로에서 반드시 대칭 해제/복구.
  • ALS "그냥 쿵" 착지는 Roll + Ragdoll 둘 다 억제, 공유 세팅은 [저장→끄기→Super→원복] 스코프.
  • 부채꼴은 전용 셰이프 없이 구체 오버랩 + Dot ≥ cos(반각) 로 충분. 여러 판정 소스를 한 배열로 합칠 땐 UE5 FHitResult.HitObjectHandle을 명시해야 GetActor()가 산다.
  • "특정 대상만 통과"는 콜리전 on/off가 아니라 채널별 Response 전환(ECC_PawnIgnore), 벽은 Block 유지. 켜기/복구는 대칭 + 공용 베이스 헬퍼.
    • 부착형 VFX는 원하는 자산 == 현재 자산이면 재스폰 스킵(diff)으로 깜빡임·누수를 막고, 소켓 부재는 경고로 방어한다.