[UE5] Directional Blocking By Shield

seunghyun·2024년 6월 4일
0

ProjectGO

목록 보기
7/7

요구사항

탱커 캐릭터가 방패로 방어하는 상황을 보다 현실적으로 구현하려 한다.

  • 기본적인 접근 방식은 캐릭터의 전방 벡터를 이용하여 방패가 활성화된 상태에서 들어오는 공격을 막는 것이다.
    그래서 기본적으로 필요한 요소는 아래와 같이 정리했다.

    • 방패가 활성화되었는지를 나타내는 변수 bool bShieldActive;
  • 방패의 방향을 나타내기 위한 변수 FVector ShieldDirection;

  • 방패의 콜리전 컴포넌트 UBoxComponent* ShieldCollision;

  • 공격이 방패에 막히는지 확인하기 위해 공격의 방향방패의 방향을 비교해야 한다.

  • 특정 각도에 따른 방어 성공률을 다르게 하고 싶다.

    • 두 벡터 (방패 방향과 공격 방향) 간의 간의 내적을 계산한다.

      • FVector::DotProduct 함수 사용
    • 내적을 통해 두 벡터 간의 각도를 계산하고, 이를 도(degree)로 변환한다. RadiansToDegrees


구현

Rogers 캐릭터의 방패 방어 메커니즘은 전방에서 들어오는 공격을 특정 각도 내에서 방어할 수 있도록 설계했고, 이 각도는 MaxBlockAngle 변수로 정의했다.

방어 각도 계산

bool AGORogersCharacter::IsShieldBlocking(const FVector& AttackDirection) const
{
    if (!bShieldActive)
    {
        return false;
    }

    float DotProduct = FVector::DotProduct(ShieldDirection, AttackDirection.GetSafeNormal());
    float AngleDegrees = FMath::RadiansToDegrees(FMath::Acos(DotProduct));

    return AngleDegrees <= MaxBlockAngle;
}

방패가 활성화되면, ActivateShield 함수는 캐릭터의 전방 벡터(GetActorForwardVector())를 ShieldDirection 변수에 저장한다.

공격이 들어오면, OnSphereOverlap 함수는 공격 방향을 계산하는데, 이 때 공격 방향은 공격자의 위치에서 캐릭터의 위치를 뺀 후, 단위 벡터로 정규화하여 얻었다.

void AGORogersCharacter::OnSphereOverlap(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult)
{
    if (bShieldActive)
    {
        UE_LOG(LogTemp, Log, TEXT("[Shield] bShieldActive : %d"), bShieldActive);

        FVector AttackDirection = (OtherActor->GetActorLocation() - GetActorLocation()).GetSafeNormal();
        if (IsShieldBlocking(AttackDirection))
        {
            // Block the attack and reduce damage to 0
            UE_LOG(LogTemp, Log, TEXT("Attack blocked by shield!"));

            // Create a damage event
            FDamageEvent DamageEvent;
            // Call TakeDamage with 0 damage
            TakeDamage(0.0f, DamageEvent, OtherActor->GetInstigatorController(), OtherActor);
        }
    }
}

각도 계산

  • 벡터 내적 : 두 벡터의 내적(dot product)으로 두 벡터 사이의 각도를 계산했다. (내적 값은 두 벡터가 얼마나 평행한지를 나타내고, 값이 1에 가까울수록 두 벡터가 같은 방향을 가리킨다)
  • 각도 변환: 내적 값을 사용하여 FMath::Acos 함수를 통해 두 벡터 사이의 각도를 라디안 단위로 계산하고 이 값을 FMath::RadiansToDegrees 함수를 통해 도(degree) 단위로 변환한다.

방어 판단은, 계산된 각도가 MaxBlockAngle보다 작거나 같으면, 방어가 가능한 걸로 판단한다!

방어 각도

  • 지금 코드에서는, MaxBlockAngle이 45도라면, 캐릭터의 전방을 기준으로 좌우 45도 각도 내에서 들어오는 모든 공격을 방패로 막을 수 있다. 즉, 캐릭터의 전방 90도 범위 내에서 들어오는 공격을 방어할 수 있다.
  • 방패가 활성화된 동안, 캐릭터의 전방 방향과 공격 방향 사이의 각도가 45도 이하인 경우, 방패는 공격을 막아 데미지를 0으로 설정합니다.
float AGORogersCharacter::TakeDamage(float DamageAmount, struct FDamageEvent const& DamageEvent, class AController* EventInstigator, AActor* DamageCauser)
{
    if (bShieldActive && IsShieldBlocking((DamageCauser->GetActorLocation() - GetActorLocation()).GetSafeNormal()))
    {
        UE_LOG(LogTemp, Log, TEXT("Attack blocked! Damage reduced to 0."));
        return 0.0f;
    }

    return Super::TakeDamage(DamageAmount, DamageEvent, EventInstigator, DamageCauser);
}

결과 영상

https://youtube.com/shorts/9M-FSliG7UM?feature=share


🔗 Reference

Directional Shield Blocking

Block Attacks Ability - Unreal Engine Action RPG #32

Blocking (Pt. 2: Backing Away) | How To Make YOUR OWN Fighting Game! | UE4 and C++ Tutorial, Part 25

Advanced Blocking Mechanics! | How To Make YOUR OWN Fighting Game | UE4/UE5 & C++ Tutorial, Part 143

0개의 댓글

관련 채용 정보