8.1 절차지향형으로 개발하기 1
Last updated
Last updated
바로 아래 화면의 우리가 만들게 될 게임의 최종 결과물을 먼저 보고나면 코드 이해가 훨씬 쉬울 것이다. 다만, 이번 과의 맨 서두에서 언급했듯이 난이도 조절을 위해 오리지널 게임의 기능동작 전체 다를 구현하지 않을 예정이고, 대신 우리는 이 게임은 두 가지 프로그래밍 패러다임(, ) 각각을 사용한 두 가지 버전으로 만들어 볼 예정이다. 먼저 이전 절에서 예고했듯이 나만의 객체를 만들고 활용하는 것으로 배워 활용해 봄으로써 이제는 보편화되어 사용되는 객체지향형이 기존의 절자지향형과 어떠한 현격한 차이를 가지는지를 이해하는 것이 이번 장의 과업이라고 할 수 있겠다.
우리는 이미 5-7장 통해 3개의 게임제작을 소화했기에 이미 튜토리얼 수준을 넘어섰다 볼 수 있다. 따라서, 이번 장부터는 이미 설명한 기본적인 내용에 대한 자세한 중복설명없이 빠르게 핵심적인 코딩에 대한 설명 위주로 진행하는 것이 여러분을 존중(?)하는 것이라 생각한다.
참고로 이번 게임제작에 필요한 이미지, 사운드 등의 리소스들은 이곳에서 다운로드 할 수 있으며 게임에 필요한 이미지 파일들은 코드에서 사용한 이름 그대로 imgaes 폴더에 이미 저장되어 있고, 효과음은 sounds 폴더에 저장되어 있다고 간주하고 시작하겠다. 또는 커스텀 뮤 에디터를 사용 중이라면 뮤 에디터의 작업 디렉토리인 (사용자계정)\mu_code\examples\pygame_zero 안에서 복사해 올 수 있다.
우선은 먼저 절차지향형으로성의 개발을 시작해 보자. 배틀시티게임은 이전에 만들어 본 블록격파 게임과 트윈비 게임을 섞어놓은 게임과 같다.
벽더미의 구성을 자세히 살펴보자. 벽 한개의 크기는 가로세로 50픽셀(WALL_SIZE)의 정사각형이다. 이 벽이 가로세로 800x600픽셀 크기의 화면에 몇 개가 들어갈지는 WIDTH / WALL_SIZE 와 HEIGHT / WALL_SIZE 계산으로 쉽게 알 수 있다. 궁금한 점은 왜 그 계산식을 int()라는 내장함수에 넣어 사용했는가인데, 그 이유는 파이썬에서의 나눗셈 연산은 결과를 항상 실수형(float)으로 값을 되돌려 주기 때문이다. 우리는 그 연산결과를 다시 range() 내장함수에서 사용할 건데, 이 함수의 인자값(argument)은 또 항상 정수형(integer)을 넘겨주어야 한다. 결국 int() 함수의 목적은 실수값을 정수값으로 변환해 주는 함수인 것이다.
34번 라인에서 전체 세로열에서 2를 뺀 것은 위의 게임결과 화면에서 보면 알 수 있듯이, 화면 전체에 전부 벽을 놓을 게 아니라 적 탱크(enemy)가 위치할 화면 맨 윗 열(row)와 내 탱크(tank)가 위치할 화면 맨 아래 하단의 한 열(row)을 비워두기 위한 목적이다. 그리고, 17번 라인은 이 게임이 벽을 다 부시는 블록격파(?) 게임은 아니고, 상대 탱크를 포로 맞춰 없에는게 과업인 게임에서 일부러 화면전체에 벽을 가득채우지 않고, 군데군데 랜덤하게 비워두기 위해서 넣은 코드이다.
다음 절부터 위에 언급된 3개 함수의 구현체를 만들어보기로 하고, 지금은 그 함수들을 제외한 나머지 코드들에 대해 더 검토해보자. 지금까지 모든 과정을 함께 공부해 온 여러분의 실력상 대부분은 이전에 이미 배웠던 것들의 약간의 변형이라 찬찬히 드려다보면 대부분 이해가 될 것으로 믿는다. 약간 낯선 부분이 있다면, bullet_delay_cnt, enemy_move_cnt 두 변수에 관한 것일 것 같다. 두 변수값은 다시 BULLET_DELAY, ENEMY_MOVE_DELAY 이란 매크로(Macro)값으로 채우고 있다(79, 91라인). 변수명이나 매크로명에서 유추할 수 있듯이 뭔가 의도적으로 코드실행의 지연(delay)을 주기 위한 것으로 유추할 수 있다.
왜 지연이 필요한가? 그렇다. 근본적인 이유는 트윈비에서도 유사한 상황이 있었는데 바로 update 콜백함수의 호출속도 (60FPS) 때문이다. 따라서, bullet_delay_cnt 는 대포알의 너무 빨리도 많이도 나가지 않게 하기 위한 목적이고, enemy_move_cnt 는 적 탱크들은 사람이 조작하는게 아니라 컴퓨터에 의해 자동조작 운행이 될 예정인데 이 때 너무 빠른 속도로 운행되지 않게 하기 위한 목적이다. 결국, 대표알이든 탱크 움직임이든 게임성을 저해하지 않게 적당해야지 대전형 게임에서 어느 한쪽이 지나친 우위를 점하면 게임성이 떨어져 재미가 없어진다는 건 누구나 아는 상식이다.
마지막으로 컴퓨터에 의한 적 탱크의 자동조정의 아이디어는 3가지 행동(움직하기, 방향전환, 포쏘기)을 무작위화(램덤화)하는 것이고, 이 때 움직이는 시간에 대해서도 랜덤화함으로써 좀 더 변칙적인 행동을 유도한다. 그럼, 이제 다음 절에서부터 우리의 3가지 사용자 함수의 구현체를 구체적으로 채워보도록 하겠다.
위의 37번째 라인까지 코드는 게임무대의 배우객체들을 생성하는 것이다. 장애물 벽더미(walls), 주인공 탱크(tank)와 3대의 적 탱크(enemies)를 생성한다. 벽더미를 만드는 부분은 기존의 블록격파 게임에서 블록생성 부분과 크게 다르지 않다. 다만, 벽더미의 생성 알고리즘이 일부 다를 뿐이다.
현재는 절차지향형으로 개발하기로 했기때문에 필요한 기능들을 함수화(functionalize) 할 필요가 있다는 것을 배웠다. 이번 게임도 함수화를 진행해보자. 코딩에서 항상 염두해 두어야 할 점은 나와 남을 위해 가독성이 좋고, 추후 수정에 대비한 유지보수(maintenance) 측면을 늘 고려해야 한다. 사실상 함수도 그러한 이유로 출연했다고 할 수 있다. 함수의 주목적은 코드중복을 줄이는 것으로 이전 트위비 게임에서는 안타깝게도 공통 기능적으로 함수로 묶을 만한게 마땅지 않았지만, 이번 배틀시티 게임에서는 그 목적에 부합하는 함수 3개(move_player, fire_bullets, collide_bullets)를 만들 수 있다.