탱커 캐릭터가 방패로 방어하는 상황을 보다 현실적으로 구현하려 한다.
기본적인 접근 방식은 캐릭터의 전방 벡터를 이용하여 방패가 활성화된 상태에서 들어오는 공격을 막는 것이다.
그래서 기본적으로 필요한 요소는 아래와 같이 정리했다.
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);
}
}
}
각도 계산
FMath::Acos
함수를 통해 두 벡터 사이의 각도를 라디안 단위로 계산하고 이 값을 FMath::RadiansToDegrees
함수를 통해 도(degree) 단위로 변환한다.방어 판단은, 계산된 각도가 MaxBlockAngle
보다 작거나 같으면, 방어가 가능한 걸로 판단한다!
MaxBlockAngle
이 45도라면, 캐릭터의 전방을 기준으로 좌우 45도 각도 내에서 들어오는 모든 공격을 방패로 막을 수 있다. 즉, 캐릭터의 전방 90도 범위 내에서 들어오는 공격을 방어할 수 있다.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
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