Dev

[Python] 5. 뱀 게임 만들기 (1)

mlslly 2024. 5. 4. 09:58

* Udemy의 PythonBootcamp 수업 내용을 참고하여 작성

 

이번에는 뱀게임을 구현해보자. 

 

뱀게임은 간단한 규칙을 가진 게임이다.

화면에 랜덤으로 사과가 생성되고, 뱀이 사과를 먹으면 성장 (길이가 길어짐)하게 되는데, 뱀의 머리가 자신의 몸통에 닿을 경우에 게임은 종료된다. 사과를 최대한 많이 먹으면서 죽지않고 뱀의 길이를 늘리는 것이 게임의 목표이다. 

https://g.co/kgs/4dPqWze 

 

스네이크

Google에서 플레이

www.google.com

 

게임을 부분으로 쪼개 구현하기  

어떤 복잡한 코드라도 부분으로 나누어 단계별로 구현할 수 있다. 

뱀게임은 아래와 같이 총 7부분으로 나눌 수 있다. 순서대로 구현해보도록 하겠다. 

 

1. 뱀의 몸체 생성 create a snake body 

2. 뱀을 움직이기 move the snake 

3. 뱀을 통제하기 (키보드 방향) control the snake 

4. 먹이와의 충돌 및 먹이 등장 구현 detect collision with food

5. 스코어 보드 구현 create a scoreboard

6. 벽과의 충돌 및 게임 오버 detect collision with wall

7. 머리와 꼬리의 충돌 및 게임오버 detect collision with tail 


뱀 게임 구현하기 

1. 뱀 몸체 생성하기 

 

1) 기본 화면 설정

from turtle import Turtle, Screen

screen = Screen()
screen.setup(width=600, height=600)
screen.bgcolor('black')
screen.title('My Snake Game')

screen.exitonclick()

 

2) 짦은 기본 몸체 생성하기 

세개의 위치를 지정해, 스크린 상에 세개의 연속된 segment가 보이도록 설정 

from turtle import Turtle, Screen
import time

screen = Screen()
screen.setup(width=600, height=600)
screen.bgcolor('black')
screen.title('My Snake Game')


starting_positions = [(0,0),(-20,0),(-40,0)] # 상수

for position in starting_positions : 
    new_segment = Turtle('square')
    new_segment.color('white')
    new_segment.goto(position)

 


 

2. 뱀을 움직이기 

 

1) 기본 움직임 구현

from turtle import Turtle, Screen

screen = Screen()
screen.setup(width=600, height=600)
screen.bgcolor('black')
screen.title('My Snake Game')

starting_positions = [(0,0),(-20,0),(-40,0)] # 상수

segments = []

for position in starting_positions : 
    new_segment = Turtle('square')
    new_segment.color('white')
    new_segment.penup()
    new_segment.goto(position)
    segments.append(new_segment)

game_is_on = True

while game_is_on : 
    for seg in segments : 
        seg.forward(20)

screen.exitonclick()

 

2) 자연스러운 움직임 구현 (연결된 상태로 움직이기) * 

screen.tracer()과 screen.update(), time.sleep()을 활용하여 자연스러운 움직임을 구현한다.

- screen.tracer() : 화면 업데이트를 일시적으로 비활성화 하여, turtle 객체를 그릴때마다 화면이 업데이트 되는 것을 방지한다 

- screen.update() : tracer로 비활성화된 화면을 갱신하는 코드로, 원하는 지점에서 update를 호출하여 화면을 한꺼번에 갱신한다.

- time.sleep() : 일정 시간동안 프로그램의 실행을 중지 시키는 코드로, 루프 내에서 사용해서 화면의 업데이트 속도를 조정한다 (뱀의 움직임, 즉 게임 속도 조정) 

 

또한 segment들을 뒤에서부터 따라오도록 설정하면 segment끼리 연결된 자연스러운 움직임을 구현할 수 있다.

첫번째 segment는 방향을 정해서 움직이고, 두번째 segment는 첫번째 segment를, 세번째 segment는 두번째 segment가 있던 곳으로 따라서 움직이도록한다.

from turtle import Turtle, Screen
import time

screen = Screen()
screen.setup(width=600, height=600)
screen.bgcolor('black')
screen.title('My Snake Game')
screen.tracer(0) # 코드 실행과정을, update가 나오기전까지 보여주지 않음

starting_position = [(0,0),(-20,0),(-40,0)] # 상수

segments = []
for position in starting_position : 
    new_segment = Turtle(shape='square')
    new_segment.color('white')
    new_segment.penup()
    new_segment.goto(position)
    segments.append(new_segment)

game_is_on = True

while game_is_on :
    screen.update()
    time.sleep(0.1)

    # 순서를 반대로 지정
    for seg_num in range(len(segments)-1, 0,-1) :
        new_x = segments[seg_num-1].xcor()
        new_y = segments[seg_num-1].ycor()
        segments[seg_num].goto(new_x, new_y) # 마지막 segment를 이전 segment로 옮기기
    segments[0].forward(20)

 

3) 코드를 클래스로 분리하기 (snake, food, scoreboard, main) 

코드를 구조화하고 클래스로 분리해서 사용하는 것은 매우 중요하다. 

앞서 뱀의 몸체 만들기와 이동까지 만들어 보았으니, 이제 뱀게임에서 '뱀'과 관련한 기본적인 내용을 담은 클래스를 분리해볼 수 있다.

뱀게임 전체가 작동하기 위해서 총 네가지 파일을 만들 건데, 1. snake 파일 2. food 파일 3. scoreboard 파일 4. main 파일이다.

1~3까지는 각각 snake, food, scoreboard가 어떻게 동작하는지 모든 내용을 담고있는 독립된 클래스를 담고있을 것이며, 

main 파일에서 그 세가지 클래스를 이용하여 게임을 작동시킬 것이다.

 

앞서 작성한 코드들을 우선 snake 파일과 main 파일로 아래와 같이 분리할 수 있다.

Snake 클래스를 생성하고, main 파일에서는 Snake 클래스를 import 해와서 작동시킨다. 코드를 분리하더라도 정의 및 동작에는 아무 차이가 없다.

 

<snake.py 파일 내용>

from turtle import Turtle

STARTING_POSITIONS = [(0,0),(-20,0),(-40,0)] # 상수
MOVE_DISTANCE = 20

class Snake : 
    def __init__(self) : 
        self.segments = []
        self.create_snake() # 객체 생성시 자동 몸체 생성

    def create_snake(self) : # 몸체 만들기
        for position in STARTING_POSITIONS : 
            new_segment = Turtle('square')
            new_segment.color('white')
            new_segment.penup()
            new_segment.goto(position)
            self.segments.append(new_segment)

    def move(self) :  # 움직이기
        for seg_num in range(len(self.segments)-1,0,-1):
            new_x = self.segments[seg_num-1].xcor()
            new_y = self.segments[seg_num-1].ycor()
            self.segments[seg_num].goto(new_x, new_y)
        self.segments[0].forward(MOVE_DISTANCE)

 

<main.py 파일 내용> 

from turtle import Turtle, Screen
from snake import Snake #클래스 임포트
import time

screen = Screen()
screen.setup(width=600, height=600)
screen.bgcolor('black')
screen.title('My Snake Game')
screen.tracer(0) 

snake = Snake()

game_is_on = True
while game_is_on :
    screen.update()
    time.sleep(0.1)

    snake.move() #화면이 갱신될때마다 움직임


screen.exitonclick()

 


3. 뱀을 통제하기 (키보드 방향)

 

이제 게임 사용자가 자유자재로 뱀의 움직임을 조작할 수 있도록 만들어보자.

snake.py의 Snake 클래스에서는 up, down, left, right 메소드를 구현하고, 

main.py에서는 이벤트 리스너 (screen.listen(), screen.onkey())를 활용하여 키보드로 뱀의 움직임을 조작할 수 있도록 만들 것이다. 

 

<snake.py 파일 내용>

뱀의 움직임에서 가장 중요한 첫번째 segment를 head로 만들어 속성에 추가, 방향의 각도를 상수로 지정하고 

반대 방향으로는 바로 움직일 수 없도록 방향 메소드에 조건을 지정했다. 

from turtle import Turtle

STARTING_POSITIONS = [(0,0),(-20,0),(-40,0)] # 상수
MOVE_DISTANCE = 20
# turtle의 heading 메소드를 활용
UP = 90
DOWN = 270
LEFT = 180
RIGHT = 0

class Snake : 
    def __init__(self) : 
        self.segments = []
        self.create_snake() # 객체 생성시 자동 몸체 생성
        self.head = self.segments[0]  

    def create_snake(self) : # 몸체 만들기
        for position in STARTING_POSITIONS : 
            new_segment = Turtle('square')
            new_segment.color('white')
            new_segment.penup()
            new_segment.goto(position)
            self.segments.append(new_segment)

    def move(self) :  # 움직이기
        for seg_num in range(len(self.segments)-1,0,-1):
            new_x = self.segments[seg_num-1].xcor()
            new_y = self.segments[seg_num-1].ycor()
            self.segments[seg_num].goto(new_x, new_y)
        self.head.forward(MOVE_DISTANCE)

    def up(self):
        if self.head.heading() != DOWN : # 반대방향으로 움직이지 못하도록
            self.head.setheading(UP)

    def down(self):
        if self.head.heading() != UP : 
            self.head.setheading(DOWN)

    def left(self):
        if self.head.heading() != RIGHT : 
            self.head.setheading(LEFT)

    def right(self):
        if self.head.heading() != LEFT :  
            self.head.setheading(RIGHT)

 

<main.py 파일 내용>

from turtle import Turtle, Screen
from snake import Snake #클래스 임포트
import time

screen = Screen()
screen.setup(width=600, height=600)
screen.bgcolor('black')
screen.title('My Snake Game')
screen.tracer(0) 

snake = Snake()
screen.update()

screen.listen()
screen.onkey(snake.up, 'Up')
screen.onkey(snake.down, 'Down')
screen.onkey(snake.left, 'Left')
screen.onkey(snake.right, 'Right')

game_is_on = True
while game_is_on :
    screen.update()
    time.sleep(0.1)
    snake.move() #화면이 갱신될때마다 움직임

screen.exitonclick()

 

 

*나머지 4~7 단계는 다음 '뱀 게임 만들기 (2)'에서 구현해보도록 하겠다.