게임수학

게임쪽을 공부하다보니 부족한 부분을 가장 많이 만나는 곳이 영어와 수학부분이였다.

필요한 공식이나 수식을 활용할 때 다른 사람의 코드를 참고하지만 100%이해한다고 보기 어렵고 활용도 제대로 못하는 것 같아서..

게임수학을 공부하려고 한다.

영어도 꼭..!

깃허브에도 공부레포를 따로 만들어서 관리하는게 좋아보여서 첨부

공부 그라운드 룰

  1. 조금이라도 궁금하거나 이해안되면 구글링 or 질문하기
  2. 배우는 개념을 바로 실행해보기(마크다운에 정리 or 유니티 실행)
  3. 수학의 아름다움을 느끼기..?

레포 주소

블로그 포스팅 내용은 아래 강의를 보고 작성한 것입니다.

강의 주소

  • 연한 파랑으로 변경

기본연산

뒤에서 다루게 될 벡터, 확통, 대수학 등 다양한 내용을 다루기 위해선 기반이 탄탄해야 한다고 생각한다.

덧셈과 뺄셈

수가 커지거나 작아질 수 있는 수단으로 수직선상에서 가운데를 0이라고 본다면 오른쪽으로 증가하면 덧셈 왼쪽으로 증가하면 뺄셈이라고 할 수 있다.

인간이 상상으로 덧셈 뺄셈의 기준은 무한하지만 컴퓨터는 그렇지 않다.

using UnityEngine;

public class IntegerOverflow : MonoBehaviour
{
    private int _bigNumber = 2147483640;
    void Update()
    {
        Debug.Log(_bigNumber);
        _bigNumber++;
    }
}

코드를 실행하면 값이 증가하다 음수가 되어 버린다.

이미지

integer를 기준으로 20억을 조금 넘는 수를 저장할 수 있기 때문이다.

따라서 우리는 컴퓨터로 수학을 할 때는 모든 수가 같은 유형이 아니라는 사실을 기억해야한다.

또한 덧셈은 순서가 중요하지 않지만 뺄셈은 순서에 유의해야 한다.

\(1 + 2 = 2 + 1\)
\(1 - 2 != 2 - 1\)

어림수

반올림은 (0, 1, 2, 3, 4)수는 버림을 하고 (5, 6, 7, 8, 9)는 올림을 한다.

9.99 소수점 첫째 자리에서 반올림을 한다면?

결과값은 10.0이다. (10이 아님)

컴퓨터의 기준으로 앞에서 설명한 숫자의 형식이 다르기 때문을 명심해야한다.

내림올림은 기준으로 숫자를 상관하지 않고 내림과 올림을 하는 것이다.

게임에서 적절한 어림수로 표현할 때가 많기 때문에 이러한 기능을 잘 사용해야한다..!

내부적으로는 실수를 다루지만 사용자에겐 정수만 보여주거나 내부적으로도 정수로 변환해야하는 상황이 오기 때문

이러한 상황에서 중요한 점은 어림수를 계산할 때는 마지막에 해줘야 하는 점이다.

계산 도중에 어림수 계산을 하게 된다면 사소한 오차들이 계속 발생하게 되어서 결과값이 매우 차이나는 상황이 초래된다.

이러한 문제점은 꽤 심각한 오류로 자리잡기 때문에 항상 어림수 계산은 마지막에..!

public class RoundNumber : MonoBehaviour
{
    void Start()
    {
        Debug.Log(Math.Round(3.67f, 1)); // 반올림
        Debug.Log(Math.Ceiling(3.67f)); // 올림
        Debug.Log(Math.Floor(3.67f)); // 내림
    }
}

대부분의 수학관련은 Math클래스에 static메서드로 존재한다.

곱셈

게임을 만들다보면 곱셉을 정말 정말 많이 사용하게 된다. (속도, 거리 등)

수직선상에서 곱셉을 다루면 생각보다 이해가 쉽게 된다.

\[2 * 3 = 6\]
\[2 * -3 = -6\]
\[-2 * 3 = -6\]
\[-2 * -3 = 6\]

마이너스 곱하기 마이너스가 플러스가 되는 이유는 여러가지 논리적, 과학적인 증명방법이 있기 때문에 한번 참고해보는 것도 좋다.

이해가 1순위

나눗셈

곱하기가 더하기를 여러번 한다는 개념이라면 나눗셈은 수를 분리한다는 개념이다.

컴퓨터를 사용한 작업을 할 때 가장 흔히하는 실수는 1만큼 차이가 나는 것이다.

0에서 시작하는 경우

숫자 8을 수직선 상에 놓고 4로 나눈다고 했을 때 필요한 분리선은 3개인것과 같은 개념이다.

간단하게 아래 수식을 제대로 이해한다.

\[2/8 / 1 = 2/8\]
\[2/8 / 2/2 = 2/8\]
\[(2/2)/(8/2) = 1/4 = 2/8\]

뺄셈에서는 계산을 반대로 하면 부호가 달라진다면 나눗셈은 크기가 달라진다.

또한 곱하기와 마찬가지로 음수 나누기 음수는 양수가 된다.

나머지

간단한 나머지 연산으로 5를 2로 나눈다면 몫은 2이고 나머지는 1이다 라는 계산이 나온다.

몫은 같은 그룹에 속한다는 것이고 나머지는 말 그대로 속하지 못한 그룹이다.

여기서 나오는 개념이 짝수와 홀수의 정의이다.

게임에서 예를 들자면 가장 많이 사용하는 2가지의 경우로 나누는 경우.

  1. 랜덤 숫자를 뽑는다.
  2. 2로 나눈 나머지를 구한다.
  3. 0이라면 짝수
  4. 1이라면 홀수

간단하게 이분법으로 두가지형태로 분류할 수 있기 때문에 확률 계산에 있어서 유리하다.

단적인 예로 동전뒤집기의 경우가 있다.

시계의 초를 60 -> 1분도 나머지의 개념이다.

코드상으로는 나머지 연산자가 존재하기 때문에 쉽게 계산이 가능하다.

BODMAS

인터넷에서 쉽게 볼 수 있는 문제가 있다.

\[6 \div 2 (1 + 2)\]

위의 식은 대부분의 컴퓨터에서 정확하게 식을 사용할 수 없다.

\[6 / 2 * (1 + 2)\]

이런식으로 적어야 컴퓨터의 계산으로 사용할 수 있다는 점.

따라서 정확한(원하는) 값을 얻고 싶다면 컴퓨터가 따를 수 있는 명령을 내려야 한다.

그렇다고 해서 계산식을 명확하게 한다고 괄호를 무지막지하게 늘리는 것 또한 불필요하고 악순환이다.

즉, 계산식에 대한 명확한 이해가 수반되어야 한다는 것.

BODMAS 보드메스란. 컴퓨터에서 계산이 될 때의 우선 순위를 나타낸다.

PEMDAS라는 다른 규약도 있지만 나눗셈과 곱셈의 우선순위 차이가 있다.

  • B(bracket) 괄호로 가장 우선 시 된다.
  • O(Order) 위수(지수)이다. 제곱이나 제곱근
  • D(Division) 나누기
  • M(Multiplication) 곱하기
  • A(Addition) 더하기
  • S(Substraction) 빼기

위 순서로 다시 식 계산한다.

\[6 / 2 * (1 + 2)\]
\[6 / 2 * (3)\]
\[3 * (3)\]
\[3 * 3\]
\[9\]

이제 비슷한 문제들에 대해 명확한 답을 낼 도구를 찾았다..

제곱연산

게임수학이기 때문에 계산식과 같이 너무 어렵게는 들어가지 않고 활용방안, 이해정도만 하고 넘어간다.

거듭제곱

제곱이란, 어떤 수를 제곱한다고 했을 때 그 수를 정사각형이 되도록 회전시킨다고 생각하면 이해가 빠르다.

4의 제곱은 16(4 * 4)의 계산이므로 1칸 짜리 블록을 가로4 세로4의 기준으로 채우는 것이다.

역으로, 9칸짜리 정사각형의 수의 제곱근은 한변의 칸수이다. 즉, 3이 제곱근이다.(3 *3 = 9)

같은 개념으로 세제곱의 경우는 3차원 정사각형을 생각하면 된다.

정면의 경우를 제곱이라고 보고 뒤로 정면 4개를 더 그려넣는다면 3차원 정사각형이 모양이 되기 때문에

\[4 * 4 * 4 = 64\]

64칸으로 계산된다. (제곱의 경우 16)

간단한 지식으로 2의 24승은 16777216으로 색을 24비트 즉, RGB를 각각 8비트식 가져가서 색을 표현하기 때문에

16777216 총 나올 수 있는 색의 개수이다.

Debug.Log($"계산 결과입니다. {Math.Pow(2,24)}"); 
// 유니티 Math.Pow사용

소수의 제곱

위의 개념을 그대로 활용하여 칸수를 사용해 2의 제곱을 구한다고 하면 2을 회전시켜 정사각형을 채워 4칸이 채워진다.

\([ ][ ]\)
\([ ][ ]\)

만약 2.5개라면?

반블록이 존재하게 되고 그대로 정사각형을 만들게 되면 반블록 4개 + 반의반블록 1개가 추가되어서 정사각형을 만든다.

\[2.5^2 = 4 + 4 * \frac{1}{2} + \frac{1}{4}\]
\[4 + 2 + \frac{1}{4}\]
\[6\frac{1}{4}\]
\[6.25\]

이해하기 어렵다면 회전을 시켜 정사각형을 만들어 본다면 확실하게 쉬워진다.

3.5의 제곱

\[3.5^2 = 9 + 6 * \frac{1}{2} + \frac{1}{4}\]
\[12.25\]

이걸 게임에 적용하는 방식은 면적, 색조의 양, 픽셀의 양등 많은 사용방법이 있을 것이다.

대부분 면적을 어림잡아 계산하기에 유용하다.

Debug.Log($"계산 결과입니다. {Math.Pow(2.5,2)}"); 
// 유니티 Math.Pow사용

제곱근

4096칸의 3차원 정사각형이 있다고 했을 때 한변의 칸수가 몇일까?

3차원 정사각형이니 x의 세제곱으로 나타낼 수 있다.

\[x * x * x = x^3 (4096)\]

그렇다면 10의 세제곱은 ?

\[10 * 10 * 10 = 1000\]
\[20 * 20 * 20 = 8000\]

위와 같이 어림잡아서 내려가며 구하는 방법도 있으며 이러한 방법을 써야할 때도 있지만 분석적 해법을 통해 쉽게 구할 수 있다.

\[(a^b)^c = a^b*^c\]

이러한 규칙을 통해

\[x^3*^\frac{1}{3} = 4096^\frac{1}{3}\]

으로 표시되고

\[4096^{0.33..}\]

을 계산기로 어림 잡아서 계산해보면 어림 16으로 나온다.

실제로 16을 세제곱하면 4096의 값임을 알 수 있다.

\[^3\sqrt{4096}\]

그래프

그래프는 게임에서 정말 많이 사용된다. 수치를 명확하게 시작적으로 보여줄 수 있기 때문에 이해할 수 있어야 한다.

차트 그리고 그래프

그래프를 그릴 줄 안다면 다른 사람들의 그래프를 쉽게 해석하고 이해할 수 있다.
따라서 그래프를 직접 그려보는 경험이 중요하다.

그래프를 그릴 때 두가지 축을 기준으로 레이블을 정해야 한다.

속도, 힘 과 같은 수치로 레이블을 만들고 단위를 정한다.

양의 기울기는 제 4분면의 형태로 봤을 때 두가지 레이블 다 증가한다고 볼 수 있다.

음의 기울기 즉, 위에서 아래로 가는 그래프의 경우 y축의 레이블 값이 감소하지만 x축의 값은 증가함을 기억해야한다.

강의에서 제목과 0에서 시작하는 이유에 대해서 강조하는데 0의 에서 시작해야 그래프의 정확한 증감을 체감할 수 있으며 모든 그래프는 이름이 존재해야함을 말해준다.

주로 y값을 종속변수로 사용한다.

그래프 아래면적

적분이란 그래프 아래면적을 계산할 수 있다.

다른 방법으로 직사각형을 이용하여 근사치를 계산할 수 있다.

아래면적의 값을 구하면 해당 값을 이용하여 총 이동거리를 알 수 있다.(속도vs시간 그래프라면)

기울기

간단하게 x축 y축의 길이에 대한 그래프를 그린다고 한다면 양의 방향으로 어느정도 기울어 진 상태로 길어질 것이다.

이때, x축방향으로 4만큼 y축방향으로 5만큼 전진했다고 한다면 기울기는 5/4이다.

거리와 속도, 시간의 관계

위의 지식을 활용해서 시간 vs 속도의 그래프의 아래면적은 거리임을 알 수 있다.

한점의 기울기를 계산하면 대략적인 가속도가 얼마나 되는지 구할 수 있다.

실제 수학에서도 속도, 시간, 거리는 밀접한 관계이다.

쉬운 방법으로 거리-속도-시간의 삼각형이라는 방식이 있다.

변화율

곡선 그래프에서 기울기를 계산하는 방법은 점을 찍고 해당 점에 작은 사각형을 그래서 기울기를 구하는 것이다.

위 방법과 같이 기울기를 여러 점에서 구하면 대략적으로 그래프의 기울기를 판단할 수 있다.

밑 변환 공식

자연상수 e와 log의 사용법을 알아봤다..

Debug.Log(Mathf.Log(32));
Debug.Log(Mathf.Log(32, 2));

계산기로 체크할 수 있지만 대부분의 함수들이 내부에 있기 때문에 위 처럼 사용 가능하다.

계승과 순열

프로그래밍을 할 때 많이 사용되는 경우의 수를 계산하는 경우 팩토리얼이 사용된다.

과학적 표기법

클리커 게임을 기준으로 숫자가 매우매우 커지는 경우는 다분하게 발생한다.

이때 최근에 개발된 게임들을 자세히 본다면 과학적 표기법을 사용하고 있음을 알 수 있다.

만약 160억을 게임에 출력해야 한다면 뒤에 0을 정말 많이 붙여야 한다.

기본적인 표기

\[16,000,000,000\]

과학적 표기

\[1.6 * 10^{10}\]

쉽게 표기하는 방법을 이해할려면 유효숫자를 알아야 한다.

유효숫자란, 변환하려고 하는 숫자중에서 0이 아닌 부분을 말한다.

여기서는 16이다.

하지만 과학적 표기법으로 나타낼려면 1~10 사이에 존재해야 하는 숫자이기에 1.6으로 변환하여 사용한다.

이후에 멱수로 소수점의 위치까지의 거리를 센다.

1.6의 경우 총 10번 이동하기 때문에 멱수는 10이다.

27,020의 경우 유효숫자는 2702이다.

따라서 표기는 2.702로 표기 되며 멱수는 4이다.

일반적 표기

\[27,020\]

과학적 표기

\[2.702 * 10^4\]

그렇다면 반대로 매우 작은 수는 과학적 표기법으로 어떻게 나타낼까?

\[0.0024\]
\[2.4 * 10^{-3}\]

계산하는 방법은 똑같이 유효숫자를 찾은다음 1~10사이로 나타내고 멱수를 10의 제곱형태로 나타내면 된다.

하지만 기본적으로 소수이기 때문에 마이너스 지수형태로 표현한다.

지금까지 설명된 과학적 표기법은 사실 컴퓨터 분야에서는 잘 사용되지 않는다.

매우 빠르게 숫자가 표기 되기 때문에 지수형태로 표기하기 보다 숫자나 기호를 사용해야 하기 때문이다.

\[2.702E4\]
\[2.4E-3\]

위 처럼 표기한다.

대부분의 게임엔진에서는 위처럼 표기 되는 것을 확인할 수 있다.

void Start()
{
    int a = 123000;
    Debug.Log(a.ToString("E"));
}

이미지

그렇다면 이러한 과학적표기법의 숫자 연산은 어떻게 이루어 질까?

\[1.4 * 10^5\]
\[2.3 * 10^4\]

두 숫자가 있다고 할 때 연산이 진행될려면 우선 지수가 같아야한다.

따라서 5로 기준을 잡고 변형해서 연산을 진행한다.

\[1.4 * 10^5 + 2.3 * 10^4\]
\[1.4 * 10^5 + 0.23 * 10^5\]
\[1.63 * 10^5\]

뺄셈도 마찬가지로 지수를 맞추고 진행해야 한다.

하지만 곱하기와 나눗셈은 조금 다르다.

  • 곱하기
\[6.3 * 10^2\]
\[2.1 * 10^5\]

유효 숫자끼리는 곱하고 지수끼리는 더하면 끝이다.

\[(6.3 * 10^2) * (2.1 * 10^5)\]
\[13.23 * 10^7\]
\[1.323 * 10^8\]
\[132,300,000 == 1.323 * 10^8\]
  • 나누기
\[6.3 * 10^2\]
\[2.1 * 10^5\]

마찬가지로 유효숫자끼리 나누고 지수끼리는 뺀다.

\[3 * 10^{-3}\]

댓글남기기