[pygame]시뮬레이트(Simulate) - 1

서희찬·2021년 4월 15일
0

이제 세번째 게임 시뮬레이트 게임을 만들어보자.
이름만 보면 뭔게임이지?
하는 생각이 들것인데 이것도 마찬가지로 플레이해보면 아하! 아니 설명만들어도 아하 ! 할것이다.

시뮬레이트는 플레이어가 화면에 무작위로 나타난 색이 다른 네 버튼의 순서를 기억하고 있다가 맞히는 게임이다!

즉! 버튼의 순서를 기억하고 그냥 따라 누르면 되는 기억력게임이다!!
플레이어는 불이 들어온 순서대로 버튼을 눌러서 방금 본 패턴을 그대로 반복하면 된다.
이를 위해서는 4개의 사운드 파일이 필요하다.
4개의 타일이라고 했으니
각 사운드별로 클릭하였을때 소리가 나게 만들어주기위함이다.

invpy.com/beep1.ogg
~ beep4 까지 다운받으면된다.

그럼 시작해보자 !

일반적인 시작 부분

# Simulate (a Simon clone)
# By Al Sweigart al@inventwithpython.com
# http://inventwithpython.com/pygame
# Released under a "Simplified BSD" license

import random, sys, time, pygame
from pygame.locals import *

FPS = 30
WINDOWWIDTH = 640
WINDOWHEIGHT = 480
FLASHSPEED = 500 # in milliseconds
FLASHDELAY = 200 # in milliseconds
BUTTONSIZE = 200
BUTTONGAPSIZE = 20
TIMEOUT = 4 #버튼을 누르지 않으면 4초후 게임 종료 

#                R    G    B
WHITE        = (255, 255, 255)
BLACK        = (  0,   0,   0)
BRIGHTRED    = (255,   0,   0)
RED          = (155,   0,   0)
BRIGHTGREEN  = (  0, 255,   0)
GREEN        = (  0, 155,   0)
BRIGHTBLUE   = (  0,   0, 255)
BLUE         = (  0,   0, 155)
BRIGHTYELLOW = (255, 255,   0)
YELLOW       = (155, 155,   0)
DARKGRAY     = ( 40,  40,  40)
bgColor = BLACK

XMARGIN = int((WINDOWWIDTH - (2 * BUTTONSIZE) - BUTTONGAPSIZE) / 2)
YMARGIN = int((WINDOWHEIGHT - (2 * BUTTONSIZE) - BUTTONGAPSIZE) / 2)

버튼 설정하기

# 4개의 버튼에 대한 Rect 객체 
YELLOWRECT = pygame.Rect(XMARGIN, YMARGIN, BUTTONSIZE, BUTTONSIZE)
BLUERECT   = pygame.Rect(XMARGIN + BUTTONSIZE + BUTTONGAPSIZE, YMARGIN, BUTTONSIZE, BUTTONSIZE)
REDRECT    = pygame.Rect(XMARGIN, YMARGIN + BUTTONSIZE + BUTTONGAPSIZE, BUTTONSIZE, BUTTONSIZE)
GREENRECT  = pygame.Rect(XMARGIN + BUTTONSIZE + BUTTONGAPSIZE, YMARGIN + BUTTONSIZE + BUTTONGAPSIZE, BUTTONSIZE, BUTTONSIZE)

앞선 슬라이드 퍼즐 처럼 버튼이 있다.
이 시뮬레이트 게임은 4개의 버튼 난 영역이 있고 플레이어가 클릭 하였을 때 어떤 동작을 해야하는지 처리해야하는 코드가 있어야 할것이다!
그렇다면 ! 당연빠따로 colidepoint() method 를 써서 사용자가 해당 영역을 클릭했는지 알아낸다!

main() 함수

def main():
    global FPSCLOCK, DISPLAYSURF, BASICFONT, BEEP1, BEEP2, BEEP3, BEEP4

    pygame.init()
    FPSCLOCK = pygame.time.Clock()
    DISPLAYSURF = pygame.display.set_mode((WINDOWWIDTH,WINDOWHEIGHT))
    pygame.display.set_caption("Chans Simulate")

    BASICFONT = pygame.font.Font("freesansbold.ttf",16)
    infoSurf = BASICFONT.render("Match the Patter by Clicking on the button or using the Q,W,A,S keys.",1,DARKGRAY)
    infoRect = infoSurf.get_rect()
    infoRect.topleft = (10,WINDOWHEIGHT - 25)

    #사운드파일 업로드 
    BEEP1 = pygame.mixer.Sound('beep1.ogg')
    BEEP2 = pygame.mixer.Sound('beep2.ogg')
    BEEP3 = pygame.mixer.Sound('beep3.ogg')
    BEEP4 = pygame.mixer.Sound('beep4.ogg')

main() 함수는 프로그램의 일부만 구현하고 필요한 함수를 호출한다.
일반적인 pygame 셋업을 진행하고 전역변수를 설정하여 다른 함수들에서도 이 변수들을 사용할 수 있게 하는것이다.
하지만 이 변수들을 기본적으로 변강하는 일이 없으므로 상수나 마찬가지이다.
그리고 사운드 효과까지 !

프로그램에서 사용한 지역변수

    #새게임에서 사용할 변수를 초기화한다.
    pattern =[] #색깔 패턴을 저장한다.
    currentStep = 0  #플레이어가 다음에 눌러야 하는 색깔 
    lastClickTime = 0 # 플레이어가 이전 버튼을 누른 시간 
    score = 0 
    #이 밑에 값이 False면 현재 시뮬레이트가 패턴을 보여주고 있는 상태이다.
    #True 면 버튼 클릭 기다리고 있다.
    waitingForInput = False 

pattern 은 색의 리스트이며 플레이어는 패턴을 기억해야한다 !
플레이어가 하나의 턴을 마치면 또 하나의 무작위 색이 이 리스트 뒤에 추가된다.

CurrentStep 변수는 플레이어가 다음에 어떤 색을 클릭해야하는지 기록 한다 !
만약 값이 0 이고 patter=[Green.RED]라면 플레이어는 초록색을 선택해야한다!
만약 다른 버튼을 눌렀다면 게임은 종료된다.

lastClickTime은 시간제한을 위해 플레이어가 마지막버튼을 누른후 지난 시간을 기록한다 !

WaitingForInput을 통해서 트루/폴스 를 통해 두가지 모드로 진행된다.

테두리 그리기와 입력 처리하기

 while True :
     clickedButton= None
     DISPLAYSURF.fill(bgColor)
     drawButtons()

     scoreSurf = BASICFONT.render("Socre : "+str(score),1,WHITE)
     scoreRect = scoreSurf.get_rect()
     scoreRect.topleft = (WINDOWWIDTH- 100,10)
     DISPLAYSURF.blit(scoreSurf,scoreRect)

     DISPLAYSURF.blit(infoSurf,infoRect)

매 시작마다 clickedButton None 리셋으로 시작하고 버튼을 클릭했다면 그 값으로 바뀐다.
그리고 버튼과 글자들을 그린다!

점수값은 계속 바뀌기 떄문에 Surface객체를 계속 새로 만들어야 한다.

마우스 클릭 검사하기

        checkForQuit()
        for event in pygame.event.get(): # event loop
            if event.type == MOUSEBUTTONUP:
                mousex, mousey = event.pos
                clickedButton = getButtonClicked(mousex,mousey)

chekcforQuit 를 통해 QUIT 이벤트를 검사한다.

GetButtonClicked 함수는 어떤 버튼을 클릭했는지 Color 객체를 반환해준다 !
만약 색을 클릭하지 않았다면 None !

키보드 입력 검사하기

           elif event.type == KEYDOWN :
               if event.key == K_q:
                   clickedButton == YELLOW
               elif event.key == K_w:
                   clickedButton = BLUE
               elif event.key == K_a:
                   clickedButton = RED
               elif event.key == K_s:
                   clickedButton = GREEN

KEYDOWN event가 발생했는지 확인하고 clickedButton을 바꿔준다 !

게임 루프의 2가지 상태

if not waitingForInput: #False 일때 
            #패턴을 자동으로 보여준다.
            pygame.display.update()
            pygame.time.wait(1000)
            pattern.append(random.choice((YELLOW,BLUE,RED,GREEN)))
            for button in pattern:
                flashButtonAnimation(button)
                pygame.time.wait(FLASHDELAY)
            waitingForInput = True 

waitingForInput : True/False에 따라 모드가 나뉜다.
False 면 패턴의 애니메이션을 보여준다.
True 면 플레이어가 버튼을 누르기를 기다린다.

pattern에 랜덤의 색을 추가하고 flashButtonAnimation을 통해서 순서대로 밝게 보여준다.
True 로 변경 후 그 후 사용자의 입력을 기다린다.

플레이어가 맞는 버튼을 눌렀는지 알아내기

        else : 
            #wait for the player to enter button
            if clickedButton and clickedButton == pattern[currentStep]:
                #맞는 버튼을 클릭(버튼클릭 && 옳은 버튼 클릭 )
                flashButtonAnimation(clickedButton)
                #클릭한 버튼 색 반쨕~ 
                currentStep += 1
                lastClickTime = time.time() # 시간 초기화(현재 시간 기록 가능) 
                

이번껀 쉬우니 주석으로 대체하겠다.

                if currentStep == len(pattern):
                    #패턴의 마지막 버튼을 클릭하였다.
                    changeBackgroundAnimation()
                    score += 1 
                    waitingForInput = False 
                    currentStep = 0 #게임 턴이 종료되고 맨 처음으로 리셋한다.

연장된 부분이다.
currentStep이 마지막 패턴의 길이와 동일해졌다는 것은 마지막 버튼을 클릭하였다는 것이고 이는 더이상 따라할 패턴이 없다는것이므로 다음 모드로 넘어가야한다.
그렇기에 점수를 올리고 모드변경과 초기화를 진행해준다.
색변경도 해준다 !

elif (clickedButton  and clickedButton != pattern[currentStep]) or (currentStep!=0 and time.time()-TIMEOUT > lastClickTime):

만약 플레이어가 맞는 버튼을 누르지 않았고 시간을 초과했다면 이 elif문을 실행한다.

"시간초과"의 조건은 처음으로 버튼을 누른 것이 아니어야 한다.
즉, 버튼을 한 번 누르기 시작했으면 계속해서 시간안에 버튼을 눌러야한다.
cureentStep != 0 이 True 라면 플레이어가 이미 버튼을 누르기 시작했다는 말이다!

기준시간

"시간 초과"가 되려면 time.time()에서 TIMEOUT을 뺀 시간이 직전에 버튼을 클릭한 시간 보다 커야한다.
time.time()-TIMEOUT >lastClickedTime 을 이해하기위해서 기준 시간에 대해 알아야한다

기준 시간은(epoch time) 1970년 1월 1일(유닉스 기원) 부터 얼마나 많은 초가 지났는지 나타내는 숫자이다.

7:30PM - 7:31 PM 같은 문자열 가지고 19초가 지났는지 확인 하기는 까다롭지만 epoch time을 사용하면 그저 뺄셈만해서되서 간단하다.

                gameOverAnimation()
                #reset for new game
                pattern=[]
                currentStep= 0
                waitingForInput = False
                score = 0
                pygame.time.wait(1000)
                changeBackgroundAnimation()

그저 끝내고 초기화닷!

화면에 게임판 그리기

        pygame.display.update()
        FPSCLOCK.tick(FPS)

길고도 긴 Main함수를 다 짰다.
그렇다면 Main 완성체를 보여주고
다음 글에서 나머지 함수들을 구현해보자 .

def main():

def main():
    global FPSCLOCK, DISPLAYSURF, BASICFONT, BEEP1, BEEP2, BEEP3, BEEP4

    pygame.init()
    FPSCLOCK = pygame.time.Clock()
    DISPLAYSURF = pygame.display.set_mode((WINDOWWIDTH,WINDOWHEIGHT))
    pygame.display.set_caption("Chans Simulate")

    BASICFONT = pygame.font.Font("freesansbold.ttf",16)
    infoSurf = BASICFONT.render("Match the Patter by Clicking on the button or using the Q,W,A,S keys.",1,DARKGRAY)
    infoRect = infoSurf.get_rect()
    infoRect.topleft = (10,WINDOWHEIGHT - 25)

    #사운드파일 업로드 
    BEEP1 = pygame.mixer.Sound('beep1.ogg')
    BEEP2 = pygame.mixer.Sound('beep2.ogg')
    BEEP3 = pygame.mixer.Sound('beep3.ogg')
    BEEP4 = pygame.mixer.Sound('beep4.ogg')

    #새게임에서 사용할 변수를 초기화한다.
    pattern =[] #색깔 패턴을 저장한다.
    currentStep = 0  #플레이어가 다음에 눌러야 하는 색깔 
    lastClickTime = 0 # 플레이어가 이전 버튼을 누른 시간 
    score = 0 
    #이 밑에 값이 False면 현재 시뮬레이트가 패턴을 보여주고 있는 상태이다.
    #True 면 버튼 클릭 기다리고 있다.
    waitingForInput = False 

    while True :
        clickedButton= None
        DISPLAYSURF.fill(bgColor)
        drawButtons()

        scoreSurf = BASICFONT.render("Socre : "+str(score),1,WHITE)
        scoreRect = scoreSurf.get_rect()
        scoreRect.topleft = (WINDOWWIDTH- 100,10)
        DISPLAYSURF.blit(scoreSurf,scoreRect)

        DISPLAYSURF.blit(infoSurf,infoRect)

        checkForQuit()
        for event in pygame.event.get(): # event loop
            if event.type == MOUSEBUTTONUP:
                mousex, mousey = event.pos
                clickedButton = getButtonClicked(mousex,mousey)
            elif event.type == KEYDOWN :
                if event.key == K_q:
                    clickedButton == YELLOW
                elif event.key == K_w:
                    clickedButton = BLUE
                elif event.key == K_a:
                    clickedButton = RED
                elif event.key == K_s:
                    clickedButton = GREEN
        
        if not waitingForInput: #False 일때 
            #패턴을 자동으로 보여준다.
            pygame.display.update()
            pygame.time.wait(1000)
            pattern.append(random.choice((YELLOW,BLUE,RED,GREEN)))
            for button in pattern:
                flashButtonAnimation(button)
                pygame.time.wait(FLASHDELAY)
            waitingForInput = True 
        else : 
            #wait for the player to enter button
            if clickedButton and clickedButton == pattern[currentStep]:
                #맞는 버튼을 클릭(버튼클릭 && 옳은 버튼 클릭 )
                flashButtonAnimation(clickedButton)
                #클릭한 버튼 색 반쨕~ 
                currentStep += 1
                lastClickTime = time.time() # 시간 초기화 

                if currentStep == len(pattern):
                    #패턴의 마지막 버튼을 클릭하였다.
                    changeBackgroundAnimation()
                    score += 1 
                    waitingForInput = False 
                    currentStep = 0 #게임 턴이 종료되고 맨 처음으로 리셋한다
            
            elif (clickedButton  and clickedButton != pattern[currentStep]) or (currentStep!=0 and time.time()-TIMEOUT > lastClickTime):
                #버튼을 잘못눌렀거나 시간 초과
                gameOverAnimation()
                #reset for new game
                pattern=[]
                currentStep= 0
                waitingForInput = False
                score = 0
                pygame.time.wait(1000)
                changeBackgroundAnimation()
        
        pygame.display.update()
        FPSCLOCK.tick(FPS)
profile
부족한 실력을 엉덩이 힘으로 채워나가는 개발자 서희찬입니다 :)

0개의 댓글