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_rect 即 screen.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!