項目1 外星人入侵 第12章(武裝飛船)


  終於,《Python編程從入門到實踐》看到了實踐的部分,有三個項目,第一個是外星人入侵(12~14章)

  在系統中新建一個文件夾,並將其命名為alien_invasion。這樣相關的import語句才能正確地工作。

12.1 規划項目

  在游戲《外星人入侵》中,玩家控制着一艘最初出現在屏幕底部中央的飛船。玩家可以用箭頭鍵左右移動飛船,還可以使用空格鍵進行射擊。游戲開始時,一群外星人出現在天空中,他們在屏幕中向下移動。玩家的任務是射殺這些外星人。玩家將所有外星人消滅干凈之后,將會出現新的外星人,他們移動的速度更快。只要有外星人撞到了玩家的飛船或達到了屏幕的地步,玩家就損失一艘飛船。玩家損失三艘飛船之后,游戲結束。

  

  在游戲的開發階段,我們將創建一艘可以左右移動的飛船,這艘飛船在用戶按空格鍵時能夠開火。設置好這種行為后,我們就能夠將注意力轉向外星人,並提高這個游戲的可玩性。

12.2 安裝Pygame

  筆者使用的是Windows系統,首先檢查自己的python版本:

  再按照書上給出的鏈接:https://www.lfd.uci.edu/~gohlke/pythonlibs/#pygame,找到與運行的Python版本匹配的Windows安裝程序。

  點擊下載,下載合適的文件后,將它復制到你的項目文件夾中。然后,選中你的項目文件夾按shift鍵右擊選擇在此處打開命令窗口,接着安裝。

  其實也不太懂這個命令,就是照着書上敲,不知道為啥最后安裝成功了,但是安裝在了

不一樣的目錄下,這里我就偷個懶也不探究了,直接到這個目錄下面去找了,然后直接把文件夾拷貝到了項目文件夾下。這樣后面import語句才能正常運行。

 12.3 開始游戲項目

   首先創建一個空的Pygame窗口,供后面用來繪制游戲元素,如飛船和外星人。我們還將讓這個游戲響應用戶輸入、設置背景顏色以及加載飛船圖像。

12.3.1 創建Pygame 窗口以及響應用戶輸入

  首先創建一個空的Pygame窗口。使用Pygame編寫的游戲的基本結構如下:

import sys
import pygame

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

    # 開始游戲的主循環
    while True:

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

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

run_game()

  pygame.init()初始化背景設置

  pygame.display.set_mode()創建一個名為screen的顯示窗口

  實參(1200,720)是一個元組,制定了游戲窗口的尺寸。

  對象screen是一個surface,surface是屏幕的一部分,用於顯示游戲元素。在這個游戲中,每個元素都是一個surface。每經過一次循環都將自動重繪這個surface。

  while循環中包含一個事件循環以及管理屏幕更新的代碼。  

  事件是用戶玩游戲時執行的操作,如按鍵或移動鼠標。為讓程序響應事件,我們編寫一個事件循環,以偵聽事件,並根據發生的事件執行相應的任務,for循環就是一個事件循環。在這個循環中,編寫一系列if語句來檢測並2特定的事件。例如,玩家單擊游戲窗口的關閉按鈕時,將檢測到pygame.QUIT事件,再調用sys.exit()來退出游戲。

  pygame.display.flip()將不斷更新屏幕,以顯示元素的新位置,並在原來的位置隱藏元素,從而營造平滑移動的效果。

12.3.2 設置背景色

import sys
import pygame

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

    # 設置背景色
    bg_color = (230,230,230)

    # 開始游戲的主循環
    while True:

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

        # 每次循環時都會重繪屏幕
        screen.fill(bg_color)

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

run_game()

  在Pygame中,顏色是以RGB值指定的。這種顏色由紅色、綠色和藍色組成,其中每個值的可能取值范圍都為0~255。顏色值為(255,0,0)表示紅色,(0,255,0)表示綠色,而(0,0,255)表示藍色。在顏色值(230,230,230)中,紅色、藍色綠色量相同,它將背景設置為一種淺灰色。

調用screen.fill(),用背景顏色填充屏幕,這個方法只接受一個實參:一種顏色。

12.3.3 創建設置類

  下面編寫一個名為settings的模塊,其中包含一個名為Settings的類,用於將所有設置存儲在一個地方,一面代碼中到處添加設置。

  下面是最初的Settings類:

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

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

  為創建Settings實例並使用它來訪問設置,將alien_invasion.py修改成下面這樣:

import sys
import pygame

from settings import Settings

def run_game():
    # 初始化pygame、設置和屏幕對象
 pygame.init()
    ai_settings = Settings()
    screen = pygame.display.set_mode(
        (ai_settings.screen_width,ai_settings.screen_height))
    pygame.display.set_caption("Alien Invasion")

    # 開始游戲的主循環
    while True:

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

        # 每次循環時都會重繪屏幕
        screen.fill(ai_settings.bg_color)

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

run_game()

12.4 添加飛船圖像

  在主項目文件夾(alien_invasion)中創建一個文件夾,將其命名為images,並將文件ship.bgm保存到這個文件夾中。

12.4.1 創建Ship類

   創建一個ship模塊,其中包含Ship類,它負責管理飛船大部分行為。

import pygame

class Ship():

    def __init__(self,screen):
        """初始化飛船並設置其初始位置"""
        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)

  調用 pygame.image.load()加載圖像,這個函數返回一個表示飛船的surface,而我們將這個surface存儲到了self.image中。

  調用get_rect()獲取相應的surface屬性rect,pygame的效率之所以如此高,一個原因是它讓你能夠像處理矩形(rect對象)一樣處理游戲元素,即便它們的形狀並非矩形。

處理rect對象時,可使用矩形死角和中心的x和y坐標。可通過設置這些值來指定矩形的位置。

  要將游戲元素居中,可設置相應rect對象的屬性center、centerx或centery。要讓游戲元素與屏幕邊緣對齊,可使用屬性top、bootom、left或right;要調整游戲元素的水平或垂直位置,可使用屬性x和y。原點(0,0)位於屏幕左上角,右下角坐標為(1200,720)

  首先將表示屏幕的矩形存儲在self.screen_rect中,再將self.rect.centerx(飛船中心的x坐標)設置為表示屏幕的矩形屬性centerx。並將self.rect.bottom(飛船下邊緣的y坐標)設置為表示屏幕的矩形的屬性bottom。

12.4.2 在屏幕上繪制飛船

  下面來更新alien_invasion.py,使其創建一艘飛船。

import sys
import pygame

from settings import Settings
from ship import Ship

def run_game():
    # 初始化pygame、設置和屏幕對象
    pygame.init()
    ai_settings = Settings()
    screen = pygame.display.set_mode(
        (ai_settings.screen_width,ai_settings.screen_height))
    pygame.display.set_caption("Alien Invasion")

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

    # 開始游戲的主循環
    while True:

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

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

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

run_game()

  導入Ship類,並在創建屏幕后創建一個名為ship的Ship實例。必須在while循環前面創建該實例,以免每次循環時創建一艘飛船。

12.5 重構:模塊game_functions

  在本節中,我們將創建一個名為game_functions的新模塊,它將存儲大量讓游戲《外星人入侵》運行的函數。

12.5.1 函數 check_events()

  我們首先把管理實踐的代碼移到一個名為check_events()的函數中,以簡化run_game()並隔離實踐管理循環。

將check_events()放在一個名為game_functions()的模塊中:

import sys

import pygame

def check_events():
    """響應按鍵和鼠標事件"""
    for event in pygame.event.get():
        if event.type() == pygame.QUIT:
            sys.exit()

  下面來修改alien_invasion.py,使其導入模塊game_functions,並將實踐循環替換為對函數check_events()的調用:

import pygame

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

def run_game():
    # 初始化pygame、設置和屏幕對象
    pygame.init()
    ai_settings = Settings()
    screen = pygame.display.set_mode(
        (ai_settings.screen_width,ai_settings.screen_height))
    pygame.display.set_caption("Alien Invasion")

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

    # 開始游戲的主循環
    while True:
        gf.check_events()

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

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

run_game()

12.5.2 函數update_screen()

  為進一步簡化run_game(),下面將更新屏幕的代碼移到一個名為update_screen()的函數中,並將這個函數放在模塊game_funtions.py中:

import sys

import pygame

def check_events():
    """響應按鍵和鼠標事件"""
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            sys.exit()
def update_screen(ai_settings,screen,ship):
    """更新屏幕上的圖像,並切換到新屏幕上"""
    # 每次循環時都會重繪屏幕
    screen.fill(ai_settings.bg_color)
    ship.blitme()

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

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

def run_game():
    # 初始化pygame、設置和屏幕對象
    pygame.init()
    ai_settings = Settings()
    screen = pygame.display.set_mode(
        (ai_settings.screen_width,ai_settings.screen_height))
    pygame.display.set_caption("Alien Invasion")

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

    # 開始游戲的主循環
    while True: gf.check_events()
        gf.update_screen(ai_settings,screen,ship)

 run_game()

   p216 12-2動手試一試 將圖像的背景色設置為屏幕的顏色,我上網查了資料,,,,不會,,,然后就用了ps。。。終於實現了圖像背景色和屏幕背景色一樣的藍色。

12.6 駕駛飛船

12.6.1 相應按鍵

  每當用戶按鍵時,都會將在Pygame中注冊一個事件。事件都是通過方法pygame.event.get()獲取的,因此在函數check_events()中,我們需要指定檢查哪些類型的事件。每次按鍵都被注冊為一個KEYDOWN事件。

  檢測到KEYDOWN事件時,我們需要檢查按下的是否是特定的鍵。例如,按下的是右箭頭鍵,我們就增大飛船的rect.centrex值,將飛船向右移動。

  修改game_functions.py:

import sys

import pygame

def check_events(ship):
    """響應按鍵和鼠標事件"""
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            sys.exit()

        elif event.type == pygame.KEYDOWN:
            if event.key == pygame.K_RIGHT:
                # 向右移動飛船
                ship.rect.centerx +=1
def update_screen(ai_settings,screen,ship):
    """更新屏幕上的圖像,並切換到新屏幕上"""
    # 每次循環時都會重繪屏幕
    screen.fill(ai_settings.bg_color)
    ship.blitme()

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

   讀取屬性event.key,以檢查按下的是否是右箭頭鍵(pygame.K.RIGHT)。在alien_invasion.py中,我們需要更新調用check_events()代碼,將ship作為實參傳遞它:

# 開始游戲的主循環
    while True:
        gf.check_events(ship)
        gf.update_screen(ai_settings,screen,ship)

12.6.2 允許不斷移動

  玩家按住右箭頭鍵不放時,我們希望飛船不斷向右移動,直到玩家松開為止。我們將讓游戲檢測到pygame.KEYUP事件,以便玩家松開右箭頭鍵時我們能夠直到這一點;然后,我們將結合使用KEYDOWN和KEYUP事件,以及一個名為moving_right的標志來實現持續移動。

  飛船不動時,標志moving_right為False。玩家按下右箭頭鍵,我們將這個標志設置為True;而玩家松開,我們將這個表中重新設置為Flase。

  飛船的屬性都由Ship控制,因此我們將給這個類添加一個名為moving_right的屬性和一個名為update()的方法。方法update()檢查標志moving_right的狀態,如果這個標志為True,就調整飛船的位置。

  下面對Ship類做修改:

import pygame

class Ship():

    def __init__(self,screen):
        """初始化飛船並設置其初始位置"""
        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

        # 移動標志
        self.moving_right = False

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

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

  下面來修改check_events():

def check_events(ship):
    """響應按鍵和鼠標事件"""
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            sys.exit()

        elif event.type == pygame.KEYDOWN:
            if event.key == pygame.K_RIGHT:
                ship.moving_right =True
                
        elif event.type == pygame.KEYUP:
            if event.key == pygame.K_RIGHT:
                ship.moving_right =False

  添加了一個新的elif代碼塊,用於響應KEYUP事件:玩家松開右箭頭鍵(K_RIGHT)時,我們將moving_right設置為Flase。

  最后,我們需要修改alien_invasion.py中的while循環,以便每次執行循環都調用飛船的方法update():

    while True: gf.check_events(ship)
        ship.update()
       gf.update_screen(ai_settings,screen,ship)

  飛船的位置將在檢測到鍵盤事件后(但在更新屏幕之前)更新。這樣,玩家輸入時,飛船的位置將更新,從而確保使用更新后的位置將飛船繪制到屏幕上。

12.6.3 左右移動

  根據向右移動,來寫向左移動。

12.6.4 調整飛船的速度

  我們可以在Settings類中添加屬性ship_speed_factor,用於控制飛船的速度。下面演示了在settings.py中添加屬性:

 def_init_(self):
    --ship--
   
    # 飛船的位置
     self.ship_speed_factor = 1.5        

  通過將速度設置指定為小數值,可在后面加快游戲的節奏時更細致地控制飛船的速度。然而,rect的centerx等屬性只能存儲整數值,因此我們需要對Ship做一些修改:

import pygame

class Ship():

    def __init__(self,ai_settings,screen):
        """初始化飛船並設置其初始位置"""
        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 = screen.get_rect()

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

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

        # 移動標志
        self.moving_right = False
        self.moving_left = False

    def update(self):
        """根據移動標志調整飛船的位置"""
        # 更新飛船的center值,而不是rect
        if self.moving_right:
            self.center += self.ai_settings.ship_speed_factor
        if self.moving_left:
            self.center -= self.ai_settings.ship_speed_factor

        # 根據self.center更新rect對象
        self.rect.centerx = self.center

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

  在alien_invasion.py中創建Ship實例,需要傳入實參ai_settings:

12.6.5 限制飛船的活動范圍

  當前,如果玩家按住箭頭鍵的時間足夠長,飛船將移到屏幕外面。下面來修復這種問題,讓飛船到達屏幕邊緣后停止移動。為此我們將修改Ship類的方法update():

  def update(self):
        """根據移動標志調整飛船的位置"""
        # 更新飛船的center值,而不是rect
        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

 

 12.6.6 重構 check_events()

  隨着游戲開發的進行,函數check_events()將越來越長,我們將其部分代碼放在兩個函數中:

  一個用來處理KEYSOWN事件,另一個處理KEYUP事件:

  game_functions.py

import sys

import pygame

def check_keydown_events(event,ship):
    """響應按鍵"""
    if event.key == pygame.K_RIGHT:
        ship.moving_right = True
    if event.key == pygame.K_LEFT:
        ship.moving_left = True

def check_keyup_events(event,ship):
    if event.key == pygame.K_RIGHT:
        ship.moving_right = False
    if event.key == pygame.K_LEFT:
        ship.moving_left = False

def check_events(ship):
    """響應按鍵和鼠標事件"""
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            sys.exit()
        elif event.type == pygame.KEYDOWN:
            check_keydown_events(event,ship)
        elif event.type == pygame.KEYUP:
           check_keyup_events(event,ship)

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

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

 

12.7 簡單回顧

12.7.1 alien_invasion.py

  主文件alien_invasion.py創建一系列整個游戲都要用到的對象:存儲ai_settings中的設置、存儲在screen再的主顯示surface以及一個飛船實例。文件alien_invasion.py還包含游戲的朱迅,這是一個調用check_events()、ship.update()和uodate_screen()的while循環。

12.7.2 settings.py

  初始化控制游戲外觀和飛船的速度屬性

12.7.3 game_function.py

  管理飛船的移動,屏幕的重繪

12.7.4 ship.py

  管理飛船位置的方法update()以及在屏幕上繪制飛船的方法blitme()。表示飛船的圖像存儲位置。

12.8 射擊

  下面來添加射擊功能。我們將編寫玩家按空格鍵時發射子彈(小矩形)的代碼。子彈將在屏幕中向上穿行,抵達屏幕上邊緣消失。

12.8.1 添加子彈設置

  首先,更新settings.py,在其方法_init_()末尾存儲新類Bullet所需的值。

        # 子彈設置
        self.bullet_speed_factor = 1
        self.bullet_width = 3
        self.bullet_height =15
        self.bullet_color = 60,60,60

12.8.2 創建Bullet類

   下面創建存儲Bullet類的文件bullet.py,其前半部分如下:

import pygame
from pygame.sprite import Sprite

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

    def __init__(self,ai_settings,screen,ship):
        """在飛船所處的位置創建一個子彈對象"""
        super().__init__()
        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
        self.rect.top = ship.rect.top
        
        # 存儲用小數表示的子彈位置
        self.y = float(self.rect.y)
        
        self.color = ai_settings.bullet_color
        self.speed_factor = ai_settings.bullet_speed_factor
        

  Bullet類繼承了我們從模塊pygame.sprite中導入的Sprite類。通過使用精靈,可將游戲中相關的元素編組,而進而同時操作編組中的所有元素。為創建子彈實例,需要向_init_()傳遞ai_settings、screen和ship實例,還調用了super()來繼承Sprite。

  創建了子彈的屬性rect,子彈並非基於圖像的,因此我們必須使用 pygame.Rect()類從空白開始創建一個矩形。創建這個類的實例,必須提供矩形左上角的x坐標和y坐標,還有矩形的寬度和高度。我們在(0,0)出創建這個矩形,但接下來的兩行代碼將其移到了正確的位置,因為子彈的初始位置取決於飛船當前的位置。子彈的寬度和高度是從ai_settings中獲取的。

  子彈的conterx就是飛船的rect.conterx。將子彈的y坐標存儲為小數值,以便能夠微調子彈的速度。

  下面是bulet.py的第二部分——方法update()和draw_bullet():

    def update(self):
        """向上移動子彈"""
        # 更新表示子彈位置的小數值
        self.y -= self.speed_factor
        # 更新表示子彈的rect的位置
        self.rect.y = self.y
    
    def draw_bullet(self):
        """在屏幕上繪制子彈"""
        pygame.draw.rect(self.screen,self.color,self.rect)

  方法update()管理子彈的位置,y坐標不斷減小,x坐標始終不變。

12.8.3 將子彈存儲到編組中

  在玩家每次按空格鍵時都會發射出一發子彈。首先,我們將alien_invasion.py中創建一個編組(group),用於存儲所有有效的子彈,以便能夠管理發出去的所有子彈。這個編組將是pygame.sprite.Group類的一個實例;pygame.sprite.Group類類似於一個列表,但提供了有助於開發游戲的額外功能。在主循環中,我們將使用這個編組在屏幕上繪制子彈,以及更新每顆子彈的位置:

import pygame
from pygame.sprite import Group
from settings import Settings
from ship import Ship
import game_functions as gf

def run_game():
    # 初始化pygame、設置和屏幕對象
    pygame.init()
    ai_settings = Settings()
    screen = pygame.display.set_mode(
        (ai_settings.screen_width,ai_settings.screen_height))
    pygame.display.set_caption("Alien Invasion")

    # 創建一艘飛船
    ship = Ship(ai_settings,screen)
    # 創建一個用於存儲子彈的編組
    bullets = Group()

    # 開始游戲的主循環
    while True:
        gf.check_events(ai_settings,screen,ship,bullets)
        ship.update()
        bullets.update()
        gf.update_screen(ai_settings,screen,ship,bullets)


run_game()

  在check_events()中,需要在玩家按空格鍵時處理bullets;而在update_screen()中,需要更新要繪制到屏幕上的bullets。當你對編組調用update()時,編組將自動對其中的每個精靈調用update(),因此代碼行bullets.update()將為編組bullets中的每顆子彈調用bullet.update()。

12.8.4 開火

  在game_function.py中,我們需要修改check_keydown_events(),以便在玩家按空格鍵時發射一顆子彈。我們無需修改check_keyup_events(),因為玩家松開空格鍵時什么也不會發生。我們還需要修改update_screen(),確保在調用flip()前在屏幕上重繪每一顆子彈。

import sys
import pygame
from bullet import Bullet

def check_keydown_events(event,ai_settings,screen,ship,bullets):
    """響應按鍵"""
    if event.key == pygame.K_RIGHT:
        ship.moving_right = True
    elif event.key == pygame.K_LEFT:
        ship.moving_left = True
    elif event.key ==pygame.K_SPACE:
        # 創建一顆子彈,並將其加入到編組bullets中
        new_bullet = Bullet(ai_settings,screen,ship)
        bullets.add(new_bullet)

def check_keyup_events(event,ship):
    if event.key == pygame.K_RIGHT:
        ship.moving_right = False
    if event.key == pygame.K_LEFT:
        ship.moving_left = False

def check_events(ai_settings,screen,ship,bullets):
    """響應按鍵和鼠標事件"""
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            sys.exit()
        elif event.type == pygame.KEYDOWN:
            check_keydown_events(event,ai_settings,screen,ship,bullets)
        elif event.type == pygame.KEYUP:
           check_keyup_events(event,ship)

def update_screen(ai_settings,screen,ship,bullets):
    """更新屏幕上的圖像,並切換到新屏幕上"""
    # 每次循環時都會重繪屏幕
    screen.fill(ai_settings.bg_color)
    # 在飛船和外星人后面會重繪所有子彈
    for bullet in bullets.sprites():
        bullet.draw_bullet()
    ship.blitme() # 讓最近繪制的屏幕可見
    pygame.display.flip()

  編組bullets傳遞給了check_keydown_events()。玩家按空格鍵時,創建一顆新子彈(一個名為new_bullet的Bullet實例),並使用方法add()加入到編組中。

12.8.5 刪除已消失的子彈

  當前,子彈並未刪除,知識屏幕外無法繪制。它們繼續消耗內存和處理能力。我們需要檢測這樣的條件,即表示子彈的rect的bottom屬性為零,它表示子彈已穿過屏幕頂端:

import pygame
from pygame.sprite import Group
from settings import Settings
from ship import Ship
import game_functions as gf

def run_game():
    # 初始化pygame、設置和屏幕對象
    pygame.init()
    ai_settings = Settings()
    screen = pygame.display.set_mode(
        (ai_settings.screen_width,ai_settings.screen_height))
    pygame.display.set_caption("Alien Invasion")

    # 創建一艘飛船
    ship = Ship(ai_settings,screen)
    # 創建一個用於存儲子彈的編組
    bullets = Group()

    # 開始游戲的主循環
    while True:
        gf.check_events(ai_settings,screen,ship,bullets)
        ship.update()
        bullets.update()
        
        # 刪除已消失的子彈
        for bullet in bullets.copy():
            if bullet.rect.bottom <= 0:
                bullets.remove(bullet)
        print(len(bullets))
        
        gf.update_screen(ai_settings,screen,ship,bullets) run_game()

  在for循環中,不應從列表或編組中刪除條目,因此必須遍歷編組的副本。

  如果這些代碼沒有問題,我們發射子彈后查看終端窗口時,將發現隨着子彈一顆顆從屏幕頂端消失,子彈數逐漸降為0。運行這個游戲並確認子彈已被刪除后,就講這條print語句刪掉。

12.8.6 限制子彈數量

  首先,在settings.py中存儲所允許的最大子彈數:

        # 子彈設置
        self.bullet_speed_factor = 1
        self.bullet_width = 3
        self.bullet_height =15
        self.bullet_color = 60,60,60
        self.bullet_allowed = 3

  這將未消失的子彈數量限制為3顆,在game_function.py的check_keydown_events()中,我們在創建新子彈前檢查未消失的子彈數是否小於該設置:

    elif event.key ==pygame.K_SPACE:
        # 創建一顆子彈,並將其加入到編組bullets中
        if len(bullets) < ai_settings.bullets_allowed:
            new_bullet = Bullet(ai_settings,screen,ship)
            bullets.add(new_bullet)

12.8.7 創建函數 update_bullets()

  編寫並檢查子彈管理代碼之后,可將其移到模塊game_functions中,讓主程序盡可能簡單。

def update_bullets(bullets):
    """更新子彈的位置,並刪除已消失的子彈"""
    # 更新子彈的位置
    bullets.update()
    # 刪除已消失的子彈
    for bullet in bullets.copy():
        if bullet.rect.bottom <= 0:
            bullets.remove(bullet)

  alien_invasion.py

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

12.8.8 創建函數fire_bullet()

  下面將發射子彈的代碼移到一個獨立的函數中。

import sys
import pygame
from bullet import Bullet

def check_keydown_events(event,ai_settings,screen,ship,bullets):
    """響應按鍵"""
    if event.key == pygame.K_RIGHT:
        ship.moving_right = True
    elif event.key == pygame.K_LEFT:
        ship.moving_left = True
    elif event.key ==pygame.K_SPACE:
       fire_bullet(ai_settings,screen,ship,bullets)
def fire_bullet(ai_settings,screen,ship,bullets):
    """如果還沒有達到限制,就發射一顆子彈"""
    # 創建一顆子彈,並將其加入到編組bullets中
    if len(bullets) < ai_settings.bullet_allowed:
        new_bullet = Bullet(ai_settings, screen, ship)
        bullets.add(new_bullet)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM