일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |
- Game
- 파싱
- API플랫폼
- 웹크롤링
- 게임
- 프로그램
- 계산기
- 상태코드
- 프로젝트
- 최저가
- ndarray
- udemy
- twilio
- SMTP
- class
- HTTP
- Sheety
- 유데미
- 파이썬
- 오류
- 웹페이지
- Tequila
- HTML
- phython
- 부트스트랩
- Pygame
- Endpoint
- API
- 쉬티
- Python
- Today
- Total
데이터 분석가
파이썬 뱀 게임(Snake game) Turtle Game 프로젝트 본문
안녕하세요 !
이번 시간에는 Turtle 함수를 불러와서 뱀 게임을 만들어 보겠습니다.
뱀 게임을 만들기 앞서 클래스를 한번 보고 갑시다
클래스라는 '붕어빵 틀'만 만들어 놓으면 이를 통해 붕어빵(인스턴스) = 붕어빵 틀(재료) 와 같은 형태로 재료(값)만
입력해주면 언제든 붕어빵(인스턴스) 를 만들어 냅니다.
첫 번째 파일, snake.py (부모 클래스)
from turtle import Turtle
STARTING_POSITIONS = [(0, 0), (-20, 0), (-40, 0)] #생성지점
MOVE_DISTANCE = 20 #생성 지점 좌표 3개는 x축 20차이 이므로 이어 붙이려면 20
UP = 90
DOWN = 270
LEFT = 180
RIGHT = 0
STARTTING_POSITIONS는 각각의 좌표 3개의 리스트입니다. **편의상 머리 몸통 꼬리 라고 표현할게요
위 좌표의 X축 20거리 만큼 떨어져 있는데 이는 머리 몸통 꼬리가 붙은 상태로 움직여야 하기 때문입니다.
UP , DOWN, LFET, RIGHT 는 각도입니다
class Snake:
def __init__(self):
self.segments = [] #뱀의 길이 리스트
self.create_snake() #뱀 생성 메소드
self.head = self.segments[0] #뱀 머리는 첫번째는 0인덱싱
자 드디어 Class Snake를 선언하였습니다
class 함수는 붕어빵 틀을 만든다고 설명해는데, 이 틀은 def __init__(self):를 이용합니다.
__init__ 메소드에서 segments, create_snake(), hed 내부 변수(붕어빵 틀)들을 선언합니다
이렇게 선언된 내부 변수들은(붕어빵 틀) 다른 메소드에서 붕어빵을 만들기 위해 사용하게 됩니다
def create_snake(self): #생성지점 def문
for position in STARTING_POSITIONS: #뱀은 3칸짜리이므로 좌표 3개를 이어붙임
self.add_segment(position) #좌표를 다음
def add_segment(self, position): #뱀 설정
new_segment = Turtle("square") #뱀은 3칸짜리 네모로 구성됨
new_segment.color("white") #하얀색
new_segment.penup() #펜 허용(색칠모드)
new_segment.goto(position) #goto는 좌표로 즉시 이동 **def extend로 맨뒤에 바로 추가
self.segments.append(new_segment) #3칸짜리 뱀의 구성요소의 집합을 추가
def extend(self): #뱀 길이 연장
self.add_segment(self.segments[-1].position()) #segment 맨뒤 -1인덱싱, goto(position)을 읽어 맨뒤에 바로 추가됨
def move(self): #뱀 움직임 메소드
for seg_num in range(len(self.segments) - 1, 0, -1): #스타팅 포인트에서 3칸짜리 뱀 리스트
new_x = self.segments[seg_num - 1].xcor() #터틀의 xcor() 함수를 이용해 x축 추가된 새로운 segment 좌표
new_y = self.segments[seg_num - 1].ycor() #ycor() 함수를 이용해 y축 추가된 새로운 segment의 좌표
self.segments[seg_num].goto(new_x, new_y) #food를 먹으면서 생성된 추가 좌표 1개를 뱀 맨뒤에 붙임
self.head.forward(MOVE_DISTANCE) #뱀 머리를 초기 설정한 거리값(20)만큼 이동 시킵니다
def create_snake 메소드는 맨 처음 시작될 때 생성된 STARTTING_POSITIONS 머리 몸통 꼬리
세 좌표를 for문 반복을 통해 add_segment 리스트에 추가시킵니다
def add_segment 메소드는 말 그대로 머리 몸통 꼬리에서 꼬리2, 꼬리3, 꼬리4 ... 생성해서 붙이는 메소드입니다
붙이는 작업은 onto라는 즉시 생성 명령어를 통해 이루어지고
def extend 메소드를 통해 해당 위치에 추가되는 좌표는 position의 새로운 좌표입니다.
여기까지 뱀의 머리 - 몸통 - 꼬리 - (꼬리2) - (꼬리3)... 의 좌표를 알아보았는데요
이렇게 추가된 뱀은
def move 메소드를 통해 움직임을 결정합니다
segment 리스트를 이용해 뱀의 크기만큼 for문을 돌려 뱀의 크기만큼(머리,몸통,꼬리, 꼬리2...)
좌표에 goto(즉시 생성) 후, forward를 이용해 움직입니다
저희 눈에는 사실 뱀이 붙어서 이동하는 것 같지만,
컴퓨터 입장에서는 좌표들을 하나하나씩 지우고 생성하는 노가다입니다..
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)
움직이는 원리는 앞서 설정한 UP(90), DOWN(270), LEFT(180), RIGHT(0) 변수들의 값을
def 로 선언하여 heading() 각도에 따른 방향 설정 명령어를 이용해 거북이 방향 설정합니다
여기서 def right 의 경우 if 문의 != LEFT:를 이용하여 반대방향 이동을 제한합니다
사실 여기까지가 snake.py라는 부모 클래스를 정의하는 파일이었습니다.
너무 길다 어렵다 복잡하다 하기싫다
두 번째 scoreboard.py (자식 클래스)
두 번째 파일로 scoreboard 클래스를 만들어 볼건데요
from turtle import Turtle
ALIGNMENT = "center" #조정은 센터
FONT = ("Courier", 24, "normal")
말 그대로 점수판인데 ALIGHTMENT(정렬)로 중앙에 위치시키고 문자의 폰트 설정합니다
class Scoreboard(Turtle): #Scoreborad는 자식 클래스, Turtle은 부모 클래스
def __init__(self):
super().__init__() # **클래스 Snake의 부모 메소드를 사용
self.score = 0
self.color("white")
self.penup()
self.goto(0, 270) #부모 메소드에선 position 리스트를 썼다.
self.hideturtle() #부모 메소드 내부 속성 추가
self.update_scoreboard() #부모 메소드 내부 속성 추가
class Scoreboard(Turtle):
class 선언은 여태까지 다르지 않은 형태인데 Turtle이 들어갔네요 ??
이는 먼저 선언된 snake.py의 Turtle이라는 부모 클래스의 붕어빵 틀을 아들 클래스(Scoreboard)가 빌려오는 거에요
이렇게 빌려온 메소드를 활용하려면
def __init__(self):
super().__init__() 를 먼저 적어야 합니다 (이는 그냥 외우세요 부모 클래스 메소드를 가져오는 의식입니다)
가져온 속성 color, penup 등등에
자식은 자신만의 붕어빵 장사를 위해 hideturtle같은 내부 속성을 추가합니다.
def update_scoreboard(self): #상단 스코어를 종합하는 메소드
self.write(f"Score: {self.score}", align=ALIGNMENT, font=FONT) #score 합산, align 정렬 중앙, 폰트
def game_over(self): #게임 종료 메소드
self.goto(0, 0) #좌표로 복귀
self.write("GAME OVER", align=ALIGNMENT, font=FONT) #해당 문구
def increase_score(self): #score = 0 에서 1씩 증가
self.score += 1
self.clear() #스코어를 초기화 시켜 +1 시킨 숫자만 남게한다
self.update_scoreboard() #스코어에 +1하고 전의 숫자 초기화 시킨 것을 업데이트
def update_scoreboard :
write 명령어로 스코어 보드를 업데이트 합니다
def game_over :
게임 종료 시 메소드
def increase_score :
score = 0 에서 뱀이 food를 먹을 때마다 1씩 증가시킵니다
그리고 직전에 있던 점수를 없애버리고(clear()) 증가시킨 점수만 남겨둡니다.
세 번째 food.py
뱀이 먹을 먹이를 생성하는 코드입니다
from turtle import Turtle
import random
class Food(Turtle): #부모 클래스 Turtle, 자식 클래스는 Food
def __init__(self):
super().__init__() #부모 클래스 Turtle을 사용합니다
self.shape("circle") #먹이 모양 원
self.penup()
self.shapesize(stretch_len=0.5, stretch_wid=0.5) #사이즈 조정
self.color("blue")
self.speed("fastest") #먹이 생성 속도
self.refresh() #초기화 메소드로 넘어갑니다
def refresh(self): #초기화 메소드
random_x = random.randint(-280, 280) #x축 좌표 -280 ~ 280 사이 무작위 랜덤
random_y = random.randint(-280, 280) #y축 좌표 -280 ~ 280 사이 무작위 랜덤
self.goto(random_x, random_y) #랜덤 좌표로 생성된 먹이를 좌표에 위치
score_board와 마찬가지로 Snake.py의 부모 클래스를 빌려옵니다(class Food(Turtle))
def __init__(self):
super().__init__() 부모 클래스를 빌려오므로 선언해주고
가져온 메소드의 속성 외에 shapesize, refresh 같은 아들 속성을 추가합니다
def resfresh 를 이용해
food 먹이를 (x,y) 좌표를 랜덤으로 받아 goto 즉시생성합니다
와 거의 다 왔습니다 !
snake.py
score_board.py
food.py
를 이용해서 main.py를 만들어 보겠습니다
네 번째 main.py
from turtle import 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(1) #애니메이션 효과
snake = Snake() #클래시 사용 시 사전 작업
food = Food() #클래시 사용 시 사전 작업
scoreboard = Scoreboard() #클래스 사용 시 사전 작업
screen.listen() #입력 대기 메소드
#onkey는 키보드를 눌렀다 뗐을 떄 발생하는 메소드
screen.onkey(snake.up, "Up") #뱀을 위로
screen.onkey(snake.down, "Down") #뱀을 아래로
screen.onkey(snake.left, "Left") #뱀을 왼쪽으로
screen.onkey(snake.right, "Right") #뱀을 오른쪽으로
우리가 선언한 각각의 클래스들을 모두 가져옵니다
그리고 나머지 screen 명령어를 onkey(키보드 방향키를 눌렀다가 떼는 작업) 시 코드 작동
game_is_on = True #while문은 True 시 종료이므로 while 활용 전 선언
while game_is_on:
screen.update()
time.sleep(0.1) #0.1초 일시정시(지연) 후 다음 코드 snake.move()로 간다
snake.move() #
if snake.head.distance(food) < 15: #먹이가 뱀 머리 기준 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:
if segment == snake.head:
pass
elif snake.head.distance(segment) < 10:
game_is_on = False
scoreboard.game_over()
screen.exitonclick()
뱀이 죽기 전까지 반복문은 작동합니다
if snake.hed.distance(food) < 15 :
food 먹이가 뱀 머리 기준 15칸 이내일 경우 먹이 재생성
if snake.head.xcor() > 280 or snake.head.xcor() < -280 or snake.head.ycor() > 280 or snake.head.ycor() < -280:
if 문이 참 길죠 ? 쉽게 설명하자면 뱀의 (x,y)좌표가 280을 넘을 넘어 벽에 부딪히는 경우 종료합니다.
for segment in snake.segments:
뱀의 머리 snake.head가 자신의 몸통 혹은 꼬리를 닿을 경우(좌표가 겹칠 경우)도 게임 종료합니다
screen.exitonclick()
클릭으로 해당 화면 종료합니다
이번 시간에는 class를 이용해서 게임을 만들어 보았는데요
부모가 어쩌고 아들 클래스가 어쩌고 참 어려운 개념이었던거 같습니다
다음시간에 봅시다 !
snake.py
score_board.py
food.py
해당 파일은 알집으로 준비해 뒀습니다 !
이를 바탕으로 main.py를 만들어서 작동시켜 봅시다
'파이썬(python) 프로젝트 모음' 카테고리의 다른 글
파이썬 마일변환기 GUI 인터페이스 구현 프로젝트 (0) | 2023.04.03 |
---|---|
파이썬 퐁 게임(PongGame) 프로젝트 (0) | 2023.03.30 |
파이썬(Python) 거북이 경주(turtle race) 프로젝트 (0) | 2023.03.28 |
파이썬(Python) 스피로 그래프 & Spot painting (0) | 2023.03.28 |
파이썬(Python) 퀴즈 프로그램(quizprogram) 프로젝트 (0) | 2023.03.27 |