Python Chrome Dinosaur Game-Mini Project-Code Description 2
날짜 2023-11-05 16:30
주제-키워드 =>
연관된 기록 =>
위에서 작성한 내용과 연관성이 있는 기록-노트를 연결.
" [[]] "
코드1
C:\Users\GUN\OneDrive - 한국산업기술대학교\바탕 화면\웹-개발-취업캠프\파이썬-크롬공룡게임\self_ChromeDinoGame\main.py
import pygame
import os
import random
from constants import * # 전역상수 코드 따로 정의 후 불러와 사용.
'''
ㅡ
게임 화면에 공룡 플레이어 존재
따라서 Dinosaur 클래스 정의 후 Dinosaur 인스턴스-객체-요소 생성 진행
'''
class Dinosaur() :
'''
ㅡ
게임 속 객체는 화면 스크린 상 위치할 좌표를 설정해야함
X_POS
Y_POS
Y_POS_DUCK
공룡 엎드려 달릴 때 좌표점 달라짐
ㅡ
JUMP_VEL 이 변수가 가지는 의미를 잘생각.
옵시디언 "점프상황-코드 이해" 내용 확인
'''
X_POS = 80
Y_POS = 310
Y_POS_DUCK = 340
JUMP_VEL = 8.5 # 공룡 점프 시 높이
'''
ㅡ
Dinosaur클래스 생성자 메서드 정의.
인스턴스가 생성되자마자 들어갈
생성자 메서드 내부에는 공룡 캐릭터 초기 설정값들을 정의한다.
ㅡ
전역 상수에 할당한 이미지 데이터들을 해당 클래스 인스턴스에 할당되도록 클래스 속성에 할당
duck_img
run_img
jump_img
'''
def __init__(self) -> None:
self.duck_img = DUCKING
self.run_img = RUNNING
self.jump_img = JUMPING
'''
ㅡ
공룡 객체 초기 자세 설정.
특정 조건에 따른 흐름제어
특정 조건에 따른 적절한 행동 이미지 사용
ex) 사용자가 특정 키를 누르면...특정 이벤트를 발생시키면 그에 따른 흐름제어
동작 이미지 설정
동작 조건 설정
따라서 초기 동작은 dino_run 이구나
'''
self.dino_duck = False
self.dino_run = True
self.dino_jump = False
'''
ㅡ
step_index는 초당 프레임과 연관성 있음
프레임.
ㅡ
공룡 객체 self.image이미지.
처음에는 run_img 리스트에 들어있는 0번 이미지로 설정
ㅡ
위에서 우리는 run 이미지를 2개 사용한다고 정의함.
0번 인덱스 이미지 <> 1번 인덱스 이미지를 번걸아 가면서 사용해 캐릭터 동작 구현
ㅡ
jump_vel
공룡 객체가 점프하는 상황을 코드로 구현하기 위해 위에서 정의한 점프한 높이_속도 값 의미를 가지는 self.JUMP_VEL값을 업데이터 되면서 계속 변해야 하기 떄문에 self.jump_vel 할당함
즉, self.JUMP_VEL 이 값은 초기 설정값
그 이후 점프라는 동작이 발생하면 self.jump_vel값은 계속 업데이트된다 변한다.
'''
self.step_index = 0
self.jump_vel = self.JUMP_VEL
self.image = self.run_img[0]
'''
ㅡ
공룡 객체 피격범위를 설정함
dino_rect이 변수 안에 get_rect()함수가 이미지 사이즈에 따른 충돌박스 사이즈를 구해줌
ㅡ
게임 화면에 존재하는 모든 것들 스크린 안에 존재하도록 좌표점 설정
피격박스 위치 설정
ㅡ
위에서 이미 공룡 객체 좌표설정함
공룡 객체 피격박스는 당연하게도 공룡 객체와 같다.
ㅡ
변수 헷갈림 주의
self.X_POS : 공룡 객체의 좌표
dino_rect.x: 공룡 객체의 충돌박스 좌표
'''
self.dino_rect = self.image.get_rect()
self.dino_rect.x = self.X_POS
self.dino_rect.y = self.Y_POS
'''
ㅡ
update()
공룡 클래스 내부에 메서드 정의
사용자가 특정 키를 눌렀을 때 = 특정 이벤트가 발생했을 때에 따른
흐름제어_동작제어 구문을 작성
따라서 update메서드 안에 매개변수_인수로 위에서 정의한 userInput값이 들어간다. 인수로서
위에서
self.dino_duck = False
self.dino_run = True
self.dino_jump = False
동작 조건이 들어있는 변수를 정의함.
'''
def update(self, userInput) :
if self.dino_duck :
self.duck()
if self.dino_run :
self.run()
if self.dino_jump :
self.jump()
'''
ㅡ
초기
공룡 객체의 self.step_index = 0 이였음.
step_index는 점점 커진다.
특정 조건_크기가 되면 다시 0부터 증가되도록 코드 작성 = 초기화 설정 코드 작성
특정 조건에 따른_상황에 따른 흐름제어를 하고 싶을 땐 if문을 사용하는것
'''
if self.step_index >= 10 :
self.step_index = 0
'''
ㅡ
사용자가 특정 이벤트를 발생시키면 = 특정 키보드의 입력이 감지되면
흐름 제어 코드 작성
동작 제어 코드 작성
ㅡ
위에서 userInput = pygame.key.get_pressed() 정의함
따라서 userInput 변수에는 특정한 값이 할당되어 있는 상태
사용자가 K_UP 누르고 점프 상태가 아니라면 점프한다
코드 작성
이해 ㅇㅇㅇ
키를 눌렀을 때 공룡 캐릭터 점프 되야함
주의 =>
단순하게 생각하면 조건 명제를 (userInput[pygame.K_UP]) 이외에 (self.dino_jump)를 한번 더 작성한 이유를 생각해보자
ㅡ
뒤 조건명제를 작성하지 않았다면
이단 점프가 발생할 수 있다.
이단점프는 내가 원하는 동작이 아니다.
따라서
추가적인 조건명제를 작성한 것.
ㅡ
사용자가 K_DOWN를 누르고 + 점프 상태가 아니라면 > 엍드리는 동작조건 True변경 > 동작 실행
ㅡ
사용자가 점프 상태가 아니라면 + K_DOWN를 누르지 않았다면 > run 조건 True 변경
= 달리는 상태라면
'''
if userInput[pygame.K_UP] and not self.dino_jump :
self.dino_duck = False
self.dino_jump = True
self.dino_run = False
elif userInput[pygame.K_DOWN] and not self.dino_jump :
self.dino_duck = True
self.dino_jump = False
self.dino_run = False
elif not (self.dino_jump or userInput[pygame.K_DOWN]) :
self.dino_duck = False
self.dino_jump = False
self.dino_run = True
'''
ㅡ 이 부분과 연관성 있음
def update(self, userInput) :
if self.dino_duck :
self.duck()
if self.dino_run :
self.run()
if self.dino_jump :
self.jump()
따라서 아래부터는 해당 동작에 따른 함수를 정의_기능을 정의
'''
def duck(self) :
self.image = self.duck_img[self.step_index//5]
self.dino_rect = self.image.get_rect()
self.dino_rect.x = self.X_POS
self.dino_rect.y = self.Y_POS_DUCK # run 달라 좌표점
self.step_index += 1
def run(self) :
'''
ㅡ
위에서 전역 상수에 할당한 "달리기"이미지를 인스턴스 변수에 할당
RUNNING = [pygame.image.load(os.path.join("Assets/Dino","DinoRun1.png")), pygame.image.load(os.path.join("Assets/Dino","DinoRun2.png")) ]
self.run_img = RUNNING
self.image = self.run_img[0]
위에서 초기 이미지로 0번 인덱스 이미지로 할당했음
하지만 게임 구현 상상.
0번 <> 1번 번갈아 가면서 self.run_img 객체 run이미지에 할당되야함
ㅡ
그 이후 self.image 객체 현재 상테 이미지에 할당되야 하고
ㅡ
이러한 상황을 구현하기 위해 리스트 안에 인덱싱을 self.step_index//5 이러한 로직을 작성한 것
ㅡ
self.step_index 객체 스탭 변수의 값이 1씩 증가됨.
0 1 2 3 4 5 6 7 8 9 10
0 0 0 0 0 1 1 1 1 1 1 ....
이러한 흐름이 발생
ㅡ
이해 ㅇㅇㅇ
ㅡ
def update(self, userInput) :
이 함수가 호출되면 step_index값을 매번 확인함.
'''
self.image = self.run_img[self.step_index//5]
self.step_index += 1 # 1씩 증가 -> 10이 되는순간 값 초기화
'''
ㅡ
위에서 아래와 같은 코드구분을 작성했었음
ㅡ
def __init__(self) 생성자 메서드 내부에 작성.
ㅡ
지금은 def run() 내부에 작성.
그때는 공룡 클래스 인스턴스 생성시 초기값을 설정한 의미
이번에 다시 같은 코드를 작성한 이유=>
특정 동작에 따라서 좌표값이 변하기 때문
달리는 상황 좌표점
점프 동작 좌표점
엎드리기 동작 좌표점
따라서 모든 동작 메서드 함수 내부에 같은 코드가 반복된다.
'''
self.dino_rect = self.image.get_rect()
self.dino_rect.x = self.X_POS
self.dino_rect.y = self.Y_POS
self.step_index += 1 # 1씩 증가 -> 10이 되는순간 값 초기화
# 점프 동작 함수 정의
'''
ㅡ
점프 동작을 했을 때 필요한 값들이 호출되도록_생성되도록 하는 함수 정의
점프 동작에 대한 자세한 내용은 기록확인
'''
def jump(self) :
'''
ㅡ
점프를 하면
객체 이미지에 점프 이미지로 초기화_할당
ㅡ
이 부분에 대한 자세한 설명은 기록확인
'''
self.image = self.jump_img
if self.dino_jump :
self.dino_rect.y = self.dino_rect.y - self.jump_vel * 4
self.jump_vel = self.jump_vel - 0.8
if self.jump_vel < -self.JUMP_VEL :
self.dino_jump = False # 점프가 끝난 상황
self.jump_vel = self.JUMP_VEL # 다시 처음값으로 초기화 // 왜? 점프는 언제든 다시 가능
'''
ㅡ
위에서 다양한 동작 함수를 정의함.
ㅡ
해당 동작함수가 호출되면 그 동작후녀에 필요한 설정값들이 생성된다.
ㅡ
따라서 그 이후에는 화면에 그 상황을 그려줘야 한다
draw함수를 정의
SCREEN을 그려줘야 한다.
따라서 함수의 매개변수_인수로서 SCREEN이 들어가기에 매개변수 이름도 SCREEN으로 설정한다.
ㅡ
그게 명명 규칙
ㅡ
내가 정의하지 않았지만
blit() 함수를 사용해 화면을 그려준다.
ㅡ
1번 매개변수에는 객체의 이미지가 들어감, 2번 매개변수에는 좌표점이 들어간다
'''
def draw(self, SCREEN) :
SCREEN.blit(self.image, (self.dino_rect.x, self.dino_rect.y))
'''
ㅡ
배경에 존재하는 구름 객체를 위한 클래스 정의
'''
class Cloud() :
'''
ㅡ
생성자 메서드 내부에는 해당 객체의 초기 설정값들이 들어간다. 작성된다.
ㅡ
초기 x 위치_좌표값 정의.
초기에는 게임 스크린 보다 오른쪽에 존재하는 상황.
따라서 처음에는 보이지 않는다.
ㅡ
초기 y 위치 좌표값 정의.
구름은 하늘에 존재한다.
따라서 코드를 저렇게 작성하면서 구름의 위치가 상단에 출력되도록 한다.
ㅡ
구름 객체에 절대 상수로 정의한 이미지 할당
ㅡ
get_width()함수를 이용해 로컬에 있는 구름 이미지의 사이즈를 파악
왜?
update()함수에서 이 값이 필요하기 때문.
그래서 구름이 계속 초기화 가능해짐
'''
def __init__(self) -> None:
self.x = SCREEN_WIDTH + random.randint(300, 500)
self.y = random.randint(50, 100)
self.image = CL
self.width = self.image.get_width()
'''
ㅡ
구름 객체의 초기 설정값이 게임이 진행됨에 따라서 계속 업데이트 되야 하기 때문에 그러한 기능을 수행하는 함수정의.
'''
def update(self) :
'''
ㅡ
배경이 어떻게 이동이 되고 있는지 자세한 설명은 옵시디언 기록 확인
ㅡ
조건문 의미 =>
구름 x 좌표값이 게임 스크린 영역을 넘어가는 순간 다시 원래 위치에서부터 시작하도록 값을 초기화
ㅡ
원래 위치로 보내도 되지만 구름이 자주 등장하지 않도록?
초기 위치보다 좀 더 멀리 보내준다.
'''
self.x -= game_speed
if (self.x < - self.width ) :
self.x = SCREEN_WIDTH + random.randint(1300, 2000)
self.y = random.randint(50, 100)
'''
ㅡ
화면에 보이는 모든 객체는 결국 게임 스크린에 출력_그려져야 함.
따라서 그러한 기능을 수행하는 draw()함수 정의
'''
def draw(self, SCREEN ) :
SCREEN.blit(self.image, (self.x, self.y))
'''
ㅡ
게임 안에 장애물 요소 존재.
따라서 해당 클래스 정의
'''
class Obstacle :
'''
ㅡ
장애물 객체 초기값 설정_정의
ㅡ
다른 클래스에서는
self.image 객체 이미지 변수에 해당 이미지를 바로 할당함.
하지면 여기선 image 매개변수를 할당함.
왜?
현재 이 클래스는 장애물-부모 클래스이기 때문
자식 클래스에서 부모 클래스를 상속받고 > 자식클래스에서 부모 클래스의 생성자를 호출해야 그떄서야 해당 이미지가 객체 이미지 변수에 할당됨.
즉, self.image = image 이부분은 자식클래스에서 image어떤 값이 들어갈지 정해짐
ㅡ
type 매개변수를 정의한 이유.
현재 장애물-선인장 이미지 다양함.
작은 선인장 이미지 3개
큰 선인장 이미지 3개
위에서 모두 리스트로 묶어 저장했음
따라서 랜덤하게 선인장 이미지들을 인덱싱 후 랜덤하게 화면에 출력하기 위함.
즉, 랜덤하게 이미지를 인덱싱하기 위한 인덱스
'''
def __init__(self, image, type) -> None:
self.image = image
self.type = type
'''
ㅡ
현재 image값이 자식클래스에 넘어온 리스트임
type에 결정된 값에 따라서 인덱싱이 된 이후에 그 이미지의 충돌박스 사이즈를 위해 코드가 저렇게 작성되어 있음
'''
self.rect = self.image[self.type].get_rect()
'''
ㅡ
장애물 좌표값 설정
게임 실행 상황 생각.
스크린 오른쪽 > 왼쪽으로 이동함
따라서 처음 x값은 딱 화면 폭값으로 설정한 것
'''
self.rect.x = SCREEN_WIDTH
# 선인장을 값을 위한 함수 정의
'''
ㅡ
게임이 진행되면서 계속 업데이트 되야함
그러한 기능을 구행하는 update 함수 정의
'''
def update(self) :
'''
ㅡ
아래 코드를 통해
오른쪽 > 왼쪽으로 이동하는 상황 코드로 정의
'''
self.rect.x -= game_speed
'''
ㅡ
위에서 정의한 다른 이동하는 객체?들은 화면 영역을 벗어나면 다시 처음 값으로 초기화를 진행함.
하지만 이 객체는 pop메서드를 사용해 아예 제거함
왜?
제거해주지 않으면 화면 영역밖을 벗어난 선인장 값들이 메모리에 계속 쌓이는 상황이 발생
'''
if self.rect.x < - self.rect.width :
'''
ㅡ
아래 코드 의미
리스트 마지막 값을 반환하고
원본 객체에서 제거한다
'''
obstacles.pop()
'''
ㅡ
게임 속 객체를 정의했으면
최종적으로 draw 그려줘야 한다.
'''
def draw(self, SCREEN) :
SCREEN.blit(self.image[self.type], self.rect)
'''
ㅡ
장애물-작은 선인장 객체 정의
'''
class SmallCactus(Obstacle) :
'''
ㅡ
생성자 내부에 초기값 정의
ㅡ
부모 클래스에 작성된 type 값이 자식클래스에서 결정된다.
의미
리스트에 들어있는 이미지들을 랜덤하게 가져오기 위해
ㅡ
super 함수를 사용해서
자식 클래스에서 결정된 값을
부모 클래스의 생성자메서드를 호출해 값을 할당하고 있다.
ㅡ
이 코드로 super함수를 제대로 이해함
부모 클래스의 메서드를 호출하고 있는 것.
직접 호출
원래는 자동호출이지만 자식 클래스에서 호출해 사용
'''
def __init__(self, image) -> None:
self.type = random.randint(0,2)
super().__init__(image, self.type)
self.rect.y = 325
class LargeCactus(Obstacle) :
def __init__(self, image ) -> None:
self.type = random.randint(0,2)
super().__init__(image, self.type)
# 큰 선인장 y 좌표값
'''
ㅡ
큰 선인장이 작은 선인장 y값보다 작다
당연하다
큰 선인장이 원점과 더 가까워야한다 더 크니까.
'''
self.rect.y = 300
class Bird(Obstacle) :
def __init__(self, image) -> None:
'''
ㅡ
새 객체는 선인장 객체와 달리 이미지가 2개일뿐더라 동작하는 방식이 다르다.
ㅡ
0번 이미지 <> 1번 이미지 번갈아 가면서 출력이 되야한다.
움직이는 것처럼 보이게 하기 위해
ㅡ
따라서
self.type = random.randint(0,2)
이부분이 달라진다.
선인장은 랜덤하게 type 값이 들어가야되고
새는 0을 넣는다.
ㅡ
공룡 달리는 상황과 비슷
따라서 self.index 작성
'''
self.type = 0
self.index = 0
super().__init__(image, self.type)
self.rect.y = 250
def draw(self, SCREEN):
if self.index >= 9 : # 0 1 2 3 ~ 7 8 9되는 순간 초기화
self.index = 0
'''
# 화면에 출력
# 0번 이미지 1번 이미지를 번걸아가면서 출력 = 날개짓
# 반복문에서 호출할떄마다 1씩 증가
'''
SCREEN.blit(self.image[self.index//5], self.rect)
self.index += 1
'''
메인 함수 정의
'''
def main() :
'''
ㅡ
아래 코드 확인
game_speed 변수 정의함.
파이썬 코드 네임스페이스_범위 생각
이 함수 내부에서 정의가 되었지만 전체적으로 사용이되는 값이다.
따라서 전역 변수로 정의.
ㅡ
x_pos_bg, y_pos_bg
게임 스크린 배경에 대한 좌표 변수는 전역변수로 정의한다.
ㅡ
공룡이 땅위에서 달리고 뛰는 상황
따라서 background() 함수 내부에 처음 x,y_pos_bg 변수가 정의된다.
ㅡ
points
게임 점수를 측정하는 변수 전역변수로 정의
ㅡ
obstacles
장애물 객체 변수 전역변수로 정의.
ㅡ
아래에서 리스트로 정의했음
ㅡ
실습코드에 작성한 폰트를 사용하면 한글이 출력되지 않는다.
이유?
따라서 선배 개발자 코드에 작성되어 있는 폰트로 변경해본다.
'''
global game_speed
global x_pos_bg, y_pos_bg
global points
global obstacles
'''
ㅡ
run 변수 정의
게임 루프문을 작성해 게임을 반복 실행하기 위해
반복문에 사용할 조건변수
ㅡ
Clock 클래스 인스턴스 생성
게임 상에서 시간을 측정하기 위해
ㅡ
공룡 클래스 인스턴스 생성 = 플레이어 생성
player
ㅡ
game_speed
게임 속도 설정값 정의
게임 구현 생각 =>
캐릭더가 움직이는 것 처럽 보이지만 사실 뒷배경이 움직이고 있는 것.
ㅡ
x_pos_bg
공룡이 달리고 있는 배경_트랙의 좌표 초기값 설정
ㅡ
points 점수는 0부터 시작
초기값 설정
ㅡ
cloud
클라우드 인스턴스 생성
'''
run = True
clock = pygame.time.Clock()
cloud = Cloud()
player = Dinosaur()
game_speed = 14
x_pos_bg = 0
y_pos_bg = 380
points = 0
# 라이브러리에 있는 폰트를 가져온다
font = pygame.font.Font('freesansbold.ttf', 20)
# font = pygame.font.Font(None, 20)
obstacles = []
death_count = 0
'''
ㅡ
점수 기능 구현을 위해 함수 정의
'''
def score() :
'''
ㅡ
게임 최종 구현 모습 생각
시간이 지남에 따라서 난이도가 상승한다.
= 게임 속도가 상승한다
이러한 상황을 구현하기 위해 점수를 1씩 증가시킨다.
'''
global points, game_speed
points += 1
'''
ㅡ
포인트 100단위로 게임 난이도를 올리는 상황을 저러한 코드로 작성
ㅡ
text
점수 표시를 위한 글씨 설정 진행
마찬가지로
화면에 출력하기 위해선 범위를 지정해줘야 한다
따라서 get_rect() 함수 사용 후 위치 설정 진행
ㅡ
textRect.center
게임 실행이 되면서 화면에 점수가 출력된다
오른쪽 상단에
'''
if points % 100 == 0 :
game_speed += 1
text = font.render(f"points : {str(points)}", True, (0,0,0))
textRect = text.get_rect()
textRect.center = (1000,40)
SCREEN.blit(text, textRect)
# 백그라운-배경을 새성하기 위한 함수 정의
'''
ㅡ
게임 속 배경 구현을 위한 함수 정의
main() 내부에 배경에 사용할 변수를 정의했었음
그것을 참조해서 사용하기 위해 global 예약어 사용
'''
def background() :
global x_pos_bg, y_pos_bg
'''
ㅡ
절대 상수에 존재하는 이미지의 폭값을 파악하기 위해 get_width() 사용 > image_width 변수에 할당
ㅡ
image_width는 배경 이미지 변수값임
주의
ㅡ
화면에 출력
blit() 이용.
'''
image_width = BG.get_width() # 대략 2404
'''
ㅡ
현재 배경은 한번에 2장?을 출력함
1번
처음 초기값_좌표값 0, 380에 배경 이미지 출력
2번
0 + 2404 , 380 좌표값에
배경이미지 한번 더 출력.
왜?
배경이미지는 1번이후 바로 뒤이어 출력이 되야 하기 때문.
'''
SCREEN.blit(BG, (x_pos_bg, y_pos_bg))
SCREEN.blit(BG, (image_width + x_pos_bg, y_pos_bg))
# 위 상황을 조건문으로 흐름 제어 / 뒷 배경이 스크린을 다 지나가면 다시 처음 처음 위치값으로 초기화
'''
ㅡ
위 상황을 조건문으로 흐름 제어 진행
뒷 배경이 스크린 영역을 다 지나가면 다시 처음 위치값으로 초기화 진행
계속 생성되도록
ㅡ
위에서 정의한 game_speed 변수 값으로 화면이 이동하는 듯한 효과를 설정
'''
if x_pos_bg <= - image_width :
SCREEN.blit(BG, (image_width + x_pos_bg, y_pos_bg))
x_pos_bg = 0
x_pos_bg -= game_speed
'''
ㅡ
게임 루프문 작성
게임 실행이 반복적으로 이루어짐
ㅡ
get()함수를 이용 사용자 이벤트를 감지함.
함수 설명 확인. : List[Event]
리스트로 반환출력됨
리스트 안에 들어있는 요소는 Event객체
내부 프로세스는 잘 모르겠으나 tpye속성에 특정한 값이 들어있음 숫자 타입으로
그 값이 QUIT값과 같다면 게임 동작을 멈춤
사용자가 종료버튼을 누르면....
Q. 뭐가 종료버튼?? 어떤 키를 눌러야??
'''
while run :
for event in pygame.event.get() :
if event.type == pygame.QUIT :
run = False
'''
ㅡ
화면 스크린 색 설정
(method) def fill(
color: ColorValue,
rect: RectValue | None = None,
special_flags: int = 0
) -> Rect
R,G,B 값으로
ㅡ
get_pressed()
사용자가 이벤트-특정한 키를 누르는것을 감지하는 함수 사용
userInput에 값 할당
ㅡ
`get_pressed()` 함수는 현재의 키보드 입력 상태를 가져오는 것
즉, 단지 현재 상태값을 가져옴 = 입력 상태를 확인하는 함수
따라서 이벤트 핸들러 코드라고는 부를 수 없음
이벤트 핸들러보다는 게임 루프 내에서 주기적으로 호출되는 입력 처리 함수가 더 적절.
'''
SCREEN.fill((255, 255, 255))
userInput = pygame.key.get_pressed()
'''
ㅡ
위에서 공룡 클래스 정의 > 인스턴스 생성함
공룡 = 플레이어를 화면에 그린다.
ㅡ
위에서 userInput변수 정의함.
이 변수 안에는 사용자가 어떤 키를 눌렀는지에 대한 값이 들어가 있음
따라서 사용자가 누른 값을 업데이트 진행
update()
'''
player.draw(SCREEN)
player.update(userInput)
'''
# 장애물 요소_객체 관련 부분
# 장애물 요소_객체 3개
# 0번 작은 선인장, 1번 큰 선인장, 2번 새
# 작은 선인장, 큰 선인장은 이미지 3개
# obstacles 리스트에 아무것도 없을 때
'''
if len(obstacles) == 0 :
if random.randint(0, 2) == 0 :
'''
# 작은 선인장 클래스 인스턴스를 obstacles리스트 요소값들로 넣어줌
# 작은 선인장 클래스 호출하기 위해선 image 인수 필요
'''
'''
# 작은 선인장 클래스가 호출되서 해당 클래스 로직이 돌아가고 결국은 최종적으로 랜덤한 작은 선인장 이미지 1개가 obstacles 리스트에 들어간다
# 큰 선인장 클래스 인스턴스를 obstacles리스트 요소값들로 넣어줌
# 큰 선인장 클래스 호출하기 위해선 image 인수 필요
'''
obstacles.append(SmallCactus(SMALL_CACTUS))
elif random.randint(0, 2) == 1 :
obstacles.append(LargeCactus(LARGE_CACTUS))
elif random.randint(0, 2) == 2 :
obstacles.append(Bird(BIRD))
'''
# 어떤 장애물 요소가 들어갔는지는 몰라도 부모 클래스에 있는 draw 메서드를 이용해서 화면에 출력
'''
for obstacle in obstacles : # 리스트 요소 반복
obstacle.draw(SCREEN) # 화면에 장애물 출력
obstacle.update() # 부모 클래스 업데이트 메서드 호출 - 객체 이동하는 모습을 위해
'''
# 플레이어-공룡-사각형과 장애물 사각형 충돌이 감지가 된다면
2초간 딜레이 발생
ㅡ
충돌이 발생하면 카운트가 되도록 변수 정의
ㅡ
위에서
death_count = 0 정의함
이 카운트를 메뉴에 표시하기 위해 menu함수 호출
'''
if player.dino_rect.colliderect(obstacle.rect) :
pygame.time.delay(2000)
death_count += 1
menu(death_count)
'''
# 위에서 배경을 위한 함수를 정의함 - 호출
# 위에서 구름 요소를 위한 클래스 정의함 - 구름 화면에 출력 - 업데이트
ㅡ
주의
객체를 그려준다음에 이동이 되야한다
따라서 draw > update 함수 호출
'''
background()
cloud.draw(SCREEN)
cloud.update()
'''
ㅡ
위에서 함수 정의했으니 호출한다
main 함수 내부에 score함수 정의함
지금은 while run 내부에서 호출.
게임 루프가 돌면서 호출이 되야하니까
'''
score()
'''
ㅡ
화면 주사율을 설정하는 코드 작성
위에서 Clock클래스 인스턴스 생성함
tick() 화면 주사율값 설정
초당 30초 업데이트 설정
update()
'''
clock.tick(30)
pygame.display.update()
'''
ㅡ
게임에서 점수_메뉴 기능을 정의
main함수 내부에 정의한 points변수를 사용하기 위해 global 예약어 사용
'''
def menu(death_count) :
global points
'''
# 초기 설정값 정의
# 게임 실행을 위한 반복문 작성
ㅡ
주의
현재 추가적인 화면을 정의하고 있따.
즉 게임이 실행되는? 화면과
게임 점수 메뉴가 출력되는 화면을 각각 정의함
출력함
'''
run = True
while run :
SCREEN.fill((255,255,255))
font = pygame.font.Font('freesansbold.ttf', 30)
'''
# 한번도 죽지 않았다면
# 화면에 적절한 내용 출력할 값을 변수에 할당
'''
if death_count == 0 :
text = font.render("Press any Key to Start!", True, (0,0,0))
SCREEN.blit(START, (SCREEN_WIDTH // 2 - 20, SCREEN_HEIGHT // 2 - 140)) # 코드 추가
elif death_count > 0 :
text = font.render("Press any Key to Start!", True, (0,0,0))
score = font.render("Your Score : " + str(points), True, (0,0,0)) # 죽었을 때 점수를 출력
scoreRect = score.get_rect() # 위치 파악을 위한 사각형 값 할당
scoreRect.center = (SCREEN_WIDTH//2, SCREEN_HEIGHT//2 + 50) # 점수판 위치 설정
SCREEN.blit(score, scoreRect)
SCREEN.blit(DEAD, (SCREEN_WIDTH // 2 - 20, SCREEN_HEIGHT // 2 - 140)) # 코드추가
SCREEN.blit(GAMEOVER, (SCREEN_WIDTH // 2 , SCREEN_HEIGHT // 2 - 100)) # 코드추가
textRect = text.get_rect() # 메뉴에 출력될 텍스트 위치 설정을 위함
textRect.center = (SCREEN_WIDTH//2, SCREEN_HEIGHT//2)# get_rect() 메서드 호출 후 반환값이 클래스 인스턴스고 그 안에 center 변수가 정의되어 있음
SCREEN.blit(text, textRect)
SCREEN.blit(RUNNING[0], (SCREEN_WIDTH//2 - 20 , SCREEN_HEIGHT//2 - 140)) # 화면에 위치 지정 후 객체_요소 출력
pygame.display.update()
'''
# 사용자의 event 감지를 통한 게임 종료 진행 흐름 제어
# pygame에 QUIT 변수에 들어있는 값과 사용자 동작 감지를 통한 값과 같다면
'''
for event in pygame.event.get() :
if event.type == pygame.QUIT : # 의미 : 사용자가 게임 스크린 x 버튼을 누른다면....
run = False
if event.type == pygame.KEYDOWN : # 사용자가 아무런 키를 누르면 main 함수가 다시 실행되도록 작성
main() # main 함수가 호출되면 > 게임 실행
'''
ㅡ
위에서 main함수 정의함
메인함수 호출 구문 작성
이렇게 작서하는 이유?
ㅡ
파이썬 코드 파일 호출 실수를 막기 위함
해당 코드파일을 직접 실행했을 때만 작동
'''
if __name__ == "__main__":
menu(death_count=0)
ㅡ 코드/오류 수정
- 오류 발생 이유
스크립트 파일에서 이미지 파일 경로를 제대로 찾지 못해 발생한 오류임
FileNotFoundError오류
ㅡ
이 오류를 해결하기 위해서는 이미지 파일 경로를 수정.
현재 코드에서 이미지 파일 경로는 슬래시 (/)로 구분되어 있으나, 윈도우즈 환경에서는 백슬래시 (\) 를 사용
+
백슬래시 (\)를 사용하는 것이 문제가 아니라 백슬래시가 이스케이프 문자로 해석되어 제대로 처리되지 않았기 때문입니다.
Note
=>
즉 백슬래시가 파일 경로를 위한 의미가 아니라 이스케이프 코드로 해석이 되어 발생한 오류
따라서 \\와 같이 백슬래시를 두 번 사용하면 하나의 백슬래시로 해석되어 파일 경로로 사용됩니다. 즉, 다음과 같이 코드를 수정해야 합니다:
코드
RUNNING = [pygame.image.load(os.path.join("Assets\\Dino", "DinoRun1.png")), pygame.image.load(os.path.join("Assets\\Dino", "DinoRun2.png"))]
- 오류 발생 이유/코드
ㅡ 처음 코드
RUNNING = [pygame.image.load(os.path.join("Assets\\Dino","DinoRun1.png")), pygame.image.load(os.path.join("Assets\\Dino","DinoRun2.png")) ] # 동작에 대한 이미지가 2개임. 따라서 리스트로 묶어서 할당.
하지만 현재 실제 경로는
C:\Users\GUN\OneDrive - 한국산업기술대학교\바탕 화면\웹-개발-취업캠프\파이썬-크롬공룡게임\self_ChromeDinoGame\Assets\Dino\DinoRun1.png
따라서
RUNNING = [pygame.image.load(os.path.join("self_ChromeDinoGame\\Assets\\Dino","DinoRun1.png")), pygame.image.load(os.path.join("self_ChromeDinoGame\\Assets\\Dino","DinoRun2.png")) ] # 동작에 대한 이미지가 2개임. 따라서 리스트로 묶어서 할당.
이렇게 작성했어야함