Python專案1“外星人入侵”遊戲(完整)及pyinstaller打包過程(含幾種報錯解決辦法)

qq_43719048發表於2020-11-14

一、專案環境背景介紹

1.Pycharm版本:社群2017.2.7版本

在這裡插入圖片描述

2.對應python版本:3.6.4版本

在這裡插入圖片描述
環境搭建的帖子(這位哥們寫得很細的 點贊):https://blog.csdn.net/wardseptember/article/details/79965836

3.基於以上環境開發一個遊戲

在遊戲《外星人入侵》中,玩家控制著一艘最初出現在螢幕底部中央的飛船。玩家可以使用箭頭鍵左右移動飛船,還可使用空格鍵進行射擊。遊戲開始時,一群外星人出現在天空中,他們在螢幕中向下移動。玩家的任務是射殺這些外星人。玩家將所有外星人都消滅乾淨後,將出現一群新的外星人,他們移動的速度更快。只要有外星人撞到了玩家的飛船或到達了螢幕底部,玩家就損失一艘飛船。玩家損失三艘飛船後,遊戲結束。(本遊戲附加各種音效)

4.遊戲專案的目錄結構如下
>D\Python-Projects
	>a book of py
		>Projects
			>Project 1(alien_invasion) chapter 12-14
				>alien_invasion
					>images	
						>alien.bmp
						>ship.bmp
					>musics
						>Bullet_biu.wav
						>Explo_Large.wav
						>Explo_Small.wav
						>order_music.mp3
					>alien_invasion.py(主檔案)
					>game_functions.py(功能函式)
					>alien.py(外星人)
					>ship.py(飛船)
					>bullet.py(子彈)
					>button.py(play按鈕)
					>settings.py(遊戲屬性設定)
					>scoreboard.py(分數)
					>game_stats.py(遊戲狀態)
					>music.py(音效)
					>Max_score.json(歷史最高分記錄檔案)

二、專案過程中所有的.py檔案程式碼

1.alien_invasion.py(主檔案)
# coding: UTF-8
# 專案1:外星人入侵

# 系統自帶的類或函式
# import sys  # 退出遊戲----集中在game_functions.py中使用,故這注釋掉
import pygame   # 含有開發遊戲所需功能
from pygame.sprite import Group     # 儲存所有有效子彈的類

# 自定義的類或函式
from settings import Settings   # 對《外星人入侵》遊戲中所有設定的類,如螢幕寬高…
from ship import Ship   # 建立飛船的類
from button import Button   # 建立按鈕的類
from game_stats import Game_Stats   # 建立跟蹤遊戲統計資訊的類
from scoreboard import Scoreboard   # 建立得分類
from music import bg_music  # 背景音樂
import game_functions as gf     # 所有的功能函


def run_game():
    # 初始化遊戲並建立一個螢幕物件
    pygame.init()
    ai_settings = Settings()
    screen = pygame.display.set_mode(
        (ai_settings.screen_width, ai_settings.screen_height))
    pygame.display.set_caption("外星人入侵")

    # 初始化聲音播放模組
    pygame.mixer.init()

    # 建立Play按鈕
    play_button = Button(ai_settings, screen, "Play")

    # 建立一艘飛船
    ship = Ship(ai_settings, screen)

    # 建立一個用於儲存子彈的編組
    bullets = Group()

    # 建立一個用於儲存外星人的編組
    aliens = Group()

    # 建立儲存遊戲統計資訊的例項
    game_stats = Game_Stats(ai_settings)

    # 建立記分牌
    scoreb = Scoreboard(ai_settings, screen, game_stats)

    # 建立外星人群
    gf.create_fleet(ai_settings, screen, ship, aliens)

    # 載入背景音樂
    bg_music()

    # 開始遊戲的主迴圈
    while True:

        # 呼叫響應滑鼠和鍵盤事件的函式
        gf.check_events(ai_settings, screen, game_stats, scoreb, play_button, ship, aliens, bullets)

        if game_stats.game_active:

            # 呼叫背景音樂
            gf.play_bg_music()
            # 呼叫飛船水平持續移動的函式
            ship.update()

            # 呼叫關於子彈相關操作的函式
            gf.update_bullets(ai_settings, screen, game_stats, scoreb, ship, aliens, bullets)

            # 呼叫外星人向右移動的函式
            gf.update_aliens(ai_settings, game_stats, scoreb, screen, ship, aliens, bullets)

        # 呼叫更新螢幕上的影像並切換到新螢幕的函式
        gf.update_screen(ai_settings, screen, game_stats, scoreb, ship, aliens, bullets, play_button)


run_game()  # 此時執行的話 會彈出一個pygame視窗

2.game_functions.py(功能函式)
# coding: UTF-8
# 《外星人入侵》執行的所有函式

import sys
import pygame

from bullet import Bullet
from alien import Alien
from time import sleep
import json
import music


def play_bg_music():
    """背景音樂"""
    if not pygame.mixer.music.get_busy():
        pygame.mixer.music.play()


def check_keydown_events(event, ai_settings, screen, ship, bullets, game_stats, scoreb, aliens):
    """"
        響應滑鼠按下操作
    """
    # 按右箭頭飛船向右移動
    if event.key == pygame.K_RIGHT:
        ship.moving_right = True
    # 按左箭頭飛船向左移動
    elif event.key == pygame.K_LEFT:
        ship.moving_left = True

    # 按空格鍵建立一顆子彈並將其加入到編組bullets中
    elif event.key == pygame.K_SPACE:
        fire_bullet(ai_settings, screen, ship, bullets)

    # 按p開始遊戲
    elif event.key == pygame.K_p:
        start_game(ai_settings, screen, ship, bullets, game_stats, scoreb,
                   aliens)

    # 按z暫停遊戲3s
    elif event.key == pygame.K_z:
        sleep(2)

    # 按q退出遊戲並把最高分寫入檔案
    elif event.key == pygame.K_q:
        with open('Max_score.json', 'w', encoding='UTF-8') as file:
            json.dump(game_stats.high_score, file)
        sys.exit()


def fire_bullet(ai_settings, screen, ship, bullets):
    """
        按照要求發射子彈數量
    """
    if len(bullets) < ai_settings.bullet_allowed:
        music.bullet_biu()                              # 發射子彈的聲音
        new_bullet = Bullet(ai_settings, screen, ship)  # 如果還沒有到達限制,就發射一顆子彈
        bullets.add(new_bullet)                         # 建立一顆子彈,並將其加入到編組bullets中


def start_game(ai_settings, screen, ship, bullets, game_stats, scoreb,
               aliens):
    """
        P264中動手試一試14-1的練習:讓玩家按p開始遊戲
    """
    # 重置遊戲統計資訊
    game_stats.reset_stats()
    game_stats.game_active = True

    # 重置記分牌影像
    scoreb.prep_score()
    scoreb.prep_high_score()
    scoreb.prep_level()
    scoreb.prep_ships()

    # 清空外星人列表和子彈列表
    aliens.empty()
    bullets.empty()

    # 建立一群新的外星人,並讓飛船居中
    create_fleet(ai_settings, screen, ship, aliens)
    ship.center_ship()

    # 暫停讓使用者反應一會
    sleep(0.5)


def check_keyup_events(event, ship):
    """"
        響應滑鼠鬆開---飛船停下
    """
    if event.key == pygame.K_RIGHT:
        ship.moving_right = False

    elif event.key == pygame.K_LEFT:
        ship.moving_left = False


def check_play_button(ai_settings, screen, game_stats, scoreb, play_button, ship,
                      aliens, bullets, mouse_x, mouse_y):
    """
        在玩家單擊Play按鈕時開始遊戲
    """
    button_clicked = play_button.button_rect.collidepoint(mouse_x, mouse_y)
    # 單擊Play按鈕且遊戲處於非活躍狀態時----避免了遊戲處於活躍狀態下單擊到了Play按鈕區域重啟遊戲
    if button_clicked and not game_stats.game_active:
        """如果滑鼠單擊位置在msg_image_rect範圍內則將遊戲置於活躍狀態"""

        # # 重置遊戲設定
        ai_settings.initialize_dynamic_settings()

        # 遊戲開始後Play按鈕隱藏起來
        pygame.mouse.set_visible(False)

        start_game(ai_settings, screen, ship, bullets, game_stats, scoreb, aliens)


def check_events(ai_settings, screen, game_stats, scoreb, play_button, ship, aliens, bullets):
    """
        響應滑鼠和鍵盤事件
    """
    # 遊戲退出並把最高分寫入檔案
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            with open('Max_score.json', 'w', encoding='UTF-8') as file:
                json.dump(game_stats.high_score, file)
            sys.exit()

        # 響應Play按鈕操作
        elif event.type == pygame.MOUSEBUTTONDOWN:
            mouse_x, mouse_y = pygame.mouse.get_pos()   # 該函式返回一個元組--獲取滑鼠單擊Play按鈕時的位置
            check_play_button(ai_settings, screen, game_stats, scoreb, play_button, ship,
                              aliens, bullets, mouse_x, mouse_y)

        # 判讀飛船向左還是向右移動
        elif event.type == pygame.KEYDOWN:
            check_keydown_events(event, ai_settings, screen, ship, bullets, game_stats, scoreb, aliens)

        # 飛船停下
        elif event.type == pygame.KEYUP:
            check_keyup_events(event, ship)


def update_screen(ai_settings, screen, game_stats, scoreb, ship, aliens, bullets, play_button):
    """
        更新螢幕上的影像並切換到新螢幕
    """
    # 每次迴圈時都重繪螢幕
    screen.fill(ai_settings.bg_color)   # 先填充背景後繪製飛船
    ship.blitem()
    aliens.draw(screen)

    # 在飛船後重繪所有子彈
    for bullet in bullets.sprites():
        bullet.draw_bullet()
    # 顯示得分
    scoreb.show_score()

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

    # 讓最近繪製的螢幕可見
    pygame.display.flip()


def start_new_level(ai_settings, screen, game_stats, scoreb, ship, aliens, bullets):
    """
        當消滅乾淨外星人提高一個等級
    """
    if len(aliens) == 0:
        # 刪除現有子彈
        bullets.empty()

        # 逐漸提高速度加快遊戲節奏
        ai_settings.increase_speed()

        # 當前螢幕外星人全部被消滅就提高一個等級
        game_stats.level += 1
        scoreb.prep_level()

        # 建立一群新的外星人
        create_fleet(ai_settings, screen, ship, aliens)  # 呼叫建立外星人群的函式


def check_bullet_alien_collisions(ai_settings, screen, game_stats, scoreb, ship, aliens, bullets):
    """子彈與外星人相撞後的操作"""
    # 檢查是否有子彈擊中外星人
    # 如果擊中了就刪除相應的子彈與外星人
    collisions = pygame.sprite.groupcollide(bullets, aliens, True, True)
    """方法sprite.groupcollide()檢查兩個編組的成員之間的碰撞(碰撞是指遊戲元素重疊在一起):
    將每顆子彈同每個外星人的rect作比較 並返回一個字典 字典中的鍵為子彈  值為被擊中的外星人"""

    # 每消滅一個外星人都將加一個points且顯示最新得分的影像
    if collisions:
        music.voice_small()                 # 子彈與外星人相撞的聲音
        for aliens in collisions.values():  # aliens指被同一顆子彈擊中的外星人---是一個列表
            game_stats.score += ai_settings.alien_points * len(aliens)
            scoreb.prep_score()
        check_high_score(game_stats, scoreb)

    start_new_level(ai_settings, screen, game_stats, scoreb, ship, aliens, bullets)


def update_bullets(ai_settings, screen, game_stats, scoreb, ship, aliens, bullets):
    """
        更新子彈位置並刪除已消失的子彈
    """
    # 更新子彈位置
    bullets.update()

    # 刪除已消失的子彈
    for bullet in bullets.copy():
        if bullet.rect.bottom <= 0:  # 當子彈底部越過螢幕頂部(0)時刪除子彈
            bullets.remove(bullet)
    check_bullet_alien_collisions(ai_settings, screen, game_stats, scoreb, ship, aliens, bullets)


def get_number_aliens_X(ai_settings, alien_width):
    """
        計算每行可容納多少個外星人
    """
    availabble_space_x = ai_settings.screen_width - 2 * alien_width  # 一行可可容納的水平長度
    number_aliens_x = int(availabble_space_x / (2 * alien_width))  # 一行可容納多少個外星人
    return number_aliens_x


def get_number_rows(ai_settings, alien_height, ship_height):
    """
        計算螢幕可容納多少行外星人
    """
    availables_space_y = (ai_settings.screen_height-ship_height-
                          (3*alien_height))
    number_rows = int(availables_space_y / (2 * alien_height))
    return number_rows


def create_alien(ai_settings, screen,  aliens, alien_number, row_number):
    """
        建立一個外星人並將其放在當前行
    """
    alien = Alien(ai_settings, screen)
    alien_width = alien.rect.width
    alien_height = alien.rect.height + 3    # 讓外星人之間間隔大些
    alien.x = alien_width + 2 * alien_width * alien_number  # 獲取新建外星人所移動的位置
    alien.rect.x = alien.x  # 新建外星人的位置
    alien.rect.y = alien_height + 2 * alien_height * row_number  # 新行的位置
    aliens.add(alien)


def create_fleet(ai_settings, screen, ship, aliens):
    """
        建立外星人群
    """
    # 建立一個外星人並計算一行可容納多少個外星人
    # 外星人間距為外星人的寬度
    alien = Alien(ai_settings, screen)
    number_aliens_x = get_number_aliens_X(ai_settings, alien.rect.width)  # 一行可容納外星人的個數
    number_rows = get_number_rows(ai_settings, alien.rect.height,
                                  ship.rect.height)   # 螢幕上可容納外星人的行數

    # 內外雙迴圈建立外星人群
    for row_number in range(number_rows):       # 外迴圈建立外星人行數
        for alien_number in range(number_aliens_x):     # 內迴圈建立外星人
            create_alien(ai_settings, screen, aliens, alien_number, row_number)  # 呼叫建立外星人的函式


def check_fleet_edges(ai_settings, aliens):
        """
            有外星人到達邊緣時採取相應的措施
        """
        for alien in aliens.sprites():
            if alien.check_edges():
                change_fleet_direction(ai_settings, aliens)
                break


def change_fleet_direction(ai_settings, aliens):
    """
        將整全外星人下移並改變它們的方向
    """
    for alien in aliens.sprites():
        alien.rect.y += ai_settings.fleet_drop_speed
    ai_settings.fleet_direction *= -1


def check_aliens_bottom(ai_settings, game_stats, scoreb, screen, ship, aliens, bullets):
    """檢測外星人是否到達螢幕底部"""
    screen_rect = screen.get_rect()
    for alien in aliens.sprites():
        if alien.rect.bottom >= screen_rect.bottom:
            # 外星人觸底爆炸聲
            music.voice_large()
            # 有外星人到達螢幕後進行像飛船被撞後一樣的操作
            ship_aliens_hit(ai_settings, game_stats, scoreb, screen, ship, aliens, bullets)
            break


def ship_aliens_hit(ai_settings, game_stats, scoreb, screen, ship, aliens, bullets):
    """
        響應外星人飛船相撞的後續操作
    """
    # 外星人飛船相撞時 發出碰撞聲音 ship_left減1
    if game_stats.ships_left > 0:
        music.voice_large()
        game_stats.ships_left -= 1

        # 更新螢幕上飛船的艘數
        scoreb.prep_ships()

    else:
        game_stats.game_active = False
        ai_settings.increase_wen()      # 遊戲結束後alien的速度得回到最初為
        pygame.mouse.set_visible(True)  # 遊戲結束後Play按鈕顯示出來

    # 清空外星人列表和子彈列表
    aliens.empty()
    bullets.empty()

    # 建立一群新的外星人並將飛船重新出現在螢幕在底部中央
    create_fleet(ai_settings, screen, ship, aliens)
    ship.center_ship()

    # 暫停讓使用者反應一會
    sleep(0.5)


def update_aliens(ai_settings, game_stats, scoreb, screen, ship, aliens, bullets):
    """
        更新外星人群中所有外星人的位置
    """
    check_fleet_edges(ai_settings, aliens)
    aliens.update()

    if pygame.sprite.spritecollideany(ship, aliens):
        """
        接收倆個實參--檢測飛船和外星人是否發生碰撞:發生了---往下走;沒發生---返回None
        """
        ship_aliens_hit(ai_settings, game_stats, scoreb, screen, ship, aliens, bullets)

    # 檢測是否有外星人到達螢幕底部
    check_aliens_bottom(ai_settings, game_stats, scoreb, screen, ship, aliens, bullets)


def check_high_score(game_stats, scoreb):
    """檢查是否誕生了新的最高得分"""
    if game_stats.score > game_stats.high_score:
        game_stats.high_score = game_stats.score
        scoreb.prep_high_score()
        
3.alien.py(外星人)
# coding: UTF-8
# 建立外星人Alien類
import pygame
from pygame.sprite import Sprite


class Alien(Sprite):
    """
        表示單個外星人的類
    """
    def __init__(self, ai_sttings, screen):
        super(Alien, self).__init__()
        self.screen = screen
        self.ai_settings = ai_sttings

        # 載入外星人的圖片並設定其rect屬性
        self.image = pygame.image.load('images/alien.bmp')
        self.rect = self.image.get_rect()

        # 設定外星人之間的間隔
        self.rect.x = self.rect.width    # 將外星人左邊距設定為外星人的寬度
        self.rect.y = self.rect.height    # 將上邊距設定為外星人的高度

        # 儲存外星人的準確位置
        self.x = float(self.rect.x)

    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:     # 外星人元素左邊<= 0時代表將要越過螢幕左邊---此時處於觸及螢幕的左邊緣
            return True

    def update(self):
        """
            向左或向右移動外星人
        """
        self.x += (self.ai_settings.alien_speed_factor
                   * self.ai_settings.fleet_direction)  # 速度 * 移動方向
        self.rect.x = self.x

    def blitem(self):
        """
            在指定位置繪製外星人的位置
        """
        self.screen.blit(self.image, self.rect)
        
4.ship.py(飛船)
# coding: UTF-8
# 建立飛船Ship類

import pygame
from pygame.sprite import Sprite


class Ship(Sprite):
    """
        初始化飛船並設定其初始位置
    """
    def __init__(self, ai_settings, screen):
        super().__init__()
        self.screen = screen
        self.ai_settings = ai_settings
        
        # 載入飛船影像並獲取其外接矩形
        self.image = pygame.image.load('images/ship.bmp')
        self.rect = self.image.get_rect()     # 獲取飛船圖片外接矩形(即是讓其可以像矩形一樣被處理)
        self.screen_rect = self.screen.get_rect()        # 獲取螢幕圖片外接矩形(即是讓其可以像矩形一樣被處理)
        
        # 將每搜新飛船放在螢幕底部中央
        self.rect.centerx = self.screen_rect.centerx  # 將飛船的centerx與螢幕的centerx捆綁在一起
        self.rect.bottom = self.screen_rect.bottom     # 將飛船的bottom與螢幕的bottom捆綁在一起

        # 在飛船的屬性center中儲存小數值
        self.center = float(self.rect.centerx)

        # 飛船水平移動標誌
        self.moving_right = False
        self.moving_left = False

    def update(self):
        """
            根據移動標誌調整飛船水平位置
        """
        # 飛船持續向右移動
        if self.moving_right and self.rect.right < self.screen_rect.right:
            self.center += self.ai_settings.ship_speed_factor

        # 飛船持續向左移動
        if self.moving_left and self.rect.left > 0:
            self.center -= self.ai_settings.ship_speed_factor

        # 根據self.center更新image_rect物件
        self.rect.centerx = self.center
        
    def blitem(self):
        """
            在指定位置繪製飛船
        """
        self.screen.blit(self.image, self.rect)

    def center_ship(self):
        """讓飛船在螢幕底部居中"""
        self.ship_center = self.screen_rect.centerx

5.bullet.py(子彈)
# coding: UTF-8
# 建立子彈Bullet類

import pygame
from pygame.sprite import Sprite


class Bullet(Sprite):
    """
        一個對飛船發射的子彈進行管理的類
    """
    def __init__(self, ai_settings, screen, ship):
        """
            在飛船所處的位置建立一個子彈物件
        """
        super(Bullet, self).__init__()  # 繼承Sprite
        self.screen = screen

        # 在(0, 0)位置上建立一個表示子彈的矩形,並設定其位置
        self.rect = pygame.Rect(0, 0, ai_settings.bullet_width, ai_settings.bullet_height)
        self.rect.centerx = ship.rect.centerx    # 將子彈的centerx設定為飛船的centerx
        self.rect.top = ship.rect.top            # 將子彈的top屬性設定為飛船的top屬性  讓子彈看起來是從飛船中射出的

        # 將子彈的垂直位置用小數表示,方便調節速度
        self.y = float(self.rect.y)

        # 獲取子彈其他屬性
        self.bullet_color = ai_settings.bullet_color
        self.bullet_speed_factor = ai_settings.bullet_speed_factor

    def update(self):
        """
           向上移動子彈
        """
        # 更新表示子彈位置的小數值
        self.y -= self.bullet_speed_factor

        # 更新表示子彈的rect的位置
        self.rect.y = self.y

    def draw_bullet(self):
        """
            在螢幕上繪製子彈
        """
        pygame.draw.rect(self.screen, self.bullet_color, self.rect)  # 順序為1 2 3 :2填充3,出現在1螢幕上

6.button.py(play按鈕)
# coding: UTF-8
# 建立開啟遊戲的play按鈕

import pygame.font


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

        # 設定按鈕的尺寸和其他屬性
        self.width, self.height = 200, 50
        self.button_color = (0, 208, 0)     # 按鈕框的顏色--和外星人一樣的顏色
        self.text_color = (255, 0, 0)   # 文字顏色--紅色
        self.font = pygame.font.SysFont(None, 48)   # 使用預設字型,字號為48

        # 設定按鈕的rect並使其居中
        self.button_rect = pygame.Rect(0, 0, self.width, self.height)
        self.button_rect.center = self.screen_rect.center

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

    def prep_msg(self, msg):
        """
            將文字渲染成影像並使其在按鈕上居中
        """
        self.msg_image = self.font.render(msg, True, self.text_color)    # 將儲存在msg中的文字轉換成影像
        self.msg_image_rect = self.msg_image.get_rect()
        self.msg_image_rect.center = self.button_rect.center

    def draw_button(self):
        """繪製一個用顏色填充的按鈕,然後繪製文字"""
        self.screen.fill(self.button_color, self.button_rect)
        self.screen.blit(self.msg_image, self.msg_image_rect)
        
7.settings.py(遊戲屬性設定)
# coding: UTF-8
# 建立Settings設定類---功能對應的設定


class Settings():
    """
        《外星人入侵》的所有設定的類
    """
    def __init__(self):
        """
            初始化遊戲的靜態設定
        """
        # 螢幕寬度和高度、背景顏色設定
        self.screen_width = 1200
        self.screen_height = 800
        self.bg_color = (230, 230, 230)

        # 飛船數量的設定
        self.ship_limit = 3

        # 子彈設定
        self.bullet_width = 3
        self.bullet_height = 10
        self.bullet_color = 255, 0, 0

        # 設定螢幕上可同時出現的子彈數
        self.bullet_allowed = 8

        # 外星人設定
        self.fleet_drop_speed = 10

        # 加快遊戲的節奏的速度
        self.speedup_scale = 1.1

        # 提高外星人得分節奏
        self.score_scale = 1.5

        self.initialize_dynamic_settings()

    def initialize_dynamic_settings(self):
        """初始化隨遊戲進行而變化的動態設定"""

        # 飛船移動速度
        self.ship_speed_factor = 2.5

        # 子彈移動速度
        self.bullet_speed_factor = 2

        # 外星人移動速度
        self.alien_speed_factor = 1

        # 每個外星人初始得分點
        self.alien_points = 50

        # fleet_direction為1表示向右移動  為-1表示向左移動
        self.fleet_direction = 1

    def increase_wen(self):
        """返回最初速度"""
        self.alien_speed_factor = 1
        # print("遊戲重新開始後的初始速度:" + str(self.alien_speed_factor))

    def increase_speed(self):
        """逐漸提高速度設定"""
        self.ship_speed_factor *= self.speedup_scale
        self.bullet_speed_factor *= self.speedup_scale
        self.alien_speed_factor *= self.speedup_scale
        # print("遊戲過程中依次累加的速度:" + str(self.alien_speed_factor) + "\n")

        # 逐漸提高外星人單次得分
        self.alien_points = int(self.alien_points * self.score_scale)
        # print(self.alien_points)

8.scoreboard.py(分數)
# # coding: UTF-8
# # 建立得分Score類

import pygame
from pygame.sprite import Group
from ship import Ship


class Scoreboard():
    """顯示得分資訊的類"""
    def __init__(self, ai_settings, screen, game_stats):
        """初始化顯示得分涉及的屬性"""
        self.screen = screen
        self.screen_rect = screen.get_rect()
        self.ai_settings = ai_settings
        self.game_stats = game_stats

        # 顯示得分資訊時使用的字型設定
        self.text_color = (30, 30, 30)
        self.font = pygame.font.SysFont(None, 48)
        self.prep_images()

    def prep_images(self):
        """準備包含 最高得分、當前得分、遊戲等級、飛船 的影像"""
        # 當前得分影像
        self.prep_score()

        # 最高得分影像
        self.prep_high_score()

        # 等級影像
        self.prep_level()

        # 飛船組影像
        self.prep_ships()

    def prep_score(self):
        """將當前得分轉換為一幅渲染的影像"""
        round_score = round(self.game_stats.score, -1)  # 使得分為10的倍數  -1----10的倍數   -2--100的倍數  -3--1000的倍數
        score_str = "score:" + "{:,}".format(round_score)
        self.score_image = self.font.render(score_str, True, self.text_color,
                                                self.ai_settings.bg_color)

        # 將當前得分放在螢幕右上角
        self.score_image_rect = self.score_image.get_rect()
        self.score_image_rect.right = self.screen_rect.right - 20
        self.score_image_rect.top = 20

    def prep_high_score(self):
        """將最高得分轉換為一幅渲染的影像"""
        high_round_score = round(self.game_stats.high_score, -1)  # 使得分為10的倍數  -1----10的倍數   -2--100的倍數  -3--1000的倍數
        high_score_str = "Mscore:" + "{:,}".format(high_round_score)
        self.high_score_image = self.font.render(high_score_str, True, self.text_color,
                                            self.ai_settings.bg_color)

        # 將最高得分放在頂部螢幕中央
        self.high_score_image_rect = self.high_score_image.get_rect()
        self.high_score_image_rect.centerx = self.screen_rect.centerx
        self.high_score_image_rect.top = self.score_image_rect.top

    def prep_level(self):
        """將等級轉換為一幅渲染的影像"""

        self.level_image = self.font.render(("lev:" + str(self.game_stats.level)), True,
                                            self.text_color, self.ai_settings.bg_color)

        # 將等級放在等分下面
        self.level_image_rect = self.level_image.get_rect()
        self.level_image_rect.right = self.score_image_rect.right
        self.level_image_rect.top = self.score_image_rect.bottom + 10

    def prep_ships(self):
        """顯示還剩下多少艘飛船"""
        self.ships = Group()
        for ship_number in range(self.game_stats.ships_left):
            ship = Ship(self.ai_settings, self.screen)
            ship.rect.x = 10 + ship_number * ship.rect.width
            ship.rect.top = 10
            self.ships.add(ship)

    def show_score(self):
        """在螢幕上顯示當前得分和最高得分"""
        self.screen.blit(self.score_image, self.score_image_rect)
        self.screen.blit(self.high_score_image, self.high_score_image_rect)
        self.screen.blit(self.level_image, self.level_image_rect)
        # 繪製飛船
        self.ships.draw(self.screen)
        
9.game_stats.py(遊戲狀態)
# coding: UTF-8
# 建立跟蹤遊戲統計資訊的Game_Stats類

import json


class Game_Stats():
    """跟蹤遊戲統計資訊的類"""
    def __init__(self, ai_settings):
        self.ai_settings = ai_settings
        self.reset_stats()
        # 遊戲剛啟動時處於活動狀態
        self.game_active = False

        # 在任何情況情況下都不應重置最高得分,載入歷史最高分
        self.high_score = 0
        with open('Max_score.json', encoding='UTF-8') as file:
            self.high_score = json.load(file)

    def reset_stats(self):
        """初始化在遊戲執行期間可能變化的統計資訊"""
        self.ships_left = self.ai_settings.ship_limit
        self.score = 0  # 每次有新的分數都應該重置
        self.level = 1  # 設定遊戲等級

10.music.py(音效)
# coding: UTF-8
# 關於遊戲中各種聲音效果的模組

import pygame


def voice_large():
    # 外星人到達了螢幕底部或者撞到飛船的大爆炸聲
    explosion_large = pygame.mixer.Sound("musics/Explo_Large.wav")
    explosion_large.play()


def voice_small():
    # 增加子彈和外星人碰撞的小爆炸聲
    explosion_small = pygame.mixer.Sound("musics/Explo_Small.wav")
    explosion_small.play()


def bullet_biu():
    # 增加子彈射出的biu聲
    bullet_whiz = pygame.mixer.Sound("musics/Bullet_biu.wav")
    bullet_whiz.play()


def bg_music():
    # 遊戲背景音樂(若遊戲開始就一直播放)
    pygame.mixer.music.load("musics/order_music.mp3")
    
11.遊戲圖片/音效素材獲取

(1)連結:https://pan.baidu.com/s/1A4VS59igzuEQi1JoDBabnA
提取碼:8lp3 (包括圖片以及音效)

(2)另外如果讀者想換音效 可進下面連結進行尋找
連結:https://www.aigei.com/music(本人也是從這扒的)

12.針對遊戲過程中各種報錯解決辦法思路的提醒

(1)最關鍵的一點:要抓住最終報錯語句 那句很很很關鍵 根據這句能夠快速定位報錯位置以及原因
(2)學會百度篩選有效解決辦法----這個能力至關重要
(3)找檔案中邏輯錯誤或書寫前後不統一的錯誤—這個是大部分人會犯的錯誤的 你以為是那樣的 實際不是那樣的

13.遊戲完成階段總結

從開始動手編寫到遊戲功能較完善的階段 花了2周 我現階段還是大學生每天有課要上 每天抽時間學 python也是自學一步一步慢慢走過來的 這其中遇到的問題也是千奇百怪的 讓人頭大 (難怪民間有種說法–計算機是玄學!!!)但很慶幸的是每個當時看起來艱難不可理解的報錯都被我ko了 這種感覺 經歷過的就會大大的懂 我個人是很享受那個過程的 進展緩慢每天打怪但每天都有收穫 如該遊戲過程中出現幾次無某某庫的時候 用“進入cmd 用pip install 報錯缺少的庫名”辦法基本能夠解決 嘻嘻…好啦 接下來讓遊戲放在朋友電腦上也可以玩 即使在沒有相應的pycharm/python的環境也可。

三、主.py檔案轉可執行.exe檔案

1.安裝pyinstaller:
win+r進入cmd介面→pip install pyinstaller→等待成功(pip list可檢測是否安裝pyinstaller)

在這裡插入圖片描述

2.主.py檔案轉可執行.exe檔案

(1)在cmd介面中進入遊戲中所有.py檔案同在的那個資料夾:

d:→cd Python-Projects\a book of py\Projects\Project1(alien_invasion) chapter 12-14\alien_invasion

(2)打包主.py檔案:

pyinstaller -F -w --add-data "musics/*.mp3;./musics" --add-data "musics/*.mav;./musics" alien_invasion.py

幾點說明:

i.*萬用字元,表示music資料夾裡所有的mp3檔案、所有的wav檔案 要使用雙引號
ii.windows系統要使用分號作為檔案原本的路徑和要存放的路徑的分隔符,這一點和linux系統不一樣,linux系統使用冒號
iii.要存放的路徑 ./music表示最外層目錄下的music目錄

引數解析:

pyinstaller  xxx.py	  打包成一個需要依賴檔案的exe
pyinstaller -F xxx.py  打包成一個單獨的exe,但是會有黑視窗
pyinstaller -F  -w xxx.py 打包成一個單獨的exe去且除黑視窗==(遊戲中沒有用多媒檔案用這條命令)==

在這裡插入圖片描述
在這裡插入圖片描述

3.轉後事件處理

打包成功後 在alien_invasion資料夾在新出現三個檔案—build資料夾、dist資料夾、alien_invasion.spec檔案 :
在這裡插入圖片描述
可執行.exe檔案在dish資料夾中 雙擊執行它就可:
在這裡插入圖片描述
最終出現遊戲執行介面 play it 哈哈哈…看著蠻順利的是不 其實不然
各種報錯後面詳說
在這裡插入圖片描述

四、報錯情況分析以及解決辦法

1.雙擊alien_invasion.exe執行檔案出現閃退

(1)閃退理由:打包的時候只打包了alien_invasion.py主檔案 生成的alien_invasion.exe在dist資料夾 與遊戲所依賴的其他.py不在同一個目錄下 所以所以所以!.exe檔案在執行的時候找!不!到!相關的模組!故而不能執行!閃退
(.exe在黃色箭標頭檔案夾中 其他.py檔案在紅色框框所在資料夾中)
在這裡插入圖片描述

(2)解決辦法:把.exe檔案抬到所匯入模組所在的資料夾內就可以了。當然也可以把匯入的模組搬到.exe檔案所在的資料夾下,同一個道理的。

2.雙擊alien_invasion.exe執行檔案彈出“Faied to execute script ***”錯誤

(1)分析:出現這個錯誤的時候 一頭的霧水 就是簡單一句話 錯誤定位也無處下手 度娘上針對這個錯誤也是各種說法
(2)思路:為了看到具體報錯情況 後面所性把打包新出現的三個檔案刪除 重回到打包步驟----還記得上面有個“引數解析”不 把命令中的-w去掉

pyinstaller -F --add-data "musics/*.mp3;./musics" --add-data "musics/*.mav;./musics" alien_invasion.py

這個時候再雙擊.exe可看到具體報錯情況以及定位:
在這裡插入圖片描述
(3)看到沒 說打不開音效檔案 報錯理由其實和報錯情況1(1)是一樣一樣的 故解決辦法也和報錯情況1(2)一樣一樣的
(當然啦如果你解決了報錯情況1的問題 原則上是不會出現情況2的錯誤的 —因為你都移動到同一個目錄下去啦)

五、壓縮.exe相關素材

終於!終於!終於!—要發給朋友玩啦(要記得把相關素材放進去哦 什麼圖片呀、音效呀等 使用說明是我自己為了朋友愉快的玩耍加進去哈)
在這裡插入圖片描述

這裡我也把遊戲放上來了哈 想試玩的朋友 可以下載玩玩
連結:https://pan.baidu.com/s/1lu9RHKrxjyBC7vfxMALQZA
提取碼:2qn0

後期有時間 會對遊戲改進完善的 可能到時候也會發帖子的呢
ps:在看這篇帖子的朋友有什麼疑問或能指出錯誤點的等等 都歡迎歡迎來訪 大家共同進步!!!

相關文章