用 python 写游戏《外星人入侵》:限制飞船的活动范围并给飞船增加射击功能
文章目錄
- 限制飛船的活動(dòng)范圍
- 重構(gòu) check_events()
- 小結(jié)
- alien_invasion.py
- settings.py
- game_funcations.py
- ship.py
- 射擊
- 添加子彈設(shè)置
- 創(chuàng)建 Bullet 類
- 將子彈存儲(chǔ)到編組中
- 開火
- 刪除已消失的子彈
- 限制子彈數(shù)量
- 創(chuàng)建函數(shù) update_bullets()
- 創(chuàng)建函數(shù) fire_bullet()
限制飛船的活動(dòng)范圍
當(dāng)前,如果玩家按住箭頭鍵的時(shí)間足夠長(zhǎng),飛船將移到屏幕外面,消失得無影無蹤。下面來修復(fù)這種問題,讓飛船到達(dá)屏幕邊緣后停止移動(dòng)。為此,我們將修改 ship類的方法 update():
def update(self):#根據(jù)移動(dòng)標(biāo)志調(diào)整飛船的位置#更新飛船的centerx值,而不是rectif 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 #根據(jù)self.center更新rect值self.rect.centerx = self.center上述代碼在修改 self.center 的值之前檢查飛船的位置。self.rect.right 返回飛船外接矩形的有邊緣的x坐標(biāo),如果這個(gè)值小于self.screen_rect.right 的值,就說明飛船未觸及屏幕右邊緣。左邊緣的情況與此類似:如果rect的左邊緣的x坐標(biāo)大于零,就說明飛船未觸及屏幕左邊緣。這確保僅當(dāng)飛船在屏幕內(nèi)時(shí),才調(diào)整 self.center 的值。
如果此時(shí) alien_invasion.py,飛船將在觸及屏幕左邊緣或右邊緣后停止移動(dòng)。
重構(gòu) check_events()
隨著游戲開發(fā)的進(jìn)行,函數(shù) check_events() 將越來越長(zhǎng),我們將其部分代碼放在兩個(gè)函數(shù)中:
一個(gè)負(fù)責(zé)處理KEYDOWN事件,另一個(gè)處理 KEYUP事件:
小結(jié)
項(xiàng)目到此,我們完成了窗口繪制,飛船繪制和位置設(shè)置。還設(shè)置了飛船的移動(dòng)速度,讓其進(jìn)行到一定程度時(shí)加快游戲節(jié)奏,當(dāng)然,還有飛船的左右移動(dòng)。我們一共創(chuàng)建了四個(gè)模塊。下面我們來總結(jié)一下四個(gè)模塊的分別作用:
alien_invasion.py
主文件創(chuàng)建一系列整個(gè)游戲都要使用的對(duì)象:存儲(chǔ)在 ai_settings中的設(shè)置、存儲(chǔ)在 screen中的
surface以及一個(gè)飛船實(shí)例。文件 alien_invasion還包含游戲的主循環(huán),這是一個(gè)調(diào)用
check_events()、ship.update()和update_screen()的while循環(huán)。
要啟動(dòng)《外星人入侵》,只需運(yùn)行文件alien_invasion.py。其他文件(settings.py、game_functions.py、ship.py)包含的代碼被直接或間接地導(dǎo)入到這個(gè)文件中。
settings.py
文件 settings.py包含Settings類,這個(gè)類只包含 __ init __(),它負(fù)責(zé)初始化控制游戲外觀和飛船速度的屬性。
game_funcations.py
該文件包含一系列函數(shù),游戲的大部分工作都是由它們完成的。函數(shù)check_events()檢測(cè)相關(guān)的事件,如松鍵或按鍵,并使用輔助函數(shù)
check_keydown_events()和check_keyup_events()來處理事件。就目前而言,這些函數(shù)管理飛船的移動(dòng)。模塊game_functions還包含
update_screen(),它用于在每次執(zhí)行主循環(huán)時(shí)都重繪屏幕。
ship.py
該文件包含 Ship類,這個(gè)類包含方法 __ init __(),管理飛船位置的方法 update()以及在屏幕上繪制飛船的方法
blitme()。表示飛船的圖像存儲(chǔ)在文件夾 images下的ship.bmp中。
射擊
下面我們來給飛船增加射擊功能,我們將編寫玩家按空格鍵時(shí)發(fā)射子彈(小矩形)的代碼。子彈將在屏幕中向上穿行,抵達(dá)屏幕邊緣后消失。
添加子彈設(shè)置
首先,更新 settings.py,在其方法__ init __() 末尾存儲(chǔ)新類 Bullet所需的值:
self.bullet_speed_factor = 1self.bullet_width = 3self.bullet_height = 15self.bullet_color = 60,60,60創(chuàng)建 Bullet 類
這里我們創(chuàng)建存儲(chǔ) Bullet類的文件 bullet.py,其前半部分如下:
import pygame from pygame.sprite import Spriteclass Bullet(Sprite):#一個(gè)對(duì)飛船發(fā)射子彈管理的類def __init__(self,ai_settings,screen,ship):#在飛船所處位置創(chuàng)建一個(gè)子彈對(duì)象super(Bullet,self).__init__()self.screen = screen#在(0,0)處創(chuàng)建一個(gè)表示子彈的矩形,再設(shè)置正確的位置self.rect = pygame.Rect(0,0,ai_settings.bullet_width,ai_settings.bullet_height)self.rect.centerx = ship.rect.centerxself.rect.top = ship.rect.top#存儲(chǔ)用小數(shù)表示的子彈位置self.y = float(self.rect.y)self.color = ai_settings.bullet_colorself.speed_factor = ai_settings.bullet_speed_factorBullet類繼承了我們從模塊pygame.sprite中導(dǎo)入的Sprite類。通過使用精靈,可將游戲中相關(guān)的元素編組,進(jìn)而同時(shí)操作編組中的所有元素。為創(chuàng)建子彈實(shí)例,需要向__ init __ ()傳遞ai_settings、screen和ship實(shí)例,還調(diào)用了super()來繼承Sprite
code 說明
self.rect = pygame.Rect(0,0,ai_settings.bullet_width,ai_settings.bullet_height)
創(chuàng)建子彈的屬性rect。子彈并非基于圖像的,因此我們必須使用pygame.Rect()類從空白開始創(chuàng)建一個(gè)矩形。創(chuàng)建這個(gè)類的實(shí)例時(shí),必須提供矩形左上角的x坐標(biāo)和y坐標(biāo),還有矩形的寬度和高度。我們?cè)?0,0)處創(chuàng)建這個(gè)矩形,但接下來的兩行代碼將其移到了正確的位置,因?yàn)樽訌椀某跏嘉恢萌Q于飛船當(dāng)前的位置。子彈的官渡和高度是從ai_settings中獲取的。
self.rect.centerx = ship.rect.centerx
self.rect.top = ship.rect.top
我們將子彈的centerx設(shè)置為飛船的rect.centerx。子彈應(yīng)從飛船頂不射出,因此我們將表示子彈的rect的top屬性設(shè)置為飛船的rect的top屬性,讓子彈看起來像是從飛船中射出的。
#存儲(chǔ)用小數(shù)表示的子彈位置
self.y = float(self.rect.y)
self.color = ai_settings.bullet_color
self.speed_factor = at_settings.bullet_speed_factor
我們將子彈的y坐標(biāo)存儲(chǔ)為小數(shù)值,以便能夠微調(diào)子彈的速度。然后將子彈的顏色和速度分別設(shè)置存儲(chǔ)到self.color和self.speed_factor中。
下面是bullet.py的第二部分----方法update()和draw_bullet():
def update(self):#向上移動(dòng)子彈#更新表示子彈位置的小數(shù)值self.y -= self.speed_factorself.rect.y = self.ydef draw_bullet(self):#在屏幕上繪制子彈pygame.draw.rect(self.screen,self.color,self.rect)方法update()管理子彈的位置。發(fā)射出去后,子彈在屏幕中向上移動(dòng),這意味著y坐標(biāo)將不斷減小,因此為更新子彈的位置,我們從self.y中減去self.speed_factor的值。接下來,我們將self.rect.y設(shè)置為 self.y的值。屬性speed_factor讓我們能夠隨著游戲的進(jìn)行或根據(jù)需要提高子彈的速度,以調(diào)整游戲的行為。子彈發(fā)射后,其x坐標(biāo)始終不變,因此子彈將沿直線垂直地往上穿行。
在需要繪制子彈時(shí),我們調(diào)用draw_bullet()。函數(shù)draw.rect()使用存儲(chǔ)在self.color中的顏色填充表示子彈的rect占據(jù)的屏幕部分。
將子彈存儲(chǔ)到編組中
定義Bullet類和必要的設(shè)置后,我們就可以編寫代碼了。
在玩家每次按空格鍵時(shí)都射出一發(fā)子彈。
首先,我們將在alien_invasion.py中創(chuàng)建一個(gè)編組(group),用于存儲(chǔ)所有有效的子彈,以便能夠管理發(fā)射出去的所有子彈。這個(gè)編組將是pygame.sprite.Group類的一個(gè)實(shí)例;pygame.sprite.Group類類似于列表,但提供了有助于開發(fā)游戲的額外功能。在主循環(huán)中,我們將使用這個(gè)編組在屏幕上繪制子彈,以及更新每顆子彈的位置:
from pygame.sprite import Group #創(chuàng)建一個(gè)用于存儲(chǔ)子彈的編組bullets = Group()# while 循環(huán)來控制游戲 while True:gf.check_events(ai_settings,screen,ship,bullets)ship.update()bullets.update() gf.update_screen(ai_settings,screen,ship,bullets)在這里我們導(dǎo)入pygame.sprite中的Group類。
這個(gè)編組是在while循環(huán)外面創(chuàng)建的,這樣就無需每次運(yùn)行該循環(huán)時(shí)都創(chuàng)建一個(gè)新的子彈編組。
將bullets傳遞給了check_events()和update_screen(),在check_events()中,需要在玩家按空格鍵時(shí)處理bullets;而在update_screen()中,需要更新要繪制到屏幕上的bullets。
當(dāng)你對(duì)編組調(diào)用update()時(shí),編組將自動(dòng)對(duì)其中的每個(gè)精靈調(diào)用update(),因此代碼行bullest.update()將為編組bullest中的每顆子彈調(diào)用bullet.update()
開火
在game_funcations.py中:
我們需要修改check_keydown_events(),以便在玩家按空格鍵時(shí)發(fā)射一顆子彈。我們無需修改check_keyup_events(),因?yàn)橥婕宜砷_空格鍵時(shí)什么都不會(huì)發(fā)生。我們還需要修改update_screen(),確保在調(diào)用flip()前在屏幕上重繪每顆子彈。
from bullet import Bulletdef check_keydown_events(event,ai_settings,screen,ship,bullets):if event.key == pygame.K_RIGHT:ship.moving_right = Trueelif event.key == pygame.K_LEFT:ship.moving_left = Trueelif event.key == pygame.K_SPACE:#創(chuàng)建一顆子彈,并將其加入到編組bullets中new_bullet = Bullet(ai_settings,screen,ship)bullets.add(new_bullet)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):#每次循環(huán)時(shí)都重繪屏幕screen.fill(ai_settings.bg_color)#在飛船和外星人后面重繪所有子彈for bullet in bullets.sprites():bullet.draw_bullet()ship.blitme()#讓最近繪制的屏幕可見pygame.display.flip()編組bullets傳遞給了check_keydown_events():
玩家按空格鍵時(shí),創(chuàng)建一顆新子彈:new_bullet的Bullet實(shí)例,并使用方法add()將其加入到編組bulllets中;代碼bullet.add(new_bullet)將新子彈存儲(chǔ)到編組bullets中。
在check_events()的定義中,我們需要添加形參bullets;調(diào)用check_keydown_events()時(shí),我們也需要將bullets作為實(shí)參傳遞給它。
def update_screen(ai_settings,screen,ship,bullets):
我們給在屏幕上繪制子彈的update_screen()添加了形參bullets。方法bullets.sprites()返回一個(gè)列表,其中包含編組bullets中的所有精靈。為在屏幕上繪制發(fā)射的所有子彈,我們遍歷編組bullets中的所有精靈,并對(duì)每個(gè)精靈都調(diào)用draw_bullet()
如果此時(shí)運(yùn)行alien_invasion.py,將能夠左右移動(dòng)飛船,并發(fā)射任意數(shù)量的子彈。子彈會(huì)在屏幕中向上穿行,抵達(dá)屏幕頂步后消失
我們可以在settings.py中修改子彈的尺寸、顏色和速度。
刪除已消失的子彈
當(dāng)前,子彈抵達(dá)屏幕頂端后消失,這僅僅是因?yàn)閜ygame無法在屏幕外面繪制它們。這些子彈實(shí)際上依然存在,它們的y坐標(biāo)為負(fù)數(shù),且越來越小。這是一個(gè)問題,因?yàn)樗鼈儗⒗^續(xù)消耗內(nèi)存和處理能力。
所以我們需要將這些已消失的子彈刪除,否則游戲所做的無謂工作將越來越多,進(jìn)而變得越來越慢。
檢測(cè)這樣的條件,即表示子彈的rect的bottom屬性為零,它表名子彈已穿過屏幕頂端:
alien_invasion.py
在for循環(huán)中,不應(yīng)從列表或編組中刪除條目,因此必須遍歷編組的副本。
我們使用方法copy()設(shè)置for循環(huán),這讓我們能夠在for循環(huán)中修改bullets。我們檢查每顆子彈,看看它是否已從屏幕頂部消失。如果是這樣,就將其從bullets中刪除。
print()語句用來顯示當(dāng)前子彈還有多少顆,從而核實(shí)已消失的子彈確實(shí)刪除了。
.
如果代碼最后沒有問題,確認(rèn)子彈可以正常刪除,我們將print語句刪除,如果留下這條語句,游戲的速度將大大降低,因?yàn)閷⑤敵鰧懭氲浇K端而花費(fèi)的時(shí)間比將圖形繪制到游戲窗口花費(fèi)的時(shí)間還多。
限制子彈數(shù)量
很多射擊游戲都對(duì)可同時(shí)出現(xiàn)在屏幕上的子彈數(shù)量進(jìn)行限制,以鼓勵(lì)玩家有目標(biāo)地射擊。下面我們也加入同樣的限制。
首先,在 settings.py中存儲(chǔ)允許的最大子彈數(shù):
self.bullets_allowed = 3將未消失的子彈限制為3顆。
在game_functions.py的check_keydown_events()中,我們?cè)趧?chuàng)建新子彈前檢查未消失的子彈數(shù)是否小于該設(shè)置:
玩家按空格時(shí),我們檢查bullets的長(zhǎng)度,如果len(bullets)小于3,我們就創(chuàng)建一個(gè)新子彈;但如果已有3顆未消失的子彈,則玩家按空格鍵時(shí)什么都不發(fā)生。如果你現(xiàn)在運(yùn)行這個(gè)游戲,屏幕上最多只能有3顆子彈。
創(chuàng)建函數(shù) update_bullets()
編寫并檢查子彈管理代碼后,可將其移動(dòng)模塊
game_functions中,以讓主程序文件alien_invasion.py盡可能簡(jiǎn)單,我們創(chuàng)建一個(gè)名為
update_bullets()的新函數(shù),并將其添加到game_functions.py的末尾。
這個(gè)函數(shù)中的代碼是從 alien_invasion.py 剪切粘貼過來的,它只需要一個(gè)參數(shù),即編組bullets。
alien_invasion.py中的while循環(huán)又變得很簡(jiǎn)單了:
# while 循環(huán)來控制游戲while True:gf.check_events(ai_settings,screen,ship,bullets)ship.update()bullets.update()gf.update_bullets(bullets) gf.update_screen(ai_settings,screen,ship,bullets)我們讓主循環(huán)包含盡可能少的代碼,這樣只要看函數(shù)名就能迅速知道游戲中發(fā)生的情況。主循環(huán)檢查玩家的輸入,然后更新飛船的位置和所有未消失的子彈的位置。接下來,我們使用更新后的位置來繪制新屏幕。
創(chuàng)建函數(shù) fire_bullet()
下面將發(fā)射子彈的代碼移到一個(gè)獨(dú)立的函數(shù)中,這樣,
在check_keydown_events()中只需使用一行代碼來發(fā)射子彈,讓elif代碼變得飛船簡(jiǎn)單。
函數(shù) fire_bullet() 只包含玩家按空格鍵時(shí)用于發(fā)射子彈的代碼;
在check_keydown_events()中,我們?cè)谕婕野纯崭矜I時(shí)調(diào)用 fire_bullet()。
總結(jié)
以上是生活随笔為你收集整理的用 python 写游戏《外星人入侵》:限制飞船的活动范围并给飞船增加射击功能的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: (附源码)计算机毕业设计SSM交通事故记
- 下一篇: websocket python爬虫_p