偷學Python之最後的項目二:飛機大戰小遊戲(純代碼)

人生苦短我用Python

偷學Python之最後的項目二:飛機大戰小遊戲(純代碼)

古之立大事者,不惟有超世之才,亦必有堅忍不拔之志。——蘇軾

甜甜先說

這次用Python中的pygame模塊來完成一個飛機大戰的小遊戲;基本思路是通過方向鍵來控制飛機的左右移動射擊飛船。

成品效果

此貼爲這個小遊戲的完整代碼,帶有完整的註釋,另外一個貼中有完整的流程。傳送門

安裝pygame

要完成這個項目肯定要安裝pygame第三方庫,安裝流程如下

  1. 首先通過命令行工具檢測系統是否安裝的pip工具

    python -m pip --version
    

    image-20200603085158803

    小甜是Windows系統,這裏只提供Windows系統的檢測方法

  2. 如果未安裝則安裝pip工具,安裝則請跳過這一步

    python get-pip.py
    

    安裝完畢以後退回第一步重新檢測

  3. 安裝pygame

    python -m pip install pygame --user
    

    或者通過pycharm安裝第三個庫,流程如下

    image-20200603090411763

    image-20200603090341028

  4. 通過import關鍵字導入第三方庫

    import pygame
    

小飛機類

文件名爲plane.py

"""
-*- coding:uft-8 -*-
author: 小甜
date:2020/6/4
"""


class GameStats:
    """跟蹤遊戲統計信息"""

    def __init__(self, setting):

        self.setting = setting
        self.reset_stats()
        # 遊戲默認爲不活動狀態
        self.game_active = False

        # 最高得分
        self.high_score = 0

    def reset_stats(self):
        # 初始化在遊戲運行期間可能變化的統計信息
        self.planes_left = self.setting.plane_limit
        # 統計得分
        self.score = 0

子彈類

文件名爲bullet.py

"""
-*- coding:uft-8 -*-
author: 小甜
date:2020/6/3
"""
import pygame
from pygame.sprite import Sprite


class Bullet(Sprite):  # 繼承pygame.sprite中的Sprite類
    """子彈的管理"""

    def __init__(self, setting, screen, plane):
        super().__init__()
        self.screen = screen
        # 在(0,0)處創建一個表示子彈的矩形
        # pygame.Rect
        # 用於存儲直角座標的pygame對象
        self.rect = pygame.Rect(0,0, setting.bullet_width, setting.bullet_height)
        # 設置顯示的位置
        self.rect.centerx = plane.rect.centerx
        self.rect.top = plane.rect.top
        # 讓子彈的位置跟小飛機重疊,當子彈飛出了以後,就顯得跟從小飛機裏面射出來一樣

        # 將子彈的座標轉換爲浮點數
        self.y = float(self.rect.y)

        # 子彈的顏色
        self.color = setting.bullet_color
        # 子彈的速度
        self.speed = setting.bullet_speed

    def update(self):
        # 向上移動子彈
        self.y -= self.speed
        # 根據self.y的值更新self.rect.y
        self.rect.y = self.y

    def draw_bullet(self):
        """繪製子彈"""
        # pygame.draw.rect()畫一個矩形的形狀
        pygame.draw.rect(self.screen, self.color, self.rect)

敵人類(飛船)

文件名爲spaceship.py

"""
-*- coding:uft-8 -*-
author: 小甜
date:2020/6/4
"""
import pygame
from pygame.sprite import Sprite


class Spaceship(Sprite):
    '''表示飛船的類'''

    def __init__(self, setting, screen):
        super().__init__()
        self.screen = screen
        self.setting = setting

        # 添加飛船圖像
        self.img = pygame.image.load("./imgs/enemy.png")
        # 獲取rect屬性
        self.rect = self.img.get_rect()

        # 每個飛船最初都在屏幕左上角附近
        self.rect.x = self.rect.width  # 飛船圖像的左邊距等於圖像的寬度

        self.rect.y = self.rect.height  # 飛船圖書的上邊距等於圖像的高度


        self.rect.w = self.rect.width
        self.rect.h = int(self.rect.height / 2) # 將高度設置爲一半

        self.x = float(self.rect.x)

    def blitme(self):
        # 繪製飛船圖像
        self.screen.blit(self.img, self.rect)

    def check_edges(self):
        '''如果有飛船位於屏幕邊緣,就返回true'''
        screen_rect = self.screen.get_rect()
        if self.rect.right >= screen_rect.right:
            return True
        elif self.rect.left <= 0:
            return True

    def update(self):
        """移動飛船"""
        self.x += (self.setting.spaceship_speed * self.setting.fleet_direction)
        self.rect.x = self.x

按鈕類

文件名爲button.py

"""
-*- coding:uft-8 -*-
author: 小甜
date:2020/6/5
"""
import pygame.font


class Button:
    def __init__(self, setting, screen, msg):
        """初始化按鈕的屬性"""
        self.screen = screen
        self.screen_rect = screen.get_rect()

        # 設置按鈕的大小
        self.width = 180
        self.height = 50
        # 設置按鈕的顏色
        self.button_color = (0, 255, 0)
        # 設置文本的顏色
        self.text_color = (200, 200, 200)
        # 設置字體的大小
        self.font = pygame.font.SysFont("SimHei", 50)  # 字體爲黑體大小爲48像素

        # 創建按鈕的rect對象
        self.rect = pygame.Rect(0, 0, self.width, self.height)
        # 使按鈕居中
        self.rect.center = self.screen_rect.center

        # 按鈕的標籤只需要創建一次
        self.prep_msg(msg)

    def prep_msg(self, msg):
        # msg渲染爲圖像
        """
        font.render方法是將msg中的文本轉換爲圖像
        * 參數True是開啓抗鋸齒模式
        * self.text_color是文本的顏色
        * self.button_color是背景顏色
        """
        self.msg_image = self.font.render(msg, True, self.text_color, self.button_color)
        # 使其在按鈕上居中
        self.msg_image_rect = self.msg_image.get_rect()
        self.msg_image_rect.center = self.rect.center

    def draw_button(self):
        # 繪製按鈕
        self.screen.fill(self.button_color, self.rect)  # 用一個顏色填充文本
        self.screen.blit(self.msg_image, self.msg_image_rect)  # 繪製文本

記錄遊戲信息

文件名爲game_stats.py

"""
-*- coding:uft-8 -*-
author: 小甜
date:2020/6/4
"""


class GameStats:
    """跟蹤遊戲統計信息"""

    def __init__(self, setting):

        self.setting = setting
        self.reset_stats()
        # 遊戲默認爲不活動狀態
        self.game_active = False

        # 最高得分
        self.high_score = 0

    def reset_stats(self):
        # 初始化在遊戲運行期間可能變化的統計信息
        self.planes_left = self.setting.plane_limit
        # 統計得分
        self.score = 0

記分類

文件名爲scoreboard.py

"""
-*- coding:uft-8 -*-
author: 小甜
date:2020/6/5
"""
import pygame.font
from pygame.sprite import Group
from plane import Plane


class Scoreboard:
    """顯示得分信息的類"""

    def __init__(self, setting, screen, stats):
        """初始化得分涉及到的屬性"""
        self.screen = screen
        self.screen_rect = screen.get_rect()
        self.stats = stats
        self.setting = setting

        # 顯示得分的字體設置
        # 設置文本的顏色
        self.text_color = (250, 250, 250)
        # 設置字體的大小
        self.font = pygame.font.SysFont("SimHei", 40)  # 字體爲黑體大小爲40像素

        # 初始化得分圖像
        self.prep_score()
        # 初始化最高分圖像
        self.prep_high_score()

    def prep_score(self):
        """將得分轉換爲圖像"""
        score_str = str(self.stats.score)
        self.score_image = self.font.render(score_str, True, self.text_color)

        # 將得分放到屏幕的右上角
        self.score_rect = self.score_image.get_rect()
        self.score_rect.right = self.screen_rect.right - 20  # 與右邊差20 像素
        self.score_rect.top = 20  # 與頂部差20像素

    def prep_high_score(self):
        """將得分轉換爲圖像"""
        high_score_str = str(self.stats.high_score)
        self.high_score_image = self.font.render(high_score_str, True, self.text_color)

        # 將得分放到屏幕的頂部中間
        self.high_score_rect = self.high_score_image.get_rect()
        self.high_score_rect.centerx = self.screen_rect.centerx
        self.high_score_rect.top = 10

    def show_score(self):
        """在屏幕上顯示得分"""
        self.screen.blit(self.score_image, self.score_rect)
        # 顯示最高分
        self.screen.blit(self.high_score_image, self.high_score_rect)

遊戲設置類

文件名爲settings.py

"""
-*- coding:uft-8 -*-
author: 小甜
date:2020/6/3
"""
import pygame
import random


class Settings:
    """存儲飛機大戰的所有設置"""

    def __init__(self):
        # 屏幕設置
        self.screen_width = 1000
        self.screen_height = 600
        self.bg_img = pygame.image.load("./imgs/bg_img.png")

        #小飛機設置
        # 小飛機的生命限制
        self.plane_limit = 3

        # 子彈的設置
        self.bullet_width = 3  # 子彈的寬
        self.bullet_height = 15  # 子彈的高
        self.bullet_color = 190, 190, 190  # 子彈的顏色
        # 限制子彈的數量
        self.bullet_allowed = 5

        # 飛船下降的速度
        self.fleet_drop_speed = 10

        # 以什麼樣的速度加快遊戲節奏
        self.speedup_scale = 1.2

        # 提高分數的速度
        self.score_scale = 1.5

        self.initialize_dynamic_settings()

    def initialize_dynamic_settings(self):
        #小飛機設置
        # 小飛機的移動速度
        self.plane_speed = 2.5

        # 子彈的設置
        self.bullet_speed = 3  # 速度

        # 飛船移動的速度
        self.spaceship_speed = 2

        # 標誌位,1表示右移 -1表示左移
        self.fleet_direction = 1  # 默認右移動


        self.spaceship_points = 15

    def increase_speed(self):
        """提高遊戲節奏"""
        self.plane_speed *= self.speedup_scale
        self.bullet_speed *= self.speedup_scale
        self.spaceship_speed *= self.speedup_scale

        # 提高飛船的分數
        self.spaceship_points = int(self.spaceship_points * self.score_scale)

遊戲的所有函數

文件名爲game_func.py

"""
-*- coding:uft-8 -*-
author: 小甜
date:2020/6/3
"""
import sys
from bullet import Bullet
from spaceship import Spaceship
import pygame
from time import sleep


def check_keydown_events(event, plane, setting, screen, bullets):
    # 捕捉用戶按下
    if event.key == pygame.K_RIGHT:
        # 當用戶按下鍵位時標誌位爲True
        plane.mv_right = True
    elif event.key == pygame.K_LEFT:
        plane.mv_left = True
    elif event.key == pygame.K_SPACE:
        if len(bullets) <= setting.bullet_allowed:
            # 創建一個子彈,並將其加入到編組bullets中
            new_bullet = Bullet(setting, screen, plane)
            bullets.add(new_bullet)


def check_keyup_events(event, plane):
    # 捕捉用戶鬆開
    if event.key == pygame.K_RIGHT:
        # 當用戶鬆開鍵位爲false
        plane.mv_right = False
    elif event.key == pygame.K_LEFT:
        plane.mv_left = False


def check_events(plane, setting, screen, bullets, stats, play_button, spaceships, score_board):
    # 爲了防止遊戲窗口啓動會立馬關閉,在其中增加一個遊戲循環(無限循環),
    for event in pygame.event.get():
        if event.type == pygame.QUIT:  # QUIT用戶請求程序關閉
            sys.exit()

        elif event.type == pygame.KEYDOWN:
            check_keydown_events(event, plane, setting, screen, bullets)

        elif event.type == pygame.KEYUP:
            check_keyup_events(event, plane)

        elif event.type == pygame.MOUSEBUTTONDOWN:  # 檢測MOUSEBUTTONDOWN事件
            mouse_x, mouse_y = pygame.mouse.get_pos()  # 返回一個元組,包含鼠標單擊時的座標
            check_play_button(plane, setting, screen, bullets, stats, play_button, mouse_x, mouse_y, spaceships, score_board)


def check_play_button(plane, setting, screen, bullets, stats, play_button, mouse_x, mouse_y, spaceships, score_board):
    # 玩家單機play按鈕時開始遊戲
    # collidepoint檢測單擊的位置是否在按鈕的rect內
    button_clicked = play_button.rect.collidepoint(mouse_x, mouse_y)
    if button_clicked and not stats.game_active:  # 當stats.game_active的值爲False時,取反纔會執行
        # 重置記分的所有圖像
        score_board.prep_score()
        score_board.prep_high_score()
        # 重置遊戲設置
        setting.initialize_dynamic_settings()
        # 隱藏光標
        pygame.mouse.set_visible(False)
        # 遊戲狀態
        stats.game_active = True
        # 重置遊戲統計信息
        stats.reset_stats()

        # 清空飛船列表和子彈列表
        spaceships.empty()
        bullets.empty()

        # 讓飛機居中
        plane.center_plane()


def update_screen(screen, bg_img, plane, bullets, spaceships, stats, play_button, score_board):
    # 更新屏幕的圖像
    # 每次循環都會重新繪製屏幕
    screen.blit(bg_img, [0, 0])  # 繪製圖像
    # 繪製子彈
    for bullet in bullets.sprites():
        bullet.draw_bullet()  # 繪製子彈
    # 繪製飛機
    plane.blitme()
    for spaceship in spaceships.sprites():
        spaceship.blitme()

    # 顯示得分
    score_board.show_score()

    # 如果遊戲處於非活動狀態,繪製Play按鈕
    if not stats.game_active:
        play_button.draw_button()


    # 將完整顯示Surface更新到屏幕
    pygame.display.flip()


def update_bullets(bullets, spaceships, setting, screen, plane, stats, score_board):
    # 將編組中的每個子彈調用bullet.update()
    bullets.update()
    # 刪除已經消失的子彈
    for bullet in bullets.copy():
        if bullet.rect.bottom <= 0:
            bullets.remove(bullet)


    collisions = pygame.sprite.groupcollide(bullets, spaceships, True, True)  # 兩個實參true則是爲了將其刪除
    """這個字典中的鍵是打中小飛船的子彈,值則爲一個列表,其中包含了所有被打中的值"""

    if collisions:  # 當發生碰撞,會有有返回值,纔會爲True
        for spaceship in collisions.values():
            """遍歷這個字典,確保每個外星人的點數都計入得分"""
            stats.score += setting.spaceship_points * len(spaceship) # 發生碰撞加分
            """因爲子彈的值是一個列表,所有這裏每次計算一下列表的長度來進行加分"""
            score_board.prep_score()  # 並繪製在屏幕上
        check_high_score(stats, score_board)

    if len(spaceships) == 0:
        bullets.empty()  # 刪除編組中的所有精靈(子彈)
        # 加快遊戲節奏
        setting.increase_speed()
        create_fleet(setting, screen, spaceships, plane)  # 重新調用生成飛船


def get_number_spaceship_x(setting, spaceship_width):
    # 計算每行可以容納多少個外星人的函數
    # 計算可以容納多個飛船的寬度
    available_space_x = setting.screen_width - (2 * spaceship_width)
    number_spaceship_x = int(available_space_x / (2 * spaceship_width))  # 將其轉換爲整數
    return number_spaceship_x


def get_number_rows(setting, plane_height, spaceship_height):
    # 計算可以容納多少行
    available_space_y = setting.screen_height - 7 * spaceship_height - plane_height
    number_rows = int(available_space_y / spaceship_height)
    return number_rows


def create_spaceship(setting, screen, spaceships, spaceship_number, number_rows):
    # 創建一個飛船並加入當前行
    spaceship = Spaceship(setting, screen)
    spaceship_width = spaceship.rect.width  # 飛船的寬度
    spaceship.x = spaceship_width + 2 * spaceship_width * spaceship_number
    spaceship.rect.x = spaceship.x
    spaceship.rect.y = spaceship.rect.height + 2 * spaceship.rect.height * number_rows
    spaceships.add(spaceship)


def create_fleet(setting, screen, spaceships, plane):
    # 創建一個飛船
    spaceship = Spaceship(setting, screen)
    number_spaceship_x = get_number_spaceship_x(setting, spaceship.rect.width)
    number_rows = get_number_rows(setting, plane.rect.height, spaceship.rect.height)

    for row_number in range(number_rows):
        for spaceship_number in range(number_spaceship_x):
            # if stats.game_active:  # 爲真纔會創建飛船
            create_spaceship(setting, screen, spaceships, spaceship_number, row_number)


def change_fleet_direction(setting, spaceships):
    """將所有飛船下移,並改變方向"""
    for spaceship in spaceships.sprites():
        spaceship.rect.y += setting.fleet_drop_speed
    setting.fleet_direction *= -1  # 如果爲1則相乘爲-1,如果爲-1相乘爲1


def check_fleet_edges(setting, spaceships):
    """有飛船到達了邊緣應採取的措施"""
    for spaceship in spaceships.sprites():
        if spaceship.check_edges():  # 如果爲true,已經到了邊緣,就執行change_fleet_direction
            change_fleet_direction(setting, spaceships)
            break


def plane_hit(setting, spaceships, plane, stats, screen, bullets):
    """
    有飛船撞擊到飛機以後已經數量減1,創建一批新的飛創,並將飛機重新反之到屏幕的原始位置
    還將引入time模塊的sleep函數實現暫停的效果
    """
    if stats.planes_left > 0:
        stats.planes_left -= 1  # 將planes_left減1
        # print(stats.planes_left)

        # 清空飛船和子彈的編組
        spaceships.empty()
        bullets.empty()

        # 創建新的飛機和飛船
        create_fleet(setting, screen, spaceships, plane)
        plane.center_plane()

        sleep(1)  # 暫停1秒
    else:
        stats.game_active = False
        # 將光標設置爲顯示
        pygame.mouse.set_visible(True)

def check_spaceship_bottom(setting, spaceships, plane, stats, screen, bullets):
    # 檢測是否有飛船觸碰到底部
    screen_rect = screen.get_rect()
    for spaceship in spaceships.sprites():
        if spaceship.rect.bottom >= screen_rect.bottom:
            # 跟飛船碰撞一樣處理
            plane_hit(setting, spaceships, plane, stats, screen, bullets)


def update_spaceships(setting, spaceships, plane, stats, screen, bullets):
    # 更新飛船的位置
    spaceships.update()
    # 檢測時候又飛船處於邊緣,並及時更新
    check_fleet_edges(setting, spaceships)

    # 檢測飛船與飛機直接的碰撞
    '''
    pygame.sprite.spritecollideany方法
    * 接受兩個參數,一個精靈和一個編組,
    * 檢測編組中的成員是否與碰撞,如果檢測到碰撞則停止遍歷編組
    * 如果沒有發生碰撞則返回None
    '''
    game_over = pygame.sprite.spritecollideany(plane, spaceships)
    if game_over:
        plane_hit(setting, spaceships, plane, stats, screen, bullets)

    # 檢測是否有飛船落地
    check_spaceship_bottom(setting, spaceships, plane, stats, screen, bullets)


def check_high_score(stats, score_board):
    """用於檢測是否產生最高分"""
    if stats.score > stats.high_score:
        stats.high_score = stats.score  # 如果的得分大於最高分,則值賦給最高分
        score_board.prep_high_score()

遊戲的開始

文件名爲plane_war.py

"""
-*- coding:uft-8 -*-
author: 小甜
date:2020/6/3
"""
import pygame
from settings import Settings  # 引入settings.py
from plane import Plane
import game_func as fg
from pygame.sprite import Group
from game_stats import GameStats
from button import Button
from scoreboard import Scoreboard



def run_game():
    # 初始化遊戲
    pygame.init()
    # 設置屏幕的分辨率
    setting = Settings()
    screen = pygame.display.set_mode((setting.screen_width, setting.screen_height))  # 大小爲1000px乘以600px
    pygame.display.set_caption("飛機大戰")  # 標題
    # 創建play按鈕
    play_button = Button(setting, screen, "Play")
    # 創建一個用於存儲遊戲統計信息的實例
    stats = GameStats(setting)
    # 創建記分的實例
    score_board = Scoreboard(setting, screen, stats)
    # 創建小飛機
    plane = Plane(screen, setting)
    # 創建一個存儲子彈的編組
    bullets = Group()
    # 創建飛船編組
    spaceships = Group()

    # 開始遊戲的主循環
    while True:
        # 不關閉窗口
        fg.check_events(plane, setting, screen, bullets, stats, play_button, spaceships, score_board)
        if stats.game_active:  # 根據遊戲狀態來判斷是否需要創建其圖像
            # 調用小飛機移動的方法
            plane.update()
            # 更新子彈位置
            fg.update_bullets(bullets, spaceships, setting, screen, plane, stats, score_board)
            # 更新飛船位置
            fg.update_spaceships(setting, spaceships, plane, stats, screen, bullets)
        # 繪製圖像
        fg.update_screen(screen, setting.bg_img, plane, bullets, spaceships, stats, play_button, score_board)


run_game()

甜甜有話說

終於把這個小項目給完成了,這裏用的pygame模塊一邊學一邊這就很難哎,反正最後也弄出來

現在好好休息幾天,開始學習爬蟲

座右銘:不要在該奮鬥的年紀選擇安逸!!

加油

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