通過python基礎教程(版本三)學習得到了提升
鼠標控制,全屏遊戲,關卡遊戲,不同的關卡會有不同的小黃人出現,難度也會隨之不同
有背景音樂,有不同的小黃人圖片
首先,先把需要的變量定義在config.py中
# 遊戲squish的配置文件
# -------------------------------------
# 可根據偏好隨意修改配置變量
# 如果遊戲的節奏太快或太慢, 可嘗試修改與速度相關的變量
# 要在這個遊戲中使用其他圖像, 可修改這些變量:
banana_image = 'banana.png'
splash_image = 'timg.jpg'
wav = 'SOY.mp3'
# 這些配置決定了遊戲的總體外觀:
screen_size = 1920, 1080
background_color = 255, 255, 255
margin = 30
full_screen = 1
font_size = 48
# 這些設置決定了遊戲的行爲:
drop_speed = 1
banana_speed = 10
speed_increase = 1
weights_per_level = 10
banana_pad_top = 40
banana_pad_side = 20
定義一個基本Sprite類,把小黃人和香蕉定義爲它的子類,放在objects.py中
import pygame, config, os, random
from random import randrange
'這個模塊包含遊戲Squish使用的遊戲對象'
class SquishSprite(pygame.sprite.Sprite):
'''
遊戲Squish中所有精靈(sprite)的超類. 構造函數
加載一幅圖像, 設置精靈的外接矩形和移動範圍. 移
動範圍取決於屏幕尺寸和邊距
'''
def __init__(self, image):
super().__init__()
self.image = pygame.image.load(image).convert()
self.rect = self.image.get_rect()
screen = pygame.display.get_surface()
shrink = -config.margin * 2
self.area = screen.get_rect().inflate(shrink, shrink)
class Weight(SquishSprite):
'''
從天而降的鉛錘. 它使用SquishSprite的構造函數來設置表
示鉛錘的圖像, 並以其構造函數的一個參數指定的速度下降
'''
def __init__(self, speed):
super().__init__('yellow ({}).png'.format(random.randrange(1, 8)))
self.speed = speed
self.reset()
def reset(self):
'''
將鉛錘移到屏幕頂端(剛好看不到)的一個隨機位置
'''
x = randrange(self.area.left, self.area.right)
self.rect.midbottom = x, 0
def update(self):
'''
根據鉛錘的速度垂直向下移動相應的距離. 同時, 根據
鉛錘是否已達到屏幕底部相應地設置屬性landed
'''
self.rect.top += self.speed
self.landed = self.rect.top >= self.area.bottom
class Banana(SquishSprite):
'''
絕望的香蕉. 它使用SquishSprite的構造函數來設置香蕉圖像, 並停留
在屏幕底部附近, 且水平位置由鼠標的當前位置決定(有一定的限制)
'''
def __init__(self):
super().__init__(config.banana_image)
self.rect.bottom = self.area.bottom
# 這些內邊表示圖像中不屬於香蕉的部分
# 如果鉛錘進入這些區域, 並不認爲它砸到了香蕉:
self.pad_top = config.banana_pad_top
self.pad_side = config.banana_pad_side
def update(self):
'''
將香蕉中心的x座標設置爲鼠標當前x座標, 再使用
矩形的方法clamp確保香蕉位於允許的移動範圍內
'''
self.rect.centerx = pygame.mouse.get_pos()[0]
self.rect =self.rect.clamp(self.area)
def touches(self, other):
'''
判斷香蕉是否與另一個精靈(如鉛錘)發生了碰撞. 這裏沒有直接
使用矩形的方法colliderect, 而是先使用矩形的方法inflat
以及pad_side和pad_top計算出一個新的矩形, 這個矩形不包含
香蕉圖像頂部和兩邊的'空白'區域
'''
# 通過剔除內邊距來計算bounds:
bounds = self.rect.inflate(-self.pad_side, -self.pad_top)
# 將bounds移動到與香蕉底部對齊:
bounds.bottom = self.rect.bottom
# 檢查bounds是否與另一個對象的rect重疊
return bounds.colliderect(other.rect)
把主程序放在Squish.py中
import os, sys, pygame
from pygame.locals import *
import objects, config
'這個模塊包含遊戲Squish的主遊戲邏輯'
class State:
'''
遊戲狀態超類, 能夠處理事件以及在指定表面上顯示自己
'''
def handle(self, event):
'''
只處理退出事件的默認事件處理
'''
if event.type == QUIT:
sys.exit()
if event.type == KEYDOWN and event.key == K_ESCAPE:
sys.exit()
def first_display(self, screen):
'''
在首次顯示狀態時使用, 它使用背景色填充屏幕
'''
screen.fill(config.background_color)
# 別忘了調用filp, 把修改反映出來
pygame.display.flip()
def display(self, screen):
'''
在後續顯示狀態時使用, 其默認行爲時什麼都不做
'''
pass
class Level(State):
'''
遊戲關卡. 他計算落下了多少個鉛錘, 移動精靈並執行其他與遊戲邏輯相關的任務
'''
def __init__(self, number = 1):
self.number = number
# 還需躲開多少個鉛錘才能通過當前關卡?
self.remaining = config.weights_per_level
speed = config.drop_speed
# 每過一關都將速度提高speed_increase:
speed += (self.number - 1) * config.speed_increase
# 創建鉛錘和香蕉:
self.weight = objects.Weight(speed)
self.banana = objects.Banana()
both = self.weight, self.banana # 可包含更多精靈
self.sprites = pygame.sprite.RenderUpdates(both)
def update(self, game):
'更新遊戲狀態'
# 更新所有的精靈
self.sprites.update()
# 如果香蕉和鉛錘發生了碰撞, 就讓遊戲切換到GameOver狀態:
if self.banana.touches(self.weight):
game.next_state = GameOver()
# 否則, 如果鉛錘已落到地上, 就將其復位
if self.weight.landed:
self.weight.reset()
self.remaining -= 1
if self.remaining == 0:
game.next_state = LevelCleared(self.number)
def display(self, screen):
'''
在第一次顯示(清屏)後顯示狀態. 不同於firstDisplay,
這個方法調用pygame.display.update並向他傳遞一個需要
更新的矩形列表, 這個列表是由self.sprites.draw提供的
'''
screen.fill(config.background_color)
updates = self.sprites.draw(screen)
pygame.display.update(updates)
class Paused(State):
'''
簡單的遊戲暫停狀態, 用戶可通過按任意鍵盤鍵或單擊鼠標來結束這種狀態
'''
finished = 0 # 用戶結束暫停了嗎?
image = None # 如果需要顯示圖像, 將這個屬性設置爲一個文件名
text = '' # 將這個屬性設置爲一些說明性文本
def handle(self, event):
'''
這樣來處理事件:將這項任務委託給State(它只處理退出事件),
並對按鍵和鼠標單擊做出相應, 如果用戶按下了鍵盤鍵或單擊了鼠標,
就將self.finish設置爲True
'''
State.handle(self, event)
if event.type in [MOUSEBUTTONDOWN, KEYDOWN]:
self.finished = 1
def update(self, game):
'''
更新關卡. 如果用戶按下了鍵盤鍵或單擊了鼠標(即self.finished爲True),
就讓遊戲切換到(由子類實現的方法)self.next_state()返回的狀態
'''
if self.finished:
game.next_state = self.next_state()
def first_display(self, screen):
'''
在首次顯示暫停狀態時調用, 它繪製圖像(如果指定了)並渲染文本
'''
# 首先, 通過使用背景色填充屏幕來清屏:
screen.fill(config.background_color)
# 創建一個使用默認外觀和指定字號的Font對象:
font = pygame.font.Font(None, config.font_size)
# 獲取self.text中的文本行, 但忽略開頭和末尾的空行:
lines = self.text.strip().splitlines()
# 使用font.get_linesize()獲取每行文本的高度, 並計算文本的總高度:
height = len(lines) * font.get_linesize()
# 計算文本的位置(在屏幕上居中):
center, top = screen.get_rect().center
top -= height // 2
# 如果有圖像要顯示:
if self.image:
# 加載該圖像
image = pygame.image.load(self.image).convert()
# 獲取其rect:
r = image.get_rect()
# 將文本下移圖像一半的距離
top += r.height // 2
# 將圖像放在文本上方20像素處:
r.midbottom = center, top - 20
# 將圖像傳輸到屏幕上
screen.blit(image, r)
antialias = 1 # 消除文本的鋸齒
black = 0, 0, 0 # 使用黑色渲染文本
# 從計算得到的top處開始渲染所有的文本行,
# 每渲染一行都向下移動font.get_linesize()像素:
for line in lines:
text = font.render(line.strip(), antialias, black)
r = text.get_rect()
r.midtop = center, top
screen.blit(text, r)
top += font.get_linesize()
# 顯示所做的所有修改:
pygame.display.flip()
class Info(Paused):
'''
顯示一些遊戲信息的簡單暫停狀態, 緊跟在這個狀態後面的是Level狀態(第一關)
'''
next_state = Level
text = '''In this game you are a Banana'''
class StartUp(Paused):
'''
顯示啓動圖像和歡迎信息的暫停狀態, 緊跟在它後面的是Info狀態
'''
next_state = Info
image = config.splash_image
class LevelCleared(Paused):
'''
指出用戶已過關的暫停狀態, 緊跟在它後面的是表示下一關的Level狀態
'''
def __init__(self, number):
self.number = number
self.text = '''CLEARED
Click to start Level {}'''.format(self.number + 1)
def next_state(self):
return Level(self.number + 1)
class GameOver(Paused):
'''
指出遊戲已結束的狀態, 緊跟在它後面的是表示第一類的Level狀態
'''
next_state = Level
text = '''
Game Over
Click to Restart, Esc to Quit'''
class Game:
'''
負責主事件循環(包括在不同遊戲狀態之間切換)的遊戲對象
'''
def __init__(self, *args):
# 獲取遊戲和圖像所在的目錄:
path = os.path.abspath(args[0])
dir = os.path.split(path)[0]
# 切換到這個目錄, 以便以後能夠打開圖像文件
os.chdir(dir)
# 最初不處於任何狀態:
self.state = None
# 在一次事件循環中迭代中切換到StartUp狀態:
self.next_state = StartUp()
def run(self):
'''
這個方法設置一些變量. 它執行一些重要的初始化任務, 並進入主事件循環
'''
pygame.init() # 初始化所有的Pygame模塊
# 決定在窗口還是整個屏幕中顯示遊戲:
flag = 0 # 默認在窗口中顯示遊戲
if config.full_screen:
flag = FULLSCREEN # 全屏模式
screen_size = config.screen_size
screen = pygame.display.set_mode(screen_size, flag)
# 背景音樂播放
pygame.mixer.init()
pygame.mixer.music.load(config.wav)
pygame.mixer.music.play()
pygame.display.set_caption('Fruit Self Defense')
pygame.mouse.set_visible(False)
# 主事件循環:
while True:
# (1)如果nextState被修改, 就切換到修改後的狀態並顯示它(首次):
if self.state != self.next_state:
self.state = self.next_state
self.state.first_display(screen)
# (2)將事件處理工作委託給當前狀態:
for event in pygame.event.get():
self.state.handle(event)
# (3)更新當前狀態:
self.state.update(self)
# (4)顯示當前狀態:
self.state.display(screen)
if __name__ == '__main__':
game = Game(*sys.argv)
game.run()
運行測試
開始界面,BGM出現
遊戲提示語
關卡進入
結束,完美