Unity에서 캐릭터를 목표 방향으로 움직이고 몬스터 앞쪽의 플레이어를 판별하려면 어떤 계산이 필요할까요? 방향 벡터의 정규화와 Time.deltaTime을 이용한 이동, 내적을 이용한 시야 판정, 외적을 이용한 좌우 방향 계산을 예제로 살펴봅니다. 행렬이 크기·회전·이동을 표현하는 방식까지 연결한 현장 강의록입니다.
2022년 1학기 한국애니메이션고등학교 2학년 「3D 게임 프로그래밍」 2강 「게임 개발에 필요한 기초 수학 지식」에서 가르친 내용입니다.
점과 벡터
2D · 3D 공간
게임의 사물은 2차원이나 3차원 공간에 놓이고, 위치와 움직임은 벡터와 행렬 계산으로 표현합니다.
점 (Point)
좌표계 안의 위치입니다.
벡터 (Vector)
방향과 크기를 함께 가집니다. 게임 엔진은 점과 벡터를 같은 Vector 클래스로 다룹니다.
스칼라는 크기만 있는 값(속력 5)이고, 벡터는 방향까지 있는 값(오른쪽으로 5)입니다. 게임에서 "얼마나" 와 "어느 쪽으로" 를 함께 말해야 하는 곳에는 모두 벡터가 쓰입니다.
두 점 사이의 방향과 거리
| 구하는 것 | 식 | Unity |
|---|---|---|
| 방향과 거리를 함께 | DV = P2 − P1 | target - current |
| 거리 | |DV| = √[(P2x − P1x)² + (P2y − P1y)² + (P2z − P1z)²] | Vector3.Distance(P1, P2) |
| 방향 (단위 벡터) | NV = DV / |DV| | (target - current).normalized |
| 이동 (Translation) | 새 위치 = 지금 위치 + NV × 거리 | pos += dir * distance |
정규화(Normalizing)는 방향은 그대로 두고 크기를 1 로 맞추는 계산입니다. 크기를 지우고 방향만 남기는 셈입니다. 방향만 남겨 두면 "이 방향으로 초당 3m" 처럼 속도를 따로 곱할 수 있습니다.

속도와 가속도
| 말 | 식 |
|---|---|
| 거리 | 거리 = 속도 × 시간 |
| 지금 속도 | 지금 속도 = 처음 속도 + 가속도 × 시간 |
| 가속할 때의 거리 | 거리 = (속도 + 가속도 × 시간) × 시간 |
게임은 프레임마다 움직이므로 시간은 Time.deltaTime(지난 프레임에서 흐른 시간)입니다. 속도에 deltaTime 을 곱해야 컴퓨터가 빠르든 느리든 1초에 같은 거리를 갑니다.
- 목적지를 정합니다:
targetPosition = new Vector3(Random.Range(-600, 600), Random.Range(-400, 400), 0); - 출발할 때 목적지 방향을 저장합니다.
- 지금 위치와 목적지의 거리가 10 이하가 될 때까지 캐릭터를 움직입니다.
myCharacter.localPosition = myCharacter.localPosition + (currentDir * Time.deltaTime * velocity);
Vector3 targetPosition;
Vector3 currentDir;
public float velocity = 300f;
void PickTarget()
{
targetPosition = new Vector3(Random.Range(-600, 600), Random.Range(-400, 400), 0);
currentDir = (targetPosition - myCharacter.localPosition).normalized;
}
void Update()
{
if (Vector3.Distance(myCharacter.localPosition, targetPosition) <= 10f)
{
PickTarget(); // 도착하면 다음 목적지
return;
}
myCharacter.localPosition += currentDir * Time.deltaTime * velocity;
}빛을 위한 벡터의 결합
Half Vector
두 벡터를 더해 만듭니다(HV = V1 + V2). 반사광(스펙큘러) 계산에 쓰고, 방향만 남기려고 보통 정규화합니다.
Reflection Vector
들어온 각도와 나가는 각도가 같은 벡터입니다. 부딪힌 면의 법선(Normal) 방향을 기준으로 계산합니다.
벡터는 더하고 빼는 것만으로도 많은 일을 합니다. 두 방향의 가운데가 필요하면 더하고, 한 점에서 다른 점으로 가는 길이 필요하면 뺍니다.
행렬로 크기, 회전, 이동
4×4 행렬과 위치 벡터를 곱하면 변환된 위치가 나옵니다. 행의 원소와 벡터의 원소를 차례로 곱해 더합니다.
│ 1, 2, 1, 5 │ │ 1 │ │ 14 │ 1·1 + 2·3 + 1·2 + 5·1 = 14 (x)
│ 0, 3, 0, 4 │ × │ 3 │ = │ 13 │ 0·1 + 3·3 + 0·2 + 4·1 = 13 (y)
│ 1, 2, 0, 0 │ │ 2 │ │ 7 │ 1·1 + 2·3 + 0·2 + 0·1 = 7 (z)
│ 0, 0, 0, 1 │ │ 1 │ │ 1 │ 0·1 + 0·3 + 0·2 + 1·1 = 1 (w)
| 변환 | 행렬이 하는 일 | Unity |
|---|---|---|
| Scaling | 원점에서 각 점까지의 거리를 늘리거나 줄여 사물을 크거나 작게 보이게 합니다. 대각선에 Sx, Sy, Sz 를 둡니다 | transform.localScale *= 2; |
| Rotation | 원점을 중심으로 각 점을 돌립니다. Yaw · Pitch · Roll 행렬을 차례로 적용합니다 | Quaternion.Euler(0, 0, 45), Quaternion.LookRotation(dirToTarget, new Vector3(0, 0, 1)) |
| Translation | 마지막 열의 값만큼 점을 옮깁니다. 그래서 위치 벡터의 네 번째 값 w 가 1 입니다 | transform.localPosition += new Vector3(0, 2, 3); |
엔진의 transform 은 이 세 행렬을 대신 곱해 줍니다. 우리가 크기 · 회전 · 위치를 따로 적어도 엔진 안에서는 행렬 하나로 합쳐져 그려집니다.
- 크기는
transform.localScale을 바꿔 적용합니다. - 이동은
transform.localPosition을 바꿔 적용합니다. - 회전은
Quaternion.Euler로 회전값을 얻어 적용합니다.
내적과 외적
| 연산 | 식 | Unity · HLSL |
|---|---|---|
| 내적 (Dot) | A · B = |A| |B| cos θ | Vector3.Dot(a, b) · dot(a, b) |
| 외적 (Cross) | A × B = (aybz − azby, azbx − axbz, axby − aybx) | Vector3.Cross(a, b) · cross(a, b) |
내적은 숫자 하나를 돌려줍니다. 두 벡터가 모두 크기 1 이면 그 숫자가 곧 cos θ 라서, 두 방향이 얼마나 같은 쪽을 보는지를 알려 줍니다. 같은 쪽이면 1, 직각이면 0, 반대쪽이면 −1 입니다. 외적은 벡터를 돌려줍니다. 두 벡터에 모두 직각인 방향입니다.
게임에서: 몬스터의 시야와 옆걸음
몬스터가 좌우 90° 안의 플레이어에게 반응한다고 해 봅시다. 몬스터가 보는 방향(forward)과 몬스터에서 플레이어로 가는 방향을 둘 다 정규화해 내적하면 cos θ 가 나옵니다. θ = acos(dot), 각도 = θ × (180 / π) 입니다. 각도가 90° 보다 작으면, 곧 내적이 0 보다 크면 앞쪽에 있는 것입니다.

Vector3 toPlayer = (player.position - transform.position).normalized;
float cos = Vector3.Dot(transform.forward, toPlayer);
float angle = Mathf.Acos(cos) * Mathf.Rad2Deg;
if (angle < 90f)
{
Debug.Log("플레이어 발견");
// 플레이어 쪽으로 이동
}Unity 로 2D 처럼 만들 때는 위쪽(up)을 z 로 두는 경우가 많습니다. 그러면 앞쪽(forward)만 알아도 외적으로 좌우를 구해 옆걸음을 걸을 수 있습니다: left = up × forward, right = forward × up, backward = up × left. 외적은 곱하는 순서를 바꾸면 방향이 반대가 됩니다.
그래픽스에서: 조명
Lambert Light
빛의 방향과 표면의 방향(법선)을 내적하면 cos θ 가 나옵니다. 1 에 가까울수록 밝게, 0 에 가까울수록 어둡게 칠하면 램버트 조명이 됩니다.
Cross Light
카메라가 보는 방향을 forward, (0, 0, 1) 을 up 으로 두면 외적으로 left 와 right 를 구합니다. 네 방향으로 빛을 번지게 하면 카메라 방향에 따라 모양이 바뀌는 빛 효과가 됩니다.
몬스터의 시야와 표면의 밝기가 같은 식에서 나온다는 것이 이 수업의 마지막 메시지였습니다. 게임 수학은 장면이 달라도 같은 도구를 계속 다시 씁니다.
정리
- 방향은 빼고, 크기는 정규화로 지웁니다. 목표 − 현재 위치가 방향과 거리입니다.
- 크기 · 회전 · 이동은 행렬 곱입니다. 엔진의 transform 이 그 행렬을 대신 다룹니다.
- 내적은 얼마나 같은 쪽을 보는지, 외적은 두 방향에 직각인 방향. 시야 판단과 조명과 옆걸음이 같은 식에서 나옵니다.
현장 강의록은 수업 자료를 정리하는 대로 이어서 올립니다.
함께 읽으면 좋은 글
AI가 만든 Unity 코드를 직접 검증해 보고 싶다면
이 글에서 살펴본 구조를 바탕으로 AI에게 작업을 요청하고 결과를 읽는 연습을 이어가세요. 「AI 코드를 읽는 게임 개발자」 무료 세션 1에서 시작하거나, 전체 커리큘럼과 수강 안내를 확인할 수 있습니다.