用pygame回顾飞机大战
  Nq4HJy6Hftbd 2023年11月02日 39 0



少小虽非投笔吏,论功还欲请长缨。

本文主要内容

  • 通过例子示范窗口绘制及其所需函数简要说明
  • 图片移动、通过用户事件移动图片及所需函数简要说明
  • 碰撞检测及所需函数简要说明
  • 音频加载及所需函数简要说明
  • 成果展示

环境

  • python3.6+
  • windows/linux

模块安装

pip install pygame

游戏窗口建立及所需函数简要说明

import os
import time
import pygame

# 屏幕宽度
SCREEN_WIDTH = 500
# 屏幕高度
SCREEN_HEIGHT = 250
# 图片绝对路径
IMG_PATH = os.path.join(os.getcwd(), "img")


def main():
   # 初始化显示模块
   pygame.display.init()
   # 创建窗口
   screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
   # 设置窗口标题
   pygame.display.set_caption("python小demo 飞机大战")
   # 设置窗口icon
   pygame.display.set_icon(pygame.image.load(os.path.join(IMG_PATH, "icon.jpg")))
   # 加载背景图片
   background = pygame.image.load(os.path.join(IMG_PATH, "test_bj.png"))

   while True:
       # 将背景图片绘制到窗口上
       screen.blit(background, (0, 0))
       # 更新屏幕上内容
       pygame.display.update()
       time.sleep(0.02)


if __name__ == "__main__":
   main()

查看下效果:


用pygame回顾飞机大战_python


x、y是我手动绘制的,让大家可以更加直观的看到窗口坐标轴情况


所需函数简要说明

display.init() -> None
# 该函数只做初始化显示模块,且不返回任何东西
# 如果在使用display其他方法未初始化,会自动调用

display.set_mode(size=(0, 0), flags=0, depth=0, display=0, vsync=0) -> Surface
# 该函数会创建并返回一个Surface的窗口对象
# 参数:
#   size=(0, 0) 设置窗口大小,第一个是屏幕宽度,第二个是窗口高度
#   flags 控制您想要的显示类型,例如是否全屏,可选参数:
#       pygame.FULLSCREEN:创建一个全屏
#       pygame.DOUBLEBUF:双缓冲模式,推荐和HWSURFACE或OPENGL一起使用
#       pygame.HWSURFACE:硬件加速,只有在 FULLSCREEN 下可以使用
#       pygame.OPENGL:创建一个 OPENGL 渲染的显示
#       pygame.RESIZABLE:创建一个可调整尺寸的窗口
#       pygame.NOFRAME:创建一个没有边框和控制按钮的窗口
#       可以使用按位或选择多个参数,如:
#           flags = pygame.OPENGL | pygame.FULLSCREEN
#   depth 参数表示颜色位数,不建议传递,默认会选择最佳的颜色深度
#   display 窗口索引,默认0
#   vsync 传入1时,可以获得垂直同步显示,只有同时设置了pygame.OPENGL、
#   pygame.FULLSCREEN才能传值

display.set_caption(title, icontitle=None) -> None
# 设置窗口标题
# 参数:
#   title 窗口的标题
#   icontitle 部分系统支持最小窗口时切换标题栏

display.set_icon(Surface) -> None
# 设置窗口LOGO
# 参数:
#   Surface 可以传入任何Surface对象作为图标,但大多数系统要求大小是32*32

display.update(rectangle=None) -> None
display.update(rectangle_list) -> None
# 更新屏幕内容显示,如果设置了pygame.OPENGL情况下调用,会触发异常
# 参数:
#   rectangle 一个矩形区域
#   rectangle_list 多个矩形区域
# 如果传入None或者空列表,就会更新整个窗口
image.load(filename) -> Surface
# 加载图像,返回一个Surface对象,pygame支持常见PNG、JPG、GIF等格式图片
#参数:
#   filename 图片路径,兼容运行环境,路径建议通过os.path.join()获取
Surface.blit(source, dest, area=None, special_flags=0) -> Rect
# 将图像绘制到另外一个图像上,返回值是一个Rect对象,表示在screen上绘制的矩形区域
# 参数:
#   source 矩形图像的Surface实例对象
#   dest 是坐标对象,绘制在屏幕的坐标
#   area 表示从scorce的area区域取出绘制的到Surface对象上
# 结合上面例子
# background = pygame.image.load(os.path.join(IMG_PATH, "test_bj.png"))
# screen.blit(background, (0, 0))
# 就是将background图片绘制到screen屏幕的0, 0坐标处

Surface.get_rect(**kwargs) -> Rect
# 获取Surface的矩形区域,该矩形区域始终从坐标0, 0开始,宽度和高度与图片大小相同
# 返回一个rect对象,rect对象记录着Surface的属性信息,如:
#   rect.x Surface对象x轴坐标
#   rect.y Surface对象y轴坐标
#   rect.width Surface对象的宽度
#   rect.height Surface对象的高度

图片移动、通过用户事件移动图片
上面代码已经能够建立游戏窗口和加载背景图,现在创建英雄,通过改变坐标实现移动

import os
import time
import pygame

# 屏幕宽度
SCREEN_WIDTH = 500
# 屏幕高度
SCREEN_HEIGHT = 250
# 图片绝对路径
IMG_PATH = os.path.join(os.getcwd(), "img")


class HeroPlane(object):
   def __init__(self):
       super(HeroPlane, self).__init__()
       self.image = pygame.image.load(os.path.join(IMG_PATH, "hero_show_1.png"))
       self.rect = self.image.get_rect()
       self.rect.x = 0  # 飞机x轴位置
       self.rect.y = int(SCREEN_HEIGHT - self.rect.height) / 2  # 飞机y轴位置
       self.is_running = True

       self.bullet_list = pygame.sprite.Group()
       self.last_time = time.time()

   def move_level(self, level):
       # 防止飞机移出屏幕外, 增加判断
       add_level = self.rect.x + level
       if 0 <= add_level <= SCREEN_WIDTH - self.rect.width:
           self.rect.x = add_level
       elif add_level < 0:
           self.rect.x = 0
       elif add_level > SCREEN_WIDTH - self.rect.width:
           self.rect.x = SCREEN_WIDTH - self.rect.width

   def move_vertical(self, vertical):
       add_vertical = self.rect.y + vertical
       if 0 <= add_vertical <= SCREEN_HEIGHT - self.rect.height:
           self.rect.y = add_vertical
       elif add_vertical < 0:
           self.rect.y = 0
       elif add_vertical > SCREEN_HEIGHT - self.rect.height:
           self.rect.y = SCREEN_HEIGHT - self.rect.height


def main():
   pygame.display.init()
   # 初始化显示模块
   # pygame.display.init()
   # 创建窗口
   screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
   # 设置窗口标题
   pygame.display.set_caption("python小demo 飞机大战")
   # 设置窗口icon
   pygame.display.set_icon(pygame.image.load(os.path.join(IMG_PATH, "icon.jpg")))
   # 加载背景图片
   background = pygame.image.load(os.path.join(IMG_PATH, "test_bj.png"))
   # 初始化英雄类
   hero = HeroPlane()

   def update_hero():
       hero.move_level(2)
       screen.blit(hero.image, (hero.rect.x, hero.rect.y))

   while True:

       # 将背景图片绘制到窗口上
       screen.blit(background, (0, 0))

       # 更新英雄在屏幕上的位置
       update_hero()

       # 更新屏幕上内容
       pygame.display.update()
       time.sleep(0.02)


if __name__ == "__main__":
   main()

执行效果如下:


用pygame回顾飞机大战_opengl_02

飞机移动

上面加载英雄并且实现英雄移动,其实就是不断改变图片在屏幕中的绘制位置。主函数的time.sleep也可以替换成pygame.time.Clock.tick方法,但本例子中,效果偏差不远,就没有使用pygame.time(https://www.pygame.org/docs/ref/time.html)提供的时间模块

上面代码虽然能够实现英雄的移动,但是无法根据玩家操作控制英雄的移动,这时候我们就需要提供特定的键盘或者鼠标事件控制英雄移动,下面示例按住鼠标实现飞机移动

import os
import sys
import time
import pygame

# 屏幕宽度
SCREEN_WIDTH = 500
# 屏幕高度
SCREEN_HEIGHT = 250
# 图片绝对路径
IMG_PATH = os.path.join(os.getcwd(), "img")
# 鼠标按键标识
MOUSE_MOVE = False


class HeroPlane(object):
   def __init__(self):
       super(HeroPlane, self).__init__()
       self.image = pygame.image.load(os.path.join(IMG_PATH, "hero_show_1.png"))
       self.rect = self.image.get_rect()
       self.rect.x = 0  # 飞机x轴位置
       self.rect.y = int(SCREEN_HEIGHT - self.rect.height) / 2  # 飞机y轴位置
       self.is_running = True

       self.bullet_list = pygame.sprite.Group()
       self.last_time = time.time()

   def move_level(self, level):
       # 防止飞机移出屏幕外, 增加判断
       if 0 <= level <= SCREEN_WIDTH - self.rect.width:
           self.rect.x = level
       elif level < 0:
           self.rect.x = 0
       elif level > SCREEN_WIDTH - self.rect.width:
           self.rect.x = SCREEN_WIDTH - self.rect.width

   def move_vertical(self, vertical):
       if 0 <= vertical <= SCREEN_HEIGHT - self.rect.height:
           self.rect.y = vertical
       elif vertical < 0:
           self.rect.y = 0
       elif vertical > SCREEN_HEIGHT - self.rect.height:
           self.rect.y = SCREEN_HEIGHT - self.rect.height


def main():
   pygame.init()
   # 初始化显示模块
   pygame.display.init()
   # 创建窗口
   screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
   # 设置窗口标题
   pygame.display.set_caption("python小demo 飞机大战")
   # 设置窗口icon
   pygame.display.set_icon(pygame.image.load(os.path.join(IMG_PATH, "icon.jpg")))
   # 加载背景图片
   background = pygame.image.load(os.path.join(IMG_PATH, "test_bj.png"))
   # 初始化英雄类
   hero = HeroPlane()

   def event_check():
       """
       事件检测,这边做了一个简单的示例
       return:
       """
       global MOUSE_MOVE
       for event in pygame.event.get():
           if event.type == pygame.QUIT:
               # 接收到退出事件后退出程序
               pygame.quit()
               sys.exit()
           elif event.type == pygame.MOUSEBUTTONDOWN:
               MOUSE_MOVE = True
           elif event.type == pygame.MOUSEBUTTONUP:
               MOUSE_MOVE = False

       if MOUSE_MOVE:
           x, y = pygame.mouse.get_pos()
           hero.move_level(x)
           hero.move_vertical(y)

   def update_hero():
       screen.blit(hero.image, (hero.rect.x, hero.rect.y))

   while True:
       # 事件检测
       event_check()

       # 将背景图片绘制到窗口上
       screen.blit(background, (0, 0))

       # 更新英雄在屏幕上的位置
       update_hero()

       # 更新屏幕上内容
       pygame.display.update()
       time.sleep(0.02)


if __name__ == "__main__":
   main()

执行效果如下:


用pygame回顾飞机大战_html_03

所需函数简要说明

event.get(eventtype=None) -> Eventlist
event.get(eventtype=None, pump=True) -> Eventlist
# 获取事件队列
# 参数:
#   eventtype 可以传入你想要监听到的事件列表,如果不传,会把所有用户触发的事件返回
#   如上面示例中只监听了三个事件,也可以:
#       pygame.event.get([pygame.QUIT, pygame.MOUSEBUTTONDOWN, pygame.MOUSEBUTTONUP])
#   但如果设置了指定类型事件,其他事件将不会被获取,存在了事件队列中中,有可能导致事件队列被堆满
#   pump 默认为False,如果设置为True,event.pump()将会被调用
# 返回事件列表
# 常见事件类型:
#   事件                  产生途径            参数
#   QUIT                 用户按下关闭按钮       none
#   KEYDOWN              键盘被按下            unicode, key, mod
#   KEYUP                键盘被放开            key, mod
#   MOUSEMOTION          鼠标移动             pos, rel, buttons
#   MOUSEBUTTONDOWN      鼠标按下             pos, button
#   MOUSEBUTTONUP        鼠标放开             pos, button
#   USEREVENT            用户自定义事件        code
# 上面列举了常见的事件,更多事件和方法可以点击上面文档链接查阅文档
  • pygame.key用于处理键盘的模块,当键盘按下或者释放时,pygame.event.get()就能获取pygame.KEYDOWN或pygame.KEYUP事件,这两个事件都有key和mod属性:key代表键盘上每个整数ID;mod代表事件发生时修饰建位掩码。
    https://www.pygame.org/docs/ref/key.html上述例子中,如果想通过长按键盘⬅、⬆、⬇、➡控制英雄,可将事件处理函数修改为:
def event_check():
   """
   事件检测,这边做了一个简单的示例
   return:
   """
   # 设置KEYDOWN标志位,0代表键盘已释放,1、2、3、4分别代表⬅、⬆、⬇、➡
   global KEYDOWN

   for event in pygame.event.get():
       if event.type == pygame.QUIT:
           # 接收到退出事件后退出程序
           pygame.quit()
           sys.exit()
       elif event.type == pygame.KEYDOWN:
           if event.key == pygame.K_LEFT:
               KEYDOWN = 1
           elif event.key == pygame.K_UP:
               KEYDOWN = 2
           elif event.key == pygame.K_DOWN:
               KEYDOWN = 3
           elif event.key == pygame.K_RIGHT:
               KEYDOWN = 4
       elif event.type == pygame.KEYUP:
           KEYDOWN = 0

   if KEYDOWN:
       if KEYDOWN == 1:
           hero.move_level(hero.rect.x - 5)
       elif KEYDOWN == 2:
           hero.move_vertical(hero.rect.y - 5)
       elif KEYDOWN == 3:
           hero.move_vertical(hero.rect.y + 5)
       else:
           hero.move_level(hero.rect.x + 5)

下面列举常见的键盘常量(更多键盘常量可点击上方连接查阅文档):

KeyASCII

描述

K_BACKSPACE

退格键(Backspace)

K_TAB

制表键(Tab)

K_CLEAR

清除键(Clear)

K_RETURN

回车键(Enter)

K_ESCAPE

退出键(Escape)

K_DELETE

删除键(delete)

K_0 - K_9

0 - 9

K_a - K_z

a - z

K_KP0 - K_KP9

0 - 9(小键盘)

K_UP

向上箭头(up arrow)

K_DOWN

向下箭头(down arrow)

K_RIGHT

向右箭头(right arrow)

K_LEFT

向左箭头(left arrow)

碰撞检测及所需函数简要说明
上面代码中,已经实现创建英雄,并且能通过事件去控制英雄的移动,下面我们可以按照创建英雄的方法,创建一个敌机,并通过让英雄与敌机碰撞发生爆炸

import os
import sys
import time
import pygame

# 屏幕宽度
SCREEN_WIDTH = 500
# 屏幕高度
SCREEN_HEIGHT = 250
# 图片绝对路径
IMG_PATH = os.path.join(os.getcwd(), "img")
# 鼠标按键标识
MOUSE_MOVE = False
# 键盘长按标识位
KEYDOWN = 0


class HeroPlane(object):
    def __init__(self):
        super(HeroPlane, self).__init__()
        self.image = pygame.image.load(os.path.join(IMG_PATH, "hero_show_1.png"))
        self.rect = self.image.get_rect()
        self.rect.x = 0  # 飞机x轴位置
        self.rect.y = int(SCREEN_HEIGHT - self.rect.height) / 2  # 飞机y轴位置
        self.is_running = True


    def move_level(self, level):
        # 防止飞机移出屏幕外, 增加判断
        if 0 <= level <= SCREEN_WIDTH - self.rect.width:
            self.rect.x = level
        elif level < 0:
            self.rect.x = 0
        elif level > SCREEN_WIDTH - self.rect.width:
            self.rect.x = SCREEN_WIDTH - self.rect.width

    def move_vertical(self, vertical):
        if 0 <= vertical <= SCREEN_HEIGHT - self.rect.height:
            self.rect.y = vertical
        elif vertical < 0:
            self.rect.y = 0
        elif vertical > SCREEN_HEIGHT - self.rect.height:
            self.rect.y = SCREEN_HEIGHT - self.rect.height


class EnemyPlane(pygame.sprite.Sprite):
    def __init__(self):
        super(EnemyPlane, self).__init__()
        self.image = pygame.image.load(os.path.join(IMG_PATH, "mp_show_3.png"))
        self.rect = self.image.get_rect()
        self.rect = self.image.get_rect()
        self.rect.x = int(SCREEN_WIDTH - self.rect.width)  # 飞机x轴位置
        self.rect.y = int(SCREEN_HEIGHT - self.rect.height) / 2  # 飞机y轴位置
        self.is_running = True


def main():
    pygame.init()
    # 初始化显示模块
    pygame.display.init()
    # 创建窗口
    screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
    # 设置窗口标题
    pygame.display.set_caption("python小demo 飞机大战")
    # 设置窗口icon
    pygame.display.set_icon(pygame.image.load(os.path.join(IMG_PATH, "icon.jpg")))
    # 加载背景图片
    background = pygame.image.load(os.path.join(IMG_PATH, "test_bj.png"))
    # 初始化英雄
    hero = HeroPlane()
    # 初始化敌机
    enemy = EnemyPlane()

    def event_check():
        """
        事件检测,这边做了一个简单的示例
        return:
        """
        global MOUSE_MOVE

        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                # 接收到退出事件后退出程序
                pygame.quit()
                sys.exit()
            elif event.type == pygame.MOUSEBUTTONDOWN:
                MOUSE_MOVE = True
            elif event.type == pygame.MOUSEBUTTONUP:
                MOUSE_MOVE = False

        if MOUSE_MOVE:
            x, y = pygame.mouse.get_pos()
            hero.move_level(x)
            hero.move_vertical(y)

    # def event_check():
    #     """
    #     事件检测,这边做了一个简单的示例
    #     return:
    #     """
    #     # 设置KEYDOWN标志位,0代表键盘已释放,1、2、3、4分别代表⬅、⬆、⬇、➡
    #     global KEYDOWN
    #
    #     for event in pygame.event.get():
    #         if event.type == pygame.QUIT:
    #             # 接收到退出事件后退出程序
    #             pygame.quit()
    #             sys.exit()
    #         elif event.type == pygame.KEYDOWN:
    #             if event.key == pygame.K_LEFT:
    #                 KEYDOWN = 1
    #             elif event.key == pygame.K_UP:
    #                 KEYDOWN = 2
    #             elif event.key == pygame.K_DOWN:
    #                 KEYDOWN = 3
    #             elif event.key == pygame.K_RIGHT:
    #                 KEYDOWN = 4
    #         elif event.type == pygame.KEYUP:
    #             KEYDOWN = 0
    #
    #     if KEYDOWN:
    #         if KEYDOWN == 1:
    #             hero.move_level(hero.rect.x - 5)
    #         elif KEYDOWN == 2:
    #             hero.move_vertical(hero.rect.y - 5)
    #         elif KEYDOWN == 3:
    #             hero.move_vertical(hero.rect.y + 5)
    #         else:
    #             hero.move_level(hero.rect.x + 5)

    def update_hero():
        screen.blit(hero.image, (hero.rect.x, hero.rect.y))

    def update_enemy():
        screen.blit(enemy.image, (enemy.rect.x, enemy.rect.y))

    def check_crash():
        is_crash = pygame.sprite.collide_rect(hero, enemy)
        if is_crash:
            # 先展示飞机爆炸,然后在屏幕中间写下game over提示
            screen.blit(pygame.image.load(os.path.join(IMG_PATH, "ex_2.png")), (hero.rect.x, hero.rect.y))
            screen.blit(pygame.image.load(os.path.join(IMG_PATH, "ex_2.png")), (enemy.rect.x, enemy.rect.y))

            game_over = pygame.font.Font(None, 100)
            game_over = game_over.render("game over", True, (220, 20, 60))
            screen.blit(game_over, [int(SCREEN_WIDTH / 2 - game_over.get_width() / 2), int(SCREEN_HEIGHT / 2)])

    while True:
        # 事件检测
        event_check()
        # 将背景图片绘制到窗口上
        screen.blit(background, (0, 0))
        # 更新英雄在屏幕上的位置
        update_hero()
        # 更新敌机在屏幕上位置
        update_enemy()
        # 碰撞检测
        check_crash()
        # 更新屏幕上内容
        pygame.display.update()
        time.sleep(0.02)


if __name__ == "__main__":
    main()

执行效果如下:


用pygame回顾飞机大战_opengl_04

所需函数简要说明

  • pygame.sprite 基本游戏对象类,上面例子中,我们英雄跟敌机都继承了pygame.sprite.Sprite,这是方便我们省略了造轮子的步骤,比如碰撞检测。
    https://www.pygame.org/docs/ref/sprite.html
sprite.collide_rect(left, right) -> bool
# 检测两个继承了pygame.sprite.Sprite且具有矩形区域属性的对象是否发生了碰撞
# 该方法会根据对象的rect属性判断是否发生了碰撞
# 参数:
#   left 第一个对象,如hero
#   right 第二个对象,如hero
# 返回bool值, 真为发生碰撞,假为没有碰撞

# 该示例中,只有一架敌机和一架英雄,但如果需要英雄与多架敌机发生碰撞
# 或者英雄发出的子弹是否击中了敌机,这就产生了一对多、多对多的检测
# 这种情况可以使用sprite.Group来完成该方法

sprite.Group() -> Group
# 用于保存和管理多个 Sprite 对象的容器类
# 返回一个组对象
sprite.Group.add(*sprites) -> None
# 增加一个sprites到该组中

sprite.spritecollide(sprite, group, dokill, collided = None) -> Sprite_list
# 查找一个sprite是否与sprite.Group()中的sprite发生碰撞
# 用于一对多的碰撞检测
# 参数:
#   sprite 单个sprite对象
#   group 一个包含多个sprite对象的组对象
#   dokill bool值,如果为真,发生碰撞时,将碰撞的sprite对象从组中移除
#   collided 是一个判断碰撞的函数,参数为两个sprite对象,并且需要返回bool值,如果你的
#   sprite对象也有像例子中设置了rect属性,可以不传
# 返回所有和sprite发生碰撞的对象,如果没有,返回空Sprite_list

sprite.groupcollide(group1, group2, dokill1, dokill2, collided = None) -> Sprite_dict
# 查找两组中所有碰撞的Sprite对象
# 用于实现多对多碰撞检测
# 参数:
#   group1 第一个sprite对象的组对象
#   group2 第二个sprite对象的组对象
#   dokill1 bool值,如果为真,发生碰撞时,将碰撞的sprite对象从第一个组中移除
#   dokill2 bool值,如果为真,发生碰撞时,将碰撞的sprite对象从第二个组中移除
#   collided 是一个判断碰撞的函数,参数为两个sprite对象,并且需要返回bool值,如果你的
#   sprite对象也有像例子中设置了rect属性,可以不传
# 返回一个Sprite_dict对象,类似于:
#   {<group1 Sprite(in 1 groups)>: [<group2 Sprite(in 1 groups)>]}
#   key 是group1 碰撞列表, value是group2碰撞列表

# 上面列举了本例需要的函数,更多使用方法可以点击上面文档链接查阅文档
font.Font(filename, size) -> Font
font.Font(object, size) -> Font
# 从给定的文件名或者python的文件对象加载新字体
# 参数:
#   filename 文件路径字符串,如os.path.join('fonts', 'ziti.ttf')
#   object python文件对象
#   如果第一个参数传None,会使用默认字体
#   size 整数类型,字体大小
# 返回一个Font对象

font.Font.render(text, antialias, color, background=None) -> Surface
# 创建一个新的 Surface,其上呈现了指定的文本,该方法只能写一行文字,不呈现换行,空字符串会引发异常
# 参数:
#   text 要展示的文本
#   antialias bool值,如果为真,字符将有平滑的边缘
#   color 字体颜色 RGB值
#   background 用于文本背景颜色,如果没有传递,背景色是透明色

# 上面列举了本例需要的函数,更多使用方法可以点击上面文档链接查阅文档

音频加载及所需函数简要说明

import os
import sys
import time
import pygame

# 屏幕宽度
SCREEN_WIDTH = 500
# 屏幕高度
SCREEN_HEIGHT = 250
# 图片绝对路径
IMG_PATH = os.path.join(os.getcwd(), "img")
# 鼠标按键标识
MOUSE_MOVE = False
# 键盘长按标识位
KEYDOWN = 0
# 背景音乐路径
MUSIC_PATH = os.path.join(os.getcwd(), "sound")


class HeroPlane(pygame.sprite.Sprite):
    def __init__(self):
        super(HeroPlane, self).__init__()
        self.image = pygame.image.load(os.path.join(IMG_PATH, "hero_show_1.png"))
        self.rect = self.image.get_rect()
        self.rect.x = 0  # 飞机x轴位置
        self.rect.y = int(SCREEN_HEIGHT - self.rect.height) / 2  # 飞机y轴位置
        self.is_running = True


    def move_level(self, level):
        # 防止飞机移出屏幕外, 增加判断
        if 0 <= level <= SCREEN_WIDTH - self.rect.width:
            self.rect.x = level
        elif level < 0:
            self.rect.x = 0
        elif level > SCREEN_WIDTH - self.rect.width:
            self.rect.x = SCREEN_WIDTH - self.rect.width

    def move_vertical(self, vertical):
        if 0 <= vertical <= SCREEN_HEIGHT - self.rect.height:
            self.rect.y = vertical
        elif vertical < 0:
            self.rect.y = 0
        elif vertical > SCREEN_HEIGHT - self.rect.height:
            self.rect.y = SCREEN_HEIGHT - self.rect.height


class EnemyPlane(pygame.sprite.Sprite):
    def __init__(self):
        super(EnemyPlane, self).__init__()
        self.image = pygame.image.load(os.path.join(IMG_PATH, "mp_show_3.png"))
        self.rect = self.image.get_rect()
        self.rect = self.image.get_rect()
        self.rect.x = int(SCREEN_WIDTH - self.rect.width)  # 飞机x轴位置
        self.rect.y = int(SCREEN_HEIGHT - self.rect.height) / 2  # 飞机y轴位置
        self.is_running = True


def main():
    pygame.init()
    # 初始化显示模块
    pygame.display.init()
    # 创建窗口
    screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
    # 设置窗口标题
    pygame.display.set_caption("python小demo 飞机大战")
    # 设置窗口icon
    pygame.display.set_icon(pygame.image.load(os.path.join(IMG_PATH, "icon.jpg")))
    # 加载背景图片
    background = pygame.image.load(os.path.join(IMG_PATH, "test_bj.png"))
    # 初始化英雄
    hero = HeroPlane()
    # 初始化敌机
    enemy = EnemyPlane()
    # 加载背景音乐并播放
    pygame.mixer.init()
    pygame.mixer.music.load(os.path.join(MUSIC_PATH, "game_music.ogg"))
    pygame.mixer.music.play(loops=-1)


    def event_check():
        """
        事件检测,这边做了一个简单的示例
        return:
        """
        global MOUSE_MOVE

        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                # 接收到退出事件后退出程序
                pygame.quit()
                sys.exit()
            elif event.type == pygame.MOUSEBUTTONDOWN:
                MOUSE_MOVE = True
            elif event.type == pygame.MOUSEBUTTONUP:
                MOUSE_MOVE = False

        if MOUSE_MOVE:
            x, y = pygame.mouse.get_pos()
            hero.move_level(x)
            hero.move_vertical(y)

    # def event_check():
    #     """
    #     事件检测,这边做了一个简单的示例
    #     return:
    #     """
    #     # 设置KEYDOWN标志位,0代表键盘已释放,1、2、3、4分别代表⬅、⬆、⬇、➡
    #     global KEYDOWN
    #
    #     for event in pygame.event.get():
    #         if event.type == pygame.QUIT:
    #             # 接收到退出事件后退出程序
    #             pygame.quit()
    #             sys.exit()
    #         elif event.type == pygame.KEYDOWN:
    #             if event.key == pygame.K_LEFT:
    #                 KEYDOWN = 1
    #             elif event.key == pygame.K_UP:
    #                 KEYDOWN = 2
    #             elif event.key == pygame.K_DOWN:
    #                 KEYDOWN = 3
    #             elif event.key == pygame.K_RIGHT:
    #                 KEYDOWN = 4
    #         elif event.type == pygame.KEYUP:
    #             KEYDOWN = 0
    #
    #     if KEYDOWN:
    #         if KEYDOWN == 1:
    #             hero.move_level(hero.rect.x - 5)
    #         elif KEYDOWN == 2:
    #             hero.move_vertical(hero.rect.y - 5)
    #         elif KEYDOWN == 3:
    #             hero.move_vertical(hero.rect.y + 5)
    #         else:
    #             hero.move_level(hero.rect.x + 5)

    def update_hero():
        screen.blit(hero.image, (hero.rect.x, hero.rect.y))

    def update_enemy():
        screen.blit(enemy.image, (enemy.rect.x, enemy.rect.y))

    def check_crash():
        is_crash = pygame.sprite.collide_rect(hero, enemy)
        if is_crash:
            # 先展示飞机爆炸,然后在屏幕中间写下game over提示
            screen.blit(pygame.image.load(os.path.join(IMG_PATH, "ex_2.png")), (hero.rect.x, hero.rect.y))
            screen.blit(pygame.image.load(os.path.join(IMG_PATH, "ex_2.png")), (enemy.rect.x, enemy.rect.y))
            pygame.mixer.Sound(os.path.join(MUSIC_PATH, "enemy1_down.wav")).play()

            game_over = pygame.font.Font(None, 100)
            game_over = game_over.render("game over", True, (220, 20, 60))
            screen.blit(game_over, [int(SCREEN_WIDTH / 2 - game_over.get_width() / 2), int(SCREEN_HEIGHT / 2)])

    while True:
        # 事件检测
        event_check()
        # 将背景图片绘制到窗口上
        screen.blit(background, (0, 0))
        # 更新英雄在屏幕上的位置
        update_hero()
        # 更新敌机在屏幕上位置
        update_enemy()
        # 碰撞检测
        check_crash()
        # 更新屏幕上内容
        pygame.display.update()
        time.sleep(0.02)


if __name__ == "__main__":
    main()

执行效果:

所需函数简要说明

mixer.init(
    frequency=44100,
    size=-16,
    channels=2,
    buffer=512,
    devicename=None,
    allowedchanges=AUDIO_ALLOW_FREQUENCY_CHANGE | AUDIO_ALLOW_CHANNELS_CHANGE
) -> None
# 初始化mixer模块以进行声音加载和播放。可以重写默认参数以提供特定的音频混合
# 参数:
#   frequency  pygame 2.0.0+时,范围44100-22050
#   size 表示每个音频样本使用了多少位。如果是负值,则使用带符号的样本值,无效值会触发异常,2.0.0后可以是32
#   channels 指定使用单声道还是立体音,1为单声道,2为立体音, 2.0.0后可以是4或者6
#   buffer 控制声音混合器mixer中使用的内部样本数,默认值应适用于大多数情况
#   allowedchanges 当allowedchanges=0,将在运行时转换示例以匹配硬件支持的内容,allowedchanges也支持:
#       AUDIO_ALLOW_FREQUENCY_CHANGE
#       AUDIO_ALLOW_FORMAT_CHANGE
#       AUDIO_ALLOW_CHANNELS_CHANGE
#       AUDIO_ALLOW_ANY_CHANGE

mixer.music.load(filename) -> None
mixer.music.load(object) -> None
# 将加载音乐文件/或者音乐文件对象并准备播放,如果音乐已经播放,调用则会停止

mixer.music.play(loops=0, start=0.0, fade_ms = 0) -> None
# 播放已经加载的音乐流,如果已经在播放时调用,则会重新播放
# 参数:
#   loops 可选整数参数,表示重复播放次数,设置为-1表示无限重复
#   start 可选浮点数,表示开始播放音乐时间的位置,起始位置决定于音乐格式
#   fade_ms 可选整数参数,大于0时,在给定的fade_ms内逐渐加大音量

pygame.mixer.Sound(filename) -> Sound
pygame.mixer.Sound(buffer) -> Sound
# 从文件或者缓冲区创建一个新的文件对象
pygame.mixer.Sound.play(loops=0, maxtime=0, fade_ms=0) -> Channel
# 开始在可用的频道上播放声音,播放时可能在必要时切断正在播放的声音
# 在本例中,调用该方法产生爆炸音效并不会影响背景音乐的播放
# 参数:
#   loops 可选整数参数,表示重复播放次数,设置为-1表示无限重复
#   maxtime 大于零时,在给定的maxtime停止播放(毫秒)
#   fade_ms 可选整数参数,大于0时,在给定的fade_ms内逐渐加大音量
# 返回所选的通道对象

# 上面列举了本例需要的函数,更多使用方法可以点击上面文档链接查阅文档

python小demo 飞机大战完整例子

在上面,大致示范了使用pygame写一个飞机大战所需模块及函数,本人也写了一个示例,包括本推文中所使用的代码都提交至github,在本公众号中回复“飞机大战”即可获取源码地址,及图片素材、音频素材,下面看下我例子效果:


【版权声明】本文内容来自摩杜云社区用户原创、第三方投稿、转载,内容版权归原作者所有。本网站的目的在于传递更多信息,不拥有版权,亦不承担相应法律责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@moduyun.com

  1. 分享:
最后一次编辑于 2023年11月08日 0

暂无评论

推荐阅读
Nq4HJy6Hftbd