Python專案實踐之一:武裝飛船
一、規劃專案
1、游戲規則設定
在游戲《外星人入侵》中,玩家控制著一艘最初出現在螢屏底部中央的飛船,玩家可以使用箭頭鍵左右移動飛船,還可使用空格鍵進行射擊,游戲開始時,一群外星人出現在天空中,他們在螢屏中向下移動,玩家的任務是射殺這些外星人,玩家將所有外星人都消滅干凈后,將出現一群新的外星人,他們移動的速度更快,只要有外星人撞到了玩家的飛船或到達了螢屏底部,玩家就損失一艘飛船,玩家損失三艘飛船后,游戲結束,
2、前期準備
1、建立專案檔案
游戲《外星人入侵》將包含很多不同的檔案,因此請在你的系統中新建一個檔案夾,并將其命名為alien_invasion,請務必將這個專案的所有檔案都存盤到這個檔案夾中,這樣相關的 import 陳述句才能正確地作業,
2、安裝pip
1、安裝pip
安裝pip,請訪問https://bootstrap.pypa.io/get-pip.py,如果出現對話框,請選擇保存檔案;如果get-pip.py的代碼出現在瀏覽器中,請將這些代碼復制并粘貼到文本編輯器中,再將檔案保存為get-pip.py,將get-pip.py保存到計算機中后,你需要以管理員身份運行它,因為pip將在你的系統中安裝新包,
2、確認是否安裝了pip
在dos視窗輸入:pip --version

3、安裝Pygame
1、前往python官網https://www.python.org/,點擊PypI

2、在搜索框搜索pygame

3、選擇要下載的pygame版本

4、點擊下載檔案

5、下載自己電腦對應的版本

6、把下載好的檔案放入python的pip目錄下

7、在dos視窗輸入對檔案對應的指令
8、提示安裝成功
3、開始專案
1、創建 Pygame 視窗以及回應用戶輸入
import sys
import pygame
def run_game():
#初始化游戲并創建一個視窗物件
pygame.init()
#設定視窗大小為1200*800像素
screen = pygame.display.set_mode((1200, 700))
#設定視窗名字
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()
解釋:
1、匯入 sys 和 pygame模塊, pygame 模塊包含開發游戲所需的功能, 通過sys 模塊實作退出游戲功能,
2、創建一個run_game()函式,用來提供游戲所需要的視窗界面并對玩家鍵盤滑鼠進行監聽,
3、screen是屬于螢屏的一個面物件,用于顯示游戲元素(飛船、外星人),游戲元素也是一個面物件,我們通過while True的死回圈,實作對游戲元素在不同時間和狀態在游戲界面里的顯示,
2、設定背景顏色
import sys
import pygame
def run_game():
#初始化游戲并創建一個視窗物件
pygame.init()
#設定視窗大小為1200*800像素
screen = pygame.display.set_mode((1200, 700))
#設定視窗名字
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()
解釋:
1、創建一個背景顏色存于 bg_color 中,在Pygame中,顏色是以RGB值指定的,這種顏色由紅色、綠色和藍色值組成,其中每個值的可能取值范圍都為0~255,顏色值(255, 0, 0)表示紅色,(0, 255, 0)表示綠色,而(0, 0, 255)表示藍色,通過組合不同的RGB值,可創建1600萬種顏色,在顏色值(230, 230, 230)中,紅色、藍色和綠色量相同,它將背景設定為一種淺灰色,
2、呼叫方法 screen.fill() ,用背景色填充螢屏,
3、創建設定類
每次給游戲添加新功能時,通常也將引入一些新設定,下面來撰寫一個名為 settings 的模塊,其中包含一個名為 Settings 的類,用于將所有設定存盤在一個地方,以免在代碼中到處添加設定,這樣,我們就能傳遞一個設定物件,而不是眾多不同的設定,另外,這讓函式呼叫更簡單,且在專案增大時修改游戲的外觀更容易:要修改游戲,只需修改settings.py中的一些值,而無需查找散布在檔案中的不同設定,
settings.py:
class Settings():
"""存盤《外星人入侵》的所有設定的類"""
def __init__(self):
"""初始化游戲的設定"""
# 螢屏設定
self.screen_width = 1200
self.screen_height = 600
self.bg_color = (230, 230, 230)
alien_invasion.py:
import sys
import pygame
from settings import Settings
def run_game():
#初始化游戲并創建一個視窗物件
pygame.init()
#初始化設定物件
ai_settings = Settings()
#設定視窗大小為1200*800像素
screen = pygame.display.set_mode((ai_settings.screen_width, ai_settings.screen_height))
#設定視窗名字
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(ai_settings.bg_color)
#繪制視窗
pygame.display.flip()
run_game()
解釋:在主程式檔案中,我們匯入 Settings 類,呼叫 pygame.init() ,再創建一個 Settings 實體,并
將其存盤在變數 ai_settings 中,創建螢屏時,使用了 ai_settings 的屬性screen_width 和 screen_height ;接下來填充螢屏時,也使用了 ai_settings 來訪問背景色,
4、添加飛船影像
4.1、尋找一張飛船影像,使得影像背景處理為透明色,我找到的飛船影像如下:

4.2、創建Ship類
ship.py:
class Ship():
def __init__(self, screen):
"""初始化飛船并設定其初始位置"""
self.screen = screen
#加載飛船,并獲取其外形矩陣
self.imge = pygame.image.load(r'images/ship.png')
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)
解釋:
1、 pygame.image.load(path)方法:打開需要加載的影像,回傳值是一個面物件
2、 get_rect()方法: 獲取相應面物件的屬性(Pygame的效率之所以如此高,一個原因是它讓你能夠像處理矩形( rect 物件)一樣處理游戲元素,即便它們的形狀并非矩形,像處理矩形一樣處理游戲元素之所以高效,是因為矩形是簡單的幾何形狀,這種做法的效果通常很好,游戲玩家幾乎注意不到我們處理的不是游戲元素的實際形狀,)
3、 通過處理rect 物件相對于screen_rect物件的位置來規定飛船的初始化位置位于底部中間,即:讓rect 物件的centerx與screen_rect物件的centerx相等,rect 物件的bottom與screen_rect的bottom相等(要將游戲元素居中,可設定相應 rect 物件的屬性 center 、 centerx 或 centery ,要讓游戲元素與螢屏邊緣對齊,可使用屬性 top 、 bottom 、 left 或 right ;要調整游戲元素的水平或垂直位置,可使用屬性 x 和 y ,它們分別是相應矩形左上角的x和y坐標),
4、在Pygame中,原點(0, 0)位于螢屏左上角,向右下方移動時,坐標值將增大,在1200×800的螢屏上,原點位于左上角,而右下角的坐標為(1200, 800),如下圖:

4.3、在螢屏上繪制飛船
alien_invasion.py:
import sys
import pygame
from settings import Settings
from ship import Ship
def run_game():
#初始化游戲并創建一個視窗物件
pygame.init()
#初始化設定物件
ai_settings = Settings()
#設定視窗大小為1200*600像素
screen = pygame.display.set_mode((ai_settings.screen_width, ai_settings.screen_height))
#設定視窗名字
pygame.display.set_caption("Alien Invasion")
# 創建一艘飛船
ship = Ship(screen)
#設定背景顏色
bg_color = (230, 230, 230)
#開始游戲主回圈
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()
5、重構:模塊 game_functions
在大型專案中,經常需要在添加新代碼前重構既有代碼,重構旨在簡化既有代碼的結構,使其更容易擴展,在本節中,我們將創建一個名為 game_functions 的新模塊,它將存盤大量讓游戲《外星人入侵》運行的函式,通過創建模塊 game_functions ,可避免alien_invasion.py太長,并使其邏輯更容易理解,
5.1、管理事件
把管理事件的代碼移到一個名為 check_events() 的函式中,以簡化 run_game() 并隔離事件管理回圈,通過隔離事件回圈,可將事件管理與游戲的其他方面(如更新螢屏)分離,
5.2、更新螢屏
為進一步簡化 run_game() ,下面將更新螢屏的代碼移到一個名為 update_screen() 的函式中,
game_functions.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()
5.3將分離的功能添加到主函式中
alien_invasion.py:
import sys
import pygame
from settings import Settings
from ship import Ship
import game_functions as gf
def run_game():
#初始化游戲并創建一個視窗物件
pygame.init()
#初始化設定物件
ai_settings = Settings()
#設定視窗大小為1200*800像素
screen = pygame.display.set_mode((ai_settings.screen_width, ai_settings.screen_height))
#設定視窗名字
pygame.display.set_caption("Alien Invasion")
# 創建一艘飛船
ship = Ship(screen)
#設定背景顏色
bg_color = (230, 230, 230)
#開始游戲主回圈
while True:
#監聽鍵盤和滑鼠事件
gf.check_events()
#每次回圈重繪螢屏
gf.update_screen(ai_settings, screen, ship)
run_game()
6、駕駛飛船
6.1、回應按鍵
每當用戶按鍵時,都將在Pygame中注冊一個事件,事件都是通過方法 pygame.event.get() 獲取的,因此在函式 check_events() 中,我們需要指定要檢查哪些型別的事件,每次按鍵都被注冊為一個 KEYDOWN 事件,
game_ functions.py中的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.rect.centerx += 1
解釋:
1、在函式 check_events() 中添加了形參 ship ,因為按右箭頭鍵時,需要將飛船向右移動,飛船是操作物件,所以要添加這個形參,
2、KEYDOWN 事件:鍵盤按鍵監聽事件,每次按鍵都被注冊為一個 KEYDOWN 事件,
3、通過回圈不斷的監聽KEYDOWN 事件,當按下鍵盤右箭頭時,程式作出對應的回應,即:將 ship.rect.centerx 的值加1,從而將飛船向右移動,
6.2、允許不斷移動
實作按住右箭頭鍵不放時,飛船不斷地向右移動,直到松開為止,要實作這個功能,就必須同時監聽KEYDOWN 和 KEYUP事件,根據鍵盤不同的狀態,程式作出不同的回應,為此,我們還需要添加一個 moving_right的標志,它標志著飛船的狀態:True為運動/False為靜止,
由于 moving_right的標志是飛船的屬性,我們現在修改ship.py的內容:
import pygame
class Ship():
def __init__(self, screen):
"""初始化飛船并設定其初始位置"""
self.screen = screen
#加載飛船,并獲取其外形矩陣
self.image = pygame.image.load(r'images/ship.png')
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)
下面,我們只需要監聽鍵盤按鍵,對self.moving_right來進行修改就能完成飛船的不斷移動,修改game_functions.py中的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
最后,我們只需要修改 alien_invasion.py 中的 while 回圈,以便每次執行回圈時都呼叫飛船的方法 update() :
#開始游戲主回圈
while True:
#監聽鍵盤和滑鼠事件
gf.check_events(ship)
#呼叫飛船目前位置
ship.update()
#每次回圈重繪螢屏
gf.update_screen(ai_settings, screen, ship)
6.3、左右移動
現在飛船只能向右移動,那么向左移動的思路和向右移動的思路是一致的,只需要修改Ship類和game_functions.py中的check_events函式即可,
1、修改Ship類:
import pygame
class Ship():
def __init__(self, screen):
"""初始化飛船并設定其初始位置"""
self.screen = screen
#加載飛船,并獲取其外形矩陣
self.image = pygame.image.load(r'images/ship.png')
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
self.moving_left = False
def update(self):
"""根據移動標志調整飛船的位置"""
if self.moving_right:
self.rect.centerx += 1
if self.moving_left:
self.rect.centerx -= 1
def blitme(self):
"""在指定位置繪制飛船"""
self.screen.blit(self.image, self.rect)
2、修改game_functions.py中的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.key == pygame.K_LEFT:
ship.moving_left = True
elif event.type == pygame.KEYUP:
if event.key == pygame.K_RIGHT:
ship.moving_right = False
elif event.key == pygame.K_LEFT:
ship.moving_left = False
6.4、調整飛船的速度
目前飛船的移動速度是1像素,隨著游戲的進行,我們需要加快游戲節奏,所以需要設定飛船的速度為一個可變的值,而且要便于修改,所以我們可以在 Settings 類中添加屬性ship_speed_factor ,用于控制飛船的速度,
setting.py中的Settings 類:
class Settings():
"""存盤《外星人入侵》的所有設定的類"""
def __init__(self):
"""初始化游戲的設定"""
# 螢屏設定
self.screen_width = 1200
self.screen_height = 600
self.bg_color = (230, 230, 230)
# 飛船的設定
self.ship_speed_factor = 1.5
我們將飛船的默認速度設定成了1.5像素,由于飛船移動像素的大小是通過ship.py中的update函式控制的,但是rect 的 centerx 等屬性只能存盤整數值,所以我們需要自頂一個center變數來使得移動像素可以是浮點數,
ship.py中的Ship類:
import pygame
class Ship():
def __init__(self, ai_settings, screen):
"""初始化飛船并設定其初始位置"""
self.screen = screen
#獲取飛船目前的速度
self.ai_settings = ai_settings
#加載飛船,并獲取其外形矩陣
self.image = pygame.image.load(r'images/ship.png')
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):
"""根據移動標志調整飛船的位置"""
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)
解釋:
1、首先需要把形參ai_settings傳入ship的__init__中,這樣飛船在初始化的時候才能獲取到當前的速度設定
2、由于rect 的 centerx、center、centery屬性只能存盤整數值(如果是小數自動取整數部分),而我們設定的初始化速度是1.5像素(浮點數),所以我們要定義一個新的屬性center來存盤浮點數,通過center來控制rect的屬性,
3、現在在 update() 中調整飛船的位置時,將 self.center 的值增加或減去的值就可以是浮點數(如果不這么做,直接用centerx加上或減去1.5像素,那么實際移動還是1像素,原因是rect 的 centerx屬性自動取整數部分的值),
4、最后將移動后的center的值傳給rect 的 centerx屬性即可,其實這里也是有誤差的,但是[0,1)像素的誤差肉眼是觀察不到的,
最后, alien_invasion.py 中創建 Ship 實體時,把實參 ai_settings 傳入:
alien_invasion.py實體Ship:
# 創建一艘飛船
ship = Ship(ai_settings, screen)
6.5、限制飛船的活動范圍
當前情況下,如果一直按住左箭頭鍵或者右箭頭鍵,飛船會飛出游戲界面的范圍,下面我們需要通過設定使得飛船只能在游戲界面內移動,
其實很簡單,我們只需要在Ship類的標志飛船位置的函式update中添加條件限定就可以實作,即飛船向右移動時,飛船的右邊界小于游戲界面的右邊界,飛船向左移動時,飛船的左邊界大于0,
Ship類的函式update:
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更新rect物件
self.rect.centerx = self.center
6.6、重構 check_events()
隨著游戲開發的進行,函式 check_events() 將越來越長,我們將其部分代碼放在兩個函式中:一個處理 KEYDOWN 事件,另一個處理 KEYUP 事件,這么做有助于代碼結構更新清晰,后期維護起來更方便,
game_functions.py:
import sys
import pygame
def check_keydown_events(event, ship):
"""回應按鍵事件"""
if event.key == pygame.K_RIGHT:
ship.moving_right = True
elif 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
elif 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()
7、射擊
射擊功能:子彈將在螢屏中向上穿行,抵達螢屏上邊緣后消失,
7.1、 添加子彈設定
在settings.py中添加子彈設定:
class Settings():
"""存盤《外星人入侵》的所有設定的類"""
def __init__(self):
"""初始化游戲的設定"""
# 螢屏設定
self.screen_width = 1200
self.screen_height = 600
self.bg_color = (230, 230, 230)
# 飛船的設定
self.ship_speed_factor = 1.5
#子彈設定
self.bullet_speed_factor = 1
self.bullet_width = 3
self.bullet_height = 15
self.bullet_color =(60, 60, 60)
分析:把子彈設定成寬3像素、高15像素的深灰色子彈,默認速度為1像素
7.2 創建 Bullet 類
bullet.py:
import pygame
from pygame.sprite import Sprite
class Bullet(Sprite):
"""一個對飛船發射的子彈進行管理的類"""
def __init__(self, ai_settings, screen, ship):
"""在飛船所處的位置創建一個子彈物件"""
super(Bullet, self).__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
解釋:
1、Bullet 類繼承了我們從模塊 pygame.sprite 中匯入的 Sprite 類,通過使用精靈,可將游戲中相關的元素編組,進而同時操作編組中的所有元素,
2、我們需要獲取子彈的設定、相對于游戲界面的位置、相對于飛船的位置,所以需要將對應引數ai_settings、screen、 ship傳入到Bullet類的__init__方法中,
3、由于子彈不是我們匯入的影像,而是直接創建的,所以首先需要創建一個子彈矩形,然后再通過此子彈矩形調整它相對于飛船的位置
4、與飛船移動一樣,rect中的center等方法只能接收int型別引數,如果不是int型別就取整數,所以為了使子彈移動能接收浮點數,所以要進行float轉化,
5、最后把獲取的子彈的顏色和速度存入變數中
子彈的初始位置設定好了,它和ship一樣,也需要重新定義一個子彈的位置函式update,方便于后期維護,
定義bullet.py中Bullet類的update函式:
def update(self):
"""向上移動子彈"""
#更新表示子彈位置的小數值
self.y -= self.speed_factor
#更新表示子彈的rect的位置
self.rect.y = self.y
還需要定義一個函式draw_bullet,用來在游戲界面繪制子彈
定義bullet.py中Bullet類的draw_bullet函式:
def draw_bullet(self):
"""在螢屏上繪制子彈"""
pygame.draw.rect(self.screen, self.color, self.rect)
7.3、 將子彈存盤到編組中
需要給子彈儲存到一個編組中,就需要用到 pygame.sprite.Group 類,它類似于一個串列,但提供了有助于開發游戲的額外功能,
在主回圈中,我們將使用這個編組在螢屏上繪制子彈,以及更新每顆子彈的位置:
alien_invasion.py:
import pygame
from pygame.sprite import Group
from settings import Settings
from ship import Ship
import game_functions as gf
def run_game():
#初始化游戲并創建一個視窗物件
pygame.init()
#初始化設定物件
ai_settings = Settings()
#設定視窗大小為1200*800像素
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()
#設定背景顏色
bg_color = (230, 230, 230)
#開始游戲主回圈
while True:
#監聽鍵盤和滑鼠事件
gf.check_events(ai_settings, screen, ship, bullets)
#呼叫飛船目前位置
ship.update()
#呼叫編組中的有效子彈目前位置
bullets.update()
#每次回圈重繪螢屏
gf.update_screen(ai_settings, screen, ship, bullets)
run_game()
解釋:
1、我們將 bullets 傳遞給了 check_events() 和 update_screen() ,在 check_events() 中,需要在玩家按空格鍵時處理 bullets ;而在 update_screen() 中,需要更新要繪制到螢屏上的 bullets ,
2、當你對編組呼叫 update() 時,編組將自動對其中的每個精靈呼叫 update() ,因此代碼行bullets.update() 將為編組 bullets 中的每顆子彈呼叫 bullet.update() ,
7.4、開火
開火:玩家按下空格鍵就會發射一顆子彈出去,因此,我們需要對空格鍵按下事件進行監聽,并且每次發射出去的子彈都需要重繪到游戲界面,所以我們只需要修改game_functions.py就可以實作,
由于發射子彈只用監聽空格鍵按下的回應,不用監聽空格鍵松開的回應,所以修改game_functions.py中的check_keydown_events方法:
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:
new_bullet = Bullet(ai_settings, screen, ship)
bullets.add(new_bullet)
由于check_events方法呼叫了check_keydown_events方法,所以也需要修改game_functions.py中的check_events方法:
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)
最后,修改game_functions.py中的繪制update_screen方法即可:
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()
解釋:
1、編組 bulltes (注意這里是編組,不是Bullet 的實體)傳遞給了 check_keydown_events() ,玩家按空格鍵時,創建一顆新子彈(一個名為 new_bullet 的 Bullet 實體),并使用方法 add() 將其加入到編組 bullets 中,
2、 方 法bullets.sprites() 回傳一個串列,其中包含編組 bullets 中的所有精靈,為在螢屏上繪制發射的所有子彈,我們遍歷編組 bullets 中的精靈,并對每個精靈都呼叫 draw_bullet(),
7.5、洗掉已消失的子彈
目前從視覺效果上可以看到子彈只要飛出游戲界面上邊界就消失了,但是實際上它并沒有消失,就好比不給飛船設定移動邊界,飛船會移出游戲界面,但是它確實是存在的,只不過是超出了游戲界面的范圍,我們需要將這些已消失的子彈洗掉,否則游戲所做的無謂作業將越來越多,進而變得越來越慢,
需要洗掉這些子彈,直接判斷每個子彈的 rect 的 bottom 屬性是否小于等于零即可,如果小于等于零,則證明子彈已經穿出上邊界,
在主程式alien_invasion.py的while回圈中添加此判斷:
#開始游戲主回圈
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)
#每次回圈重繪螢屏
gf.update_screen(ai_settings, screen, ship, bullets)
解釋:
1、在 for 回圈中,不應從串列或編組中洗掉條目,因此必須遍歷編組的副本(python在洗掉第一個元素后,后面的所有元素會向前移一位, 相應的,索引值也會發生改變),我們使用了方法copy() 來設定 for 回圈,這讓我們能夠在回圈中修改 bullets ,
7.6、限制子彈數量
為了讓游戲體驗性更好,我們應該限制子彈的數量,以鼓勵玩家能有效的發射子彈,還能減少服務器的壓力,
首先,在settings.py中存盤所允許的最大子彈數:
#子彈設定
self.bullet_speed_factor = 1
self.bullet_width = 3
self.bullet_height = 15
self.bullet_color =(60, 60, 60)
self.bullets_allowed = 3
其次,在game_functions.py的 check_keydown_events() 中,我們在創建新子彈前檢查未消失的子彈數是否小于該設定:
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:
if len(bullets) < ai_settings.bullets_allowed:
new_bullet = Bullet(ai_settings, screen, ship)
bullets.add(new_bullet)
7.7、 創建函式 update_bullets()
為了讓讓主程式檔案alien_invasion.py 盡可能簡單,而且邏輯層次明確,我們應將子彈管理代碼移到模塊 game_functions 中,
在game_functions.py添加一個新函式 update_bullets(),用于更新子彈的位置,并洗掉已消失的子彈:
def update_bullets(bullets):
"""更新子彈的位置,并洗掉已消失的子彈"""
# 更新子彈的位置
bullets.update()
# 洗掉已消失的子彈
for bullet in bullets.copy():
if bullet.rect.bottom <= 0:
bullets.remove(bullet)
然后再主程式alien_invasion.py 的while回圈把子彈管理相關的代碼替換成呼叫格式:
while True:
#監聽鍵盤和滑鼠事件
gf.check_events(ai_settings, screen, ship, bullets)
#呼叫飛船目前位置
ship.update()
#呼叫管理子彈的函式
gf.update_bullets(bullets)
#每次回圈重繪螢屏
gf.update_screen(ai_settings, screen, ship, bullets)
7.8、創建函式 fire_bullet()
將發射子彈的代碼移到一個獨立的函式中,這樣,在 check_keydown_events() 中只需使
用一行代碼來發射子彈,讓 elif 代碼塊變得非常簡單:
給game_functions.py添加一個新函式 fire_bullet,用于創建子彈:
def fire_bullet(ai_settings, screen, ship, bullets):
"""如果還沒有到達限制,就發射一顆子彈"""
#創建新子彈,并將其加入到編組bullets中
if len(bullets) < ai_settings.bullets_allowed:
new_bullet = Bullet(ai_settings, screen, ship)
bullets.add(new_bullet)
然后,在game_functions.py中的check_keydown_events呼叫以上函式即可:
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)
這是一篇讀書筆記,如有不妥之處,請各位指正!!!
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/244361.html
標籤:其他
