* Udemy의 PythonBootcamp 수업 내용을 참고하여 작성
이전 포스팅에 이어 뱀 게임을 마저 구현해본다.
아래 1~3번까지는 이전 포스팅에서 구현한 바 있다. 4~7을 마저 살펴볼 것이다. 이전 포스팅 : https://ysryuu.tistory.com/46
[Python] 5. 뱀 게임 만들기 (1)
* Udemy의 PythonBootcamp 수업 내용을 참고하여 작성 이번에는 뱀게임을 구현해보자. 뱀게임은 간단한 규칙을 가진 게임이다.화면에 랜덤으로 사과가 생성되고, 뱀이 사과를 먹으면 성장 (길이가
ysryuu.tistory.com
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
4. 먹이와의 충돌 및 먹이 등장 구현
뱀게임에서는 사과 (먹이)가 등장하고, 먹이를 먹을때마다 뱀 몸통의 길이는 길어진다.
food.py 파일을 만들어서 화면에 먹이의 등장, 뱀이 먹이를 먹을때 동작 등을 구현해보자.
먹이를 등장시키기
Food 클래스를 만들고 Turtle 클래스를 상속받는다.
먹이 속성에서도 shape, color, speed 등 turtle이 가지고 있던 모든 속성들을 활용할 수 있다.
이를 활용해 작은 사이즈의 파란 원형으로 먹이를 구현한다. 먹이의 위치는 random 기능을 활용해서 임의의 x, y 좌표로 등장하도록 한다.
<food.py 파일 내용>
from turtle import Turtle
import random
class Food(Turtle) : # turtle 클래스를 상속받기
def __init__(self):
super().__init__()
self.shape('circle')
self.penup()
self.shapesize(stretch_len=0.5, stretch_wid=0.5)
self.color('blue')
self.speed('fastest') # 0
rand_x = random.randint(-280, 280)
rand_y = random.randint(-280, 280)
self.goto(rand_x, rand_y) # 랜덤 x, y 위치로 이동
<main.py 파일>
이제 main.py에서 먹이 객체도 함께 생성해주면 뱀과 함께 먹이를 스크린에서 확인할 수 있다.
...
snake = Snake()
food = Food()
screen.update()
...
뱀과 먹이가 충돌할때 동작하기
뱀과 먹이가 만날때 먹이는 다른 위치로 옮겨져야 한다.
turtle의 distance 메소드를 활용해서, 일정 distance 미만으로 먹이와 뱀이 가까워 졌을 때에는,
먹이를 다시 랜덤한 위치로 옮겨둔다. 아래 'refresh'라는 메소드가 추가 및 동작하는 것을 확인 할 수 있다.
<food.py 파일 내용>
from turtle import Turtle
import random
class Food(Turtle) : # turtle 클래스를 상속받기
def __init__(self):
super().__init__()
self.shape('circle')
self.penup()
self.shapesize(stretch_len=0.5, stretch_wid=0.5)
self.color('blue')
self.speed('fastest') # 0
rand_x = random.randint(-280, 280)
rand_y = random.randint(-280, 280)
self.goto(rand_x, rand_y) # 랜덤 x, y 위치로 이동
self.refresh()
def refresh(self): # 다른 위치로 이동
rand_x = random.randint(-280, 280)
rand_y = random.randint(-280, 280)
self.goto(rand_x, rand_y)
<main.py 파일 내용>
...
game_is_on = True
while game_is_on :
screen.update()
time.sleep(0.1)
snake.move() #화면이 갱신될때마다 움직임
if snake.head.distance(food) < 15 :
print('nom nom nom') # 먹이 먹음 사인
food.refresh() # 먹이 이동
screen.exitonclick()
5. 스코어 보드 구현
이제 스코어 보드를 구현할 건데, 스코어는 뱀이 먹이를 얼마나 먹었는지를 보여준다.
turtle.write() 메소드를 활용해서, 게임 화면의 최상단 가운데에, 먹이를 먹을때마다 1씩 증가하는 스코어 보드판을 만들어보자.
코드들의 순서가 중요할 뿐 아니라, 중복되는 기능이 없도록 메소드를 구현해야 한다.
<scoreboard.py 파일 내용>
from turtle import Turtle
ALIGNMENT = 'center'
FONT = ('Courier', 24, 'normal')
class Scoreboard(Turtle):
def __init__(self):
super().__init__()
self.score = 0
self.color('white') # write이전에
self.penup()
self.goto(0, 270)
self.hideturtle()
self.update_scoreboard()
def update_scoreboard(self):
self.write(f"Score: {self.score}", align= ALIGNMENT, font = FONT)
def increase_score(self):
self.score += 1
self.clear()
self.update_scoreboard()
<main.py 파일 내용>
from turtle import Turtle, Screen
from snake import Snake #클래스 임포트
from food import Food
from scoreboard import Scoreboard
import time
screen = Screen()
screen.setup(width=600, height=600)
screen.bgcolor('black')
screen.title('My Snake Game')
screen.tracer(0)
snake = Snake()
food = Food()
scoreboard = Scoreboard()
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() #화면이 갱신될때마다 움직임
if snake.head.distance(food) < 15 :
food.refresh() # 먹이 이동
scoreboard.increase_score()
screen.exitonclick()
6. 벽과의 충돌 및 게임오버
이제 게임이 끝나는 상황들만 정의하면 된다. 우선 벽과 뱀이 충돌하면 게임 오버되는 것을 정의한다.
score board 클래스에서 game_over 메소드를 정의하고 main에서 동작시킨다.
<scoreboard.py 파일 내용>
from turtle import Turtle
ALIGNMENT = 'center'
FONT = ('Courier', 24, 'normal')
class Scoreboard(Turtle):
...
def game_over(self):
self.goto(0.0)
self.write(f"Score: {self.score}", align= ALIGNMENT, font = FONT)
<main.py 파일 내용>
...
game_is_on = True
while game_is_on :
screen.update()
time.sleep(0.1)
snake.move() #화면이 갱신될때마다 움직임
...
# 벽과의 충돌 감지
if snake.head.xcor() > 280 or snake.head.xcor() < -280 or snake.head.ycor() > 280 or snake.head.ycor() < -280 :
game_is_on = False
screen.exitonclick()
7. 머리와 꼬리의 충돌 및 게임오버
뱀의 길이가 먹이를 먹을때마다 증가하는 부분, 머리와 꼬리가 충돌하는 부분을 구현해야 한다.
먹이를 먹을 수록 길어지는 뱀
Snake 클래스에서 create_snake, add_segment, extend 메소드를 다시 구현한다.
<snake.py 파일 내용>
from turtle import Turtle
...
class Snake :
def __init__(self) :
self.segments = []
self.create_snake() # 객체 생성시 자동 몸체 생성
self.head = self.segments[0]
def create_snake(self) : # 몸체 만들기
for position in STARTING_POSITIONS :
self.add_segment(position)
def add_segment(self,position):
new_segment = Turtle('square')
new_segment.color('white')
new_segment.penup()
new_segment.goto(position)
self.segments.append(new_segment)
def extend(self) :
self.add_segment(self.segments[-1].position()) # 마지막 segment추가
...
머리와 꼬리의 충돌시 게임오버 (최종 코드)
main.py에 머리와 꼬리의 충돌시 게임 오버 한다는 내용까지 포함하여 게임을 구현하는 최종 코드는 아래와 같다.!
이로써 뱀 게임 구현이 완성된 것을 확인할 수 있다.
<snake.py>
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 :
self.add_segment(position)
def add_segment(self,position):
new_segment = Turtle('square')
new_segment.color('white')
new_segment.penup()
new_segment.goto(position)
self.segments.append(new_segment)
def extend(self) :
self.add_segment(self.segments[-1].position())
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)
<scoreboard.py>
from turtle import Turtle
ALIGNMENT = 'center'
FONT = ('Courier', 24, 'normal')
class Scoreboard(Turtle):
def __init__(self):
super().__init__()
self.score = 0
self.color('white') # write이전에
self.penup()
self.goto(0, 270)
self.hideturtle()
self.update_scoreboard()
def update_scoreboard(self):
self.write(f"Score: {self.score}", align= ALIGNMENT, font = FONT)
def increase_score(self):
self.score += 1
self.clear()
self.update_scoreboard()
def game_over(self):
self.goto(0.0)
self.write(f"Score: {self.score}", align= ALIGNMENT, font = FONT)
<food.py>
from turtle import Turtle
import random
class Food(Turtle) : # turtle 클래스를 상속받기
def __init__(self):
super().__init__()
self.shape('circle')
self.penup()
self.shapesize(stretch_len=0.5, stretch_wid=0.5)
self.color('blue')
self.speed('fastest') # 0
rand_x = random.randint(-280, 280)
rand_y = random.randint(-280, 280)
self.goto(rand_x, rand_y) # 랜덤 x, y 위치로 이동
self.refresh()
def refresh(self): # 다른 위치로 이동
rand_x = random.randint(-280, 280)
rand_y = random.randint(-280, 280)
self.goto(rand_x, rand_y)
<main.py>
from turtle import Turtle, Screen
from snake import Snake #클래스 임포트
from food import Food
from scoreboard import Scoreboard
import time
screen = Screen()
screen.setup(width=600, height=600)
screen.bgcolor('black')
screen.title('My Snake Game')
screen.tracer(0)
snake = Snake()
food = Food()
scoreboard = Scoreboard()
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() #화면이 갱신될때마다 움직임
# 먹이를 먹을 시
if snake.head.distance(food) < 15 :
food.refresh() # 먹이 이동
snake.extend() # 뱀이 늘어남
scoreboard.increase_score()
# 벽과 충돌감지시 게임오버
if snake.head.xcor() > 280 or snake.head.xcor() < -280 or snake.head.ycor() > 280 or snake.head.ycor() < -280 :
game_is_on = False
scoreboard.game_over()
# 머리와 꼬리 충돌감지시 게임오버
for segment in snake.segments[1:] :
if snake.head.distance(segment) < 10 :
game_is_on = False
scoreboard.game_over()
screen.exitonclick()