這邊博客我們為大型敵機和中型敵機設置血量并以血槽的形式顯示出來,同時解決之前遇到的聲道阻塞的BUG(聲道阻塞的問題詳見之前的博文)。
1、為敵機添加表示血量的成員變量
所謂敵機血量,就是指敵機在掛掉之前能夠挨幾發子彈,這是敵機對象的一個成員屬性,因此我們在中型敵機(MidEnemy)和大型敵機(BigEnemy)中添加energy成員變量:
~~~
class MidEnemy(pygame.sprite.Sprite):
energy = 5
def __init__(self, bg_size):
………………
self.energy = MidEnemy.energy
~~~
~~~
class BigEnemy(pygame.sprite.Sprite):
energy = 15
def __init__(self, bg_size):
………………
self.energy = BigEnemy.energy
~~~
注意這里之所以將energy初始化為類的全局變量以及類對象的成員變量兩種形式,是因為在接下來繪制血槽的過程中,需要計算當前血量和總血量的比值,全局energy用以保存總血量(定值),類對象的成員變量energy(隨著被擊中的次數而遞減)表示當前血量,這一點在后面繪制血槽的過程中將再次解釋。從代碼中可以看出,我們將中型敵機血量設計為承受5發子彈,大型敵機的血量設計為承受15發子彈的數量(皮糙肉厚)。
當然在對應的reset()函數中需要重置energy變量的值,將其重新設置為滿血:
~~~
self.energy = MidEnemy.energy
~~~
以及
~~~
self.energy = BigEnemy.energy
~~~
2、為中型敵機和大型敵機添加中彈受損圖片
既然中型敵機和大型敵機并不是一擊斃命,因此有必要在其中彈時配以特效圖片(雖然是5毛特效)以表征該敵機血量正在減少,相關圖片資源已經存于image文件夾中,只需在中型敵機和大型敵機的類內部(__init__()函數中)進行加載:
~~~
self.image_hit = pygame.image.load("image/enemy2_hit.png") # 加載中型敵機中彈圖片
~~~
~~~
self.image_hit = pygame.image.load("image/enemy3_hit.png") # 加載大型敵機中彈圖片
~~~
與此同時,我們需要知道中型敵機和大型敵機在什么時候被擊中,以便播放中彈畫面,因此需要在中型敵機和大型敵機內部添加一個表示當前飛機是否被擊中的標志位:
~~~
self.hit = False # 飛機是否被擊中標志位
~~~
并且在對應reset()函數中重置其屬性:
~~~
self.hit = False
~~~
加載完相關資源后,接下來開始轉入主程序模塊進行繪制血槽的工作。
3、定義繪制血槽所用背景顏色
為了反映當前血量情況,突出剩余血量的比重,我們采用如下的血槽繪制機制:血槽的底色為黑色,當前血量用綠色線條表示,并隨energy變量的減少而縮短,當血量低于百分之二十時,血量的顯示由綠色變為紅色,為了方便對指定顏色的調用,我們在main函數開始部分(while循環之外)對各個顏色進行宏定義:
~~~
color_black = (0, 0, 0)
color_green = (0, 255, 0)
color_red = (255, 0, 0)
color_white = (255, 255, 255)
~~~
稍微注意這里Pygame的彩色分量順序就是平時大家所熟知的R,G,B。(opencv中為BGR)
4、繪制血槽
繪制血槽的操作是在敵方飛機繪制過程中同步進行的,考慮到代碼的多層嵌套關系,這里先將繪制血槽的完整代碼給出,再作解釋:
~~~
for each in big_enemies: # 繪制大型敵機并自動移動
if each.active: # 如果飛機正常存在
# 飛機移動move()
if not each.hit:
# 如果飛機未被擊中# 繪制大型敵機的兩種不同的形式
else:
screen.blit(each.image_hit, each.rect)
each.hit = False
# ====================繪制血槽====================
pygame.draw.line(screen, color_black,
(each.rect.left, each.rect.top - 5),
(each.rect.right, each.rect.top - 5),
2)
energy_remain = each.energy / enemy.BigEnemy.energy
if energy_remain > 0.2: # 如果血量大約百分之二十則為綠色,否則為紅色
energy_color = color_green
else:
energy_color = color_red
pygame.draw.line(screen, energy_color,
(each.rect.left, each.rect.top - 5),
(each.rect.left + each.rect.width * energy_remain, each.rect.top - 5),
2)
if each.rect.bottom == 0:
# 播放大型飛機的音效(循環播放)
else: # 如果飛機已撞毀
pass
~~~
這里代碼有點多,我們逐行解釋。通過for語句輪詢大型敵機精靈組中的每個敵機對象,如果該飛機對象為激活狀態(if each.active:),則進行接下來的飛機移動、血槽繪制的工作,否則進入飛機損毀的代碼程序,包括播放音效以及損毀圖片等等(之前博文已經介紹過)。如果飛機狀態為激活狀態,則需要判斷其內部的“hit”標志位來判斷當前飛機是否中彈,如果hit = false,則說明飛機沒有中彈,正常繪制飛機的狀態即可(大型敵機有幀切換特效,詳見之前博文),如果hit = true,則說明飛機當前中彈,需要繪制飛機的中彈圖片,同時重置hit標志位。
接下來是血槽,只要飛機處于激活狀態,就需要繪制血槽,而無需考慮飛機當前是否中彈。首先通過pygame.draw.line()函數繪制血槽背景,背景顏色為黑色,線的長度與精靈對象的寬度相等,位置處于精靈圖片上方五個像素的位置。之后通過energy_remain = each.energy / enemy.BigEnemy.energy計算當前剩余血量百分比,這里即體現出了之前在定義energy時將其定義為類全局變量和對象局部變量兩種形式的優勢。
得到剩余血量比重后,則判斷當前剩余血量是否大于百分之二十,如果大于0.2,則血槽顏色為綠色,否色為紅色。指定好血槽顏色之后即可再次調用pygame.draw.line()來繪制血槽長度,位置和背景位置相同,但血槽長度需要通過“血槽背景長度(總長度)*剩余血量百分比”來獲得,即代碼中的“each.rect.width * energy_remain”,這樣當前血槽長度就和當前血量(self.energy)成正比。
同理,中型敵機的血槽繪制方式和上面大型敵機的繪制方式基本一樣的,只需將在計算剩余血量時將“enemy.BigEnemy.energy”改為“enemy.MidEnemy.energy”即可。程序編寫到這里,運行將會看到中型敵機和大型敵機都將會頂著一個血槽光環出場了,但這里仍然是一擊斃命,原因是我們只是繪制了血槽,還沒有真正的對血量(energy)進行操作。
5、添加碰撞檢測血量遞減機制
血量的真正意義是通過中彈遞減來體現的,即中彈一次(飛機和子彈發生一次碰撞),energy變量就減一,當energy = 0時,飛機損毀,因此我們需要修改一下之前寫的碰撞檢測處理函數:
~~~
# ====================子彈與敵機的碰撞檢測====================
for b in bullets:
if b.active: # 只有激活的子彈才可能擊中敵機
# 子彈移動,碰撞檢測if enemies_hit: # 如果子彈擊中飛機
# 子彈損毀
for e in enemies_hit:
if e in big_enemies or e in mid_enemies:
e.energy -= 1
e.hit = True # 表示飛機已經被擊中
if e.energy == 0:
e.active = False # 大中型敵機損毀
else:
e.active = False # 小型敵機損毀
~~~
這段代碼相對來說容易理解,之前在完成子彈和敵機的碰撞檢測后,對于發生碰撞的敵機(enemies_hit),我們直接令e.active = false來銷毀敵機。然而在這里我們需要判斷,如果碰撞的是中型敵機和大型敵機,則將其energy變量減一,并將hit賦值為true表示中彈,在當energy = 0時才執行銷毀操作;若發生碰撞的是小型敵機,那無話可說,一擊斃命。
ok,程序運行到這里應該能夠順利執行,本來還想繼續介紹一些關于聲道阻塞的BUG解決方案,鑒于內容已經不少了,還是放到下次博文吧。