6.1 게임무대에 배경과 배우 등장시키기
Last updated
Last updated
이번 절에는 코딩의 첫 시작이니 우리 게임의 무대인 게임화면에 배경입히고, 게임무대의 배우들인 블록더미, 블록더미의 블록을 격파하게 될 공, 사용자의 조작이 필요한 공을 반사시킬 반사판(영어로는 패들(paddle)이라 부르나, 우리는 bar라는 이름을 사용함)을 등장시켜보는 것까지 해보겠다. 우리가 만들게 될 게임의 최종 결과물은 다음과 같다. 결과화면을 먼저 보고나면 코드 이해가 훨씬 쉬울 것이다.
🔢 21번 라인의 게임의 배경을 그리는 부분은 이미 본서의 첫 게임을 만들면서 5.1절 절 전체를 할애해 설명했기 때문에 독자분들이 충분히 잘 이해하고 있을 것으로 추가적인 설명이 필요 없을 것 같다. 참고로 이번 게임제작에 필요한 이미지는 이곳에서 다운로드 할 수 있다. 또는 커스텀 뮤 에디터를 사용 중이라면 뮤 에디터의 작업 디렉토리인 (사용자계정)\mu_code\examples\pygame_zero\images 안에서 복사해 올 수 있다. 다만, 기억해야 할 것은 5.1절에서 이미 언급했듯이, 우리가 코딩시 사용하는 이미지들은 항상 코딩하는 소스코드가 존재하는 폴더(디렉토리)의 하위 폴더로서 존재해야 하며 폴더명은 images 여야만 정상인식되어 사용할 수 있다 규칙을 다시한번 기억하면 좋겠다.
🔢 6-7라인에서 먼저, 두 주연배우인 볼(ball)과 반사판(bar)의 배우(Actor)객체를 만들면서 이미지이름과 이 이미지가 화면상에 위치할 위치정보, 이렇게 두 가지의 생성인자값을 사용해 생성하였다. 이어서 공이 닿아 부서지게 될 블록더미라 불리는 블록객체들의 모음을 만들어야 하는데, 보기에는 한 덩어리처럼 보여도 공이 닿아 부셔져야 하는 블록 하나하나가 다 사실은 독립된 블록객체이다. 그 전에 먼저 위에서 살펴본 본 게임의 최종화면을 잘 관찰해보면 블록더미는 게임화면(screen) 최상단에서 어느 정도의 빈 공간의 간격를 떼고 이후에 블록들이 하나의 더미를 이뤄 모여있는 구조와 동시에 공을 반사시키는 반사판 역시도 화면 최하단에서 윗방향으로 어느 정도 간격을 뗀 위치를 갖는 특징을 발견했을 것이다. 이 어느정도의 간격은 우리가 임의로 정한 것이고, 5번 라인에서 이를 의미하는 변수값으로 GAP_FROM_SCREEN 라고 이름짓고, 50px(픽셀)의 간격을 할당하였다.
이제 블록더미의 구성을 자세히 살펴보자. 가로로 8개씩, 세로로 3열의 형태를 갖고 있는데, 2번 라인에서 정한 게임화면의 가로크기가 800px(픽셀)이기 때문에, 그 안에 8개의 블록을 연이어 붙혀 한 행으로 놓으려면, 예상하기로 블록 1개의 가로길이는 100px 이어야할 것으로 유추할 수 있다. 그 예상이 맞았고, 1개의 블록객체는 (블록 이미지)크기는 가로세로(100 x 32px) 크기다.
🔢 이제 이 정보를 기반으로 코딩을 하면 되는데, 먼저 독립된 각각의 블록객체를 차곡차곡 담아서 하나의 블록더미를 만들 공간이 필요한데 이 경우, 리스트(List)라는 데이터 형태가 딱이고, 따라서 10번 라인에서 blocks라는 빈 리스트를 하나 생성했다. 이후, 이 리스트에 각각의 생성된 독립적인 블록 하나하나들을 화면에 보여줄 순서에 맞게 차곡차곡 담으면 되는데, 이 때 화면에 보여지는 순서라는 것은 행을 먼저 쌓고 이후 열을 쌓는 순서로 하던, 반대로 열을 먼저 쌓으면서 행을 늘려가는 방식으로 쌓던 어떤 방식이든 관계는 없다 본인이 임의로 택한 방식으로 하면 되는데, 본 서에는 먼저 한 행을 다 쌓고, 이후 다음행으로 넘어가서 쌓는 방식으로 할 것이며, 이런 상황에 적합한 코딩이 11-12라인에서 볼 수 있는 2중 for 루프문(nested loop statements)이다.
for 문 안에 for 문이 들어가는 구조로, 블록더미 한 행 안에서 8개의 블록을 채우는 안쪽 루프와 그 다음행으로 넘어가는 즉 행을 옮기는 바깥쪽 루프를 번갈아 돌면서 생성된(13~17번 라인) 각각의 독립된 블록객체를 리스트 안에 차곡차곡 채우는(18번 라인) 것이다. 리스트에 값을 추가하는 함수는 List 객체(List 자체가 사실은 그 본질은 객체였다 라는 것도 참고로 알아두자)가 제공하는 멤버함수인 append 함수를 이용할 수 있다.
먼저 range 함수는 파이썬언어의 내장함수로 아주 간략화시켜서(본서는 순한맛 버전으로 일단 이정도만 알고 넘어가고) 설명하면, 다음과 같은 행동을 하는 함수이다.
range(n) 함수
파라미터 n: 양의 정수값
리턴값: 0부터 n-1 까지의 연속적인 정수값을 같는 수의 모음을 리턴
예시로 list(range(4))를 실행시켜 어떤 결과 리턴되는지 확인해보자.
range함수의 결과가 range객체들이기 때문에 list함수로 감싸서, 이를 list형태로 변환해 보는게 우리가 그 실체를 한눈에 이해하기 편한다. 즉, 인자로 4를 넘기면, 0부터 시작해서 3까지의 연속적인 정수값을 담겨져 있다라고 이해할 수 있다.
이제 for-in 문법구문을 이해해 보자. 여기서 "연속형 객체"라는 것은 리스트 같이 여러 개의 값을 값는 데이터타입을 말한다.
for (연속형 객체)아이템 값의 변수명 in 연속형 객체:
<반복할 내용>
다음의 코드 예시를 보면 이해가 빠를 것이다. for-in 구문 안에 내용을 총 4회 반복 수행할 것이고, 매 반복 회차마다 리스트 안에 값(이를 아이템이라 부름)을 우리가 정한 변수명으로 하나하나씩 순차적으로 자동적으로 가져오게 되면서 반복을 수행한다.
위에 예시로 사용한 for item in [0, 1, 2, 3]은 사실은 for item in range(4) 코드와 동일한 것으로 간주할 수 있다. 여기까지 이해가 되었다면, 이제 11번 라인의 for block_row in range(4): 구문을 이해할 수 있을 것이다. 총 4회 반복을 수행하되 매 반복회차마다 block_row 변수값이 차례대로 0~3까지의 값이 될 것이고, 12번 라인의 for block_col in range(8):은 총 8회 반복을 수행하되 매 반복회차마다 block_col 변수값이 차례대로 0~7까지의 값이 될 것라는 것을 예상할 수 있다. 이것을 활용해서 15라인에 block_row, block_col변수값을 활용해 Actor 객체생성시 위치값을 산정할 수 있게 되는 것이다.
🔢 블록객체 생성시 활용하는 파라미터 중에 전에 배우지 않은 1가지가 더 남아 있는데, 16번 라인에 있는 anchor에 관한 것이다. anchor는 우리가 엔트리 블록코딩에서 이미 사용해봤던 각 오브젝트마다 갖고 있는 중심점을 말하는 것으로, 파이게임에서는 이를 anchor라고 지칭하고, 엔트리코딩에 코딩시 때로는 그 중심점의 이동이 필요했었던 것처럼, 텍스트코딩에서도 마찬가지 이유로 필요할 때가 있는데, 지금 이순간이 바로 그 순간이다. 엔트리 때도 그랬지만, 기본적으로 Actor 객체의 디폴트 anchor는 이미지의 한 가운데(center) 중심이다. 우리는 이것을 아래 화면상에 보이는 좌측최상단(topleft 또는 lefttop)으로 옮기고자 하며, 이 경우 16번 라인에서 보는 것과 같이 anchor=('left', 'top') 이란 인자값을 넘기면 되는 것이다.
왜 anchor를 옮겨야 하느냐라고 묻는다면, 게임화면의 좌측면에 딱 붙혀서 거기서부터 출발해 블록객체 더미를 화면에 그려나가고 싶어서이다. anchor를 옮기지 않고 화면에 블록객체 더미를 그려보면 이 말의 의미를 곧바로 알게 되는데, anchor의 디폴트값이 이미지의 중앙이므로, anchor를 옮기지 않고 그리면 아래의 그림의 상단처럼 블록의 절반이 잘려나간 채로 그려지기 때문이다.
🔢 이제 마지막으로 draw 함수 안에서 블록더미를 그리는 23-24라인의 코드를 살펴보자. 우리가 이미 배운 for-in 반복문을 사용하고 있고, blocks 라고 하는 독립된 블록객체들을 모아놓은 리스트 안에 존재하는 블록객체 하나하나씩 꺼내서 화면에 일일이 그리는 일을 리스트 안에 객체가 다 꺼내질 때까지 반복하고 있다는 것을 알 수 있다.
본 장의 1절부터 기존에 공부하지 않았던 개념이 몇 개 등장해 예상보다 설명이 많아지고, 길어진 면이 있다. 다음 2절에서는 등장인물의 움직임 알고리즘 위주로 재미있게 공부해보자.
🔢 이제 반복문을 이해해보도록 하자. 를 거쳐오지 않은 분들을 위해 for-in range() 구문을 한번 더 설명하면, 반복에 사용하는 이 구문은 우리가 엔트리 블록코딩에서 숱하게 사용해 봤던 "몇 회 반복하기" 코딩블럭이라고 생각하면 될 것이다. 일단 저 구문에서 for-in과 range 함수를 분리해서 살펴보는게 필요하다.