수학 - 삼각함수, 행렬 & [UE5] Unreal

김영웅·2025년 2월 11일

삼각함수

직각삼각형을 기반으로 정의된다.

출처 : HardCore in Programing

반지름이 1인 원에서의 삼각함수를 생각하면 빗변이 1이기 때문에
sinθ=높이1sin{\theta} = \frac{높이}1

sin⁡θcos⁡θ\frac{\sin{\theta}}{\cos{\theta}}

반지름이 1인 단위 원의 방정식은 다음과 같다.
x2+y2=1x^2 + y^2 = 1

삼각함수의 주요 공식

  • x가 cos이고 y가 sin이니까 피타고라스 방정식에 넣으면 cos^2 + sin^2 = 1 이다.
  • tan⁡θ=sin⁡θcos⁡θ\tan{\theta} = \frac{\sin{\theta}}{\cos{\theta}}
  • cos⁡(A+B)=cos⁡Acos⁡B−sin⁡Asin⁡B\cos(A + B) = \cos A \cos B - \sin A \sin B
  • sin⁡(A+B)=sin⁡Acos⁡B+cos⁡Asin⁡B\sin(A + B) = \sin A \cos B + \cos A \sin B

반지름이 r인 원은 다음과 같이 표현할 수 있다.
(x,y)=(rcosθ,rsinθ)(x,y) = (rcos{\theta}, rsin{\theta})

호도법

각도법은 원을 360개로 나누고 거기에 도(degree)를 붙여 사용한다.
하지만 수학적 계산을 할 때 불편하므로 호도법을 사용한다.

원의 반지름 길이와 같은 호의 길이가 만드는 각을 1rad라고 정의하는 것을 호도법(Radian)이라고 부른다.

각도법과 호도법의 관계

원의 반지름과 호의 길이가 같을 때의 각도를 1rad이라 할 때, 반원의 각도인 180°는 πrad이라 할 수 있다.
이를 통해 다음과 같은 관계식을 유도할 수 있다.

180°=πrad180° = πrad

1°=π180rad1° = \frac{\pi}{180}rad

1rad=π180​°1rad = π 180​°


좌표계

대부분의 게임 엔진은 직교좌표계(Cartesian Coordinate System)를 기본적으로 사용하지만, 극좌표계(Polar Coordinate System)도 특정한 경우에서 매우 유용하게 활용된다.

극좌표계란 물체의 위치를 원점으로하고 거리(r)와 회전 각도(θ{\theta})로 표현하는 좌표계이다.
극좌표계의 특징
1. (r, θ) 형태로 좌표를 정의한다.
2. 원점을 중심으로 회전하는 오브젝트를 다룰 때 효과적이다.
3. 각도(θ)를 기반으로 위치를 표현하므로, 회전 변환이 간단해진다.

극좌표 → 직교좌표 변환

x=rcos⁡θx = r \cos\theta

y=rsin⁡θy = r \sin\theta


직교좌표 → 극좌표 변환

r=x2+y2r = \sqrt{x^2 + y^2}

θ=tan⁡−1(yx)\theta = \tan^{-1} \left( \frac{y}{x} \right)


행렬

현실 세계와 달리 가상 세계를 구현할 땐 수학적 개념으로 구현된다.
그렇기에 게임에서 현실적인 환경을 표현하기 위해서는 객체들의 위치와 형태를 변환하는 과정이 필요하다.

이러한 변환은 선형 변환(Linear Transformation)을 기반으로 이루어지며, 게임 내에서 캐릭터 이동, 카메라 시점 변경, 애니메이션, 충돌 판정 등에 광범위하게 사용된다.
선형 변환은 입력 벡터에 특정한 규칙을 적용하여 새로운 벡터를 생성하는 변환을 의미한다.
이 변환은 벡터 공간의 성질을 유지하면서 좌표를 변경하는 수학적 연산을 수행한다.

선형 변환(Linear Transformation)의 특징

  1. 덧셈 보존 : 변환 함수 T 에 대해 T(v+w)=T(v)+T(w)T(\mathbf{v} + \mathbf{w}) = T(\mathbf{v}) + T(\mathbf{w})
  2. 스칼라 곱 보존 : 변환 함수 T 에 대해 T(cv)=cT(v)T(c \mathbf{v}) = c T(\mathbf{v}) ( c 는 임의의 실수 스칼라 값)

이러한 변환들을 표현할 때 행렬을 사용한다.
행렬을 사용하면 복잡한 변환을 간단하게 처리할 수 있으며, 여러 개의 변환을 하나의 행렬로 합쳐서 표현할 수도 있기 때문이다.

행렬을 사용하니 당연히 행렬을 알아야한다.
행렬은 다른 분이 쉽고 자세하게 써놨으므로 봐보도록 하자

이동 변환

이동 변환은 객체의 위치를 특정 방향으로 변경하는 변환이다.
즉, 좌표를 (dx,dy)(dx, dy)만큼 이동시키는 연산이다.

이동 변환 공식

(x′,y′)=(x+dx,y+dy)(x{\prime}, y{\prime}) = (x + dx, y + dy)
이를 행렬로 표현하면 다음과 같다.

이동 변환 행렬 (3×3)

T=[10dx01dy001]T = \begin{bmatrix} 1 & 0 & dx \\ 0 & 1 & dy \\ 0 & 0 & 1 \end{bmatrix}

[x′y′1]=T×[xy1]\begin{bmatrix} x{\prime} \\ y{\prime} \\ 1 \end{bmatrix} = T \times \begin{bmatrix} x \\ y \\ 1 \end{bmatrix}

왜 2D를 표현하는데 3x3 행렬을 사용하는지 의문이 생길 수 있다.
이는 행렬 곱셈으로 덧셈으로 표현하기 위한 방법이다.
위의 공식에 나오듯이 특정 좌표를 (dx,dy)(dx, dy)만큼 이동시키는 것은 단순히 기존 좌표에서 더하기만 하면 된다.

물론 이동 하나만이라면 그냥 벡터로 더하면 되지만 추후 나올 기능들까지 생각하면 곱으로 나타내야한다.
그런데 2x2 행렬을 곱한 것으로 단순 좌표 덧셈을 표현할 수 없다.
다음과 같이 있을 때 를 봐보자

[x′y′]=[abce][xy]\begin{bmatrix} x{\prime} \\ y{\prime} \end{bmatrix} = \begin{bmatrix} a & b \\ c & e \end{bmatrix} \begin{bmatrix} x \\ y \end{bmatrix}

(d는 위에서 썼기에 e로 표현)
위의 행렬을 계산해보면

x′=a⋅x+b⋅yx{\prime} = a \cdot x + b \cdot y

y′=c⋅x+e⋅yy{\prime} = c \cdot x + e \cdot y

이때, 이동 변환의 공식 (x′=x+dx)(x{\prime} = x + dx)와 비교해보면, 이 수식 안에는 덧셈 연산이 포함될 수 있는 방법이 없다.
예를들어 b=dx,c=dyb = dx, c = dy으로 한다고 해도

[x′y′]=[adxdye][xy]\begin{bmatrix} x{\prime} \\ y{\prime} \end{bmatrix} = \begin{bmatrix} a & dx \\ dy & e \end{bmatrix} \begin{bmatrix} x \\ y \end{bmatrix}

을 계산하면

x′=a⋅x+dx⋅yx{\prime} = a \cdot x + dx \cdot y
y′=dy⋅x+e⋅yy{\prime} = dy \cdot x + e \cdot y

이 되서 x 값에도 y가 들어가기 때문에 안된다고한다.

근데

[x′y′]=[a∗dx00e∗dy][xy]\begin{bmatrix} x{\prime} \\ y{\prime} \end{bmatrix} = \begin{bmatrix} a*dx & 0 \\ 0 & e*dy \end{bmatrix} \begin{bmatrix} x \\ y \end{bmatrix}

면 되는거 아닌가??
찾아봐야할 것 같다.

아무튼 문제를 해결하기 위해 좌표를 한 차원 증가시켜 3×3 행렬을 사용하는 것이다.
이를 동차 좌표계(Homogeneous Coordinates)라고 한다.
동차 좌표계에서는 기존의 2D 좌표 (x,y)(x, y)를 한 차원 추가시켜 (x,y,1)(x, y, 1)로 표현한다.
여기서 추가된 값 1은 좌표를 확장하여 이동 변환을 행렬 연산으로 표현할 수 있도록 도와준다.

이렇게 하면 이동 변환을 포함한 모든 변환을 3×3 행렬로 표현할 수 있다.

= 이동 변환 행렬 (3×3) =

T=[10dx01dy001]T = \begin{bmatrix} 1 & 0 & dx \\ 0 & 1 & dy \\ 0 & 0 & 1 \end{bmatrix}

= 이동 변환 적용 =

[x′y′1]=T×[xy1]\begin{bmatrix} x{\prime} \\ y{\prime} \\ 1 \end{bmatrix} = T \times \begin{bmatrix} x \\ y \\ 1 \end{bmatrix}

= 계산 과정 =

x′=1⋅x+0⋅y+dx=x+dxx{\prime} = 1 \cdot x + 0 \cdot y + dx = x + dx
y′=0⋅x+1⋅y+dy=y+dyy{\prime} = 0 \cdot x + 1 \cdot y + dy = y + dy

문제

점  P(8,2)P(8,2) 를  (dx=6,dy=2)(dx=6, dy=2) 만큼 이동하는 것을 행렬로 표현하면?

초기 좌표 (동차 좌표계 적용)

P=[821]P = \begin{bmatrix} 8 \\ 2 \\ 1 \end{bmatrix}

이동 변환 행렬

T=[106012001]T = \begin{bmatrix} 1 & 0 & 6 \\ 0 & 1 & 2 \\ 0 & 0 & 1 \end{bmatrix}

행렬 연산 수행

[x′y′1]=[106012001][821]\begin{bmatrix} x{\prime} \\ y{\prime} \\ 1 \end{bmatrix} = \begin{bmatrix} 1 & 0 & 6 \\ 0 & 1 & 2 \\ 0 & 0 & 1 \end{bmatrix} \begin{bmatrix} 8 \\ 2 \\ 1 \end{bmatrix}

계산 과정

x′=(1×8)+(0×2)+(6×1)=8+6=14x{\prime} = (1 \times 8) + (0 \times 2) + (6 \times 1) = 8 + 6 = 14
y′=(0×8)+(1×2)+(2×1)=2+2=4y{\prime} = (0 \times 8) + (1 \times 2) + (2 \times 1) = 2 + 2 = 4


결과 좌표

P′=(14,4)P{\prime} = (14,4)
즉, 점  (8,2) 가 (14,4)로 이동했다.


Enhanced Input System

향상된 입력 시스템이다.
Input Mapping Context와 Input Action으로 구현되어있다

Input Mapping Context (IMC)

  • IA 들을 총괄해서 관리하는 컨텍스트
  • 여기서 IA에 맞게 입력들을 매칭 시켜준다.

Input Action (IA)

  • 추상적인 행동에 대한 것을 정의한다
  • 예를들어 점프, 카메라 회전, 이동 같은 것들을 말한다.
IA에서 위와 같이 전달될 '값'만 설정해주고 해당 행동에 맞는 액션만 연결해주면 된다. 입력에 관한 건 Input Mapping Context가 전부 맡고 있기 때문에 해당 행동에 따른 액션만 만들어주면 된다.

ULocalPlayer*는 GetLocalPlayer()로 가져오면 된다.
현재 플레이어의 LocalPlayer를 가져오는 함수
LocalPlayer는 그 플레이어의 입력이나 화면 뷰 등을 관리하는 클래스이다.
LocalPlayer는 사용자라고 보면 된다

Enhanced Input을 사용하려면 IMC를 인식할 수 있게 연결해 주어야 한다.
Controller에선 BeginPlay()로 연결해 주어야 하고,
캐릭터에선 SetupPlayerInputComponent()로 연결해 주어야 한다.

Controller에선 BeginPlay에서 LocalPlayer의 EnhancedInputLocalPlayerSubsystem을 가져와 IMC를 추가해주면 된다.

if (ULocalPlayer* LocalPlayer = GetLocalPlayer())
{
	if (UEnhancedInputLocalPlayerSubsystem* Subsystem = LocalPlayer->GetSubsystem<UEnhancedInputLocalPlayerSubsystem>())
    {
		if (InputMappingContext)
		{
			Subsystem->AddMappingContext(InputMappingContext, 0);
		}
    }
}

캐릭터에선 SetupPlayerInputComponent에서 제공되는 UInputComponent를 이용해 UEnhancedInputComponent로 cast해서 InputAction에 BindAction으로 바인딩 해주면 된다.

if (UEnhancedInputComponent* EnhancedInput = Cast<UEnhancedInputComponent>(PlayerInputComponent))
{
		if (ACPlayerController* PlayerController = Cast<ACPlayerController>(GetController()))
		{
			if (PlayerController->MoveAction)
			{
				EnhancedInput->BindAction(PlayerController->MoveAction, ETriggerEvent::Triggered, this, &ACCharacter::Move);	
			}
        }
}

입력 바인딩 함수는 리플렉션 시스템과 연동이 되있어서 리프렉션 시스템과 연결이 되어 있어야만 인식할 수 있다.

UEnhancedInputComponent->BindAction(InputAction, ETriggerEvent, Object, &Fucntion)으로 연결해주면 된다.
&Function은 함수 주소를 말하는데 void(const FInputActionValue&) 형태 이어야 들어간다.


언리얼에서 캐릭터에게 명령이 주어질때 다음과 같은 과정을 거친다고한다.

  1. 사용자가 A라고하면
  2. PlayerController가 A를 인식
  3. Local Player Subsystem에게 A가 무슨 뜻이냐 질문
  4. 어떤 동작이다 알려줌
  5. PlayerController가 캐릭터한테 A가 이런 뜻이니 거기에 맞는 행동 함수를 호출
  6. 캐릭터가 A에 맞는 함수를 실행



블루프린터에서 이 상태의 변수를 존재 하는지 안하는지 검사하고 싶으면 우클릭을 누르면

이렇게 뜰 텐데 검증된 Get으로 변환을 누르면

이런식으로 검사 가능하게 바뀐다.

AnimBlueprint의 Control Rig는 뼈를 지정해서 움직여준다.
주로 IK에 많이 쓴다.

Animation의 트랜지션(Transition)의 디테일창에서 스테이스 시퀀스 플레이어에 따른 자동 규칙(Automatic Rule Based on Sequence Player in State)를 체크하면 애니메이션이 끝날 때 다음 애니메이션으로 전환된다.


애님 그래프(Anim Graph)에서 State Machin을 Cached Pose로 등록할 수 있다.
애니메이션이 구성이 커질 때 선으로만 연결하면 연결이 너무 복잡해져 잘 알아볼 수 없기 때문에 이런식으로 pose를 저장한 후 다른데에서 pose를 사용해 애니메이션에 여러 기능들을 추가할 수 있다.


State Alias로 Cached Pose처럼 State를 마치 새로운 State마냥 쓸 수 있다.
조금 다른 점은 원하는 State를 디테일 창에서 체크해 해당 State 상태일 때 항상 전환될 수 있게 만들 수 있다는 점이다.

profile
게임 프로그래머

0개의 댓글