終極項目-遊戲-小黃人大作戰

通過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出現

遊戲提示語

關卡進入

 

結束,完美

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章