1. 程式人生 > >pygame編寫飛機大戰(9)-播放爆炸動畫

pygame編寫飛機大戰(9)-播放爆炸動畫

當子彈和敵機碰撞,我們並不像單單將飛機和子彈消失,應該在其發生碰撞的位置播放一組爆炸動畫,遊戲效果更為逼真一些。那如何實現動畫呢?動畫實現思路很簡單,我們將一組圖片按照一定的時間間隔,將其按順序繪製到螢幕上,即可顯示為動畫效果。


這幾張圖片,我們首先將其load到遊戲中,存放到一個列表中。當發生爆炸的時候,我們就將列表中的圖片按照預先定義好的順序,繪製到螢幕上。

那麼爆炸類如何寫呢?

class Bomb(object):
    # 初始化爆炸
    def __init__(self, scene):
        self.main_scene = scene
        # 載入爆炸資源
        self.image = [pygame.image.load("res/bomb-" + str(v) + ".png") for v in range(1, 8)]
        # 設定當前爆炸播放索引
        self.index = 0
        # 圖片爆炸播放間隔
        self.interval = 20
        self.interval_index = 0
        # 爆炸位置
        self.position = [0, 0]
        # 是否可見
        self.visible = False

    # 設定爆炸播放的位置
    def set_pos(self, x, y):
        self.position[0] = x
        self.position[1] = y

    # 爆炸播放
    def action(self):
        # 如果爆炸物件狀態不可見,則不計算座標
        if not self.visible:
            return

        # 控制每一幀圖片的播放間隔
        self.interval_index += 1
        if self.interval_index < self.interval:
            return
        self.interval_index = 0

        self.index = self.index + 1
        if self.index >= len(self.image):
            self.index = 0
            self.visible = False

    # 繪製爆炸
    def draw(self):
        # 如果物件不可見,則不繪製
        if not self.visible:
            return
        self.main_scene.scene.blit(self.image[self.index], (self.position[0], self.position[1]))
建立好爆炸的類,我們也預先在場景上建立多個爆炸物件,當子彈與敵機發生碰撞時,從列表中取出一個爆炸物件,將其播放顯示。

在GameScene類的__init__方法中建立爆炸物件列表如下:

   # 初始化主場景
    def __init__(self):
        # 場景尺寸
        self.size = (512, 768)
        # 場景物件
        self.scene = pygame.display.set_mode([self.size[0], self.size[1]])
        # 設定標題
        pygame.display.set_caption("飛機大戰-v1.0")
        # 地圖物件
        self.map = GameBackground(self)
        # 英雄物件
        self.hero = HeroPlane(self)
        # 建立多個敵機
        self.enemy_list = [EnemyPlane(self) for _ in range(3)]
        # 建立爆炸物件
        self.bombs = [Bomb(self) for _ in range(5)]

碰撞檢測detect_colision函式修改如下:

   # 碰撞檢測
    def detect_conlision(self):
        # 檢測英雄子彈是否和敵機碰撞
        for bullet in self.hero.bullets:
            # 如果子彈不可見,說明子彈處於閒置狀態,直接continue
            if not bullet.visible:
                continue
            for enemy in self.enemy_list:
                # 判斷子彈的矩形和飛機的矩形是否相交
                if pygame.Rect.colliderect(bullet.rect, enemy.rect):
                    # 子彈設定為不可見
                    bullet.visible = False
                    # 從預先建立完畢的爆炸中取出一個爆炸物件
                    for bomb in self.bombs:
                        if not bomb.visible:
                            # 爆炸物件設定爆炸位置
                            bomb.set_pos(enemy.rect[0], enemy.rect[1])
                            # 爆炸物件狀態設定為True
                            bomb.visible = True
                            break
                    # 敵人飛機重新設定位置和速度
                    enemy.set_pos(random.randint(0, self.size[1] - enemy.rect[2] - 20), 0)
                    enemy.speed = random.randint(2, 5)
                    break
draw_elements函式修改如下:
    # 繪製
    def draw_elements(self):
        # 繪製地圖
        self.map.draw()
        # 繪製英雄飛機
        self.hero.draw()
        # 依次繪製英雄飛機每一顆發射出去的子彈
        for bullet in self.hero.bullets:
            if bullet.visible:
                bullet.draw()
        # 繪製敵人飛機和飛機子彈
        for plane in self.enemy_list:
            plane.draw()
        # 繪製爆炸圖片
        for bomb in self.bombs:
            if bomb.visible:
                bomb.draw()
action_elements函式修改如下:
    # 動作
    def action_elements(self):
        # 計算座標地圖
        self.map.action()
        # 依次計算英雄飛機每一顆發射子彈的座標
        for bullet in self.hero.bullets:
            if bullet.visible:
                bullet.action()
        # 計算敵人飛機和其飛機子彈
        for plane in self.enemy_list:
            plane.action()
        # 切換爆炸圖片
        for bomb in self.bombs:
            if bomb.visible:
                bomb.action()
完整程式碼為:
import pygame
import random


# 子彈
class Bullet(object):
    # 初始化子彈
    def __init__(self, scene, enemy=False):
        # 子彈移動速度
        self.speed = 2
        # 是否是敵人子彈
        self.is_enemy = enemy
        # 子彈資源
        if self.is_enemy:
            # 載入敵人子彈圖片
            self.image = pygame.image.load("./res/bullet_1.png")
            # 設定子彈移動方向
            self.speed = self.speed
        else:
            # 載入英雄子彈圖片
            self.image = pygame.image.load("./res/bullet_11.png")
            # 設定子彈移動方向
            self.speed = -self.speed
        # 子彈是否可見
        self.visible = False
        # 持有主場景物件
        self.main_scene = scene
        # 獲得子彈矩形(x, y, width, height)
        self.rect = self.image.get_rect()

    # 設定子彈位置
    def set_pos(self, x, y):
        self.rect[0] = x
        self.rect[1] = y

    # 設定子彈速度
    def set_speed(self, speed):
        if self.is_enemy:
            self.speed = speed
        else:
            self.speed = -speed

    # 子彈移動
    def action(self):
        if not self.visible:
            return
        # 假設飛機矩形為plane_rect(10, 20, 200, 300)
        # plane_rect.move_ip(10, 20), 那麼結果是plane_rect(20, 40, 200, 300)
        # 也就是原矩形x和y座標加上move_ip函式x和y座標,就是當前矩形新位置
        self.rect.move_ip(0, self.speed)
        # 如果子彈超出場景範圍,則設定為不可見
        if self.rect[1] < 0 or self.rect[1] > self.main_scene.size[1]:
            self.visible = False

    # 繪製子彈
    def draw(self):
        if not self.visible:
            return
        self.main_scene.scene.blit(self.image, (self.rect[0], self.rect[1]))


# 地圖
class GameBackground(object):
    # 初始化地圖
    def __init__(self, scene):
        # 載入相同張圖片資源,做交替實現地圖滾動
        self.image1 = pygame.image.load("res/img_bg_level_3.jpg")
        self.image2 = pygame.image.load("res/img_bg_level_3.jpg")
        # 儲存場景物件
        self.main_scene = scene
        # 輔助移動地圖
        self.y1 = 0
        self.y2 = -self.main_scene.size[1]

    # 計算地圖圖片繪製座標
    def action(self):
        self.y1 = self.y1 + 1
        self.y2 = self.y2 + 1
        if self.y1 >= self.main_scene.size[1]:
            self.y1 = 0
        if self.y2 >= 0:
            self.y2 = -self.main_scene.size[1]

    # 繪製地圖的兩張圖片
    def draw(self):
        self.main_scene.scene.blit(self.image1, (0, self.y1))
        self.main_scene.scene.blit(self.image2, (0, self.y2))

# 飛機
class HeroPlane(object):
    # 飛機初始化
    def __init__(self, scene):
        # 載入飛機資源
        self.image = pygame.image.load("./res/hero2.png")
        # 快取主場景物件
        self.main_scene = scene
        # 飛機矩形
        self.rect = self.image.get_rect()
        # 矩形起始點
        self.rect[0] = self.main_scene.size[0] / 2 - self.rect[2] / 2
        self.rect[1] = self.main_scene.size[1] - self.rect[3] * 2
        # 飛機子彈列表
        self.bullets = [Bullet(self.main_scene) for _ in range(1, 30)]

    # 獲得飛機  self.mImage = pygame.image.load("./res/bullet_11.png")矩形
    def rect(self):
        return self.rect

    # 發子彈
    def shot(self):
        # 每次發射三顆子彈
        wait_for_shot = []
        # 從子彈列表取出3顆目前尚未發射的子彈
        # 如果子彈的visible為false,說明子彈尚未發射
        for bullet in self.bullets:
            # 如果子彈不可見,說明子彈閒置狀態
            if not bullet.visible:
                wait_for_shot.append(bullet)
                if len(wait_for_shot) >= 3:
                    break
        # 子彈發射位置,從posx位置開始 向右排列三顆子彈
        posx = self.rect[0] - 15
        # 依次設定選擇子彈的初始位置,並將其設定為發射狀態、移動速度
        for bullet in wait_for_shot:
            bullet.visible = True
            posx = posx + 30
            bullet.set_speed(4)
            bullet.set_pos(posx + 5, self.rect[1] - self.rect[3] / 2)

    # 飛機動作
    def action(self, x, y):
        self.rect[0] = x - self.rect[2] / 2
        self.rect[1] = y - self.rect[3] / 2

    # 飛機繪製
    def draw(self):
        self.main_scene.scene.blit(
            self.image, (self.rect[0], self.rect[1]))

# 敵人飛機
class EnemyPlane(object):
    # 初始化敵人飛機
    def __init__(self, scene):
        # 載入飛機資源
        self.image = pygame.image.load("./res/img-plane_1.png")
        # 快取主場景物件
        self.main_scene = scene
        # 飛機矩形
        self.rect = self.image.get_rect()
        # 子彈列表
        self.bullet = Bullet(self.main_scene, True)
        # 飛機速度
        self.speed = 2

    # 獲得飛機矩形
    def rect(self):
        return self.rect


    # 設定飛機位置
    def set_pos(self, x, y):
        self.rect[0] = x
        self.rect[1] = y

    # 飛機動作
    def action(self):
        # 子彈每次移動向上移動self.speed速度
        self.rect.move_ip(0, self.speed)
        # 如果飛機移動出螢幕則將飛機設定為不可見狀態
        if self.rect[1] > self.main_scene.size[1]:
            # 當飛機飛出螢幕,重新設定飛機的初始位置,移動速度
            # 隨機產生x座標,縱座標使用為0
            self.set_pos(random.randint(0, self.main_scene.size[1] - self.rect[2] - 20), 0)
            # 隨機設定飛機移動速度
            self.speed = random.randint(2, 5)

    # 繪製飛機
    def draw(self):
        self.main_scene.scene.blit(self.image, (self.rect[0], self.rect[1]))

class Bomb(object):
    # 初始化爆炸
    def __init__(self, scene):
        self.main_scene = scene
        # 載入爆炸資源
        self.image = [pygame.image.load("res/bomb-" + str(v) + ".png") for v in range(1, 8)]
        # 設定當前爆炸播放索引
        self.index = 0
        # 圖片爆炸播放間隔
        self.interval = 20
        self.interval_index = 0
        # 爆炸位置
        self.position = [0, 0]
        # 是否可見
        self.visible = False

    # 設定爆炸播放的位置
    def set_pos(self, x, y):
        self.position[0] = x
        self.position[1] = y

    # 爆炸播放
    def action(self):
        # 如果爆炸物件狀態不可見,則不計算座標
        if not self.visible:
            return

        # 控制每一幀圖片的播放間隔
        self.interval_index += 1
        if self.interval_index < self.interval:
            return
        self.interval_index = 0

        self.index = self.index + 1
        if self.index >= len(self.image):
            self.index = 0
            self.visible = False

    # 繪製爆炸
    def draw(self):
        # 如果物件不可見,則不繪製
        if not self.visible:
            return
        self.main_scene.scene.blit(self.image[self.index], (self.position[0], self.position[1]))


# 主場景
class MainScene(object):
    # 初始化主場景
    def __init__(self):
        # 場景尺寸
        self.size = (512, 768)
        # 場景物件
        self.scene = pygame.display.set_mode([self.size[0], self.size[1]])
        # 設定標題
        pygame.display.set_caption("飛機大戰-v1.0")
        # 地圖物件
        self.map = GameBackground(self)
        # 英雄物件
        self.hero = HeroPlane(self)
        # 建立多個敵機
        self.enemy_list = [EnemyPlane(self) for _ in range(3)]
        # 建立爆炸物件
        self.bombs = [Bomb(self) for _ in range(5)]


    # 繪製
    def draw_elements(self):
        # 繪製地圖
        self.map.draw()
        # 繪製英雄飛機
        self.hero.draw()
        # 依次繪製英雄飛機每一顆發射出去的子彈
        for bullet in self.hero.bullets:
            if bullet.visible:
                bullet.draw()
        # 繪製敵人飛機和飛機子彈
        for plane in self.enemy_list:
            plane.draw()
        # 繪製爆炸圖片
        for bomb in self.bombs:
            if bomb.visible:
                bomb.draw()


    # 動作
    def action_elements(self):
        # 計算座標地圖
        self.map.action()
        # 依次計算英雄飛機每一顆發射子彈的座標
        for bullet in self.hero.bullets:
            if bullet.visible:
                bullet.action()
        # 計算敵人飛機和其飛機子彈
        for plane in self.enemy_list:
            plane.action()
        # 切換爆炸圖片
        for bomb in self.bombs:
            if bomb.visible:
                bomb.action()

    # 處理事件
    def handle_event(self):
        # 如果玩家點選右上角的X按鈕,則關閉視窗
        event_list = pygame.event.get()
        # 遍歷事件列表
        for event in event_list:
            # 如果判斷使用者點選了X按鈕,則結束程式
            if event.type == pygame.QUIT:
                pygame.quit()
                exit()
            # 判斷事件型別是否是鍵盤按下事件
            if event.type == pygame.KEYDOWN:
                # 如果是鍵盤按下,再判斷玩家按下的是不是j鍵
                if event.key == pygame.K_j:
                    self.hero.shot()

            # 判斷是否發生了滑鼠拖動事件
            if event.type == pygame.MOUSEMOTION:
                # 獲得滑鼠點選三個按鈕的點選情況(1,0,0)
                # 如果第一個引數為1,表示左鍵被按下
                # 如果第二個引數為1,表示滾輪被按下
                # 如果第三個引數為1,表示右鍵被按下
                buttons = pygame.mouse.get_pressed()
                # 我們只處理左鍵被按下的情況
                if buttons[0]:
                    # 獲得拖動滑鼠的拖動位置
                    position = pygame.mouse.get_pos()
                    # 飛機跟隨座標移動
                    # print(position)
                    self.hero.action(position[0], position[1])


    # 碰撞檢測
    def detect_conlision(self):
        # 檢測英雄子彈是否和敵機碰撞
        for bullet in self.hero.bullets:
            # 如果子彈不可見,說明子彈處於閒置狀態,直接continue
            if not bullet.visible:
                continue
            for enemy in self.enemy_list:
                # 判斷子彈的矩形和飛機的矩形是否相交
                if pygame.Rect.colliderect(bullet.rect, enemy.rect):
                    # 子彈設定為不可見
                    bullet.visible = False
                    # 從預先建立完畢的爆炸中取出一個爆炸物件
                    for bomb in self.bombs:
                        if not bomb.visible:
                            # 爆炸物件設定爆炸位置
                            bomb.set_pos(enemy.rect[0], enemy.rect[1])
                            # 爆炸物件狀態設定為True
                            bomb.visible = True
                            break
                    # 敵人飛機重新設定位置和速度
                    enemy.set_pos(random.randint(0, self.size[1] - enemy.rect[2] - 20), 0)
                    enemy.speed = random.randint(2, 5)
                    break

    # 主迴圈,主要處理各種事件
    def run_scene(self):

        while True:
            # 計算元素座標
            self.action_elements()
            # 繪製元素圖片
            self.draw_elements()
            # 處理事件
            self.handle_event()
            # 碰撞檢測
            self.detect_conlision()
            # 重新整理顯示
            pygame.display.update()


# 入口函式
if __name__ == "__main__":
    # 建立主場景
    mainScene = MainScene()
    # 開始遊戲
    mainScene.run_scene()
最終執行效果為:


至此,基本的遊戲就已經完成了。細節大家自己來完善。整體來講就是一個視窗,背景地圖、飛機、子彈、爆炸等等都是在視窗上的一個元素,就像是舞臺和演員的關係,什麼樣的演員什麼時間什麼時刻什麼條件下出現在舞臺的某個位置做某件事。最主要的邏輯,就是計算圖片位置,繪製圖片位置,遊戲中加入了事件處理,可以讓玩家來控制角色位置和行為,最終形成簡單的遊戲實現思路。

完整程式碼和素材下載:連結:http://pan.baidu.com/s/1hslDFDy  密碼:1qsi