2018년 3월 10일 토요일

고교 수학 수준 컴퓨터 게임 엔진 만들기

왜 엔진이라고 이름을 붙일까? 잘 모르겠다. 자동차 엔진은 빙빙 돌며 같은 짓을 반복 하는데 동력을 공급한다. 게임 엔진도 비슷하게 같은 짓을 반복하는 프로그램을 말한다. 게임 엔진은 3가지를 반복한다.

  1. 입력 : 사람의 명령(마우스 키보드 입력)
  2. 계산 : 인공 지능(이라고 하지만 아니다), 물리 시뮬레이션, 애니메이션
  3. 출력 : 그림, 소리



1/30초 또는 1/60초 동안 위의 3단계를 계속 무한 반복하는 것이 게임 엔진이다. 보통 애니메이션은 최소 1/20초 단위로 반복해야 자연스럽게 느껴진다. 그러나 동작의 변화나 위치의 변화가 클 경우, 나이가 많이 든 경우 눈의 피로를 느끼게 된다. 모니터 화면은 보통 1/60초 단위로 표시한다. 공이 화면의 좌측에서 우측 끝까지 1초에 이동하는데 필요한 간격이 모니터 화면의 1/60 거리다. 이 거리가 너무 크면 순간 이동을 한 것처럼 느껴지기 때문에 피로를 느낀다. 프레임 시간 간격과 화면 크기 사이엔 적당한 비율을 지켜야 한다. 고로 이 사이클로 애니메이션을 표시하는 게 최선이다. 그 이상 잘게 나누어도 화면에 표시 되지 않으니까.

물리 시뮬레이션의 경우는 필요한 수준으로 정밀하게 반복 계산한다. 가상의 게임 세계에선 최소 시간, 최소 공간이 존재한다. 즉 이보다 짧은 시간도 없고, 이보다 작은 공간과 물체는 없다. 그 시간과 공간 사이의 균형을 미리 계산하여 물체가 이동 중에 순간 이동을 하여 벽을 통과하는 일이 없도록 한다. 빛의 속도를 능가하지 못 하는 것처럼 게임 공간에서도 최고 속도가 있다. 그러나 게임 공간 속에선 순간 이동과 같은 마법이 얼마든지 가능하다. 화면 표시는 1/60초 단위로 하지만 물리 시뮬레이션은 더 짧은 시간으로 나누어 반복해도 된다. 계산만 하는 것이지 화면에 표시 되는 게 아니다.

인공 지능(이라고 하지만 아니다)의 경우는 계산할 것이 많아 1/60초 안에 모든 개체의 모든 동작 사이클을 반복할 수 없다. 1/60초 애니메이션을 유지하고, 물리 시뮬레이션도 하니, 인공 지능의 사이클은 더 길 수밖에 없다. 충돌은 물리 시뮬레이션에서 담당한다. 상대와 거리 계산(감지)은 인공 지능 쪽이다. 인공 지능은 상태변화도(상태변화표)에 따른 다음 행동을 결정(판단)하고, 이동 경로 계산(길 찾기)을 한다. 거리 계산, 길 찾기는 시간 소모가 많다. 고로 1/60초 시간을 나누어 써야 한다. 고로 1 사이클에 1 동작(거리 계산, 행동 결정, 길 찾기)만 한다.

고교 수학 수준 컴퓨터 게임 물리 시뮬레이션
고교 수학 수준 컴퓨터 게임 인공 지능 길 찾기


게임 엔진에 대해 구체적으로 이해하고 싶다면, 게임메이커, Unity(유너티) 같은 게임 제작기를 사용해 볼 것. 물리 시뮬레이션, 길 찾기까지는 구현 되어 있으나 인공지능은 본인이 구현해야 한다. 인공지능이라고 해서 알파고 같은 인공지능을 말하는 게 아니다. 그냥 조건문의 반복 사용이다. 이럴 때는 이렇게 하고, 저럴 때는 저렇게 하고, 그럴 때는 그렇게 해라. 이런 명령문 집합이다.

  1. Game Maker(게임 메이커) : 쉬운 게임 제작기
  2. Unity(유니티) : 쉬운 3D 게임 제작기




게임 엔진과 이미지, 소리, 개체 데이터는 나중에 수정/삽입/삭제하기 쉽도록 분리 제작한다. 보통 디자인, 코딩, 데이터를 다루는 3가지 직업이 분업화 되어 있다. 디자이너, 프로그래머, 게임 기획자, 시나리오 작가 등 말이다. 고로 서로 일이 꼬이지 않도록 분리하는 게 좋다. 가장 먼저 해야 하는 것이 지도, 유닛 개체 데이터의 구조이다. 프로그램은 이것을 이용하여 계산하기 때문이다. 게임 세계를 설계하지 않고 그 세계의 사물 모습이나 그 세계의 자연 법칙을 구현할 수는 없다.

소리 믹싱이란 것은 그냥 음파를 더하는 것이다. 고로 배경 음악 데이터에서 지금 연주중인 부분 바로 뒤에 지금 발생한 효과음 데이터를 더하기만 하면 된다. 이런 작업을 해 주는 라이브러리들이 있으니 프로그래머가 신경 쓸 건 없다. 여러 소리의 믹싱을 계속하면 소리가 너무 커진다. 고로 음원 데이터의 진폭을 좀 작게 조절해 줄 필요는 있다. 배경음과 효과음 편집은 전문 편집기로 한다. 인터넷 검색하면 있을 것이다.


그림의 경우는 다르다. 먼저 그릴 것(배경)과 나중에 그릴 것(전경)을 거리에 따라 계산해야 한다. 그림을 그려주는 라이브러리는 있으나 그리는 위치, 각도, 크기, 순서는 프로그래머가 지정해 주어야 한다. 보통 지도에 배치된 유닛을 지도 배열에 저장하면 쉽게 처리 가능 하다. 이 때 지정한 배경색은 투명색으로 처리 되어 그려지지 않는다. 배경색은 그림에 사용하지 않는 색이면 된다. 알파 값이란 것을 조정하면 반투명 효과를 낼 수 있다. 불투명도라고 생각하면 된다. 너무 멀리 있는 것은 너무 작은데 그릴 것은 많다. 이런 경우 기준 거리 이상이면 생략하는 방법도 있다. 즉 카메라에 보이는 부분만 그리는 것이다. 카메라에 보이는 영역을 결정하는 방법이 필요하겠지? 카메라의 지도 상의 위치를 계산할 수 있다.


지도에는 많은 것들이 들어 있는데, 화면에 표시하는 것만 애니메이션 계산을 하고 그린다. 화면에 보이지 않는 것들은 애니메이션 처리까지 할 필요 없다. 인공 지능 처리와 물리 시뮬레이션만 하면 된다. 화면 이동 방법은 3가지 정도가 있는데 여기에 원근법을 적용하면 3D가 된다. 2D 이미지만 가지고도 3D 효과를 낼 수 있다. 3D 효과를 내는 방법이다.

  1. 먼 것은 채도가 낮게, 가까운 것은 채도가 높게
  2. 먼 것은 느리게 이동, 가까운 것은 빠르게 이동
  3. 먼 것은 작게 그리고, 가까운 것은 크게 그린다.


스프라이트는 약간의 변형이 가능하다. 2D에서도 크기 변화나 화면 기준 시계/반시계 방향 회전(갸우뚱갸우뚱)은 가능하다. 그런데 도리도리/끄덕끄덕 회전은 3D에서만 가능하다. 그래서 2D로도 거의 3D나 원근법을 적용한 것처럼 보이게 만들 수 있다. 3D 엔진을 한다면 3각형 면으로 이루어진 3D 모델을 화면에 그리고, 조명에 따른 그늘과 그림자 처리를 해 주고, 3각형 면에 입혀야 할 이미지를 계산해 주는 등의 일을 해야 한다. 이건 좀 골치 아플 거 같다. ㅋㅋㅋ


2D 이미지를 3D 표면에 매핑(덮어 씌우기)하거나 3D 모델을 3D 공간에 여러 자세로 옮겨 그릴 때는 좌표 변환 계산을 해야 한다. 초창기 3D 게임은 4각형 면만 가지고 만들었다. 주로 지하 복도에서 싸우는 내용인데, 벽, 천정, 바닥, 스프라이트가 4각형이다. 원근법을 적용하더라도 간단한 계산만 하면 되니까 쉬웠다. 스프라이트는 항상 카메라만 바라보도록 했고, 거리에 따라 크기만 바꿔 주면 되었다. 천정, 바닥, 벽만 좌표 변환으로 원근법 처리를 해 주면 되었고, 벽의 무늬와 질감을 입히기만 하면 되었다. 이렇게 직각 배치된 4각형만 사용하면 2D로도 어느 정도 3D 효과를 낼 수 있다. 마인크래프트 같은 게 이런 형태다. 직각 배치된 4각형 상자로만 계산하기 때문에 엄청 쉽다.



좌측 애니메이션은 달랑 3개의 그림으로 만든 것이다. 그림을 보여주는 순서만 역순으로 조절해서 걷는 모습을 만든 것이다. 

1 - 2 - 3 - 2 - 1 - 2 - 3 ~ 반복

메모리를 아끼자. 헌데 다리 간격이 일정하여 실감이 나지 않는다. 현실 세계에선 갑자기 속도가 빨라질 수가 없다. 동작이 멈추는 장면에서 속도가 떨어진다. 걷기를 할 때 다리가 가장 많이 벌어진 순간에 동작이 잠깐 멈추고 반대로 돌아간다. 



그래서 다리가 벌어진 순간은 2번 반복하게 만든다. 좌측 애니는 보여주는 반복 회수만 조절해서 실감나게 만든 것이다.

1 - 1 - 2 - 3 - 3 - 2 - 1 - 1 ~ 반복

여기에 잔상 효과를 약간 넣어주면 더 자연스럽다. 애니메이션 그림도 부분으로 분리해서 저장했다가 보여줄 때 조립하는 방법이 있다. 메모리를 절약하는 대신 컴퓨터 속도를 소모한다. 좌우 대칭인 부분은 하나만 있어도 된다. 거울 반사시키면 되니까. 여기에 잔상 효과를 따로 저장했다가 덧칠해 준다.


게임 사이클 1/60초 간격으로 다음 보일 그림을 선택하기만 하면 된다. 각 개체는 애니메이션 순서를 저장한 배열을 가지고 있다. 이 배열에 보여 줄 이미지에 대한 링크만 넣으면 된다. 이렇게 제자리 애니메이션과 지도(화면) 위에서 위치 변화를 결합하면 이동하는 것처럼 보인다. 게임 데이터와 게임 엔진은 분리해 두는 것이 나중에 수정할 때 편하다.

유닛 객체의 일반적인 설계는 아래와 같을 것이다.


  1. 번호 : ID
  2. 이름 : 있다면
  3. 종류 : 스프라이트 선택
  4. 소속 : 어느 부대 소속
  5. 수명 : 0이 되면 사망
  6. 크기 : 부력/양력에서 밀도 계산 등
  7. 질량 : 무게
  8. 연료 : 에너지
  9. 파워 : 힘
  10. 가속 : XYZ 속도 변화량
  11. 속도 : XYZ 위치 변화량
  12. 위치 : XYZ 좌표 (보통 Z 값은 카메라로부터 거리이다)
  13. 방향 : 향하는 각도
  14. 상태 : 애니메이션 관련
  15. 건물 : 이동 유닛 or 고정 유닛
  16. 이동 : 비행, 육로, 수면, 수중
  17. 시야 : 감지 가능 거리
  18. 장갑 : 장갑 두께는 방어력과 무게 증가


유닛 객체에 일반적인 무기 특성을 추가할 수 있다.


  1. 최대사거리 : 원거리 공격 거리, 0은 근접전
  2. 유효사거리 : 적중률 50% 거리, 거리 제곱에 반비례
  3. 연사속도 : 공격 사이 시간 간격
  4. 관통력 : 장갑 관통력
  5. 타격도 : 폭발/타격 충격(내상)
  6. 화염도 : 화염 방사기 종류(내상)


객체 설계할 때 가능한 일반적으로 만들어야 수정이 쉽고, 다양한 유닛 추가가 쉽다. 스프라이트는 여러 개를 조립할 수 있다. 바닥의 그늘, 무기, 방어구, 수송체, 소속을 나타내는 색상이나 깃발 등이 추가 될 수 있다. 고로 스프라이트 조립 과정, 그리는 순서를 기록할 수 있도록 하는 게 좋을 것이다. 가장 일반적인 설계는 모든 경우를 다 반영해 두는 것이다. 그리고 쓰지 않을 때는 그 기능을 반영구적으로 죽이는 방식이다.





충돌은 물리 시뮬레이션에서 담당한다. 게임 속 우주 공간을 작은 격자 공간으로 나눈 후에 그 격자 공간 안에 함께 겹쳐 있다면 충돌이다. 아니면 거리 계산을 해야 하는데 유닛의 개수가 많을수록 제곱에 비례하여 거리 계산량이 늘어난다. 계산 시간을 줄이기 위해서 X, Y 따로 계산하여 사각형 안에 들어오면 같은 거리로 취급하는 게임들도 있었다. (블리자드의 초창기 게임이 그렇다.) 정지한 건물이나 나무도 유닛이다. 나무 같은 경우는 중립 유닛이라 배경으로 취급하고 필요한 경우(벌목할 때)만 거리 계산을 한다.

인공지능이라고 하면 오해를 할 것이니 게임지능이라고 고치자. 인공지능은 스스로 학습하는 프로그램을 말한다. 게임 지능은 이런 인공지능이 아니다. 미리 행동 방식을 정해 주고 조건에 따라 행동하는 단순 프로그램이다. 길 찾기도 마찬가지다. 옛날 게임들은 정확히 언제 어디로 이동하는 패턴을 정해주었다. 요즘 게임은 외부 조건에 따라 상태를 바꾸며 그 상태에 맞는 행동을 한다. 이런 것을 상태변화도, 상태변화표로 표현한다. 

현재 상태 + 외부 조건 → 다음 상태 + 다음 행동.
상태와 외부 조건을 정수로 표현하고 배열 위치로 검색하면 아주 빠른 검색 가능.


인간이 조절하는 유닛은 지능이 필요 없다. 허나 컴퓨터가 조절하는 유닛들은 통제하는 전략전술 인공지능이 필요하다. 옛날 게임들은 정해진 순서에 따라 언제 어느 정도 병력이 모이면 그냥 공격하는 단순한 패턴이었다. 그래서 1대 3까지도 가능했다. 좀 더 지능적으로 행동하도록 만들려면 통계 처리 기능을 넣어야 한다. 상대의 실력에 따라 수준 조절을 하는 것이다. 유리하면 공격, 불리하면 수비 등 판세를 판단할 능력이 있어야 한다. 지형까지 이용하고 유인, 기습까지 할 수준이면 인간을 능가하겠지.

판단 함수(병력, 병종, 진법) = 공수 판단 





옛날엔 충돌 스프라이트를 이용해서 충돌 감지를 했었다. 그러니까 화면 표시용 스프라이트가 있고 마치 그림자처럼 생긴 충돌 감지 스프라이트가 있었다. 충돌 감지 스프라이트는 보이지 않는 화면에 그려지는데 이 때 그릴 점 위치에 이미 그려진 색상이 검출 되면 주먹과 얼굴 접촉처럼 미세한 충돌까지 감지하는 것이다. 스트리트 파이터 같은 게임에서 쓰는 방법이다. 이런 충돌이 아닌 몸과 몸의 충돌이라면 발밑의 그림자 스프라이트만 그런 용도로 쓰면 된다. 이걸 확장하여 유닛이 위치한 지도의 격자에 다른 유닛이 있다면 역시 충돌이다. 이렇게 충돌 감지는 그림 그릴 때, 한 발 이동할 때 감지된다.

지도에서 아주 먼 거리에 있는 놈과 거리 계산을 할 필요는 없다. 그래서 거리 계산이 필요할 정도로 접근을 했는지 여부를 이런 방법으로 확인할 수 있다. 격자의 넓이를 좀 더 키우고, 각 격자엔 몇 개의 유닛이 있는지, 어떤 유닛이 있는지만 표시한다. 충돌 감지 격자엔 유닛 1개만 들어간다면, 접근 감지 격자엔 유닛이 4개, 9개가 들어가는 식이다. 유닛은 자신이 속한 격자를 계산하고, 그 격자 주변 8개 격자에 있는 유닛들만 찾아 정확한 거리 계산을 한다. 유닛들이 이동할 때만 이 격자의 정보를 수정한다.

유닛은 격자의 정중앙에만 배치하지 않는다. 레드얼랏 시리즈에선 큰 격자 1개에 4개의 유닛이 정확히 4등분 된 작은 격자 중앙에 위치했었다. 그러다 보니 너무 질서정연했다. 큰 격자 1개엔 큰 유닛(전차/야포)이 1개 들어간다. 건물은 큰 격자 4개, 6개, 9개를 점유한다. 스타크래프트에선 큰 격자 1개에 작은 격자 4개, 격자 1개에 유닛 1개는 같지만 격자 안에서 위치가 좀 더 자유롭다. 그러니 자연스럽게 보인다. 에이지 오브 엠파이어에선 큰 격자 1개에 작은 유닛 격자 9개로 보인다. 건물은 큰 격자 단위로 배치 된다. 자연스러움은 이게 가장 높았다.





유닛이나 아이템이 많은 게임을 하다 보면 다중 선택 기능이 아쉬운 경우가 많다. 옛날엔 유닛 하나, 아이템 하나씩 선택했었다. 손가락도 아프고 시간 소모도 많다. 다중 선택 기능, 집합 선택 기능 구현은 그리 어렵지 않으니 게이머를 위해 편의를 제공하자. 배열들을 이용해 서로 링크를 걸어 두는 것에 불과하다.





성진국엔 처녀가 없다고 하지.
어릴 때 강간 당해서 그렇다.
강간은 아는 사람에게 당한다.
주로 애인, 선배, 친척, 스승이다.

성교육은 섹스/피임 교육이다.
강간 회피 교육도 성교육이다.
순결/정조 교육은 도덕교육이다.
처녀 싫어하는 남자는 쓰레기다.

한국이 강간, 불륜 천국인 줄 몰랐다.
한국놈들은 도덕성, 양심이 없다.
한국 구세대의 정신 상태는 썩었다.
정치인, 경제인, 언론인, 종교인, 예술인 믿지 마라.

반드시 실망할 것이다.