Turtle graphics Module์ ์ฌ์ฉํด์ ๋ฑ ๊ฒ์์ ๋ง๋ค์ด๋ณด์.
๋จน์ด๋ฅผ ๋จน์ผ๋ฉด ๊ผฌ๋ฆฌ๊ฐ ํ ์นธ์ฉ ์๋ผ๊ณ , ๊ผฌ๋ฆฌ๋ ์ฐฝ ๋์ ๋ถ๋ชํ๋ฉด ์ฃฝ๋ ๊ฒ์์ด๋ค.
main.py, snake.py, scoreBoard.py, food.py ์ด 4๊ฐ์ง ํ์ผ๋ก ์ด๋ฃจ์ด์ ธ ์๋ค.
# main.py
import time
from turtle import Screen, Turtle
from snake import Snake
from food import Food
from scoreBoard import ScoreBoard
screen = Screen()
screen.setup(width=600, height=600)
screen.bgcolor("black")
screen.title("Snake Game")
screen.tracer(0)
snake = Snake()
food = Food()
score = ScoreBoard()
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.08)
snake.move()
# detect collision with food
if snake.head.distance(food) < 15:
food.refresh()
score.increase_score()
snake.extend()
# detect collision with wall
if snake.head.xcor() > 280 or snake.head.xcor() < -280 or snake.head.ycor() > 280 or snake.head.ycor() < -280:
game_is_on = False
score.game_over()
# detect collision with it's own tail
for segment in snake.segments[1:]:
if snake.head.distance(segment) < 10:
game_is_on = False
score.game_over()
screen.exitonclick()
sceen์ ์ค์ ํ๊ณ snake, food, scoreboard ๊ฐ์ฒด๋ฅผ ๋ง๋ค์๋ค.
ํ์ผ ๋งจ ๋์ screen.exitonclick()์ ํ๋ฉด์ด ์ผ์ง์๋ง์ ๊บผ์ง์ง ์๊ฒ ํ๊ธฐ ์ํด ํ์์ ์ด๋ค.
screen ๊ฐ์ฒด๋ screen.listen() ๋ฉ์๋๋ก ํค ์ ๋ ฅ์ ๊ฐ์งํ๊ฒ ๋๊ณ , ํ์ดํ ํค์ ๋ฐ๋ผ ์์ง์ด๊ฒ ๋๋ค.
์์ง์ด๋ ๋ฉ์๋๋ snake.py์ ํฌํจ๋์ด ์๋ค.
๋ฐ๋ณต๋ฌธ ๋ด์ screen.update()๋ฅผ ๋ฃ์ ์ด์ ๋ ์๋จ์์ screen์ tracer ๊ฐ์ด 0์ผ๋ก ์ง์ ๋์๊ธฐ ๋๋ฌธ์ธ๋ฐ, ์ด๋ turtle graphic์ ํ๋ฉด์ด ์ ๋ฐ์ดํธ๋๋ ์๊ฐ์ ํต์ ํ๋ค. 0์ผ๋ก ์ค์ ํ๋ฉด ์ ๋ฐ์ดํธ๊ฐ ๋์ง ์๊ธฐ ๋๋ฌธ์, screen.update()๋ก ๋ฐ๋ณต๋ฌธ์ด ๋ ๋๋ง ์ ๋ฐ์ดํธ ๋๊ฒ๋ ์ค์ ํ๋ค. ์ด์ ๋ ๋ฑ์ ์์ง์์ ๋ํ ์๊ฐ์ ํจ๊ณผ์ ์๋ค.
time.sleep()์ ์ ๋ฐ์ดํธ ์ฌ์ด์ ์๊ฐ์ ๊ฐ๊ฒฉ์ ์กฐ์ ํ๋๋ฐ, ๋ค๋ฅด๊ฒ ๋งํ๋ฉด ๊ฒ์์ ์๋์ ๋์ด๋๋ฅผ ๋ฐ๊ฟ ์ ์๋ค.
์๋๊ฐ ์ค์ด๋ค์๋ก ํ๋ฉด์ ์ ๋ฐ์ดํธ๊ฐ ๋นจ๋ผ์ง๊ณ , ์ ๋ฐ์ดํธ๊ฐ ๋นจ๋ผ์ง ์๋ก ๋ฑ์ด ๋น ๋ฅด๊ฒ ์์ง์ด๊ธฐ ๋๋ฌธ์ ๋์ด๋๊ฐ ์ฌ๋ผ๊ฐ๋ค.
snake.move()์ ์๊ณ ๋ฆฌ์ฆ์ snake.py์์ ํ์คํ๊ฒ ๋ณผ ์ ์๋ค.
๋ฐ์์ ๋ค์ ์์ธํ๊ฒ ์ค๋ช ํ๊ฒ ๋ค.
์ดํ๋ ์์(๋๋ ์ ์)์์ ์ถฉ๋, ํ๋ฉด ํ ๋๋ฆฌ์์ ์ถฉ๋, ์ค์ค๋ก์ ๊ผฌ๋ฆฌ์์ ์ถฉ๋์ ์ฐจ๋ก๋ก ํด๊ฒฐํ๋ค.
์์ ์ถฉ๋์์,
turtle.distance()๋ ํน์ turtle ๊ฐ์ฒด์ ๋๋ค๋ฅธ turtle ๊ฐ์ฒด, ๋๋ ํน์ ์ขํ์์ ๊ฑฐ๋ฆฌ๋ฅผ ๊ณ์ฐํด์ ์๋ ค์ค๋ค.
food๋ 10x10 px, snake๋ 20x20px์ธ๋ฐ, 10/2 + 20/2 = 15๋ณด๋ค ๊ฑฐ๋ฆฌ๊ฐ ์ ์ด์ก์ ๋๊ฐ ๊ฐ์ฅ ์๊ฐ์ ์ผ๋ก ์ ์ ํ ์ถฉ๋ ์ํ๋ก ๋๊ปด์ง๋ค.
๋ด๋ถ์ 3๊ฐ์ง ๋ฉ์๋๋ ๊ฐ ํ์ผ์์ ์ค๋ช ํ๊ฒ ๋ค.
ํ ๋๋ฆฌ ์ถฉ๋์์,
turtle.xcor()๋ turtle ๊ฐ์ฒด์ x-coordinate, ์ฆ x ์ขํ๋ฅผ ๋ฐํํ๊ณ , turtle.ycor()๋ y ์ขํ๋ฅผ ๋ฐํํ๋ค.
์๊น ๋งํ๋ฏ์ด snake ๊ฐ์ฒด์ ํฌ๊ธฐ๊ฐ 20px x 20px ์ด๋ฏ๋ก 300(์ฐฝ ํฌ๊ธฐ) - 20(๊ฐ์ฒด ํฌ๊ธฐ) = 280์ด ์ ํํ ์ถฉ๋ ์ํ์ด๋ค.
์ถฉ๋ํ ์ ๊ฒ์ ์ค๋ฒ๊ฐ ๋์ผ ํ๋ฏ๋ก, ๋ฐ๋ณต๋ฌธ์ ์ค๋จ์ํค๊ณ game_over ๋ฉ์๋๋ฅผ ์ถ๋ ฅํ๋ค.
์ด ๋ํ scoreBoard์์ ์ค๋ช ํ๊ฒ ๋ค.
๊ผฌ๋ฆฌ์์ ์ถฉ๋์์,
segments๋ ๊ฐ snake ๊ฐ์ฒด, snake.segments๋ snake ๊ฐ์ฒด๋ฅผ ๋ชจ์๋ ๋ฆฌ์คํธ, snake.head๋ ๋ฑ์ ๋จธ๋ฆฌ, ์ฆ ๊ฐ์ฅ ์์ snake ๊ฐ์ฒด์ด๋ค.
๋ฑ์ ๋จธ๋ฆฌ์ ๊ฐ ๊ฐ์ฒด๊ฐ์ ๊ฑฐ๋ฆฌ๊ฐ 10๋ณด๋ค ์ ์ด์ก์ ๋, ํ ๋๋ฆฌ ์ถฉ๋๊ณผ ๋์ผํ ๋ฐฉ์์ผ๋ก ๊ฒ์ ์ค๋ฒ ์ํจ๋ค.
ํ์ง๋ง segments์๋ snake.head๋ ํฌํจ๋์ด ์์ผ๋ฏ๋ก, snake.head์ดํ์ ๊ฐ์ฒด์ ๋ํด์๋ง ์ถฉ๋ ๊ฒ์ฌ๋ฅผ ์งํํ๋ค.
# snake.py
from turtle import Turtle
STARTING_POSITIONS = [(0, 0), (-20, 0), (-40, 0)]
MOVE_DISTANCE = 20
UP = 90
DOWN = 270
LEFT = 180
RIGHT = 0
class Snake:
def __init__(self) -> None:
self.segments = []
self.create_snake()
self.head = self.segments[0]
def create_snake(self):
for position in STARTING_POSITIONS:
new_segment = Turtle(shape="square")
new_segment.color("green")
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)
def add_segment(self, position):
new_segment = Turtle(shape="square")
new_segment.color("green")
new_segment.penup()
new_segment.goto(position)
self.segments.append(new_segment)
def extend(self):
# add a new segment into a snake
self.add_segment(self.segments[-1].position())
snake.py์๋ ์ฌ๋ฌ ์์๊ฐ ์ฌ์ฉ๋๋ค.
STARTING_POSITION์ ์์ํ ๋ ์์ฑ๋๋ 3๊ฐ์ snake ๊ฐ์ฒด์ ์์น๋ฅผ ์๋ฏธํ๋ค
MOVE_DISTANCE๋ ํ๋ฒ ์์ง์ผ๋ ์์ง์ด๋ ๊ฑฐ๋ฆฌ, 20px์ ๋ปํ๋ค
UP, DOWN, LEFT, RIGHT๋ turtle ๊ฐ์ฒด์ heading ๋ฐฉํฅ์ ๋ํ๋ธ๋ค.
self.segments์๋ ์์ฑ๋ ๋ฑ ๊ฐ์ฒด๋ค์ด ์ ์ฅ๋๋ค.
self.head๋ ๋ฑ ๊ฐ์ฒด๋ค ์ค ๊ฐ์ฅ ์ ๋ ๊ฐ์ฒด๋ฅผ ์๋ฏธํ๋ค.
create_snake๋ ๊ฒ์ ์์ ์ 3๊ฐ์ ๋ฑ์ ์์ฑํ๋ ์ญํ ์ ํ๋ค.
move๋ ๋ฑ์ด ์ง์์ ์ผ๋ก ์์ง์ด๋ ๊ฐ์ฅ ์ค์ํ ์๊ณ ๋ฆฌ์ฆ์ธ๋ฐ, ์ด ์๊ณ ๋ฆฌ์ฆ ๋๋ฌธ์ main.py์์ screen.update๋ฅผ ๊ตณ์ด ์ฌ์ฉํ ๊ฒ์ด๋ค.
๋ฑ์ ๊ธธ์ด๋ ์ ์ฌ๊ฐํ ๊ฐ์ฒด ์ฌ๋ฌ๊ฐ๋ก ์ด๋ฃจ์ด์ ธ์๊ธฐ ๋๋ฌธ์, ๋ฑ์ด ์ด๋ํ๋ ํจ๊ณผ๋ฅผ ์ํด์๋ ๊ฐ ๊ฐ์ฒด ๋์ผํ ์๋งํผ ์ด๋ํด์ผ ํ๋ค.
1๋ฒ turtle ๊ฐ์ฒด๋ฅผ ๋จธ๋ฆฌ๋ก ์ค์ ํ์ ๋, 1๋ฒ์ ์์ MOVE_DISTANCE = 10 ๋งํผ ํญ์ ์ ์งํ๋ค.
์ฌ๊ธฐ์ ์ ์งํ๋ ๋ฐฉํฅ(๋ ์ ๋จ ๋ถ)์ ์์์ ์ค๋ช ํ screen.listen()๊ณผ screen.onkey() ๋ฉ์๋๋ก ์กฐ์ ๋๋ค.
ํ์ง๋ง 1๋ฒ์ด ๋จผ์ ์ ์งํ์ ์, 1๋ฒ์ ๊ธฐ์กด ์์น๋ฅผ ๋ฐ๋ก ๋ณ์์ ํ ๋นํ ํ, 2๋ฒ์ด ํด๋น ์์น๋ก ์์ง์ด๊ณ , 2๋ฒ์๊ฒ ๋ณ์๋ฅผ ํ ๋นํ๊ณ , 3๋ฒ์ด ์์ง์ด๋ ์์ผ๋ก ๋นํจ์จ์ ์ด๊ฒ ๋๋, ๋ฐฉ๋ฒ์ ๋ฐ๊พผ๋ค.
4๋ฒ์ด 3๋ฒ์ผ๋ก ์ด๋ํ๊ณ , 3๋ฒ์ด 2๋ฒ์ผ๋ก ์ด๋ํ๊ณ , 2๋ฒ์ด 1๋ฒ์ผ๋ก ์ด๋ํ ๋ค, 1๋ฒ์ด forward(MOVE_DISTANCE)๋ฅผ ์ทจํ๋ค.
์ญ์ ์ ์งํ๋ ๋ฐฉํฅ์ ๋์ผํ๊ฒ ์กฐ์ ๋๋ค.
์ด๋ ๊ฒ ๋ ์ ๋ฌธ์ ๋ ์๊ฐ์ ํจ๊ณผ๊ฐ ๋ฑ์ด ์ ๋ฒ๋ ์ฒ๋ผ ๋ชจ์๋ค๊ฐ ๋์ด๋๊ณ ๋ชจ์๋ค๊ฐ ๋์ด๋๋ ๊ฒ์ด ๋ณด์ธ๋ค๋ ๊ฒ์ด๋ค.
๊ทธ๊ฒ์ ์ฐ๋ฆฌ๊ฐ ์ํ๋ ํจ๊ณผ๊ฐ ์๋๊ธฐ ๋๋ฌธ์, tracer์ update ๋ฉ์๋๋ฅผ ์ฌ์ฉํจ์ผ๋ก์จ ๋ฑ์ ๋ชจ๋ turtle ๊ฐ์ฒด๋ค์ด ํ๋ฒ์ฉ ์์ง์์ ๋ง์น๊ณ ๋ ๋ค์ update๋ฅผ ํจ์ผ๋ก์จ ๋ฑ์ด ๋ชจ๋ ๋์์ ์์ผ๋ก ๊ฐ๋ ๊ฒ ์ฒ๋ผ ๋ณด์ด๊ฒ ํ๋ ํจ๊ณผ๋ฅผ ๋ธ๋ค.
๋ค์์ ํค ์ ๋ ฅ์ด ๊ฐ์ง๋์์ ๋, ๋ฑ์ ๋จธ๋ฆฌ ๋ฐฉํฅ์ ๋ฐ๊พธ๋ ๋ฉ์๋ 4๊ฐ์ง์ด๋ค.
ํ์ง๋ง ์ฌ๊ธฐ์ ๊ฐ๊ณ ์๋ ๋ฐฉํฅ์ 180๋ ๋ฐ๋ ๋ฐฉํฅ์ผ๋ก๋ ํฅํ ์ ์๊ธฐ ๋๋ฌธ์, ์ด๋ฅผ ์ ๊ฒฝ์ฐ๋ฉฐ ์์ฑํ๋ค.
add_segment๋ ์ ๋ ฅ๋ฐ์ position์์น๋ก, ์๋ก์ด ๋ฑ์ ์์ฑํ๋ ๋ฉ์๋์ด๋ค.
position์ ์์น๋ ๋ฑ์ ์ตํ๋ฐฉ์ ๋ฑ ๊ฐ์ฒด์ ์ขํ์ผ ๊ฒ์ด๋ค.
extend๋ add_segment์ ํด๋น ๋ฑ์ position์ ์ ๋ ฅํด์ฃผ๋ ๋ฉ์๋์ด๋ค.
# food.py
from turtle import Turtle
import random
class Food(Turtle):
def __init__(self) -> None:
super().__init__()
self.shape("circle")
self.penup()
self.shapesize(stretch_len=0.5, stretch_wid=0.5)
self.color("orange")
self.speed("fastest")
random_x = random.randint(-280, 280)
random_y = random.randint(-280, 280)
self.goto(x=random_x, y=random_y)
def refresh(self):
random_x = random.randint(-280, 280)
random_y = random.randint(-280, 280)
self.goto(x=random_x, y=random_y)
food.py์๋ ์์, ๋๋ ์ ์ ๊ฐ์ฒด์ ๋ํ ๋ด์ฉ์ด ์๋ค.
์ ์ ๊ฐ์ฒด๋ Turtle์ ์์๋ฐ๊ณ , ์ ๋ถ ์ค๋ ์ง์ ์์ด๋ค.
์ ๋ํ ๊ธฐ๋ณธ ํฝ์ ์ด 20 x 20์ด๋ฏ๋ก 0.5๋ฐฐ๋ฅผ ์ทจํด์ 10x10 px๋ก ๊ณ ์ ์ํจ๋ค.
speed("fastest")์ ์ด์ ๋ ์์ ์์ฑ ์ดํ ์๋ 3์ค์ ์ฝ๋์ ๋ฐ๋ผ ๋๋ค ์ขํ๋ก ์ด๋ํ๋๋ฐ, ์ด๋ํ๋ ๋ชจ์ต์ด ์๊ฐ์ ์ผ๋ก ๋ณด์ด๋ฉด ์ด์ํ๊ธฐ ๋๋ฌธ์ด๋ค.
์ฆ, ์ด๋ํ๊ณ ๋ ์ดํ๋ง ๋ณด์ฌ์ ํ๋ ์ด ํ ๋ ํด๋น ์์น์์ ์๊ฒจ๋ ๊ฒ ์ฒ๋ผ ๋ณด์ฌ์ค๋ค.
refresh ๋ฉ์๋๋ ๋จน์ด๋ฅผ ๋จน์์ ๋์ ์ํฉ์ผ๋ก, ์๋ก์ด ๋จน์ด๋ฅผ ์์ฑํ๋ค.
# scoreBoard.py
from turtle import Turtle
FONT = ('Courier', 25, 'normal')
class ScoreBoard(Turtle):
def __init__(self) -> None:
super().__init__()
self.score = 0
self.color("white")
self.penup()
self.hideturtle()
self.goto(0,270)
self.write(arg=f"Score: {self.score}", align="center", font=FONT)
def increase_score(self):
self.score += 1
self.clear()
self.write(arg=f"Score: {self.score}", align="center", font=FONT)
def game_over(self):
self.goto(0,0)
self.write("Game Over", align="center", font=('Courier', 35, 'normal'))
scoreBoard์๋ ์ ์ํ, ๊ทธ๋ฆฌ๊ณ ๊ฒ์์ค๋ฒ ์ ๊ฒ์์ค๋ฒ ํ ์คํธ๊ฐ ๋จ๋ ๊ฐ์ฒด๋ค์ ๊ด๋ฆฌํ๋ค.
FONT๋ ํฐํธ ์ ๋ณด๋ฅผ ๋ด์ ์์์ธ๋ฐ, ์ด๋ ํน๋ณํ ์ด์ ๋ ์๊ณ ์ ์ด์ธ๋ฆฌ๋ ํฐํธ์ ํฌ๊ธฐ๋ฅผ ์ ํ๋ฉด ๋๋ค.
(600x600์ ํ๋ฉด์์๋ 25์ ๋๊ฐ ์ ์ ํด๋ณด์ธ๋ค)
ScoreBoard๊ฐ์ฒด ์ญ์ Turtle์ ์์๋ฐ๊ณ , self.score๋ฅผ 0์ผ๋ก ์ค์ ํ ๋ค food์์ ํ๋๊ฒ๊ณผ ๋น์ทํ๊ฒ ์งํํ๋ค.
์ฌ๊ธฐ์ self.write๋ ์ง๊ด์ ์ผ๋ก ํด๋น ๋ฌธ๊ตฌ๋ฅผ ํ๋ฉด์ ๋์ฐ๋ ํจ๊ณผ๋ฅผ ํ๋ค.
increase_score๋ฉ์๋๋ ๋ฑ์ด ์์์ ๋จน์์๋, ์ฆ ๋ฑ์ ์ต์ ๋ ๊ฐ์ฒด์ธ snake.head์ ์ขํ๊ฐ food ๊ฐ์ฒด์ distance < 15์ ์ฐจ์ด๋ฅผ ๋ณด์ผ ๋, ๋ฐ๋ํ๋ค.
์ ์๋ฅผ 1 ์ฌ๋ฆฌ๊ณ , ๊ธฐ์กด์ ํ ์คํธ๋ฅผ ์ญ์ ํ ๋ค ์ ๋ฐ์ดํธ๋ ์ ์๋ก ํ ์คํธ๋ฅผ ์ถ๋ ฅํ๋ค.
self.clear()๋ฅผ ํ์ง ์์ผ๋ฉด ๊ธฐ์กด์ ํ ์คํธ ์์ ๋ ํ ์คํธ๊ฐ ์น์ด์ง๋ฉด์ ์ฝ์ ์ ์๊ฒ ๋๋ค.
game_over๋ฉ์๋๋ ๊ฒ์ ์ค๋ฒ ์ํฉ์ ์ฌ์ฉํ๊ธฐ ์ํ ๋ฉ์๋์ธ๋ฐ, (0,0), ์ฆ ํ๋ฉด ์ ์ค์์ Game Over ํ ์คํธ๋ฅผ ๋์ฐ๋ ์ญํ ์ด๋ค.
๋ณธ ํฌ์คํธ๋ udemy ๊ฐ์ข
'100 Days of Code: The Complete Python Pro Bootcamp for 2023' Section 20 - 21
์ ๋ด์ฉ์ ์ ๋ฆฌํ ๊ฒ ์ ๋๋ค.
'python' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
[programmers] ๋ฌธ์์ด ์ฌ๋ฌ๋ฒ ๋ค์ง๊ธฐ (python) (8) | 2023.10.11 |
---|---|
[python] tkinter miles km converter / tkinter ๋ง์ผ ํฌ๋ก๋ฏธํฐ ๋ณํ๊ธฐ (3) | 2023.10.10 |
[python] *args ๊ณผ **kwargs (6) | 2023.10.10 |
[python] list comprehension ๋ฆฌ์คํธ ์ปดํ๋ฆฌํจ์ (0) | 2023.10.09 |
[python] code-runner extension ํ์ผ ์คํ ์ ํฐ๋ฏธ๋์ ์ด๊ธฐํ, file path ์ถ๋ ฅ ์๋๊ฒ ๋ณ๊ฒฝ (0) | 2023.10.08 |