본문 바로가기

렌파이/시스템 추가하기

제작자 정의 디스플레이어블 - 2 미니게임 퐁 스크립트 분석


개요


CDD 두 번째 예제 스크립트 분석입니다. 이번 스크립트는 렌파이 길라잡이 게임에 있는 미니게임 퐁의 스크립트입니다. 렌파이 설치 폴더/tutorial/game/demo_minigame.rpy 파일에서 찾아볼 수 있습니다. 일단 런처를 켜고 튜토리얼을 실행해서 minigames 메뉴를 누르고 퐁 게임을 실행해 몇 판 해본 뒤에 이 글을 읽으시는 게 이해에 도움이 될 겁니다.

또한 이 스크립트는 미니게임을 만들기 위한 스크립트라 지난번에 봤던 스크립트와 다른 점이 많습니다. 두 스크립트를 비교해서 살펴보세요.


expand source를 클릭하면 엄청난 양의 스크립트가 펼쳐집니다.
지난 글은 괜찮았지만 이번 글은 물흐르듯 읽으면 이해하기 힘듭니다. 옆에 스크립트를 펼쳐놓고 스크롤을 위 아래로 왔다갔다 하면서 그때 그때 내용을 확인하세요.

물론 그런다고 해서 단번에 이해되는 글은 아닙니다... 저도 이 글을 쓰겠다고 이틀 동안 이 스크립트만 붙잡고 있었네요-_-;




분석


image bg pong = "pong_field.png"

퐁 게임판을 bg pong 으로 배정했습니다.

게임판을 제작자 정의 디스플레이어블 자체에서 띄우지 않고 렌파이 명령문 scene으로 따로 띄우려고 이렇게 설정했나보네요.


class PongDisplayable(renpy.Displayable):

제작자 정의 디스플레이어블 PongDisplayable을 만들기 위해 renpy.Displayable 상속합니다.





paddle(68, self.playery, 68 + self.PADDLE_WIDTH)
paddle(724, self.computery, 724)

바로 위에서 만든 함수를 이용하여 판때기를 두 개 그립니다. 하나는 플레이어 것이고 하나는 컴퓨터 것입니다.

그리기도 하고 또 여러 상황을 처리하기도 하겠죠.

ball = renpy.render(self.ball, 800, 600, st, at)
r.blit(ball, (int(self.bx - self.BALL_WIDTH / 2),
              int(self.by - self.BALL_HEIGHT / 2)))

공을 그립니다.

self.bx 와 self.by 값은 계속 변하니 공 위치는 스크립트에 적은 대로 처리됩니다.

부딪히면 부딪힌 대로 튕겨나가면 튕겨나가는대로 공이 움직이는 모습이 그려지겠죠.

뒤에 공의 가로 세로 길이를 둘로 나눈 값을 뺀 건 공의 한 가운데를 기준으로 삼아서 x, y 값에 따라 움직이게 하려고 저렇게 처리한 듯.


player = renpy.render(self.player, 800, 600, st, at)
r.blit(player, (20, 25))

player라는 글씨를 그립니다.

eileen = renpy.render(self.eileen, 800, 600, st, at)
ew, eh = eileen.get_size()
r.blit(eileen, (790 - ew, 25))

Eileen이라는 글씨를 그립니다.
get_size를 이용한 건 글씨를 그릴 위치를 계산하기 위해서입니다.

if self.stuck:
    ctb = renpy.render(self.ctb, 800, 600, st, at)
    cw, ch = ctb.get_size()
    r.blit(ctb, (400 - cw / 2, 30))

공이 판때기에 붙어있는 상태, 즉 게임을 시작하기 전이라면
Click to Begin 그림을 그립니다.
          
if self.bx < -200:
    self.winner = "eileen"
    renpy.timeout(0)

공의 x 위치가 -200이면, 즉 왼쪽 화면 밖으로 아예 나가버리면
승자는 아이린입니다.
renpy.timeout(0) 은 승자를 알리는 이벤트 메소드가 확실히 호출될 수 있도록 합니다.
이게 딱 -200이 아니라 딱 0이었으면 아마 플레이어가 공을 놓쳤다는 걸 제대로 인식하기도 전에 미니 게임 화면이 사라지니 약간 당황스러울 수도 있을 겁니다.

elif self.bx > 1000:
     self.winner = "player"
     renpy.timeout(0)

오른쪽 화면으로 아예 나가버리면 플레이어가 승자입니다.
 
renpy.redraw(self, 0)
다음 프레임을 바로바로 표시할 수 있도록 즉시 화면을 다시 렌더링합니다.

return r
Render 객체를 반환합니다.

_M#]



'이게 뭔 소리죠'라고 물으셔도 할 말이 없습니다; 글로만 하는 설명에도 한계가 있고 또 스크립트도 복잡해서 이것만 보고는 절대 전부 이해할 수 없을 겁니다. 이 내용은 참고만 하면서 스크립트를 이해하시는 게 훨씬 도움이 될 것 같네요.

0. 매뉴얼을 보고 CDD가 뭐하는 건지 대충만 읽어봅니다. 물론 처음 보면 무슨 소린지 알 수 없습니다. 그래도 그냥 읽습니다.
1. 미니 게임을 직접 해봅니다.
2. 이 게임을 CDD로 만들기 위해 필요한 것이 무엇인지 생각합니다.
3. 이 게임을 구현하기 위한 스크립트를 큰 줄기만 머릿속으로 대충 짜봅니다. 컴퓨터에 대충 끄적여도 봅니다.
4. 전혀 이해가 안 가는 이 글을 읽습니다
5. 게임을 다시 해보면서 어떤 변수가 무엇을 가리키는지, 게임에서 어떻게 적용되는지 정리합니다.
6. 스크립트를 다시 봅니다.
7. 이해가 안 가는 이 글을 읽고 그나마 손톱 만큼 도움이 되는 말을 부분적으로 참고하면서 스크립트를 분석합니다.
8. 5번부터 7번 과정을 반복합니다.


이 과정을 거치면 어느 정도 이해하는 데에 도움이 되지 않을까 싶습니다.

제일 좋은 방법은 CDD를 이용하여 직접 새로운 디스플레이어블을 만들어보는 것이겠지만요.


지적 질문 태클 환영합니다.

참고

렌파이 매뉴얼 - 제작자 정의 디스플레이어블

min(), max(), abs() (각 함수에 대한 설명은 ctrl+F로 찾으세요)

pygame.MOUSEBUTTON(영어)