Alien invasion外星人入侵[Pygame上]

Alien invasion外星人入侵

武裝飛船

1、規(guī)劃項目

開發(fā)大型項目時,做好規(guī)劃后再動手編寫項目很重要。規(guī)劃可確保你不偏離軌道,從而提高項目成功的可能性。

開發(fā)出來的效果

在游戲《外星人入侵》中,玩家控制著一艘最初出現(xiàn)在屏幕底部中央的飛船。玩家可以使用箭頭鍵左右移動飛船,還可以使用空格鍵進行射擊。

游戲開始時,一群外星人出現(xiàn)在天空中,他們在屏幕中向下移動。玩家的任務(wù)是射殺這些外星人。

2、安裝Pygame

使用pip安裝python包

# 安裝pip window和osx下載方式不同
python get-pip.py

# 使用 pip 下載pygame 
python -m pip install --user pygame  


3、開始游戲項目

創(chuàng)建Pygame窗口以及響應(yīng)用戶輸入

創(chuàng)建空的Pygame的窗口,編寫游戲基本結(jié)構(gòu)

  • 初始化背景設(shè)置
  • 游戲主體
  • 監(jiān)聽事件等

alien_invasion.py

import sys
import pygame

def run_game():
    # 初始化游戲并創(chuàng)建一個屏幕對象
    pygame.init()
    screen = pygame.display.set_mode((1200, 800))
    pygame.display.set_caption("Alien Invasion")

    # 開始游戲的主循環(huán)
    while True:

        # 監(jiān)視鍵盤和鼠標(biāo)事件
        for event in pygame.event.get():
            if event.type == pygame.quit():
                sys.exit()

        # 讓最近繪制的屏幕可見
        pygame.display.flip()


run_game()
設(shè)置背景色

Pygame默認(rèn)創(chuàng)建黑色屏幕


# 設(shè)置背景色 淺灰色
bg_color = (230, 230, 230)
    
# 每次循環(huán)時都重繪制屏幕
screen.fill(bg_color)
創(chuàng)建設(shè)置類

每次增加游戲新功能時,通常也將引入一些新設(shè)置,避免在項目中到處添加設(shè)置,項目增大時修改游戲外觀更容易。要修改游戲,只需要修改settings.py的一些值,無需查找散步在文件中的不同設(shè)置。

settings.py

class Settings():
    """儲存《外星人入侵》的所有設(shè)置類"""

    def __init__(self):
        """初始化游戲的設(shè)置"""
        # 屏幕設(shè)置
        self.screen_width = 1200
        self.screen_height = 800
        self.bg_color = (230, 230, 230)

修改alien_invasion.py

import sys
import pygame
from settings import Settings

def run_game():
    # 初始化游戲并創(chuàng)建一個屏幕對象
    pygame.init()
    ai_settings = Settings()
    screen = pygame.display.set_mode((ai_settings.screen_width, ai_settings.screen_height))
    pygame.display.set_caption("Alien Invasion")
    
    # 開始游戲的主循環(huán)
    while True:

        # 監(jiān)視鍵盤和鼠標(biāo)事件
        for event in pygame.event.get():
            if event.type == pygame.quit():
                sys.exit()

        # 每次循環(huán)時都重繪制屏幕
        screen.fill(ai_settings.bg_color)
        
        # 讓最近繪制的屏幕可見
        pygame.display.flip()

調(diào)用pygame.init(), 再創(chuàng)建一個Settings實例,將其存儲在變量ai_settings中

4、添加飛船圖像

加載一幅圖像,再使用Pygame方法blit()繪制它。

可以在此網(wǎng)站找 https://pixabay.com/

游戲中幾乎可以使用任何圖像文件,單使用位圖(.bmp) 文件最為簡單,Pygame默認(rèn)加載位圖。

選擇圖像時盡可能選擇背景透明的圖像。

[圖片上傳失敗...(image-8cb399-1676467847121)]

創(chuàng)建Ship類

將其顯示到屏幕上,它負(fù)責(zé)管理飛船的大部分行為

ship.py

import pygame


class Ship():

    def __init__(self, screen):
        """初始化飛船并設(shè)置其初始值位置"""
        self.screen = screen

        # 加載飛船圖像并獲取其外接矩形
        self.image = pygame.image.load('images/ship.bmp')
        self.rect = self.image.get_rect()
        self.screen_rect = screen.get_rect()

        # 將每艘新飛船放在屏幕底部中央
        self.rect.centerx = self.screen_rect.centerx
        self.rect.bottom = self.screen_rect.bottom

    def blitme(self):
        """在指定位置繪制飛船"""
        self.screen.blit(self.image, self.rect)

__init__() 接受兩個參數(shù),引用self和screen,screen指定了將飛船繪制到什么地方

pygame.image.load() 加載圖像 返回一個表示飛船的surface,存儲到self.image中

self.rect.centerx 飛船中心的x坐標(biāo)

self.rect.bottom 飛船下邊緣的y坐標(biāo)

blitme方法將圖像繪制到屏幕上

在屏幕上繪制飛船

alien_invasion.py

import sys
import pygame
from settings import Settings
from ship import Ship


def run_game():
    # 初始化游戲并創(chuàng)建一個屏幕對象
    pygame.init()
    ai_settings = Settings()
    screen = pygame.display.set_mode((ai_settings.screen_width, ai_settings.screen_height))
    pygame.display.set_caption("Alien Invasion")

    # 創(chuàng)建一艘飛船
    ship = Ship(screen)

    # 開始游戲的主循環(huán)
    while True:

        # 監(jiān)視鍵盤和鼠標(biāo)事件
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                sys.exit()

        # 每次循環(huán)時都重繪制屏幕
        screen.fill(ai_settings.bg_color)
        ship.blitme()

        # 讓最近繪制的屏幕可見
        pygame.display.flip()


run_game()

啟動報錯

"C:/Users/ss/code/PycharmProjects/crash/part2/alieninvasion/alien_invasion.py", line 36, in run_game
    ship.blitme()
  File "C:\Users\ss\code\PycharmProjects\crash\part2\alieninvasion\ship.py", line 29, in blitme
    self.screen_rect.blit(self.image, self.rect)
AttributeError: 'pygame.Rect' object has no attribute 'blit'

Process finished with exit code 1

AttributeError: 'pygame.Rect' object has no attribute 'blit'

self.screen_rectscreen.get_rect() 不能加載到bilt

錯誤原因代碼引用錯,參考其他博主也是如此錯的 是screen不是screen_rect

self.screen.blit(self.image, self.rect)

修改之后便可正常啟動項目

5、重構(gòu): 模塊game_functions

在大型項目中,經(jīng)常需要在添加新代碼前重構(gòu)既有的代碼

重構(gòu)旨在簡化既有代碼的結(jié)構(gòu),使其更容易擴展。

當(dāng)前項目使用模塊game_function,避免alien_invasion太長,使邏輯更容易理解。

game_function.py

隔離事件管理循環(huán),將事件管理與游戲的其他方法分離

import pygame
import sys


def check_event():
    """響應(yīng)按鍵和鼠標(biāo)事件"""
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            sys.exit()



def update_screen(ai_setting, screen, ship):
    """更新屏幕上的圖像,并切換到新屏幕"""
    # 每次循環(huán)時都重繪制屏幕
    screen.fill(ai_settings.bg_color)
    ship.blitme()

    # 讓最近繪制的屏幕可見
    pygame.display.flip()

調(diào)用game_function.py 方法

import sys
import pygame
from settings import Settings
from ship import Ship
import game_functions as gf

def run_game():
    # 初始化游戲并創(chuàng)建一個屏幕對象
    pygame.init()
    ai_settings = Settings()
    screen = pygame.display.set_mode((ai_settings.screen_width, ai_settings.screen_height))
    pygame.display.set_caption("Alien Invasion")

    # 創(chuàng)建一艘飛船
    ship = Ship(screen)

    # 開始游戲的主循環(huán)
    while True:

        # 監(jiān)視鍵盤和鼠標(biāo)事件
        gf.check_event()

        gf.update_screen(ai_settings, screen, ship)


run_game()

6、駕駛飛船

來讓玩家能夠左右移動飛船

在用戶按左或者右箭頭鍵時做出響應(yīng)

在函數(shù)check_events() 中,指定事件類型,每次按下都被注冊未一個KEYDOWN事件。

檢測到KEYDOWN事件時,我們要檢查按下的是否是特定的鍵。左、右等,就可以控制屏幕圖像移動

響應(yīng)按鍵

game_function.py

按下右箭頭鍵,就增大飛船的rect.conterx值,飛船向右移動

def check_event(ship):
    """響應(yīng)按鍵和鼠標(biāo)事件"""
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            sys.exit()
        elif event.type == pygame.KEYDOWN:
            if event.type == pygame.K_RIGHT:
                # 向右移動飛船
                ship.rect.centex += 1

alien_invasion.py

# 開始游戲的主循環(huán)
while True:
    # 監(jiān)視鍵盤和鼠標(biāo)事件
    gf.check_event(ship)

    gf.update_screen(ai_settings, screen, ship)

允許不斷移動

玩家按住右箭頭不妨?xí)r,我們希望飛船不斷地向右移動,知道玩家松開為止

KEYDOWN和KEYUOP事件 鍵盤的按下與松開

我們用標(biāo)志來實現(xiàn)持續(xù)移動,飛船不動時,標(biāo)志moving_right將為False。

飛船的屬性Ship添加一個moving_right的屬性和一個為update()的方法

ship.py

class Ship():

    def __init__(self, screen):
        """初始化飛船并設(shè)置其初始值位置"""
        self.screen = screen

    
        # 移動標(biāo)志 鼠標(biāo)按下事件
        self.moving_right = False

    def update(self):
        """根據(jù)移動標(biāo)志調(diào)整飛船的位置"""
        if self.moving_right:
            self.rect.centerx += 1

     

alien_invasion.py

def run_game():
    # 初始化游戲并創(chuàng)建一個屏幕對象
    pygame.init()
    ai_settings = Settings()
    screen = pygame.display.set_mode((ai_settings.screen_width, ai_settings.screen_height))
    pygame.display.set_caption("Alien Invasion")

    # 創(chuàng)建一艘飛船
    ship = Ship(screen)

    # 開始游戲的主循環(huán)
    while True:
        # 監(jiān)視鍵盤和鼠標(biāo)事件
        gf.check_event(ship)
        ship.update()
        gf.update_screen(ai_settings, screen, ship)


run_game()

當(dāng)前程序,運行起來之后,按住右箭頭飛船將不斷向右移動,知道松開為止。(也可以移動到屏幕外表,哈哈)

左右移動

同樣的方式添加向作移動的邏輯

使用moving_left

def check_event(ship):
    """響應(yīng)按鍵和鼠標(biāo)事件"""
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            sys.exit()
        elif event.type == pygame.KEYDOWN:
            print("鍵盤事件: " + str(event.key))
            if event.key == pygame.K_RIGHT:
                # 向右移動飛船
                # ship.rect.centex += 1
                ship.moving_right = True
            if event.key == pygame.K_LEFT:
                ship.moving_left = True

        elif event.type == pygame.KEYUP:
            if event.key == pygame.K_RIGHT:
                ship.moving_right = False
            if event.key == pygame.K_LEFT:
                ship.moving_left = False

class Ship():

    def __init__(self, screen):
        """初始化飛船并設(shè)置其初始值位置"""
        self.screen = screen

        # 移動標(biāo)志 鼠標(biāo)按下事件
        self.moving_right = False
        self.moving_left = False
    
    def update(self):
        """根據(jù)移動標(biāo)志調(diào)整飛船的位置"""
        if self.moving_right:
            self.rect.centerx += 1
        if self.moving_left:
            self.rect.centerx -= 1
調(diào)整飛船的速度

當(dāng)前執(zhí)行while循環(huán)時,飛船最多移動1像素,但在Settings類中添加屬性ship_speed_factor, 用于控制飛船的速度。

在settings.py 添加屬性

class Settings():
    """儲存《外星人入侵》的所有設(shè)置類"""

    def __init__(self):
        """初始化游戲的設(shè)置"""
        # 屏幕設(shè)置
        self.screen_width = 1000
        self.screen_height = 700
        # 灰 230, 230, 230 藍 R:0 G:191 B:243
        self.bg_color = (0, 191, 243)
        self.bg_image = 'images/stars.bmp'

        # 飛船的設(shè)置
        self.ship_speed_factor = 1.5
class Ship():

    def __init__(self, ai_settings, screen):
        """初始化飛船并設(shè)置其初始值位置"""
        self.screen = screen
        self.ai_settings = ai_settings

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

        # 移動標(biāo)志 鼠標(biāo)按下事件
        self.moving_right = False
        self.moving_left = False

    def update(self):
        """根據(jù)移動標(biāo)志調(diào)整飛船的位置"""
        if self.moving_right:
            self.center += self.ai_settings.ship_speed_factor
        if self.moving_left:
            self.center -= self.ai_settings.ship_speed_factor

        # 根據(jù)self.centerx 更新rect對象
        self.rect.centerx = self.center

    def blitme(self):
        """在指定位置繪制飛船"""
        self.screen.blit(self.image, self.rect)
限制飛船的活動范圍

當(dāng)前飛船無線左右移動,只要不松開鍵盤,可以移動到宇宙之外

def update(self):
    """根據(jù)移動標(biāo)志調(diào)整飛船的位置"""

    # 限制飛船活動范圍
    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

向右移動不能超過屏幕最大寬度,向左移動要大于1

重構(gòu)check_events()

把響應(yīng)鍵按下和響應(yīng)鍵松開獨立出來

def check_event(ship):
    """響應(yīng)按鍵和鼠標(biāo)事件"""
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            sys.exit()
        elif event.type == pygame.KEYDOWN:
            print("鍵盤事件: " + str(event.key))
            check_keydown_event(event, ship)
        elif event.type == pygame.KEYUP:
            check_keyup_event(event, ship)


def check_keydown_event(event, ship):
    """鍵盤按下"""
    if event.key == pygame.K_RIGHT:
        # 向右移動飛船
        # ship.rect.centex += 1
        ship.moving_right = True
    if event.key == pygame.K_LEFT:
        ship.moving_left = True


def check_keyup_event(event, ship):
    """鍵盤松開"""
    if event.key == pygame.K_RIGHT:
        ship.moving_right = False
    if event.key == pygame.K_LEFT:
        ship.moving_left = False

7、簡單回顧

4個py文件,作別作用

alien_invasion.py

主文件,整個游戲都要用到,包含游戲主題循環(huán),調(diào)用check_enets(), ship.update()的while循環(huán)

其他文件也是直接或者間接引入

settings.py 包含Settings類,初始化控制游戲外觀和飛船速度的屬性

game_function.py 包含一系列函數(shù),游戲大部分工作都是他們完成的。

檢測事件check_enevts

8、射擊

玩家按空格鍵時發(fā)射子彈(小矩形)。子彈將在屏幕中向上穿行,抵達屏幕上邊緣后消失。

添加子彈設(shè)置

settings.py

# 子彈的設(shè)置
self.bullet_speed_factor = 1
self.bullet_width = 3
self.bullet_height = 15
self.bullet_color = 60, 60, 60

創(chuàng)建寬3像素,高15像素的深灰色子彈,子彈的速度比飛船稍低

創(chuàng)建Bullet類

bullet.py

import pygame
from pygame.sprite import Sprite


class Bullet(Sprite):
    """一個對飛船發(fā)射的子彈進行管理的類"""

    def __init__(self, ai_settings, screen, ship):
        """在飛船所處的位置創(chuàng)鍵一個子彈"""
        super(Bullet, self).__init__()
        
        self.screen = screen

        # 在(0,0)處創(chuàng)建一個表示子彈的矩形,再設(shè)置正確的位置
        self.rect = pygame.Rect(0, 0, ai_settings.bullet_width, ai_settings.bullet_height)
        self.rect.centerx = ship.rect.centerx
        self.rect.top = ship.rect.top

        # 存儲用小數(shù)表示的子彈位置
        self.y = float(self.rect.y)

        self.color = ai_settings.bullet_color
        self.speed_factor = ai_settings.bullet_speed_

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

    def draw_bullet(self):
        """在屏幕上繪制子彈"""
        pygame.draw.rect(self.screen, self.color, self.rect)

Bullet類繼承Sprite類,通過使用精靈將游戲元素編組,同時操作編組中的所有元素。調(diào)用super().__init()來繼承Sprite。

創(chuàng)建的子彈屬性rect,并非基于圖像,使用pagame.Rect()類從空白開始創(chuàng)建一個矩形。創(chuàng)建實例時,必須提供矩形左上角的x,y坐標(biāo)和矩形的寬度高度,在(0,0)處創(chuàng)建這個矩形,再將其移到正確的位置

子彈的centerx設(shè)置為飛船的rect.centerx。子彈從飛船頂部射出,子彈的rect的top屬性為飛船的rect的top屬性,這樣子彈看起來就像是從飛船中射出來的

方法update()管理子彈的位置。放射出去后,子彈在屏幕中向上移動,y坐標(biāo)將不斷減少,來更新子彈的位置,self.y中減去self.speed_factor的值。子彈發(fā)射后,x坐標(biāo)始終不變,子彈沿直線垂直地往上穿行。

將子彈存儲到編組中

創(chuàng)建編組group,用于儲存所有有效的子彈,以便于能后管理發(fā)射出去的所有字段。

編組在屏幕上繪制子彈,以及更新每顆子彈的位置

from pygame.sprite import Group


def run_game():
    # 初始化游戲并創(chuàng)建一個屏幕對象
    pygame.init()
    ai_settings = Settings()
    screen = pygame.display.set_mode((ai_settings.screen_width, ai_settings.screen_height))
    pygame.display.set_caption("Alien Invasion")

    # 創(chuàng)建一艘飛船
    ship = Ship(ai_settings, screen)

    # 創(chuàng)建儲存子彈的編組
    bullets = Group()

    # 開始游戲的主循環(huán)
    while True:
        # 監(jiān)視鍵盤和鼠標(biāo)事件
        gf.check_event(ai_settings, screen, ship, bullets)
        ship.update()
        bullets.update()
        gf.update_screen(ai_settings, screen, ship, bullets)
開火

修改check_keydown_events() 玩家按空格鍵時發(fā)射一顆子彈,松開空格鍵時什么都不會發(fā)生。

def check_event(ai_settings, screen, ship, bullets):
    """響應(yīng)按鍵和鼠標(biāo)事件"""
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            sys.exit()
        elif event.type == pygame.KEYDOWN:
            print("鍵盤事件: " + str(event.key))
            check_keydown_event(event, ai_settings, screen, ship, bullets)
        elif event.type == pygame.KEYUP:
            check_keyup_event(event, ship)


def check_keydown_event(event, ai_settings, screen, ship, bullets):
    """鍵盤按下"""
    if event.key == pygame.K_RIGHT:
        # 向右移動飛船
        # ship.rect.centex += 1
        ship.moving_right = True
    if event.key == pygame.K_LEFT:
        ship.moving_left = True

    if event.key == pygame.K_SPACE:
        # 創(chuàng)建一顆子彈, 并將其加入到編組bullets中
        new_bullet = Bullet(ai_settings, screen, ship)
        bullets.add(new_bullet)
        
def update_screen(ai_settings, screen, ship, bullets):
    """更新屏幕上的圖像,并切換到新屏幕"""
    # 每次循環(huán)時都重繪制屏幕
    screen.fill(ai_settings.bg_color)

    # 添加背景
    bg_image = pygame.image.load(ai_settings.bg_image).convert()
    screen.blit(bg_image, (0, 0))

    # 在飛船和外星人后面重新繪制子彈
    for bullet in bullets.sprites():
        bullet.draw_bullet()        

啟動報錯,查看Bullet類是否有初始化,調(diào)用super()繼續(xù)Sprite

 File "C:\Users\shenshuaihu\lib\site-packages\pygame\sprite.py", line 160, in add_internal
    self.__g[group] = 0
AttributeError: 'Bullet' object has no attribute '_Sprite__g'

<img src="C:\Users\shenshuaihu\AppData\Roaming\Typora\typora-user-images\image-20220914144536965.png" alt="image-20220914144536965" style="zoom:50%;" />

刪除以消失的子彈

當(dāng)子彈到達屏幕頂端后消失,不僅僅是Pygame不能在屏幕繪制他們,子彈實際依然存在,y坐標(biāo)為負(fù)數(shù),越來越少。他們繼續(xù)消耗內(nèi)存

如不刪除的話,游戲的無謂工作越來越多,進而變得越來越慢。

子彈的rect的bottom屬性為零時,表面以及穿過屏幕頂端了

def run_game():
    # 初始化游戲并創(chuàng)建一個屏幕對象
    
    ....

    # 創(chuàng)建儲存子彈的編組
    bullets = Group()

    # 開始游戲的主循環(huán)
    while True:
        # 監(jiān)視鍵盤和鼠標(biāo)事件
        gf.check_event(ai_settings, screen, ship, bullets)
        ship.update()
        bullets.update()

        # 刪除已消失的子彈
        for bullet in  bullets.copy():
            if bullet.rect.bottom <= 0:
                bullets.remove(bullet)
        gf.update_screen(ai_settings, screen, ship, bullets)


run_game()

限制子彈數(shù)量

對屏幕上子彈數(shù)量進行限制,鼓勵玩家有目標(biāo)的射擊

settings.py

self.bullets_allowed = 3
def check_keydown_event(event, ai_settings, screen, ship, bullets):
    """鍵盤按下"""
    if event.key == pygame.K_RIGHT:
        # 向右移動飛船
        # ship.rect.centex += 1
        ship.moving_right = True
    if event.key == pygame.K_LEFT:
        ship.moving_left = True

    if event.key == pygame.K_SPACE:
        # 創(chuàng)建一顆子彈, 并將其加入到編組bullets中
        if len(bullets) < ai_settings.bullets_allowed:
            new_bullet = Bullet(ai_settings, screen, ship)
            bullets.add(new_bullet)
創(chuàng)建函數(shù)

update_bullets()

子彈管理代碼獨立

game_funcitions.py

def update_bullets(bullets):
    """更新子彈的位置,并刪除已消失的子彈"""
    bullets.update()

    # 刪除已消失的子彈
    for bullet in bullets.copy():
        if bullet.rect.bottom <= 0:
            bullets.remove(bullet)

alien_invasion.py

# 開始游戲的主循環(huán)
while True:
    # 監(jiān)視鍵盤和鼠標(biāo)事件
    gf.check_event(ai_settings, screen, ship, bullets)
    ship.update()
    gf.update_bullets(bullets)

fire_bullet()

發(fā)射子彈的代碼移動到一個獨立的函數(shù)中

外星人

項目第二篇,從屏幕上方邊緣添加一個外星人,然后生成一群外星人,外星人向兩邊和下面移動,并刪除被子彈擊中的外星人,最后,我們將顯示玩家擁有飛船的數(shù)量,在玩家的飛船用完之后變結(jié)束游戲。

創(chuàng)建一個外星人

在平時上放置類似飛船的,創(chuàng)建Alien類,像Ship那樣的。

創(chuàng)建alien類

alien.py

"""
======================
@title: game_functions
@description: 游戲方法
@author: elijah
@date: 2022/9/9 10:10
=====================
"""

import pygame
import sys
from bullet import Bullet


def check_event(ai_settings, screen, ship, bullets):
    """響應(yīng)按鍵和鼠標(biāo)事件"""
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            sys.exit()
        elif event.type == pygame.KEYDOWN:
            print("鍵盤事件: " + str(event.key))
            check_keydown_event(event, ai_settings, screen, ship, bullets)
        elif event.type == pygame.KEYUP:
            check_keyup_event(event, ship)


def check_keydown_event(event, ai_settings, screen, ship, bullets):
    """鍵盤按下"""

    if event.key == pygame.K_q:
        sys.exit()

    if event.key == pygame.K_RIGHT:
        # 向右移動飛船
        # ship.rect.centex += 1
        ship.moving_right = True
    if event.key == pygame.K_LEFT:
        ship.moving_left = True

    if event.key == pygame.K_SPACE:
        # 創(chuàng)建一顆子彈, 并將其加入到編組bullets中
        fire_bullet(ai_settings, screen, ship, bullets)
    if event.key == pygame.K_RETURN:
        # 加大活力
        fire_full_bullet(ai_settings, screen, ship, bullets)



def check_keyup_event(event, ship):
    """鍵盤松開"""
    if event.key == pygame.K_RIGHT:
        ship.moving_right = False
    if event.key == pygame.K_LEFT:
        ship.moving_left = False


def update_screen(ai_settings, screen, ship, alien, bullets):
    """更新屏幕上的圖像,并切換到新屏幕"""
    # 每次循環(huán)時都重繪制屏幕
    screen.fill(ai_settings.bg_color)

    # 添加背景
    bg_image = pygame.image.load(ai_settings.bg_image).convert()
    screen.blit(bg_image, (0, 0))

    # 在飛船和外星人后面重新繪制子彈
    for bullet in bullets.sprites():
        bullet.draw_bullet()

    ship.blitme()
    alien.blitme()

    # 讓最近繪制的屏幕可見
    pygame.display.flip()


def update_bullets(bullets):
    """更新子彈的位置,并刪除已消失的子彈"""
    bullets.update()

    # 刪除已消失的子彈
    for bullet in bullets.copy():
        if bullet.rect.bottom <= 0:
            bullets.remove(bullet)


def fire_bullet(ai_settings, screen, ship, bullets):
    """如果沒有達到限制,就發(fā)射一顆子彈"""
    # 創(chuàng)建一顆子彈, 并將其加入到編組bullets中
    if len(bullets) < ai_settings.bullets_allowed:
        new_bullet = Bullet(ai_settings, screen, ship)
        bullets.add(new_bullet)


def fire_full_bullet(ai_settings, screen, ship, bullets):
    """加大火力"""
    # 創(chuàng)建一顆子彈, 并將其加入到編組bullets中
    i = 0
    while i < 80:
        # new_ship = ship.copy()
        # new_ship.rect.centerx = ship.rect.centerx
        ai_settings.ship_full_fire = True
        ai_settings.ship_full_fire_num = i * 2 + i

        new_bullet = Bullet(ai_settings, screen, ship)
        bullets.add(new_bullet)
        i += 1
創(chuàng)建Alien實例

在alien_invasion.py 中創(chuàng)建Alien實例


from alien import Alien


def run_game():
    # 初始化游戲并創(chuàng)建一個屏幕對象
   
   ...

    # 創(chuàng)建一艘飛船
    ship = Ship(ai_settings, screen)

    # 創(chuàng)建儲存子彈的編組
    bullets = Group()

    # 創(chuàng)建一個外星人
    alien = Alien(ai_settings, screen)

    # 開始游戲的主循環(huán)
    while True:
        # 監(jiān)視鍵盤和鼠標(biāo)事件
        gf.check_event(ai_settings, screen, ship, bullets)
        ship.update()
        gf.update_bullets(bullets)
        gf.update_screen(ai_settings, screen, ship, alien, bullets)


run_game()

在外星人出現(xiàn)在屏幕上

game_funcitons.py

def update_screen(ai_settings, screen, ship, alien, bullets):
    """更新屏幕上的圖像,并切換到新屏幕"""
    # 每次循環(huán)時都重繪制屏幕
    screen.fill(ai_settings.bg_color)

    # 添加背景
    bg_image = pygame.image.load(ai_settings.bg_image).convert()
    screen.blit(bg_image, (0, 0))

    # 在飛船和外星人后面重新繪制子彈
    for bullet in bullets.sprites():
        bullet.draw_bullet()

    ship.blitme()
    alien.blitme()

    # 讓最近繪制的屏幕可見
    pygame.display.flip()

啟動項目,第一個外星人變出現(xiàn)了

創(chuàng)建一群外星人

簡單計算空間

首先需要確定一行可以容納多少個外星人

屏幕的寬度為ai_setting.screen_width,但屏幕兩邊需要留空白,兩個外邊距,放置外星人的水平空間為屏幕寬度減去外星人寬度的兩倍

available_space_x = ai_setting.screen_width - (2 * alien_width)

外星人直接也需要流出空白,一個外星人水平需要的寬度是兩個外星人的寬度,一行容納多少外星人可以 可利用空間除以一個外星人的寬度

number_aliens_x = available_space_x / (2 * alien_width)

創(chuàng)建多行外星人

先創(chuàng)建aliens的編組,用于儲存外星人,再調(diào)用創(chuàng)建函數(shù)create_fleet

alien_invasion.py

def run_game():
    # 初始化游戲并創(chuàng)建一個屏幕對象
   ...

    # 創(chuàng)建一艘飛船
    ship = Ship(ai_settings, screen)

    # 創(chuàng)建儲存子彈的編組
    bullets = Group()

    # 一個外星人編組
    aliens = Group()

    # 創(chuàng)建外星人群
    gf.create_fleet(ai_settings, screen, aliens)

    # 開始游戲的主循環(huán)
    while True:
        # 監(jiān)視鍵盤和鼠標(biāo)事件
        ....
        gf.update_screen(ai_settings, screen, ship, aliens, bullets)


run_game()

game_funcitons.py的update_screen() 修改參數(shù)名稱 aliens

創(chuàng)建外星人群
def create_fleet(ai_settings, screen, aliens):
    """創(chuàng)建外星人艦隊"""

    # 創(chuàng)建一個外星人 并計算一行容納多少人外星人
    # 外星人間距為外星人寬度
    alien = Alien(ai_settings, screen)
    alien_width = alien.rect.width
    available_space_x = ai_settings.screen_width - 2 * alien_width
    number_aliens_x = int(available_space_x / (2 * alien_width))

    # 創(chuàng)建一行外星人
    for alien_number in range(number_aliens_x):
        # 創(chuàng)建一個外星人并將其加入當(dāng)前行
        alien = Alien(ai_settings, screen)
        alien.x = alien_width + 2 * alien_width * alien_number
        alien.rect.x = alien.x
        aliens.add(alien)

新增運行游戲即可以看到一行外星人了

重構(gòu)create_fleet()

創(chuàng)建兩個新函數(shù)

game_functions.py

def create_fleet(ai_settings, screen, aliens):
    """創(chuàng)建外星人艦隊"""

    # 創(chuàng)建一個外星人 并計算一行容納多少人外星人
    # 外星人間距為外星人寬度
    alien = Alien(ai_settings, screen)
    alien_width = alien.rect.width
    number_aliens_x = get_number_aliens_x(ai_settings, alien_width)

    # 創(chuàng)建一行外星人
    for alien_number in range(number_aliens_x):
        # 創(chuàng)建一個外星人并將其加入當(dāng)前行
        create_alien(ai_settings, screen, aliens, alien_number)


def get_number_aliens_x(ai_settings, alien_width):
    """計算每行容納多少外星人"""
    available_space_x = ai_settings.screen_width - 2 * alien_width
    number_aliens_x = int(available_space_x / (2 * alien_width))
    return number_aliens_x

def create_alien(ai_settings, screen, aliens, alien_number):
    """創(chuàng)建一個外星人并將其放在當(dāng)前行"""
    alien = Alien(ai_settings, screen)
    alien_width = alien.rect.width
    alien.x = alien_width + 2 * alien_width * alien_number
    alien.rect.x = alien.x
    aliens.add(alien)
添加行

計算可以容納外形人的有限高度是多少, 垂直空間,屏幕高度減去第一行外星人的上邊距,飛船高度和最初外星人高度加上外星人間距

屏幕高度 - 兩處留白的位置 - 第一行外星人 - 飛船

available_space_y = ai_settings.screen_height - (3 * alien_height) - ship_height

可容納多少行

number_rows = available_space_y / (2 * alien_height)

game_functions.py

def create_fleet(ai_settings, screen, ship, aliens):
    """創(chuàng)建外星人艦隊"""

    # 創(chuàng)建一個外星人 并計算一行容納多少人外星人
    # 外星人間距為外星人寬度
    alien = Alien(ai_settings, screen)
    alien_width = alien.rect.width
    number_aliens_x = get_number_aliens_x(ai_settings, alien_width)
    number_rows = get_number_rows(ai_settings, ship.rect.height, alien.rect.height)

    # 創(chuàng)建一群外星人
    for number_row in range(number_rows):
        for alien_number in range(number_aliens_x):
            # 創(chuàng)建一個外星人并將其加入當(dāng)前行
            create_alien(ai_settings, screen, aliens, alien_number, number_row)


def get_number_aliens_x(ai_settings, alien_width):
    """計算每行容納多少外星人"""
    available_space_x = ai_settings.screen_width - 2 * alien_width
    number_aliens_x = int(available_space_x / (2 * alien_width))
    return number_aliens_x


def create_alien(ai_settings, screen, aliens, alien_number, row_number):
    """創(chuàng)建一個外星人并將其放在當(dāng)前行"""
    alien = Alien(ai_settings, screen)
    alien_width = alien.rect.width
    alien.x = alien_width + 2 * alien_width * alien_number
    alien.rect.x = alien.x
    alien.rect.y = alien.rect.height + 2 * alien.rect.height * row_number
    aliens.add(alien)


def get_number_rows(ai_settings, ship_height, alien_height):
    """計算屏幕可容納多少行外星人"""
    # 屏幕高度 - 兩處留白的位置 - 第一行外星人 - 飛船
    available_space_y = ai_settings.screen_height - (3 * alien_height) - ship_height
    number_rows = int(available_space_y / (2 * alien_height))
    return number_rows

創(chuàng)建多行時,需要兩個嵌套的循環(huán),內(nèi)部循環(huán)場景一行外星人,外部循環(huán)從零數(shù)到要創(chuàng)建的行數(shù)

調(diào)用方法

# 創(chuàng)建外星人群
gf.create_fleet(ai_settings, screen, ship, aliens)

現(xiàn)在運行游戲變可以看到外星人。

也可以使用隨機數(shù)來創(chuàng)建

# 隨機創(chuàng)建
random_number = randint(0, 10)
if alien_number == random_number or number_row == random_number:
    continue

讓外星人群移動

讓外星人在屏幕上向右移動,撞到屏幕邊緣后下移一定距離,再沿相反的方向移動。

向右移動
 # 開始游戲的主循環(huán)
    while True:
        # 監(jiān)視鍵盤和鼠標(biāo)事件
        gf.check_event(ai_settings, screen, ship, bullets)
        ship.update()
        gf.update_bullets(bullets)
        gf.update_aliens(aliens)
        gf.update_screen(ai_settings, screen, ship, aliens, bullets)

game_functions.py


def update_aliens(aliens):
    """更新外星人群中的所有外星人位置"""
    aliens.update()

alien.py

def update(self):
    """向右移動外星人"""
    self.x += self.ai_settings.alien_speed_factor
    self.rect.x = self.x

settings.py

# 外星人設(shè)置
self.alien_speed_factor = 1
移動方向控制
# 外星人設(shè)置
self.alien_speed_factor = 1
self.fleet_drop_speed = 10
# fleet_direction 為1表示向右移 -1表示向左移動
self.fleet_direction = 1

由于是兩個方向,使用-1或者1就不會影響兩個方向切換

檢查是否撞到了屏幕邊緣
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.ai_settings.alien_speed_factor * self.ai_settings.fleet.direction)
    self.rect.x = self.x
向下移動并改變方向

調(diào)用change_fleet_direction后,跳出循環(huán),不然會一直向下

def update_aliens(ai_settings, aliens):
    """
    檢查是否有外星人位于屏幕邊緣
    更新外星人群中的所有外星人位置
    """
    check_fleet_edges(ai_settings, aliens)
    aliens.update()


def check_fleet_edges(ai_settings, aliens):
    """有外星人到達邊緣時采取相應(yīng)的措施"""
    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

類似的可以做成雨點,下雨天

射殺外星人

sprite.groupcollide() 將每顆子彈的rect同每個外星人rect進行比較,并返回一個字典,包含碰撞的外星人和子彈,相應(yīng)的值都是被擊中的外星人, 兩個True是刪除響應(yīng)的元素,如果是高能子彈的話 第一個參數(shù)可以為false不用刪除。

# 檢查是否有子彈擊中外星人 如果是這樣的 刪除相應(yīng)的子彈和外星人
collisions = pygame.sprite.groupcollide(bullets, aliens, True, True)

可以改變子彈的寬度來提高射殺率

self.bullet_width = 300
生成新的外星人
if len(aliens) == 0:
    # 刪除現(xiàn)有的子彈并新建一群外星人
    bullet.empty()
    create_fleet(ai_settings, screen, ship, aliens)

提交子彈的速度,厚禮等

重構(gòu)update_bullets()

結(jié)束游戲

增加趣味性

檢測外星人和飛船碰撞
def update_aliens(ai_settings, ship, aliens):
    """
    檢查是否有外星人位于屏幕邊緣
    更新外星人群中的所有外星人位置
    """
    ...

    # 檢測外形人和飛船之間碰撞
    if pygame.sprite.spritecollideany(ship, aliens):
        print("--------------- ship hit!!! ------------------")

響應(yīng)外星人和飛船碰撞

碰撞時,不銷毀ship實例并創(chuàng)建一個新的ship,通過追蹤游戲統(tǒng)計信息來記錄飛船被撞的次數(shù),便于后續(xù)記分

新建GameStats 用于跟蹤游戲統(tǒng)計信息的類

game_stats.py

class GameStats():
    """跟蹤游戲的統(tǒng)計信息"""

    def __init__(self, ai_settings):
        """初始化統(tǒng)計信息"""
        self.ai_settings = ai_settings
        self.reset_stats()

    def reset_stats(self):
        """初始化在游戲運行期間可能變化信息統(tǒng)計"""
        self.ships_left = self.ai_settings.ship_limit
# 創(chuàng)建一個用于儲存游戲統(tǒng)計信息的實例
stats = GameStats(ai_settings)

# 開始游戲的主循環(huán)
while True:
    # 監(jiān)視鍵盤和鼠標(biāo)事件
  
    gf.update_aliens(ai_settings, ship, aliens, stats)

響應(yīng)被外星人撞到的飛船

撞擊之后清空子彈和外星人, 創(chuàng)建新的外星人,把飛船放在屏幕中間,暫停1s

game_functions.py

def ship_hit(ai_settings, stats, screen, ship, aliens, bullets):
    """響應(yīng)被外星人撞到的飛船"""
    # 將ships_left減1
    stats.ships_left -= 1

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

    # 創(chuàng)建一群新的外星人 并將飛船放到屏幕底端中央
    create_fleet(ai_settings, screen, ship, aliens)
    ship.center_ship()

    # 暫停
    sleep(1)
def update_aliens(ai_settings, stats, screen, ship, aliens, bullets):
    """
    檢查是否有外星人位于屏幕邊緣
    更新外星人群中的所有外星人位置
    """
    check_fleet_edges(ai_settings, aliens)
    aliens.update()

    # 檢測外形人和飛船之間碰撞
    if pygame.sprite.spritecollideany(ship, aliens):
        print("--------------- ship hit!!! ------------------")
        ship_hit(ai_settings, stats, screen, ship, aliens, bullets)

ship.py

def center_ship(self):
    """讓飛船在屏幕上居中"""
    self.center = self.screen_rect.centerx

有外星人到達屏幕底端

也做出與外星人撞擊屏幕相同的響應(yīng)

def check_aliens_bottom(ai_settings, stats, screen, ship, aliens, bullets):
    """檢查是否有外星人到達屏幕底端"""
    screen_rect = screen.get_rect()
    for alien in aliens.sprites():
        if alien.rect.bottom >= screen_rect.bottom:
            """像飛船撞擊一樣處理"""
            ship_hit(ai_settings, stats, screen, ship, aliens, bullets)
游戲結(jié)束

給定一個游戲狀態(tài),飛船用完后結(jié)束游戲

game_stats.py

class GameStats():
    """跟蹤游戲的統(tǒng)計信息"""

    def __init__(self, ai_settings):
        """初始化統(tǒng)計信息"""
        # 游戲啟動處于活動狀態(tài)
        self.game_active = True

game_function.py

def ship_hit(ai_settings, stats, screen, ship, aliens, bullets):
    """響應(yīng)被外星人撞到的飛船"""
    if stats.ships_left > 0:
        # 將ships_left減1
        stats.ships_left -= 1
        ...
        # 暫停
        sleep(1)
    else:
        # game over
        stats.game_active = False

alien_invasion.py

    # 開始游戲的主循環(huán)
    while True:
        # 監(jiān)視鍵盤和鼠標(biāo)事件
        gf.check_event(ai_settings, screen, ship, bullets)

        if stats.game_active:
            ship.update()
            gf.update_bullets(ai_settings, screen, ship, bullets, aliens)
            gf.update_aliens(ai_settings, stats, screen, ship, aliens, bullets)

        gf.update_screen(ai_settings, screen, ship, aliens, bullets)


run_game()

當(dāng)前游戲主體已經(jīng)完成了,只是確認(rèn)積分系統(tǒng),游戲關(guān)卡,交互性比較好的UI。

也是托著連續(xù)失眠疲憊身體,堅持著,也盼望著可以看到我們所期待有意義的樣子

2022/09/22 chengdu work in home!

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容