본문 바로가기

파이썬(Python)으로 만든 게임 - Catch Turtle

두둥

<데이터 과학 개론>이라는 수업에서 한 개인 프로젝트이다. 수업명과는 달리 데이터 분석과 관련된 내용은 전혀 배우지 않았고 파이썬 수업이라 해도 과언이 아니었다(지금은 팀티칭으로 달라졌다고 들었다.) 프로젝트도 게임을 기획하고 파이썬으로 개발하는 것이었다. 

 

게임에 대해 간단히 설명하자면, Catch Turtle, 말 그대로 거북이를 잡는 게임이다. 거북이는 오른쪽(200,0), 왼쪽(-200,0), 위쪽(0,200), 아래쪽(0,-200) 중에 랜덤으로 나타난다. 그러면 키보드 방향키를 이용하여 거북이를 잡아야 한다. 거북이가 나타나는 방향과 키보드 키의 방향이 일치하면 1점을 얻는다. 제한 시간은 20초이고, 게임의 재미를 더하기 위해 거북이가 나타나는 속도는 0.1초에서 1초 사이 랜덤으로 정했다.

 

import turtle as t # 터틀 그래픽 모듈을 불러온다.
import random # 난수 모듈을 불러온다.

def message(m1, m2): # 메시지를 출력하는 함수
    t.clear() # 화면을 지운다.
    t.goto(0,100) # (0, 100)으로 이동한다.
    t.write(m1, False, "center", ("Arial", 20, "bold")) # Arial 폰트, 폰트 크기 20, 굵게, 가운데 정렬로 m1을 작성한다.
    t.goto(0,-100) # (0, -100)으로 이동한다.
    t.write(m2, False, "center", ("Arial", 15)) # Arial 폰트, 폰트 크기 15, 가운데 정렬로 m2를 작성한다.
    t.home() # 거북이를 원점으로 이동한다.
    t.showturtle() # 거북이를 표시한다.

def start(): # 게임 시작하는 함수
    global playing # 전역변수 playing의 값을 변경하기 위해 global 사용
    if playing == False: # playing이 False라면
        playing = True # playing을 True로 변경한다.
        t.clear() # 화면을 지운다.
        play() # play함수를 호출한다.
        t.ontimer(end, 20000) # 20초 후에 end 함수를 호출한다.

def play(): # 게임 진행중인 함수
    global playing # 전역변수 playing의 값을 변경하기 위해 global 사용
    if playing == True: # playing이 True라면
        t.showturtle() # 거북이를 표시한다.
        t.up() # 펜을 든다.
        location = random.choice(list_Location) # 리스트에서 거북이의 위치를 랜덤으로 골라 location에 저장한다.
        t.goto(location) # 거북이를 location으로 이동한다.
        time = random.randint(100, 1000) # 100에서 1000사이를 랜덤으로 골라 time에 저장한다.
        t.ontimer(play, time) # time(ms)이 지난 후 play 함수를 다시 호출한다.

def show_score(): # 점수를 보여주는 함수
    s.clear() # 점수 화면을 지운다.
    s.color("white") # 색깔을 흰색으로 정한다.
    s.goto(150, 150) # (150, 150)으로 이동한다.
    s.pencolor("black") # 펜 색깔을 검정색으로 정한다.
    s.write("Score : %d" % score, False, "left", ("Arial", 13, "bold")) # Arial 폰트, 폰트 크기 13, 굵게, 왼쪽 정렬로 점수를 작성한다.
    s.color("white") # 색깔을 흰색으로 정한다.

def turn_right(): # 오른쪽 키를 눌렀을 때의 함수
    global score # 전역변수 score의 값을 변경하기 위해 global 사용
    if t.position() == (200.00,0.00): # 거북이의 위치가 (200.00, 0.00)이라면
        t.hideturtle() # 거북이를 숨긴다.
        score = score + 1 # 점수에 1점을 추가한다.
        show_score() # 점수를 보여준다.

def turn_left(): # 왼쪽 키를 눌렀을 때의 함수
    global score
    if t.position() == (-200.00,0.00): # 거북이의 위치가 (-200.00,0.00)이라면
        t.hideturtle()
        score = score + 1
        show_score()
        
def turn_up(): # 위쪽 키를 눌렀을 때의 함수
    global score
    if t.position() == (0.00,200.00): # 거북이의 위치가 (0.00,200.00)이라면
        t.hideturtle()
        score = score + 1
        show_score()
        
def turn_down(): # 아래쪽 키를 눌렀을 때의 함수
    global score
    if t.position() == (0.00,-200.00): # 거북이의 위치가 (0.00,-200.00)이라면
        t.hideturtle()
        score = score + 1
        show_score()

def end(): # 게임 끝내는 함수
    global score # 전역변수 score의 값을 변경하기 위해 global 사용
    global playing # 전역변수 playing의 값을 변경하기 위해 global 사용
    if playing == True: # playing이 True라면
        playing = False # playing을 False로 변경한다.
        s.clear() # 점수 화면을 지운다.
        text = "Score : %d" % score # 최종 점수를 text에 저장한다.
        message("Game Over", text) # "Game Over"와 점수를 출력하는 함수를 호출한다.
        score = 0 # 점수를 초기화한다.

t.shape("turtle") # 모양을 거북이로 한다.
t.title("Catch Turtle") # 그래픽 창 이름을 지정한다.
t.setup(500, 500) # 창 크기를 500*500으로 설정한다.
t.speed(0) # 속도를 0으로 한다.
t.up() # 펜을 든다.

s = t.Turtle() # 점수 객체를 만든다.
s.color("white") # 색깔을 흰색으로 정한다.
s.goto(150,150) # (150,150)으로 이동한다.

playing = False # playing을 선언하고 False로 초기화한다.
score = 0 # score을 선언하고 0으로 초기화한다.

list_Location = [(-200,0), (200,0), (0,-200), (0,200)] # 거북이의 위치(위, 아래, 오른쪽, 왼쪽)를 리스트로 작성한다.

t.onkeypress(turn_right, "Right") # 오른쪽 키를 누르면 turn_right 함수를 실행한다.
t.onkeypress(turn_left, "Left")
t.onkeypress(turn_up, "Up")
t.onkeypress(turn_down, "Down")
t.onkeypress(start, "space")
t.listen() # 사용자 입력이 잘 처리되도록 거북이 그래픽 창에 포커스를 준다.

message("Catch Turtle", "[Space]") # 게임 시작하기 전 첫 화면으로 "Catch Turtle"과 "[Space]"를 출력한다.

 

그 당시(2018년)에 작성한 코드는 이랬다. 그때도 몇 가지 아쉬운 점이 있었는데 지금 보면 고칠 점이 많았다. 일단 제일 눈에 띄는 것은 global이 너무 많다^^ global을 쓰면 안 좋다는 말을 들어서 지우려 했는데 생각해보니 왜 안 좋은 걸까 의문이 생겨 구글링 해보았다.

 

3.4 전역변수와 지역변수 | 파이썬 프로그래밍 입문서 (가제)

박연오가 도서출판 인사이트와 함께 준비하고 있는 파이썬 프로그래밍 입문서입니다. 질문과 의견은 페이지 하단의 댓글란에 남겨주세요.

python.bakyeono.net

위의 책에 잘 나와있는데, 요약하자면 함수는 문제를 작은 문제로 나눠서 해결하기 위해 사용한다. 따라서 함수는 자기 문제에 필요한 데이터만을 조작해야 하고 관련이 없는 프로그램 전체 데이터나 다른 함수의 데이터에 손을 대면 안 된다. 만약 전역 변수가 수시로 변하고 여러 함수에서 저마다 손을 댄다면 프로그램의 흐름을 파악하기 어려워질 것이다! 처음에는 보안 문제로만 생각했는데 이런 이유가 있었구나.. 

 

global 이외에도 if playing == True를 if playing으로 고쳐주고 싶고 list_Location이라는 변수명도 location_list로 바꿔주고 싶었다(후..) player와 screen을 따로 객체로 생성하지 않고 하나로 쓴 것도 이상했다. 그리고 쓸데없는 주석이 왜 이렇게 많아! 해서 다시 고쳐 보았다.

 

import turtle as t
import random
import time

def message(str_up, str_down): # 위아래로 메시지 출력
    player.goto(0,100)
    player.write(str_up, False, "center", ("Arial", 20, "bold")) # Arial 폰트, 폰트 크기 20, 굵게, 가운데 정렬로 str_up 작성
    player.goto(0,-100)
    player.write(str_down, False, "center", ("Arial", 15, "normal"))
    player.home() # 거북이 원점으로 이동
    player.showturtle()

def start(): # 게임 시작
    playing = True
    player.clear()
    playing = play(playing)
    end(playing)

def play(playing): # 게임 진행
    max_time = time.time() + 20 # 최대 20초의 시간 제한

    while playing:
        if time.time() > max_time:
            playing = False
            return playing
        player.showturtle()
        player.up() # 펜 들기
        location = random.choice(location_list)
        player.goto(location)
        time.sleep(random.uniform(0.1, 1)) # 거북이가 나타나는 시간을 0.1초에서 1초 사이 랜덤으로 지연

def show_score(score): # 점수 출력
    score_board.clear()
    score_board.color("white")
    score_board.goto(150, 150)
    score_board.pencolor("black")
    score_board.write("Score : %d" % score, False, "left", ("Arial", 13, "bold"))
    score_board.color("white")

def right():  # 유저가 누른 방향키가 오른쪽이면 점수 획득
    global score
    if player.position() == (200.00, 0.00):  # 거북이의 위치가 오른쪽이면
        player.hideturtle()
        score = score + 1
        show_score(score)

def left():  # 유저가 누른 방향키가 왼쪽이면 점수 획득
    global score
    if player.position() == (-200.00, 0.00):  # 거북이의 위치가 왼쪽이면
        player.hideturtle()
        score = score + 1
        show_score(score)

def up():  # 유저가 누른 방향키가 위쪽이면 점수 획득
    global score
    if player.position() == (0.00, 200.00):  # 거북이의 위치가 위쪽이면
        player.hideturtle()
        score = score + 1
        show_score(score)

def down():  # 유저가 누른 방향키가 아래쪽이면 점수 획득
    global score
    if player.position() == (0.00, -200.00):  # 거북이의 위치가 아래쪽이면
        player.hideturtle()
        score = score + 1
        show_score(score)

def end(playing): # 게임 종료
    global score
    if not playing:
        score_board.clear()
        text = "Score : %d" % score
        message("Game Over", text) # 게임 종료 화면으로 "Game Over"와 점수를 출력
        score = 0

player = t.Turtle() # 거북이 객체 생성
player.shape("turtle")
player.speed(0)
player.up()

screen = t.Screen() # screen 객체 생성
screen.title("Catch Turtle") # 그래픽 창 이름 지정
screen.setup(500, 500) # 창 크기 500*500으로 설정

score_board = t.Turtle() # 점수판 객체 생성
score_board.color("white")
score_board.goto(150,150)

score = 0

location_list = [(0,200), (0,-200), (200,0), (-200,0)] # 거북이의 위치(위, 아래, 오른쪽, 왼쪽) 리스트로 생성

screen.onkeypress(start, "space") # 스페이스 바를 누르면 start 함수 실행
screen.onkeypress(right, "Right") # 오른쪽 키를 누르면 right 함수 실행
screen.onkeypress(left, "Left")
screen.onkeypress(up, "Up")
screen.onkeypress(down, "Down")
screen.listen() # 이 명령어를 실행시켜야 키 입력모드가 실행되어 입력된 키에 반응

message("Catch Turtle", "[Space]") # 게임 시작하기 전 첫 화면으로 "Catch Turtle"과 "[Space]"를 출력

 

휴.. 우선 global을 다 없애려 했으나 함수의 실행결과를 score에 누적해야 했다. 오른쪽 키를 누르면 진짜 거북이가 오른쪽에 있는지 확인하고 점수를 더하는 방식이라 right, left, up, down 함수에서 점수를 더하면 이를 score에 계속 누적할 필요가 있었다. 어떻게든 global문을 사용하지 않기 위해 함수의 매개변수와 반환 값을 이용하려 했으나 onkeypress에 들어가는 함수는 인자가 없는 함수여야 했다(OTL...) 하는 수 없이 score는 global을 사용했다. playing은 꼭 전역 변수가 아니어도 되어서 지역변수로 사용하고 필요한 경우 반환 값으로 받아 사용했다. 이외에 변수명도 좀 다듬고 설명이 필요한 부분만 주석을 달았다. 편-안하군.

 

사실 이 게임이 어디 출시할만한 게임은 아니지만(^^) 그래도 나름 파이썬을 처음 배워서 직접 만들어본 게임이라는 데 의미가 있다. 만들고 보니 뿌듯했다. 2년 지나서 다시 수정하고 보니 더 뿌듯.. 프로젝트로 배운 점을 총정리해보겠다.

 

- global 문은 최대한 사용하지 않는 것이 좋다. 보안 문제뿐만 아니라 프로그램의 흐름을 파악하기 어렵기 때문!

  대신 함수의 매개변수와 반환 값을 이용하자!

- 주석은 설명이 필요한 곳만 달아야g

- 변수명은 언더바를 이용하거나 대문자를 이용하자!

   Ex) location_list나 locationList

 

GitHub - leeejihyun/Catch-Turtle: 거북이를 잡는 게임

거북이를 잡는 게임. Contribute to leeejihyun/Catch-Turtle development by creating an account on GitHub.

github.com