로그인

회원가입 | ID/PW 찾기

연재

게임 프로그래밍의 맛! 2,000 원을 벌고 책을 쓰다

게임 프로그래머 문기영의 ‘게임 프로그래머 이야기’ ③

ProgC 2013-10-05 08:16:12

이전 이야기

게임 프로그래머의 길, 게임을 잘하던 아이

고대하던 컴퓨터의 구입, 그리고 ‘동급생’을 해킹하다 


 

컴퓨터를 구입하고 나서 PC게임을 주로 즐겼고 그 이후로 오락실을 자주 가지 못하게 되었다. 하이텔을 이용하거나 사설 BBS에서 채팅을 하는 것이 오락실에서 게임을 하는 것보다 더 재밌었다.

 

정겨운 하이텔, 지금은 사라졌다.


하이텔 게제동(게임제작동호회)에 올라온 아마추어 제작자들의 게임을 하거나 채팅방에서 플레이 가능한 머드 게임을 하면서 시간을 보냈다. 그렇게 머드 게임을 밤낮으로 하다 보니까 타자를 빠르게 칠 수 있게 되었다.

 

오랜만에 <한메타자>를 설치해서 연습 없이 바로 테스트해 본 결과.

세월이 많이 지났지만 지금도 평균 분당 700타 이상을 칠 수 있고, 학교에 다닐 당시에는 분당 1,000타 이상 가능했었다. 물론 프로그래밍을 할 때는 이렇게 빨리 칠 이유가 없다. 속도보다 중요한 것은 로직의 견고함이기 때문이다.

 

 

※ 노트: 타자를 남들보다 더 빠르게 치는 방법이 있다. 예를 들어 ‘어머니’라는 글자를 치기 위해서 ㅇ,ㅓ,ㅁ,ㅓ,ㄴ,ㅣ를 차례대로 눌러야 ‘어머니’라는 글자를 완성할 수 있다. 이제 ㅇ와 ㅓ를 동시에 눌러보자. 보통 80~90% 이상은 ‘어’라는 글자를 완성시킬 수 있다. 그래서 ‘어머니’를 쓰기 위해서 여섯 번을 따로 누르는 게 아니라 두 글자씩 동시에 누르면 세 번 만에 글자를 쓸 수 있다. 메모장을 띄우고 ㅇ와 ㅓ를 동시에 누르고 ㅁ와 ㅓ를 동시에 누르고 ㄴ와 ㅣ를 동시에 눌러서 3번 만에 완성해 보자. 이게 익숙해지면 글자를 남들보다 더 빨리 칠 수 있다.

 

 

당시에는 네트워크를 위해 모뎀을 사용했는데 속도가 느릴 때는 100kb 파일 하나를 받는 데도 10분 이상을 기다려야 했다. 그렇게 오래 걸려서 파일 하나를 받고 게임을 시작하면 게임이 재미없더라도 모든 콘텐츠(?)를 다 즐길 때까지, 끝까지 플레이했다. 내려받는 데 걸린 시간이 무척 아까웠기 때문이다.

 

그렇게 시간을 보내다가 한양 공업 고등학교의 전자과에 입학했다. 당시에는 전자과에서 배우는 내용들이 컴퓨터와 얼마나 밀접한 관련이 있는지 알지 못했다. 지금 과거로 돌아가 공부할 수 있다면 아마 학교에서 배우는 모든 내용을 정말 열심히 공부하지 않았을까 싶다. 아무튼 당시에는 그런 것을 몰랐고 학교에서 배우는 내용은 나와는 아무런 상관이 없는 것이라고 생각했다. 학교에 들어가 교실에 앉아 있었는데 학교 선배들이 교실로 들어와서 질문을 하기 시작했다.

 

“컴퓨터 잘하는 사람!!!”

 

컴퓨터 잘하는 사람은 앞으로 나오라는 것이었다. 게임 외에 그나마 잘하는 건 컴퓨터밖에 없어서 앞으로 나갔다.

 

정보처리 기능반에는 정보처리 기능대회를 준비하는 학생들이 모여 있었는데 엑셀, 액세스, 워드, 그림 그리기 등등 컴퓨터를 두루 사용하는 방법에 대해 ‘최대한 빠르고, 가장 정확한 답을 내놓는 대회였다.

 

컴퓨터를 잘하는 것을 증명할 방법은 없고 실제로 게임 해킹이나 잡다한 지식만 있었지 제대로 할 줄 아는 건 별로 없었다. 선배들이나 선생님이 보기에 확실하게 컴퓨터를 잘한다고 보여줄 만한 것이 있긴 했다. 바로 타자 빠르게 치기였다. 그렇게 정보처리 기능반에 들어가게 되었다.

 

정보처리 기능반에 들어가서 엑셀이라는 것을 처음 접했는데 사실 이것은 옛날 어릴 적에 보던 로터스123과 너무 흡사했었다.

 

 

 ※ 노트: 로터스 123이란? (//ko.wikipedia.org/wiki/%EB%A1%9C%ED%84%B0%EC%8A%A4_1-2-3)

 

 

로터스123.(출처: //it.wikipedia.org/wiki/File:Lotus_123.gif)

 

데이터를 행과 열로 구분하고 수식을 사용해서 자동화가 가능했다. 특히 엑셀은 알면 알수록 너무 신기하고 재밌었다. 보통은 수식셀에 iif와 같은 논리를 작성하거나 sum()와 같은 함수를 이용해 합계를 구하는 등의 단순한 계산만이 가능하지만 VBA(VisualBasic for Applications)를 이용하면 그 안에 비주얼 베이직이라는 프로그래밍 언어를 사용해서 프로그래밍이 가능했다. 심지어는 게임을 만들 수도 있었다!

 

엑셀로 만들어진 게임.(출처: //carywalkin.ca/2013/03/17/arena-xlsm-released/)

 

기능대회를 준비하면서 엑셀을 많이 사용했었는데 내가 관심을 가졌던 부분은 VBA 쪽이었다. 어셈블리어보다 훨씬 쉬우면서 내가 원하는 로직을 작성하는 데는 이만큼 쉬운 언어가 없어 보였다.

 

 

 ※ 노트: 비주얼 베이직 //ko.wikipedia.org/wiki/%EB%B9%84%EC%A3%BC%EC%96%BC_%EB%B2%A0%EC%9D%B4%EC%A7%81

 

 

물론 베이직이라는 언어를 처음 사용해본 것은 아니다.(이 연재를 쓰면서 내가 살아왔던 모든 부분을 이야기하기에는 분량과 재미 면에서 어느 정도 뺄 부분은 제외했기 때문에 이 부분은 생략했다.) 어릴 때 GW 베이직을 간략하게나마 본 적은 있었기 때문에 비주얼 베이직도 나름 친근하게 다가왔던 게 아닐까 싶다.

 

 

Private Sub Form_Load()

    ' Hello, World라는 내용의 메시지 상자를 띄웁니다.

    MsgBox "Hello, World!"

End Sub

 

 

예를 들어서 위와 같은 비주얼 베이직 코드는 폼(Form)이 로드될 때 메시지 박스(MsgBox)를 화면에 띄우고 그 내용은 “Hello, World!”로 하는 로직이다. 이것을 실행하면 화면에 다음과 같은 메시지 박스가 나타난다.

 

화면에 메시지 박스를 띄워 본 결과.


비주얼 베이직을 사용해서 메시지 박스를 띄우는 것은 너무 쉽다. 반면에 C언어를 사용해서 메시지 박스를 띄우려면 같은 동작을 하는 반면에 너무 어려운 감이 있지 않다.

 

 

#include "stdafx.h"

#include "tigsample_withc.h"

 

#define MAX_LOADSTRING 100

 

HINSTANCE hInst;

TCHAR szTitle[MAX_LOADSTRING];

TCHAR szWindowClass[MAX_LOADSTRING];

 

ATOM MyRegisterClass(HINSTANCE hInstance);

BOOL InitInstance(HINSTANCE, int);

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);

INT_PTR CALLBACK About(HWND, UINT, WPARAM, LPARAM);

 

int APIENTRY _tWinMain(HINSTANCE hInstance,

                     HINSTANCE hPrevInstance,

                     LPTSTR    lpCmdLine,

                     int       nCmdShow)

{

UNREFERENCED_PARAMETER(hPrevInstance);

UNREFERENCED_PARAMETER(lpCmdLine);

 

MSG msg;

HACCEL hAccelTable;

 

LoadString(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);

LoadString(hInstance, IDC_TIGSAMPLE_WITHC, szWindowClass, MAX_LOADSTRING);

MyRegisterClass(hInstance);

 

if (!InitInstance (hInstance, nCmdShow))

{

return FALSE;

}

 

 

 

hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_TIGSAMPLE_WITHC));

 

// 기본 메시지 루프입니다.

while (GetMessage(&msg, NULL, 0, 0))

{

if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))

{

TranslateMessage(&msg);

DispatchMessage(&msg);

}

}

 

return (int) msg.wParam;

}

 

ATOM MyRegisterClass(HINSTANCE hInstance)

{

WNDCLASSEX wcex;

 

wcex.cbSize = sizeof(WNDCLASSEX);

 

wcex.style = CS_HREDRAW | CS_VREDRAW;

wcex.lpfnWndProc = WndProc;

wcex.cbClsExtra = 0;

wcex.cbWndExtra = 0;

wcex.hInstance = hInstance;

wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_TIGSAMPLE_WITHC));

wcex.hCursor = LoadCursor(NULL, IDC_ARROW);

wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);

wcex.lpszMenuName = MAKEINTRESOURCE(IDC_TIGSAMPLE_WITHC);

wcex.lpszClassName = szWindowClass;

wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));

 

return RegisterClassEx(&wcex);

}

 

BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)

{

   HWND hWnd;

 

   hInst = hInstance;

 

   hWnd = CreateWindow(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,

      CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL);

 

   if (!hWnd)

   {

      return FALSE;

   }

 

   ShowWindow(hWnd, nCmdShow);

   UpdateWindow(hWnd);

 

   return TRUE;

}

 

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)

{

int wmId, wmEvent;

PAINTSTRUCT ps;

HDC hdc;

 

switch (message)

{

case WM_CREATE:

MessageBox(hWnd, _T("Hello, World!"), _T("Caption"), MB_OK );

break;

case WM_COMMAND:

wmId    = LOWORD(wParam);

wmEvent = HIWORD(wParam);

// 메뉴 선택을 구문 분석합니다.

switch (wmId)

{

case IDM_ABOUT:

DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);

break;

case IDM_EXIT:

DestroyWindow(hWnd);

break;

default:

return DefWindowProc(hWnd, message, wParam, lParam);

}

break;

case WM_PAINT:

hdc = BeginPaint(hWnd, &ps);

// TODO: 여기에 그리기 코드를 추가합니다.

EndPaint(hWnd, &ps);

break;

case WM_DESTROY:

PostQuitMessage(0);

break;

default:

return DefWindowProc(hWnd, message, wParam, lParam);

}

return 0;

}

 

INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)

{

UNREFERENCED_PARAMETER(lParam);

switch (message)

{

case WM_INITDIALOG:

return (INT_PTR)TRUE;

 

case WM_COMMAND:

if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)

{

EndDialog(hDlg, LOWORD(wParam));

return (INT_PTR)TRUE;

}

break;

}

return (INT_PTR)FALSE;

}

 

 

실제로 메시지 박스는 빨간색으로 처리 한 부분밖에 없다. 나머지 코드들은 윈도우 프로그램을 띄우기 위한 베이스 코드다. 이러한 베이스 코드는 거의 대부분의 응용 프로그램들이 같은 모양을 하고 있기 때문에 모든 것을 기억해야 할 필요성은 없지만 이런 사소한 것 하나하나를 모두 프로그래머가 직접 작성하거나 관리해야 한다는 게 프로그래밍 초보자들에게 어려움으로 다가오는 건 아닐까 싶다.

 

당시에 VBA를 접하고 나서

 

‘이건 좀 공부해 볼 만한 가치가 있겠는데…

 

라는 생각이 들었고 우연한 기회로 당시에 기능대회에 관련된 호서대 교수님으로부터 공짜로 책을 받았다.

 

반장호 저자가 쓴 비주얼 베이직 책.


이 책에 나오는 예제들을 하나하나 집에서 작성하고 실행해 보면서 본격적으로 프로그래밍을 공부하게 되었다. 물론 기능대회 선수로서 본업(?)에 충실하기 위해 엑셀, 액세스, 워드, 그림판 혹은 코렐드로우 같은 잡다한 것들을 모두 공부했다. 하지만 이것들은 내가 프로그래밍을 공부하는 데는 별로 상관이 없다고 생각하고 열심히 하진 않았다.

 

비주얼 베이직을 이용해 화면에 그림을 그리는 방법을 알게 되었고 키보드로부터 입력을 받아 내가 원하는 로직을 실행하게 하는 방법을 터득했다.

 

그리고 가장 처음으로 만든 프로그램이 바로 ‘이사쿠라는 게임 캐릭터가 화면 좌우로 움직이는 것이었다. 나는 순수하게(?) 일본 엘프(ELF)사의 게임에 영향을 많이 받았다….

 

게임 <취작>을 처음 실행하면 나오는 장면. 엘프의… 운명에 대한 자세한 설명은 생략한다.


당시에 애니메이션을 구하기 위해 게임을 계속 실행하면서 캐릭터가 움직이는 애니메이션을 하나하나 캡처했다. 그리고 키보드 상하좌우 키를 누르면 이사쿠가 화면을 걸어다니는 프로그램을 작성했다.

 

“화면에 무언가를 그리고 컴퓨터 사용자의 입력을 받아 거기에 맞게 어떠한 로직을 수행한다.”

 

컴퓨터 게임을 만들 수 있는 기본을 드디어 알게 된 것이다. 내가 처음으로 만든 게임 같아(?) 보이는 데모를 완성해 친구들과 선생님께 보여드렸는데 반응은 괜찮았던 것 같다.

 

“이 캐릭터 어디서 많이 본 것 같은데…”

 

물론 거기에 대한 대답은 하지 않았다. 너도 알고 나도 알고 다 아는 거니까….

 

그리고 한 가지 더 깨달은 사실은 내가 궁금해 하던 거의 모든 질문에 대한 답은 책에 있었다는 점이다.

 

나는 게임 하는 게 너무 좋았고 그걸 더 잘하려면 공부하는 시간이 너무 낭비 같았다. 그리고 공부를 안 하다 보니까 더 공부를 못하게 되었고, 게임은 열심히 하다 보니까 더 잘하게 되었다. 그런데 내가 좋아하는 게임을 만들려고 하니까 공부를 해야 한다는 최악의 상황이 온 것이다.

 

‘난 진짜 공부는 죽어도 하기 싫은데…

 

공부는 싫었지만 게임은 너무 좋았다. 공부는 열심히 하지 않았지만 프로그래밍하는 것 만큼은 그나마 버틸 만했다. 왜냐하면 내가 공부한 만큼 그 수준에 맞는 게임을 만들 수 있었으니까….

 

첫 번째 비주얼 베이직 책을 모두 읽었지만 게임을 만드는 방법에 대한 내용은 사실 많지 않았다.  그래서 관련된 책을 읽으려고 서점에 갔지만 거의 모든 게임 제작 관련 책들은 C언어로 작성되어 있었다.

 

‘C언어를 모르면… 아무것도 할 수 없어.

 

하지만 당시에 C언어를 배우기에는 시간이 너무 없었고 비주얼 베이직으로도 충분히 내가 원하는 수준은 만들 수 있었기 때문에 계속해서 비주얼 베이직을 공부했다.

 

<비주얼 베이직 프로그래밍 바이블>.


위의 책은 비주얼 베이직으로 할 수 있는 거의 대부분을 총망라한 것이었는데, 특히 게임 프로그래밍에 관련이 있는 부분이 많았다. 그래서 이 책에 있는 모든 소스를 입력해 보면서 프로그램을 만들어 보았다. 무식하면 겁 없다는 말도 있듯이 잘 모르니까 그냥 무식하게 프로그래밍 공부를 했다.

 

그렇게 비주얼 베이직을 게임 프로그래밍용(?)으로 공부하면서 교내 프로그래밍 대회에서 1등도 했었다. 다른 친구들은 멀티미디어 재생기와 같은 윈도우 응용 프로그램을 만들었지만 나는 게임이 좋았기 때문에 게임 프로그램을 제작했다. 제대로 완성된 게임을 만든 건 그때가 처음이었다. <UFO 찾기>라는 게임이었는데플레이 방식은 두더지 잡기 게임과 같았다.

 

<UFO 찾기> 게임 영상

   

[새 창에서 영상보기]


 

UFO가 화면에서 이리저리 움직이다가 사라지는데 마지막으로 사라진 위치를 마우스로 클릭해서 UFO를 찾아내면 UFO가 폭파되면서 점수를 따는 게임이었다.

 

게임은 다른 응용 프로그램과는 다르게 리소스가 많이 들어가는 컴퓨터 프로그램이다. 화면에 그림이 나와야 하니까 이미지가 많이 필요했고 상황에 맞게 멋진 사운드가 나와야 하기 때문에 음악, 사운드 이펙트가 많이 필요했다. 게다가 스토리를 위해 허구의 글을 쓸 줄 알아야 되기도 했으며 거기에 맞는 로직도 많이 작성해야 했다.

 

‘정말… 힘든데… 재밌다.

 

라는 게 게임 프로그래밍의 맛이었다. 그 이후로도 <옐로우맨>이라는 게임(<너구리>와 같은 게임)도 만들어서 넷츠고 자료실에 올리고 <Duake>라는 게임도 만들어서 돈을 받고(?) 팔기도 했다. 가격은 1,000 원이었는데 무려 2명이나 샀었다!

 

게임 <옐로우맨>의 스크린샷.


기능대회 선수였고 대회 준비도 나름 열심히 했지만 프로그래밍이 더 재밌었다. 운 좋게도 서울 기능대회에서 3등을 하고 지방 기능대회에서는 장려상으로 끝났지만 (사실 난 이 부분에 있어서는 불만이 좀 많다. 심사가 끝나기 전까지는 내가 1등이라고 생각했지만 현실은 실력과는 별개라고 보인다.) 별로 관심이 없었다. 그러다가 우연한 기회로 책을 쓰게 되었다. 그때 고3이었다.

 

비주얼 베이직으로 게임을 만드는 사람이 국내에는 거의 없었고 나는 2,000 원이나 번 사람이었기 때문에 나름 자부심이 있었다.

 

피씨북에서 출판된 <비주얼 베이직 6 게임 만들기>.


책을 처음으로 쓰면서 나름대로 내가 공부했던 내용을 모두 다뤄 보려고 했지만 역시 모르는 게 많았기 때문에 공부하면서 정리한 것이 많았다. 책을 쓰는 데는 3개월밖에 걸리지 않았다. 무식하면 겁 없다라는 말처럼 내가 쓴 내용이 모든 상황에서 맞는지 틀리는지 알려면 내가 그 모든 상황을 알아야 하는데, 이 책을 집필할 당시에는 내가 아는 게 많지 않았다. ‘일단 내가 알고 있는 걸 열심히 쓰고 보자!는 정신으로 썼기 때문에 3개월 만에 쓸 수 있었다고 생각한다. 그 이후로 <게임 개발 테크닉>을 쓸 때는 1년 이상을 소비했다.

 

재미있는 에피소드가 있다면 이 책에 내가 이런 그림까지 그려서 넣었다는 점이다.

 

펩시맨.

 

오해하지 말자… 정말로 나는 손 그리기가 어려워서 저렇게 그렸다. 손 위치도 자세히 보면 배꼽임을 추측할 수 있다.

 

비주얼 베이직으로 게임 프로그래밍이 가능했지만 여전히 문제는 있었다.

 

‘왜 내가 만든 건 느리지?

 

당시에는 프로그램이 완벽하게 동작하는 건 중요하지 않았고 일단 동작하는 게임을 만들어 내는 것이 중요했다. 하지만 게임을 몇 개 만들어 보니 반복적으로 코드를 작성하는 부분도 많았고 특히 너무 느렸다. 그러다가 갑자기 그런 생각이 들었다.

 

‘게임은 C언어로 만들어야 해.

 

그래서 C언어를 공부하기로 마음 먹었다.

  • 게임 프로그래머의 길 ① 게임을 잘하던 아이

  • 고대하던 컴퓨터의 구입, 그리고 ‘동급생’을 해킹하다

  • 게임 프로그래밍의 맛! 2,000 원을 벌고 책을 쓰다

  • 게임 프로그래밍을 위해 C언어를 배우다

  • 고3에 첫 게임 도전, 실전으로 게임 프로그래밍을 배우다