1. 程式人生 > >Python 植物大戰殭屍程式碼實現: 圖片載入和顯示切換

Python 植物大戰殭屍程式碼實現: 圖片載入和顯示切換

遊戲介紹
以前很火的植物大戰殭屍遊戲, 本想在網上找個python版本遊戲學習下,無奈沒有發現比較完整的,那就自己來寫一個把。圖片資源是從github上下載的,因為圖片資源有限,只能實現幾種植物和殭屍。
功能實現如下:

支援的植物型別:太陽花,豌豆射手,寒冰射手,堅果,櫻桃炸彈。新增加植物:雙重豌豆射手,三重豌豆射手。
支援的殭屍型別:普通殭屍,棋子殭屍,路障殭屍,鐵桶殭屍。
使用json檔案儲存關卡資訊,設定殭屍出現的時間和位置。
新增加除草機。
下面是遊戲的截圖:
圖1

                                                                                              圖2

 

 

 

圖片顯示切換


從圖1和圖2可以看到,殭屍的行走和攻擊時的圖片顯示會有不同,這篇文章講下如何進行圖片顯示的切換。
以上面的路障殭屍為例,一共有下面幾種圖片型別。

  • 帶著路障行走
  • 帶著路障攻擊
  • 不帶路障行走(即變成普通殭屍的行走)
  • 不帶路障攻擊(即變成普通殭屍的攻擊)
  • 沒有頭的行走
  • 沒有頭的攻擊
  • 死亡

圖3是路障殭屍的這7種圖片型別的示例

 

圖片載入

植物大戰殭屍的圖片資源比較特別,一種圖片型別的每一個動作是一個單獨的圖片,如圖4是路障殭屍帶著路障攻擊的動作圖片,一共有11個圖片,所以載入圖片的程式碼要做對應的修改。

 

 

完整程式碼

遊戲實現程式碼的github連結 植物大戰殭屍
這邊是csdn的下載連結 植物大戰殭屍

圖片載入
在 source\tool.py 中 load_all_gfx 函式遍歷resources\graphics 目錄和子目錄。
程式碼中做了一個簡單的區分:

  • 如果在resources\graphics\subfolder\ 目錄中是圖片,那就是單獨的一個圖片,比如resources\graphics\Screen 目錄中的介面圖片
  • 如果在resources\graphics\subfolder\ 目錄中是子目錄,那這個子目錄或子子目錄中的所有圖片都屬於一個圖片型別,比如resources\graphics\Zombies\ConeheadZombie\ConeheadZombieAttack 目錄下就是路障殭屍帶著路障攻擊的動作圖片, 如圖4所示。
 1 def load_all_gfx(directory, colorkey=c.WHITE, accept=('.png', '.jpg', '.bmp', '.gif')):
 2     graphics = {}
 3     for name1 in os.listdir(directory):
 4         # subfolders under the folder resources\graphics
 5         dir1 = os.path.join(directory, name1)
 6         if os.path.isdir(dir1):
 7             for name2 in os.listdir(dir1):
 8                 dir2 = os.path.join(dir1, name2)
 9                 if os.path.isdir(dir2):
10                 # e.g. subfolders under the folder resources\graphics\Zombies
11                     for name3 in os.listdir(dir2):
12                         dir3 = os.path.join(dir2, name3)
13                         # e.g. subfolders or pics under the folder resources\graphics\Zombies\ConeheadZombie
14                         if os.path.isdir(dir3):
15                             # e.g. it's the folder resources\graphics\Zombies\ConeheadZombie\ConeheadZombieAttack
16                             image_name, _ = os.path.splitext(name3)
17                             graphics[image_name] = load_image_frames(dir3, image_name, colorkey, accept)
18                         else:
19                             # e.g. pics under the folder resources\graphics\Plants\Peashooter
20                             image_name, _ = os.path.splitext(name2)
21                             graphics[image_name] = load_image_frames(dir2, image_name, colorkey, accept)
22                             break
23                 else:
24                 # e.g. pics under the folder resources\graphics\Screen
25                     name, ext = os.path.splitext(name2)
26                     if ext.lower() in accept:
27                         img = pg.image.load(dir2)
28                         if img.get_alpha():
29                             img = img.convert_alpha()
30                         else:
31                             img = img.convert()
32                             img.set_colorkey(colorkey)
33                         graphics[name] = img
34     return graphics
35 
36 GFX = load_all_gfx(os.path.join("resources","graphics"))

 

 load_image_frames 函式 將目錄中的所有圖片按照圖片名稱中的index值為key,儲存在tmp 字典中。比如圖片名稱為"ConeheadZombieAttack_2",它的index值就為2。
然後將圖片按index值依次加入到 frame_list 中。

 

 1 def load_image_frames(directory, image_name, colorkey, accept):
 2     frame_list = []
 3     tmp = {}
 4     # image_name is "Peashooter", pic name is 'Peashooter_1', get the index 1
 5     index_start = len(image_name) + 1 
 6     frame_num = 0;
 7     for pic in os.listdir(directory):
 8         name, ext = os.path.splitext(pic)
 9         if ext.lower() in accept:
10             index = int(name[index_start:])
11             img = pg.image.load(os.path.join(directory, pic))
12             if img.get_alpha():
13                 img = img.convert_alpha()
14             else:
15                 img = img.convert()
16                 img.set_colorkey(colorkey)
17             tmp[index]= img
18             frame_num += 1
19 
20     for i in range(frame_num):
21         frame_list.append(tmp[i])
22     return frame_list

 

圖片顯示切換
在 source\component\zombie.py 中, Zombie 類是所有殭屍類的父類,初始化 函式呼叫loadImages函式載入所有支援的圖片型別,設定Sprite 精靈類顯示需要的成員變數 image和rect。
loadFrames函式給具體的子類來呼叫,獲取圖片。

 1 class Zombie(pg.sprite.Sprite):
 2     def __init__(self, x, y, name, health, head_group=None, damage=1):
 3         pg.sprite.Sprite.__init__(self)
 4         
 5         self.name = name
 6         self.frames = []
 7         self.frame_index = 0
 8         self.loadImages()
 9         self.frame_num = len(self.frames)
10 
11         self.image = self.frames[self.frame_index]
12         self.rect = self.image.get_rect()
13         self.rect.centerx = x
14         self.rect.bottom = y
15         ...
16 
17     def loadFrames(self, frames, name, image_x):
18         frame_list = tool.GFX[name]
19         rect = frame_list[0].get_rect()
20         width, height = rect.w, rect.h
21         width -= image_x
22 
23         for frame in frame_list:
24             frames.append(tool.get_image(frame, image_x, 0, width, height))

 

基本的功能都在Zombie 父類中實現,如果子類有特殊需求,可以重定義同名函式。

update 函式:每個tick 都會呼叫的入口函式,用來更新殭屍的位置,切換狀態和更新圖片顯示。
handleState 函式:根據殭屍當前的狀態來執行不同的函式。
animation 函式:每隔指定的 animate_interval 時間會顯示圖片型別的下一個動作。

 1     def update(self, game_info):
 2         self.current_time = game_info[c.CURRENT_TIME]
 3         self.handleState()
 4         self.animation()
 5     
 6     def handleState(self):
 7         if self.state == c.WALK:
 8             self.walking()
 9         elif self.state == c.ATTACK:
10             self.attacking()
11         elif self.state == c.DIE:
12             self.dying()
13     
14     def animation(self):
15         if (self.current_time - self.animate_timer) > self.animate_interval:
16             self.frame_index += 1
17             if self.frame_index >= self.frame_num:
18                 if self.state == c.DIE:
19                     self.kill()
20                     return
21                 self.frame_index = 0
22             self.animate_timer = self.current_time
23         
24         self.image = self.frames[self.frame_index]

 

下面四個函式是修改殭屍的當前狀態和圖片顯示。

  • setWalk 函式:修改為行走狀態,圖片顯示會根據不同值設定不同的圖片型別。
  • setAttack 函式:修改為攻擊狀態,圖片顯示會根據不同值設定不同的圖片型別。
  • setDie 函式:修改為死亡狀態。
  • changeFrames 函式:修改圖片型別後,需要重新設定成員變數frame_num,frame_index, image和rect的值。
 1  def setWalk(self):
 2         self.state = c.WALK
 3         self.animate_interval = 150
 4         
 5         if self.helmet:
 6             self.changeFrames(self.helmet_walk_frames)
 7         elif self.losHead:
 8             self.changeFrames(self.losthead_walk_frames)
 9         else:
10             self.changeFrames(self.walk_frames)
11 
12     def setAttack(self, plant):
13         self.plant = plant
14         self.state = c.ATTACK
15         self.animate_interval = 100
16         
17         if self.helmet:
18             self.changeFrames(self.helmet_attack_frames)
19         elif self.losHead:
20             self.changeFrames(self.losthead_attack_frames)
21         else:
22             self.changeFrames(self.attack_frames)
23     
24     def setDie(self):
25         self.state = c.DIE
26         self.animate_interval = 200
27         self.changeFrames(self.die_frames)
28     
29     def changeFrames(self, frames):
30         '''change image frames and modify rect position'''
31         self.frames = frames
32         self.frame_num = len(self.frames)
33         self.frame_index = 0
34         
35         bottom = self.rect.bottom
36         centerx = self.rect.centerx
37         self.image = self.frames[self.frame_index]
38         self.rect = self.image.get_rect()
39         self.rect.bottom = bottom
40         self.rect.centerx = centerx

 

路障殭屍類就比較簡單,只需要實現 loadImages 函式,呼叫loadFrames函式載入該種殭屍支援的圖片型別,這邊主要的差異在於不同種類殭屍的圖片型別的名稱會有區別。

 1 class ConeHeadZombie(Zombie):
 2     def __init__(self, x, y, head_group):
 3         Zombie.__init__(self, x, y, c.CONEHEAD_ZOMBIE, c.CONEHEAD_HEALTH, head_group)
 4         self.helmet = True
 5 
 6     def loadImages(self):
 7         self.helmet_walk_frames = []
 8         self.helmet_attack_frames = []
 9         self.walk_frames = []
10         self.attack_frames = []
11         self.losthead_walk_frames = []
12         self.losthead_attack_frames = []
13         self.die_frames = []
14         
15         helmet_walk_name = self.name
16         helmet_attack_name = self.name + 'Attack'
17         walk_name = c.NORMAL_ZOMBIE
18         attack_name = c.NORMAL_ZOMBIE + 'Attack'
19         losthead_walk_name = c.NORMAL_ZOMBIE + 'LostHead'
20         losthead_attack_name = c.NORMAL_ZOMBIE + 'LostHeadAttack'
21         die_name = c.NORMAL_ZOMBIE + 'Die'
22 
23         frame_list = [self.helmet_walk_frames, self.helmet_attack_frames,
24                       self.walk_frames, self.attack_frames, self.losthead_walk_frames,
25                       self.losthead_attack_frames, self.die_frames]
26         name_list = [helmet_walk_name, helmet_attack_name,
27                      walk_name, attack_name, losthead_walk_name,
28                      losthead_attack_name, die_name]
29         
30         for i, name in enumerate(name_list):
31             self.loadFrames(frame_list[i], name, tool.ZOMBIE_RECT[name]['x'])
32 
33         self.frames = self.helmet_walk_frames

學習視訊關注討論群:887934385 原始碼、及相關素材

編譯環境

python3.7 + pygam