본문 바로가기

게임프로그래밍/실습1

[언리얼 실습] 29. 백터의 외적을 통해 Hit 방향 알아내기


목차

  1. 외적이란?
  2. 외적을 통해 히트 방향 알아내기
  3. c++에서 적용하기

1. 외적이란?

 

백터의 외적은 3차원 공간에서 백터와 백터끼리 곱한 것으로 크기만 있는 내적과 달리 크기와 방향 모두 존재한다. 그리고 이 방향은 오른손 법칙을 통해 알아낼 수 있다.

 

전자기학에서도 이 오른손 법칙이 쓰이는데 재밌는 점은 백터의 외적 자체가 자기장 속 전하가 받는 힘의 크기와 방향을 구할 때도 쓰인다고 하니 본질적으로는 둘이 같은 것이라고 볼 수도 있을 것 같다. (출처)

 

외적은 크기와 방향 모두 구할 수 있지만 현재 알고 싶은 것은 방향이기 때문에 오른손 법칙이 주요하게 쓰일 것이다.


2. 외적을 통해 히트 방향 알아내기

 

백터의 내적은 그 특징상 양수의 값만 나오고 그렇기 때문에 이것만으로는 히트의 방향을 정확히 알 수가 없다. 그렇기 때문에 외적을 통해 추가적으로 방향을 알아내어야 한다.

 

출처 https://ko.wikipedia.org/wiki/%EB%B2%A1%ED%84%B0%EA%B3%B1

 

A방향의 백터가 있고 B방향의 백터가 있을 경우 이 두 백터의 곱의 방향은 그림에서 처럼 위를 향하거나 아래를 향할 것이다. 이때 어느 방향으로 향할지는 어느 순서로 곱하냐에 따라 다르다.

 

그림에 나온 것처럼 A * B 의 경우 위를 향하고 B * A 의 경우 아래를 향하는데 이때 바로 오른손 법칙이 쓰일 수 있다.

 

 

그림으로만 보면 직관적으로 안 와닿을 수도 있는데 동영상을 보면 훨씬 직관적으로 이걸 알아볼 수 있다.

 

어쨌든 이것을 통해 드디어 히트 방향을 알아낼 수 있다.

 

전의 포스팅에서 이어 쓰면 에너미에게는 전방백터와 타격지점을 향하는 백터(ToHit)가 존재한다. 이렇게 두 개의 백터가 존재하고 이 두 백터의 곱의 방향을 알아내고 이를 내적에서 구한 각도와 합치면 정확한 타격방향을 알 수 있다.


3. c++에서 적용하기

 

이제 비주얼 스튜디오에서 이를 구현해보자. 백터의 외적은 대학 과정은 가야 배우는 어려운 수학 학문중 하나이고 그렇기 때문에 이 외적을 일일이 구하는 것은 현실적으로 힘들다.

 

하지만 다행이 외적또한 엔진에 있는 라이브러리를 통해 쉽게 구할 수 있다.

 

여기서 주의사항은 언리얼 엔진에서 실제 외적을 구할때 방향은 오른손 법칙과 반대라는 것이다. 그림에서 A * B 의 방향은 위를 향하지만 엔진에서는 방향이 아래를 향한다. 이것을 조심하면서 외적을 구해보자.

 

	// 만약 외적 방향이 아래를 향하면 세타는 마이너스이다.
const FVector CrossProduct = FVector::CrossProduct(Forward, ToHit);
if (CrossProduct.Z < 0)
{
	Theta *= -1.f;
}

 

다음과 같이 쉽게 구할 수 있다.

 

전체 코드는 아래와 같다.

 

void AEnemy::GetHit(const FVector& ImpactPoint)
{
	if (GetWorld())
	{
		DrawDebugSphere(GetWorld(), ImpactPoint, 8.f, 12, FColor::White, false, 5.f);
	}
	PlayHitReactMontage(FName("FromFront"));

	// Forward = U
	const FVector Forward = GetActorForwardVector();
	// ImpactPoint의 Z축을 에너미의 Z축과 동일하게 설정
	const FVector ImpactLowered(ImpactPoint.X, ImpactPoint.Y, GetActorLocation().Z);
	// ToHit = V
	const FVector ToHit = (ImpactLowered - GetActorLocation()).GetSafeNormal();

	// U * V = |U||V| cosθ
	// |U| = 1, |V| = 1, 따라서
	//  cosθ = U * V
	const double CosTheta = FVector::DotProduct(Forward, ToHit);
	// cosθ의 역함수 구하기
	double Theta = FMath::Acos(CosTheta);
	// 라디안 단위를 디그리 단위로 변환
	Theta = FMath::RadiansToDegrees(Theta);

	// 만약 외적 방향이 아래를 향하면 세타는 마이너스이다.
	const FVector CrossProduct = FVector::CrossProduct(Forward, ToHit);
	if (CrossProduct.Z < 0)
	{
		Theta *= -1.f;
	}

	if (GEngine)
	{
		GEngine->AddOnScreenDebugMessage(1, 10.f, FColor::Cyan, FString::Printf(TEXT("Theta : %f"), Theta));
	}
}

 

 

에디터에서 확인하면 이렇게 왼쪽에서 공격할 경우 마이너스의 값이 나오는 것을 확인할 수 있다. (오른손 법칙으로 따지면 이때 양수의 값이 나와야한다)

 

이제 세타를 정확히 알아낼 수 있으니 이 값을 통해 타격방향을 정할 수 있고 이를 통해 타격 애니메이션을 방향에 따라 다르게 줄 수 있다.

 

다음에 이것을 해보도록 하자.