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