Python 植物大戰殭屍代碼實現(1):圖片加載和顯示切換

python 植物大戰殭屍代碼實現(1):圖片加載和顯示切換

遊戲介紹

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

  • 支持的植物類型:太陽花,豌豆射手,寒冰射手,堅果,櫻桃炸彈。
  • 支持的殭屍類型:普通殭屍,棋子殭屍,路障殭屍,鐵桶殭屍。
  • 使用json文件保存關卡信息,設置殭屍出現的時間和位置。

下面是遊戲的截圖:
圖1demo1
圖2demo2

圖片顯示切換

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

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

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

圖片加載

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

完整代碼

遊戲實現代碼的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所示。
def load_all_gfx(directory, colorkey=c.WHITE, accept=('.png', '.jpg', '.bmp', '.gif')):
    graphics = {}
    for name1 in os.listdir(directory):
        # subfolders under the folder resources\graphics
        dir1 = os.path.join(directory, name1)
        if os.path.isdir(dir1):
            for name2 in os.listdir(dir1):
                dir2 = os.path.join(dir1, name2)
                if os.path.isdir(dir2):
                # e.g. subfolders under the folder resources\graphics\Zombies
                    for name3 in os.listdir(dir2):
                        dir3 = os.path.join(dir2, name3)
                        # e.g. subfolders or pics under the folder resources\graphics\Zombies\ConeheadZombie
                        if os.path.isdir(dir3):
                            # e.g. it's the folder resources\graphics\Zombies\ConeheadZombie\ConeheadZombieAttack
                            image_name, _ = os.path.splitext(name3)
                            graphics[image_name] = load_image_frames(dir3, image_name, colorkey, accept)
                        else:
                            # e.g. pics under the folder resources\graphics\Plants\Peashooter
                            image_name, _ = os.path.splitext(name2)
                            graphics[image_name] = load_image_frames(dir2, image_name, colorkey, accept)
                            break
                else:
                # e.g. pics under the folder resources\graphics\Screen
                    name, ext = os.path.splitext(name2)
                    if ext.lower() in accept:
                        img = pg.image.load(dir2)
                        if img.get_alpha():
                            img = img.convert_alpha()
                        else:
                            img = img.convert()
                            img.set_colorkey(colorkey)
                        graphics[name] = img
    return graphics

GFX = load_all_gfx(os.path.join("resources","graphics"))

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

def load_image_frames(directory, image_name, colorkey, accept):
    frame_list = []
    tmp = {}
    # image_name is "Peashooter", pic name is 'Peashooter_1', get the index 1
    index_start = len(image_name) + 1 
    frame_num = 0;
    for pic in os.listdir(directory):
        name, ext = os.path.splitext(pic)
        if ext.lower() in accept:
            index = int(name[index_start:])
            img = pg.image.load(os.path.join(directory, pic))
            if img.get_alpha():
                img = img.convert_alpha()
            else:
                img = img.convert()
                img.set_colorkey(colorkey)
            tmp[index]= img
            frame_num += 1

    for i in range(frame_num):
        frame_list.append(tmp[i])
    return frame_list

圖片顯示切換

在 source\component\zombie.py 中, Zombie 類是所有殭屍類的父類,初始化 函數調用loadImages函數加載所有支持的圖片類型,設置Sprite 精靈類顯示需要的成員變量 image和rect。
loadFrames函數給具體的子類來調用,獲取圖片。

class Zombie(pg.sprite.Sprite):
    def __init__(self, x, y, name, health, head_group=None, damage=1):
        pg.sprite.Sprite.__init__(self)
        
        self.name = name
        self.frames = []
        self.frame_index = 0
        self.loadImages()
        self.frame_num = len(self.frames)

        self.image = self.frames[self.frame_index]
        self.rect = self.image.get_rect()
        self.rect.centerx = x
        self.rect.bottom = y
		...

    def loadFrames(self, frames, name, image_x):
        frame_list = tool.GFX[name]
        rect = frame_list[0].get_rect()
        width, height = rect.w, rect.h
        width -= image_x

        for frame in frame_list:
            frames.append(tool.get_image(frame, image_x, 0, width, height))

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

  • update 函數:每個tick 都會調用的入口函數,用來更新殭屍的位置,切換狀態和更新圖片顯示。
  • handleState 函數:根據殭屍當前的狀態來執行不同的函數。
  • animation 函數:每隔指定的 animate_interval 時間會顯示圖片類型的下一個動作。
    def update(self, game_info):
        self.current_time = game_info[c.CURRENT_TIME]
        self.handleState()
        self.animation()
    
    def handleState(self):
        if self.state == c.WALK:
            self.walking()
        elif self.state == c.ATTACK:
            self.attacking()
        elif self.state == c.DIE:
            self.dying()
    
    def animation(self):
        if (self.current_time - self.animate_timer) > self.animate_interval:
            self.frame_index += 1
            if self.frame_index >= self.frame_num:
                if self.state == c.DIE:
                    self.kill()
                    return
                self.frame_index = 0
            self.animate_timer = self.current_time
        
        self.image = self.frames[self.frame_index]

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

  • setWalk 函數:修改爲行走狀態,圖片顯示會根據不同值設置不同的圖片類型。
  • setAttack 函數:修改爲攻擊狀態,圖片顯示會根據不同值設置不同的圖片類型。
  • setDie 函數:修改爲死亡狀態。
  • changeFrames 函數:修改圖片類型後,需要重新設置成員變量frame_num,frame_index, image和rect的值。
    def setWalk(self):
        self.state = c.WALK
        self.animate_interval = 150
        
        if self.helmet:
            self.changeFrames(self.helmet_walk_frames)
        elif self.losHead:
            self.changeFrames(self.losthead_walk_frames)
        else:
            self.changeFrames(self.walk_frames)

    def setAttack(self, plant):
        self.plant = plant
        self.state = c.ATTACK
        self.animate_interval = 100
        
        if self.helmet:
            self.changeFrames(self.helmet_attack_frames)
        elif self.losHead:
            self.changeFrames(self.losthead_attack_frames)
        else:
            self.changeFrames(self.attack_frames)
    
    def setDie(self):
        self.state = c.DIE
        self.animate_interval = 200
        self.changeFrames(self.die_frames)
    
    def changeFrames(self, frames):
        '''change image frames and modify rect position'''
        self.frames = frames
        self.frame_num = len(self.frames)
        self.frame_index = 0
        
        bottom = self.rect.bottom
        centerx = self.rect.centerx
        self.image = self.frames[self.frame_index]
        self.rect = self.image.get_rect()
        self.rect.bottom = bottom
        self.rect.centerx = centerx

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

class ConeHeadZombie(Zombie):
    def __init__(self, x, y, head_group):
        Zombie.__init__(self, x, y, c.CONEHEAD_ZOMBIE, c.CONEHEAD_HEALTH, head_group)
        self.helmet = True

    def loadImages(self):
        self.helmet_walk_frames = []
        self.helmet_attack_frames = []
        self.walk_frames = []
        self.attack_frames = []
        self.losthead_walk_frames = []
        self.losthead_attack_frames = []
        self.die_frames = []
        
        helmet_walk_name = self.name
        helmet_attack_name = self.name + 'Attack'
        walk_name = c.NORMAL_ZOMBIE
        attack_name = c.NORMAL_ZOMBIE + 'Attack'
        losthead_walk_name = c.NORMAL_ZOMBIE + 'LostHead'
        losthead_attack_name = c.NORMAL_ZOMBIE + 'LostHeadAttack'
        die_name = c.NORMAL_ZOMBIE + 'Die'

        frame_list = [self.helmet_walk_frames, self.helmet_attack_frames,
                      self.walk_frames, self.attack_frames, self.losthead_walk_frames,
                      self.losthead_attack_frames, self.die_frames]
        name_list = [helmet_walk_name, helmet_attack_name,
                     walk_name, attack_name, losthead_walk_name,
                     losthead_attack_name, die_name]
        
        for i, name in enumerate(name_list):
            self.loadFrames(frame_list[i], name, tool.ZOMBIE_RECT[name]['x'])

        self.frames = self.helmet_walk_frames

編譯環境

python3.7 + pygame1.9

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章