자 이제 6번째 게임을 만들어보자 !
다람쥐 먹기 게임이다!
처음들어볼것인데 그 이유는 그냥 게임내용 그 대로 제목을 땄기 때문이다.
게임은 간단하다 !
처음으로 여러명의 적이 나오고 자신보다 작은 다람쥐를 먹고 큰 다람쥐는 피하면서 덩치를 늘려가는 게임이다.
이 게임을 통해서 우리는
적을 여러 개 만드는 방법과 각각의 다람쥐에 동일한 값을 적용해 게임 루프에서 여러 다람쥐들을 한 번에 다룰 수 있고 카메라와 수학 함수인 사인에 대한 개념을 배워 다람쥐가 점프할 때 자연스럽게 보이도록 하는 방법을 배울것이다!
자 !
우선
이제 게임을 만들어보자 !
이 게임에서는 세 개의 데이터 구조가 있고 모두 딕셔너리 값이다.
데이터 타입은 모두 플레이어 다람쥐, 적 다람쥐, 잔디 객체이다.(단지 딕셔너리 값이지만 이 게임에서 "객체"의 의미를 "게임 세계 안에 존재하는 것"으로 사용하자)
모든 객체는 딕셔너리 값으로 'x','y','rect'를 갖는다.
x,y 는 게임 세계 좌표계에서 객체의 왼쪽 상단의 좌표를 말한다.
이것은 픽셀 좌표계와는 다르다!(픽셀 좌표계는 rect에 기록된다)
자 ! 이제 코드를 짜보자!!
# Squirrel Eat Squirrel (a 2D Katamari Damacy clone)
# By Al Sweigart al@inventwithpython.com
# http://inventwithpython.com/pygame
# Released under a "Simplified BSD" license
import random, sys, time, math, pygame
from pygame.locals import *
FPS = 30 # frames per second to update the screen
WINWIDTH = 640 # width of the program's window, in pixels
WINHEIGHT = 480 # height in pixels
HALF_WINWIDTH = int(WINWIDTH / 2)
HALF_WINHEIGHT = int(WINHEIGHT / 2)
GRASSCOLOR = (24, 255, 0)
WHITE = (255, 255, 255)
RED = (255, 0, 0)
이 게임에선 윈도우의 너비와 높이의 1/2길이를 이용하는 경우가 많기 떄문에 HALF 를 미리 정의해줬다.
MOVERATE = 9 # how fast the player moves
BOUNCERATE = 6 # how fast the player bounces (large is slower)
BOUNCEHEIGHT = 30 # how high the player bounces
STARTSIZE = 25 # how big the player starts off
WINSIZE = 300 # how big the player needs to be to win
INVULNTIME = 2 # how long the player is invulnerable after being hit in seconds
GAMEOVERTIME = 4 # how long the "game over" text stays on the screen in seconds
MAXHEALTH = 3 # how much health the player starts with
NUMGRASS = 80 # number of grass objects in the active area
NUMSQUIRRELS = 30 # number of squirrels in the active area
SQUIRRELMINSPEED = 3 # slowest squirrel speed
SQUIRRELMAXSPEED = 7 # fastest squirrel speed
DIRCHANGEFREQ = 2 # % chance of direction change per frame
LEFT = 'left'
RIGHT = 'right'
상수 설명은 주석을 참고 하자 !
"""
이 프로그램 에는 3개의 데이터 구조가 있다. player, enemy squirrel.grass 객체이다. 각 데이터 구조는 딕셔너러이며
해당 키를 가지고 있다.
3개의 데이터 구조에서 공통으로 사용하는 키 :
'x' - 게임 세계 왼쪽 좌표
'y' - 게임 세계 위쪽 좌표
'rect' - 스크린에 객체가 위치해야 하는 영역이며 pygame.Rect 객체로 표시
Player 데이터 구조에서 사용하는 키 :
'Surface' - 화면에 그릴 다람쥐 이미지를 저장하는 pygame.Surface object
'facing' - setting LEFT or RIGHT 플레이어가 어느 방향으로 보고 있을지 결정
'size' - 플레이어의 너비와 높이. 픽셀단위
'bounce' - 현재 플레이어가 점프 동작 중 어떤 위치에 있는지 나타낸다. 0은 그냥 서잇고 BOUNCERATE 까지 점프
'health'- 라이프가 얼마나 남아 있느지 보여주는 정수 값.
Enemy Squirrel 데이터 구조에서 사용하는 키 :
'surface'
'movex' 프레임당 다람쥐가 몇 픽셀을 수평으로 움직였는지 저장. 음수는 왼쪽 양수 오른쪽
'movey' - 프레임당 다람쥐가 몇 픽셀을 수직으로 움직였는지 저장. 음수는 위쪽 양수 아래쪽
'width' - 다람쥐 이미지의 너비
'height' - 다람쥐 이미지의 높이
'bounce' - 현재 플레이어가 점프 동작 중 어떤 위치에 있는지 나타낸다.
'bouncerate' - 다람쥐가 얼마나 빨리 점프
'bounceheight' - 다람쥐가 얼마나 높이 점프
Grass 데이터 구조에서 사용하는 키 :
'grassImage' - GRASSIMAGES의 pygame.Surface 객체를 가리키는 인덱스 숫자 해당 grass객체를 위해서 사용한다.
"""
객체의 키에 대해 미리 설명한드아!
def main():
global FPSCLOCK, DISPLAYSURF, BASICFONT, L_SQUIR_IMG, R_SQUIR_IMG, GRASSIMAGES
pygame.init()
FPSCLOCK = pygame.time.Clock()
pygame.display.set_icon(pygame.image.load('gameicon.png'))
DISPLAYSURF = pygame.display.set_mode((WINWIDTH,WINHEIGHT))
pygame.display.set_caption("Chans Squirrel Eat Squirrel")
BASICFONT = pygame.font.Font("freesansbold.ttf",32)
한 줄 말고는 이때까지 메인함수들의 시작과 동일하다.
pygame.display.set_icon("") 함수는 윈도우 타이틀 바에 아이콘을 그린다 !
def main():
global FPSCLOCK, DISPLAYSURF, BASICFONT, L_SQUIR_IMG, R_SQUIR_IMG, GRASSIMAGES
pygame.init()
FPSCLOCK = pygame.time.Clock()
pygame.display.set_icon(pygame.image.load('gameicon.png'))
DISPLAYSURF = pygame.display.set_mode((WINWIDTH,WINHEIGHT))
pygame.display.set_caption("Chans Squirrel Eat Squirrel")
BASICFONT = pygame.font.Font("freesansbold.ttf",32)
기존 다람쥐의 사진은 왼쪽을 햐하고 있지만!
우리는 오른쪽으로 향하고 있는 image의 Surface object 도 필요하다!
이 것을 pygame.transform.flip()으로 만들 수 있는데 파라미터로 뒤집을값,좌우반전,상하반전을 불리언 값으로 받는다.
while True:
runGame()
이제 기본 설정이 끝났으니 runGame() 을 호출해서 게임을 시작하자.
def runGame():
# set up variables for the start of a new game
invulnerableMode = False # if the player is invulnerable
invulnerableStartTime = 0 # time the player became invulnerable
gameOverMode = False # if the player has lost
gameOverStartTime = 0 # time the player lost
winMode = False # if ther player has won
게임 상태를 기록하는 변수들이다.
# create the surfaces to hold game text
gameOverSurf = BASICFONT.render("Game Over",True,WHITE)
gameOverRect = gameOverSurf.get_rect()
gameOverRect.center = (HALF_WINWIDTH,HALF_WINHEIGHT)
winSurf = BASICFONT.render("You have achieved OMEGA SQUIRREL! ",True,WHITE)
winRect = winSurf.get_rect()
winRect.center = (HALF_WINWIDTH,HALF_WINHEIGHT)
winSurf2 = BASICFONT.render('(press "R" to restart.)',True,WHITE)
winRect2 = winSurf2.get_rect()
winRect2.center = (HALF_WINWIDTH,HALF_WINHEIGHT+30)
쉬운거니 설명 생략!
# camerx and camery are the top left of where tge camera view is
camerax = 0
cameray = 0
게임 세계가 무한한 2차원 공간 이라고 생각하면 어떤 화면도 이 게임 세계를 전부 보여줄 수 없다.
무한한 공간에서 일부만 화면에 보여줘야 하기 때문에 이 일부의 세계를 카메라 라고한다!
이 세계를 카메라로 볼때 일부만 렌즈에 담을 수 있는 것과 비슷하기 때문이다!
카메라가 볼 수 있는 공간이 바로 플레이어의 화면이 되므로 "카메라"좌표계는 "픽셀"좌표계와 동일하다.
다람쥐의 픽셀 좌표계를 알아내려면, 다람쥐의 게임 좌표계에서 카메라 원점의 게임 좌표계를 뼤면 된다!
활성영역은 게임 세계에서 카메라 뷰와 그 둘레에 카메라 뷰만큼의 영역을 합친 부분을 부르기 위해 만들어낸 용어이다.
적 다람쥐나 잔디 객체를 새로 만들때는 카메라 뷰 영역안에 만들면 갑자기 튀어나온것처럼 보이기 때문에 밖에서 생성 한다. 그렇지만 너무 멀리 떨어진곳에 생성하면 안되기에 어느 정도 가까운 영역인 활성영역에 적 다람쥐와 잔디를 만들어야한다. 따라서 활성영역 밖의 다람쥐와 잔디는 삭제하여 메모리를 아낀다.
grassObjs = [] #stores all the grass objects in the game
squirrelObjs = [] #stores all the non-plater squirrel objects
#store the player object
playerObj = {'surface': pygame.transform.scale(L_SQUIR_IMG, (STARTSIZE, STARTSIZE)),
'facing': LEFT,
'size': STARTSIZE,
'x': HALF_WINWIDTH,
'y': HALF_WINHEIGHT,
'bounce':0,
'health': MAXHEALTH}
moveLeft = False
moveRight = False
moveUp = False
moveDown = False
grass객체를 생성하면 리스트에 추가되고 제거하면 삭제된다. 적다람쥐도 마찬가지!
playerObj 는 딕셔너리 값 하나이다.
그리고 플레이어가 현재 어떤 화살표 키를 눌렀는지 기록한다.
#start off with some random grass images on the screen
for i in range(10):
grassObjs.append(makeNewGrass(camerax, cameray))
grassObjs[i]['x'] = random.randint(0,WINWIDTH)
grassObjs[i]['y'] = random.randint(0,WINHEIGHT)
게임을 시작하면 활성영역에 잔디 몇개를 만든다!
while True : # main game loop
#check if we should turn off invulnerability
if invulnerableMode and time.time() - invulnerableStartTime> INVULNTIME:
invulnerableMode = False
무적 시간 상태는 2초이다. 무적 상태가 끝나면 Mode =False 로 설정한다.
#move all squirrels
for sObj in squirrelObjs:
#move ther sq, and adjust for their bounce
sObj['x'] += sObj['movex']
sObj['y'] += sObj['movey']
적 다람쥐는 movex,movey 키의 값에 따라 움직인다.
이 값이 양수이면 오른쪽/아래로! 음수이면 반대!
값이 크면 클수록 게임 루프 반복마다 더 멀리 움직여야한다
모든 다람쥐 객체에 for문을 줬다 !
sObj['bounce'] += 1
if sObj['bounce']>sObj["bouncerate"]:
sObj['bounce'] = 0 # reset bounce amount
sObj['bounce'] 값은 게임 루프를 돌 떄마다 모든 다람쥐에 대해 증가한다.
이 값이 0 이면 다람쥐는 점프 하기 전에 바로 시작 위치에 있고 이 값이 rate와 동일하면 점프가 끝난 것으로 한다.
bouncerate 값이 적으면 더 빨리 점프하는 이유다 !!
#random chance they change direction
if random.randint(0,99) < DIRCHANGEFREQ:
sObj['movex'] = getRandomVelocity()
sObj['movey'] = getRandomVelocity()
if sObj['movex'] > 0: # faces right
sObj['suface'] = pygame.transform/scale(R_SQUIR_IMG,(sObj['width'],sObj['height']))
else : faces left
sObj['surface'] = pygame.transform.scale(L_SQUIR_IMG,(sObj['width'],sObj['height']))
무작위로 0-99보다 작은 숫자가 선택되면 방향을 바꾼다!
그에 맞게 이미지의 크기를 조정해주었다.
#go through all the objects and see if any need to be deleted.
for i in range(len(grassObjs)-1,-1,-1):
if isOutsideActiveArea(camerax,cameray,grassObjs[i]):
del grassObjs[i]
for i in range(len(squirrelObjs)-1,-1,-1):
if isOutsideActiveArea(camerax,cameray,squirrelObjs[i]):
del squirrelObjs[i]
모든 잔디와 적 다람쥐를 검사해서 "활성 영역" 바깥에 있는지 확인 후 벗어 났다면 True를 반환하고 그 객체를 삭제한다 !
위의 코드를 보면
for i in range(len(grassObjs)-1,-1,-1)
del grassoBjs[i] 이런식으로 리스트의 뒷 부분부터 삭제를 진행하는데 왜 이렇게 하는것일까?
간단한 예시를 보자
animals = ['cat','mouse','dog','horse']
에서
for i in range(len(animals)):
if animals[i] == 'dog':
del animals[i]
라고하고 삭제를 진행하면 에러가 발생한다.
그 이유는 for의 i 값은 0,1,2,3으로 진행되는데 i 가 2 가되고 난 후 dog를 삭제하고 나면 horse가 앞땅겨져 다음 i값인 3이animals 의 인덱스값에 없는 값이 된다.
그러므로 오류가 발생하지만
뒤에서 한칸씩 내려오는 과정을 거치면 에러가 발생하지 않는다!
#add more grass & sq if we dont have enough
while len(grassObjs)<NUMGRASS:
grassObjs.append(makeNewGrass(camerax, cameray))
while len(squirrelObjs)<NUMSQUIRRELS:
squirrelObjs.append(makeNewSquirrel(camerax,cameray))
초기 설정 상수보다 작으면 잔디와 다람쥐를 객체를 추가 생성해준다.
playerCenterx = playerObj['x'] + int(playerObj['size'] / 2)
playerCentery = playerObj['y'] + int(playerObj['size'] / 2)
if (camerax + HALF_WINWIDTH) - playerCenterx > CAMERASLACK:
camerax = playerCenterx + CAMERASLACK - HALF_WINWIDTH
elif playerCenterx - (camerax + HALF_WINWIDTH) > CAMERASLACK:
camerax = playerCenterx - CAMERASLACK - HALF_WINWIDTH
if (cameray + HALF_WINHEIGHT) - playerCentery > CAMERASLACK:
cameray = playerCentery + CAMERASLACK - HALF_WINHEIGHT
elif playerCentery - (cameray + HALF_WINHEIGHT) > CAMERASLACK:
cameray = playerCentery - CAMERASLACK - HALF_WINHEIGHT
플레이어가 움직이면 카메라의 위치도 바꿔야한다.
카메라의 위치를 바꾸지 않고 플레이어가 몇 픽셀이나 움직일 수 있는지를 카메라 슬랙 이라고 한다.
즉, 중앙에서 플레이어가 90픽셀 이상 멀어지면 카메라는 플레이어를 따라가기 위해 카메라의 위치가 변한다.
camerax+HALF_WINWIDTH,HEIGHT 는 스크린의 중앙에 해당되는 위치의 XY 게임 세계 좌표계이다.
CAMERASLACK 값보다 커지면 플레이억가 카메라의 중심보다 슬랙보다 더 오른쪽으로 이동한 것이다.
플레이어 다람쥐가 카메라 슬랙 가장자리에 왔기에 camerax 값을 바꿔야한다.
우리는 플레이어가 아닌 카메라를 옮긴다!! !
#draw the green background
DISPLAYSURF.fill(GRASSCOLOR)
#draw all the grass objects on the screen
for gObj in grassObjs:
gRect = pygame.Rect((gObj['x']-camerax,gObj['y']-cameray,gObj['width'],gObj['height']))
DISPLAYSURF.blit(GRASSIMAGES[gObj['grassImage']],gRect)
배경을 그리고 grassObjs 리스트의 모든 grass 객체에 대해 Rect 객체를 만든다.
이것은 gRect 변수에 저장한다.
모든 잔디 객체가 직접 이미지를 가지고 있을 필요는 없다 모든 grass 객체가 가지고 있는 gObj['grassImage']에 어떤 이미지를 쓸지 이미지 번호만 저장하고, 잔디 이미지는 GRASSIMAGES에 가지고 있는 편이 메모리를 아낄 수 있다.
#draw the other sq
for sObj in squirrelObjs:
sObj['rect'] = pygame.Rect( (sObj['x'] - camerax,
sObj['y'] - cameray - getBounceAmount(sObj['bounce'], sObj['bouncerate'], sObj['bounceheight']),
sObj['width'],
sObj['height']) )
DISPLAYSURF.blit(sObj['surface'], sObj['rect'])
잔디를 그리는 법과 동일하지만 Rect 객체를 'rect'키 값으로 저장하는 차이가 있다.
이렇게 Rect 객체를 저장해서 나중에 플레이어 다람쥐와 충돌했는지 검사할때 쓸 수 있다.
그리고 크기가 재각각이므로 공통 Surface를 사용하지 않는다.
#draw the player squirrel
flashIsOn = round(time.time(),1)*10%2 == 1
플레이어가 무적 상태일때 깜빡이는 효과를 주기위해 0.1초 그리고 안그리고를 반복함으로써 깜빡이는 것처럼 보이게 한다.
time.time(),1 round() 로 넘겨서 반올림하는데 두 번째 파라미터로 1을 줘서 소수점 다음 한 자리로 맞춰지고 여기에 10 을 곱해서 2로 모듈러 연산을 진행하는 트릭을 할 수 있다 !
이렇게 트루/폴스를 시간이 지남에 따라 반복한다.
if not gameOverMode and not (invulnerableMode and flashIsOn):
playerObj['rect'] = pygame.Rect( (playerObj['x'] - camerax,
playerObj['y'] - cameray - getBounceAmount(playerObj['bounce'], BOUNCERATE, BOUNCEHEIGHT),
playerObj['size'],
playerObj['size']) )
DISPLAYSURF.blit(playerObj['surface'], playerObj['rect'])
모드 확인후 다람쥐를 그려준다.
#draw the health meter
drawHealthMeter(playerObj['health'])
체력을 그린당
for event in pygame.event.get(): # event handling loop
if event.type == QUIT:
terminate()
elif event.type == KEYDOWN:
if event.key in (K_UP, K_w):
moveDown = False
moveUp = True
elif event.key in (K_DOWN, K_s):
moveUp = False
moveDown = True
elif event.key in (K_LEFT, K_a):
moveRight = False
moveLeft = True
if playerObj['facing'] != LEFT: # change player image
playerObj['surface'] = pygame.transform.scale(L_SQUIR_IMG, (playerObj['size'], playerObj['size']))
playerObj['facing'] = LEFT
elif event.key in (K_RIGHT, K_d):
moveLeft = False
moveRight = True
if playerObj['facing'] != RIGHT: # change player image
playerObj['surface'] = pygame.transform.scale(R_SQUIR_IMG, (playerObj['size'], playerObj['size']))
playerObj['facing'] = RIGHT
elif winMode and event.key == K_r:
return
elif event.type == KEYUP:
# stop moving the player's squirrel
if event.key in (K_LEFT, K_a):
moveLeft = False
elif event.key in (K_RIGHT, K_d):
moveRight = False
elif event.key in (K_UP, K_w):
moveUp = False
elif event.key in (K_DOWN, K_s):
moveDown = False
elif event.key == K_ESCAPE:
terminate()
이제는 많이~ 익숙한 이벤트 처리이다.
그런데!
left/right 를 눌렀을때 playerObj['surface']값은 새로운 방향에 맞는 이미지 크기를 조절해서 설정해줘야한다!
r는 승리했을때 재시작이다!
if not gameOverMode:
# actually move the player
if moveLeft:
playerObj['x'] -= MOVERATE
if moveRight:
playerObj['x'] += MOVERATE
if moveUp:
playerObj['y'] -= MOVERATE
if moveDown:
playerObj['y'] += MOVERATE
게임이 진행되는동안 방향키에 따라 움직인다 ㅇㅇ moverate 만큼 움직인다.
if (moveLeft or moveRight or moveUp or moveDown) or playerObj['bounce'] != 0:
playerObj['bounce'] += 1
if playerObj['bounce'] > BOUNCERATE:
playerObj['bounce'] = 0 # reset bounce amount
점프~ 한다~ 다차면 리셋한당~
#check if the player has collided with any squirrels
for i in range(len(squirrelObjs)-1, -1, -1):
sqObj = squirrelObjs[i]
플레이어가 포문 안에서 다람쥐 냠! 해서 삭제할수 있으므로 뒤에서 부터 검사한다!
if 'rect' in sqObj and playerObj['rect'].colliderect(sqObj['rect']):
#player and sq collision has occured
if sqObj['width'] * sqObj['height']<= playerObj['size']**2: # player is larger than enemy
playerObj['size']+= int((sqObj['width']*sqObj['height'])**0.2)+1
del squir
플레이어보다 작은 적이라면 먹고 그 크기에 비례해서 커진다
if playerObj['facing'] == LEFT:
playerObj['surface'] = pygame.transform.scale(L_SQUIR_IMG, (playerObj['size'], playerObj['size']))
if playerObj['facing'] == RIGHT:
playerObj['surface'] = pygame.transform.scale(R_SQUIR_IMG, (playerObj['size'], playerObj['size']))
더 커진 이미지를 transform.scale()이 반환해준다.
왼/오에 따라 이미지 다른걸 넘겨준당 !
if playerObj['size'] > WINSIZE:
winMode = True # turn on "win mode"
충분히 커지면 ~ 승리 ~ !
elif not invulnerableMode:
# player is smaller and takes damage
invulnerableMode = True
invulnerableStartTime = time.time()
playerObj['health'] -= 1
if playerObj['health'] == 0:
gameOverMode = True # turn on "game over mode"
gameOverStartTime = time.time()
만약 무적모드가 아닌 상태고 적이 더 크면 플레이어는 무적모드가 되고 체력이 깎인다.
그리고 체력이 0 이되면 게임오버 !
else:
# game is over, show "game over" text
DISPLAYSURF.blit(gameOverSurf, gameOverRect)
if time.time() - gameOverStartTime > GAMEOVERTIME:
return # end the current game
게임오버를 하고 하고 게임오버화면 보여주는 시간이 지나면 사라진다링~
if winMode:
DISPLAYSURF.blit(winSurf, winRect)
DISPLAYSURF.blit(winSurf2, winRect2)
pygame.display.update()
FPSCLOCK.tick(FPS)
승리시 이러고 ! 게임을 끝내준다 !
(winmode == True 가 되니 조건문 실행)
캬...
핵심 함수는 끝이났고
이제 세부적인 기능들을 위한 함수들만 남아있다.
지금까지 짠 코드를 보여주고
나머지는 다음 포스트에서 짜보자 !
# Squirrel Eat Squirrel (a 2D Katamari Damacy clone)
# By Al Sweigart al@inventwithpython.com
# http://inventwithpython.com/pygame
# Released under a "Simplified BSD" license
import random, sys, time, math, pygame
from pygame.locals import *
FPS = 30 # frames per second to update the screen
WINWIDTH = 640 # width of the program's window, in pixels
WINHEIGHT = 480 # height in pixels
HALF_WINWIDTH = int(WINWIDTH / 2)
HALF_WINHEIGHT = int(WINHEIGHT / 2)
GRASSCOLOR = (24, 255, 0)
WHITE = (255, 255, 255)
RED = (255, 0, 0)
CAMERASLACK = 90 # 중앙에서 다람쥐가 얼마나 멀어지면 카메라를 움직일지 결정
MOVERATE = 9 # how fast the player moves
BOUNCERATE = 6 # how fast the player bounces (large is slower)
BOUNCEHEIGHT = 30 # how high the player bounces
STARTSIZE = 25 # how big the player starts off
WINSIZE = 300 # how big the player needs to be to win
INVULNTIME = 2 # how long the player is invulnerable after being hit in seconds
GAMEOVERTIME = 4 # how long the "game over" text stays on the screen in seconds
MAXHEALTH = 3 # how much health the player starts with
NUMGRASS = 80 # number of grass objects in the active area
NUMSQUIRRELS = 30 # number of squirrels in the active area
SQUIRRELMINSPEED = 3 # slowest squirrel speed
SQUIRRELMAXSPEED = 7 # fastest squirrel speed
DIRCHANGEFREQ = 2 # % chance of direction change per frame
LEFT = 'left'
RIGHT = 'right'
"""
이 프로그램 에는 3개의 데이터 구조가 있다. player, enemy squirrel.grass 객체이다. 각 데이터 구조는 딕셔너러이며
해당 키를 가지고 있다.
3개의 데이터 구조에서 공통으로 사용하는 키 :
'x' - 게임 세계 왼쪽 좌표
'y' - 게임 세계 위쪽 좌표
'rect' - 스크린에 객체가 위치해야 하는 영역이며 pygame.Rect 객체로 표시
Player 데이터 구조에서 사용하는 키 :
'Surface' - 화면에 그릴 다람쥐 이미지를 저장하는 pygame.Surface object
'facing' - setting LEFT or RIGHT 플레이어가 어느 방향으로 보고 있을지 결정
'size' - 플레이어의 너비와 높이. 픽셀단위
'bounce' - 현재 플레이어가 점프 동작 중 어떤 위치에 있는지 나타낸다. 0은 그냥 서잇고 BOUNCERATE 까지 점프
'health'- 라이프가 얼마나 남아 있느지 보여주는 정수 값.
Enemy Squirrel 데이터 구조에서 사용하는 키 :
'surface'
'movex' 프레임당 다람쥐가 몇 픽셀을 수평으로 움직였는지 저장. 음수는 왼쪽 양수 오른쪽
'movey' - 프레임당 다람쥐가 몇 픽셀을 수직으로 움직였는지 저장. 음수는 위쪽 양수 아래쪽
'width' - 다람쥐 이미지의 너비
'height' - 다람쥐 이미지의 높이
'bounce' - 현재 플레이어가 점프 동작 중 어떤 위치에 있는지 나타낸다.
'bouncerate' - 다람쥐가 얼마나 빨리 점프
'bounceheight' - 다람쥐가 얼마나 높이 점프
Grass 데이터 구조에서 사용하는 키 :
'grassImage' - GRASSIMAGES의 pygame.Surface 객체를 가리키는 인덱스 숫자 해당 grass객체를 위해서 사용한다.
"""
def main():
global FPSCLOCK, DISPLAYSURF, BASICFONT, L_SQUIR_IMG, R_SQUIR_IMG, GRASSIMAGES
pygame.init()
FPSCLOCK = pygame.time.Clock()
pygame.display.set_icon(pygame.image.load('gameicon.png'))
DISPLAYSURF = pygame.display.set_mode((WINWIDTH,WINHEIGHT))
pygame.display.set_caption("Chans Squirrel Eat Squirrel")
BASICFONT = pygame.font.Font("freesansbold.ttf",32)
# 이미지 파일을 로드 한다.
L_SQUIR_IMG = pygame.image.load('squirrel.png')
R_SQUIR_IMG = pygame.transform.flip(L_SQUIR_IMG,True,False)
GRASSIMAGES = []
for i in range(1,5):
GRASSIMAGES.append(pygame.image.load('grass%s.png' %i))
while True:
runGame()
def runGame():
# set up variables for the start of a new game
invulnerableMode = False # if the player is invulnerable
invulnerableStartTime = 0 # time the player became invulnerable
gameOverMode = False # if the player has lost
gameOverStartTime = 0 # time the player lost
winMode = False # if ther player has won
# create the surfaces to hold game text
gameOverSurf = BASICFONT.render("Game Over",True,WHITE)
gameOverRect = gameOverSurf.get_rect()
gameOverRect.center = (HALF_WINWIDTH,HALF_WINHEIGHT)
winSurf = BASICFONT.render("You have achieved OMEGA SQUIRREL! ",True,WHITE)
winRect = winSurf.get_rect()
winRect.center = (HALF_WINWIDTH,HALF_WINHEIGHT)
winSurf2 = BASICFONT.render('(press "R" to restart.)',True,WHITE)
winRect2 = winSurf2.get_rect()
winRect2.center = (HALF_WINWIDTH,HALF_WINHEIGHT+30)
# camerx and camery are the top left of where tge camera view is
camerax = 0
cameray = 0
grassObjs = [] #stores all the grass objects in the game
squirrelObjs = [] #stores all the non-plater squirrel objects
#store the player object
playerObj = {'surface': pygame.transform.scale(L_SQUIR_IMG, (STARTSIZE, STARTSIZE)),
'facing': LEFT,
'size': STARTSIZE,
'x': HALF_WINWIDTH,
'y': HALF_WINHEIGHT,
'bounce':0,
'health': MAXHEALTH}
moveLeft = False
moveRight = False
moveUp = False
moveDown = False
#start off with some random grass images on the screen
for i in range(10):
grassObjs.append(makeNewGrass(camerax, cameray))
grassObjs[i]['x'] = random.randint(0,WINWIDTH)
grassObjs[i]['y'] = random.randint(0,WINHEIGHT)
while True : # main game loop
#check if we should turn off invulnerability
if invulnerableMode and time.time() - invulnerableStartTime> INVULNTIME:
invulnerableMode = False
#move all squirrels
for sObj in squirrelObjs:
#move ther sq, and adjust for their bounce
sObj['x'] += sObj['movex']
sObj['y'] += sObj['movey']
sObj['bounce'] += 1
if sObj['bounce']>sObj["bouncerate"]:
sObj['bounce'] = 0 # reset bounce amount
#random chance they change direction
if random.randint(0,99) < DIRCHANGEFREQ:
sObj['movex'] = getRandomVelocity()
sObj['movey'] = getRandomVelocity()
if sObj['movex'] > 0: # faces right
sObj['suface'] = pygame.transform/scale(R_SQUIR_IMG,(sObj['width'],sObj['height']))
else : #faces left
sObj['surface'] = pygame.transform.scale(L_SQUIR_IMG,(sObj['width'],sObj['height']))
# go through all the objects and see if any need to be deleted.
for i in range(len(grassObjs) - 1, -1, -1):
if isOutsideActiveArea(camerax, cameray, grassObjs[i]):
del grassObjs[i]
for i in range(len(squirrelObjs) - 1, -1, -1):
if isOutsideActiveArea(camerax, cameray, squirrelObjs[i]):
del squirrelObjs[i]
#add more grass & sq if we dont have enough
while len(grassObjs)<NUMGRASS:
grassObjs.append(makeNewGrass(camerax, cameray))
while len(squirrelObjs)<NUMSQUIRRELS:
squirrelObjs.append(makeNewSquirrel(camerax,cameray)
#adjust camerax and cameray if beyond the "camera slack"
playerCenterx = playerObj['x'] + int(playerObj['size'] / 2)
playerCentery = playerObj['y'] + int(playerObj['size'] / 2)
if (camerax + HALF_WINWIDTH) - playerCenterx > CAMERASLACK:
camerax = playerCenterx + CAMERASLACK - HALF_WINWIDTH
elif playerCenterx - (camerax + HALF_WINWIDTH) > CAMERASLACK:
camerax = playerCenterx - CAMERASLACK - HALF_WINWIDTH
if (cameray + HALF_WINHEIGHT) - playerCentery > CAMERASLACK:
cameray = playerCentery + CAMERASLACK - HALF_WINHEIGHT
elif playerCentery - (cameray + HALF_WINHEIGHT) > CAMERASLACK:
cameray = playerCentery - CAMERASLACK - HALF_WINHEIGHT
#draw the green background
DISPLAYSURF.fill(GRASSCOLOR)
#draw all the grass objects on the screen
for gObj in grassObjs:
gRect = pygame.Rect((gObj['x']-camerax,gObj['y']-cameray,gObj['width'],gObj['height']))
DISPLAYSURF.blit(GRASSIMAGES[gObj['grassImage']],gRect)
#draw the other sq
for sObj in squirrelObjs:
sObj['rect'] = pygame.Rect( (sObj['x'] - camerax,
sObj['y'] - cameray - getBounceAmount(sObj['bounce'], sObj['bouncerate'], sObj['bounceheight']),
sObj['width'],
sObj['height']) )
DISPLAYSURF.blit(sObj['surface'], sObj['rect'])
#draw the player squirrel
flashIsOn = round(time.time(),1)*10%2 == 1
if not gameOverMode and not (invulnerableMode and flashIsOn):
playerObj['rect'] = pygame.Rect( (playerObj['x'] - camerax,
playerObj['y'] - cameray - getBounceAmount(playerObj['bounce'], BOUNCERATE, BOUNCEHEIGHT),
playerObj['size'],
playerObj['size']) )
DISPLAYSURF.blit(playerObj['surface'], playerObj['rect'])
#draw the health meter
drawHealthMeter(playerObj['health'])
for event in pygame.event.get(): # event handling loop
if event.type == QUIT:
terminate()
elif event.type == KEYDOWN:
if event.key in (K_UP, K_w):
moveDown = False
moveUp = True
elif event.key in (K_DOWN, K_s):
moveUp = False
moveDown = True
elif event.key in (K_LEFT, K_a):
moveRight = False
moveLeft = True
if playerObj['facing'] != LEFT: # change player image
playerObj['surface'] = pygame.transform.scale(L_SQUIR_IMG, (playerObj['size'], playerObj['size']))
playerObj['facing'] = LEFT
elif event.key in (K_RIGHT, K_d):
moveLeft = False
moveRight = True
if playerObj['facing'] != RIGHT: # change player image
playerObj['surface'] = pygame.transform.scale(R_SQUIR_IMG, (playerObj['size'], playerObj['size']))
playerObj['facing'] = RIGHT
elif winMode and event.key == K_r:
return
elif event.type == KEYUP:
# stop moving the player's squirrel
if event.key in (K_LEFT, K_a):
moveLeft = False
elif event.key in (K_RIGHT, K_d):
moveRight = False
elif event.key in (K_UP, K_w):
moveUp = False
elif event.key in (K_DOWN, K_s):
moveDown = False
elif event.key == K_ESCAPE:
terminate()
if not gameOverMode:
# actually move the player
if moveLeft:
playerObj['x'] -= MOVERATE
if moveRight:
playerObj['x'] += MOVERATE
if moveUp:
playerObj['y'] -= MOVERATE
if moveDown:
playerObj['y'] += MOVERATE
if (moveLeft or moveRight or moveUp or moveDown) or playerObj['bounce'] != 0:
playerObj['bounce'] += 1
if playerObj['bounce'] > BOUNCERATE:
playerObj['bounce'] = 0 # reset bounce amount
#check if the player has collided with any squirrels
for i in range(len(squirrelObjs)-1, -1, -1):
sqObj = squirrelObjs[i]
if 'rect' in sqObj and playerObj['rect'].colliderect(sqObj['rect']):
#player and sq collision has occured
if sqO bj['width'] * sqObj['height']<= playerObj['size']**2: # player is larger than enemy
playerObj['size']+= int((sqObj['width']*sqObj['height'])**0.2)+1
del squirrelObjs[i]
if playerObj['facing'] == LEFT:
playerObj['surface'] = pygame.transform.scale(L_SQUIR_IMG, (playerObj['size'], playerObj['size']))
if playerObj['facing'] == RIGHT:
playerObj['surface'] = pygame.transform.scale(R_SQUIR_IMG, (playerObj['size'], playerObj['size']))
if playerObj['size']> WINSIZE:
winMode = True
elif not invulnerableMode:
# player is smaller and takes damage
invulnerableMode = True
invulnerableStartTime = time.time()
playerObj['health'] -= 1
if playerObj['health'] == 0:
gameOverMode = True # turn on "game over mode"
gameOverStartTime = time.time()
else:
# game is over, show "game over" text
DISPLAYSURF.blit(gameOverSurf, gameOverRect)
if time.time() - gameOverStartTime > GAMEOVERTIME:
return # end the current game
if winMode:
DISPLAYSURF.blit(winSurf, winRect)
DISPLAYSURF.blit(winSurf2, winRect2)
pygame.display.update()
FPSCLOCK.tick(FPS)