python pygbag教程 —— 在网页上运行pygame程序(全网中文教程首发)

pygame是一款流行的游戏制作模块,经过特殊的方式编译后,可以在浏览器web网页上运行。web上的打包主要使用第三方模块pygbag。

pygame教程:Python pygame(GUI编程)模块最完整教程(1)_pygame模块详解_Python-ZZY的博客-CSDN博客

1 pygbag简介

pygbag是经过官方认可的一个第三方模块,专用于编译pygame使其在网页上运行。该网址演示了一个经过pygbag打包的pygame程序:Minesweeper by Python ZZY -China

注意:在国内访问itch和github速度可能较慢,通常要加载很久。(建议开VPN)

pygbag打包后文件经过压缩,占用较少的空间。pygbag应用首次加载速度较慢,后面的速度会变快。

需要注意的是:pygbag依赖于Chrome内核的浏览器(如google, firefox),否则无法正常运行

pygbag源码:GitHub - pygame-web/pygbag: python and pygame wasm for everyone ( packager + test server + simulator )

2 安装

通过pip即可安装:

pip install pygbag

需要注意的是,pygbag依赖于一大堆的第三方库,所以强烈建议安装和使用都在虚拟环境中进行。

3 规范

进行打包的python-pygame程序文件夹中必须包含一个main.py文件(或者不使用文件夹而是一个单独的程序文件),作为应用运行的进入点。并且程序中的主循环必须以异步的形式进行,在进行异步的循环之前,最好提前定义常量、导入素材,以避免出现网络错误。

注意事项如下:

  • pygbag依赖于Chrome内核的浏览器(如google, firefox),否则无法正常运行
  • 需要包含一个main函数,并且是异步的形式
  • 可以在main函数的外面导入模块或导入一些必要的素材文件,这样能避免一些网络错误。
  • 素材中需要在pygame中播放的声音文件必须为*.ogg格式
  • 调用pygame.display.flip或pygame.display.update刷新pygame屏幕时,必须加上await asyncio.sleep(0)这一句。
  • 如果使用了3D/WebGL,打包时需要添加选项:--template noctx.tmpl
  • 在代码中使用time.sleep, pygame.time.wait是无效的,必须使用await asyncio.sleep(...)来替代

下面的例子叙述了代码的规范,读者可查看其中的注释:


import asyncio
import pygame as pg

# 在此处一次性定义所有常量,以便于编译
WIDTH = 700
HEIGHT = 300

# 如果有附带文件(图片、声音等素材文件),则在此处载入(可通过缓存lru_cache),以避免网络错误
# 在pygame播放的声音文件必须为*.ogg
...

async def main():

    pg.init()
    screen = pg.display.set_mode((WIDTH, HEIGHT))

    while True:
        # 在此处绘制屏幕、处理事件、控制帧率……

        for event in pg.event.get():
            ...
        
        pg.display.flip() # 或者:pg.display.update()
        await asyncio.sleep(0)  # 这行代码非常重要,相当于刷新屏幕,和flip()一起调用

# 这是程序的进入点
asyncio.run(main())

# 此处不要写任何代码

4 打包

打包时可以通过cmd调用pygbag。打包需要提供一个python文件或者一个包含main.py的文件夹。pygbag会将这个文件所在目录的所有东西打包到一起(或者将指定文件夹下的所有东西打包到一起),因此,无需提供依赖的文件。(这也意味着没有把素材放在同一文件夹而是随意放置的方式是行不通的)

最基本的打包方式如下:

python -m pygbag your.app.folder

此外还支持如下命令行选项(虽然数量多,但有用的不多):

选项如下(后面的[default]中是选项的默认值):
  -h, --help            显示
  --bind ADDRESS        指定备用绑定地址 [default: localhost]
  --directory DIRECTORY 指定替代目录 [default:/data/git/pygbag/your.app.folder/build/web]
  --PYBUILD PYBUILD     指定python版本 [default:3.11]
  --app_name APP_NAME   指定应用程序的用户名称 [default:your.app.folder]
  --ume_block UME_BLOCK 指定在运行前等待用户媒体参与 [default:1]
  --can_close CAN_CLOSE 指定是否窗口要求确认关闭(如果是,在关闭网页时会显示提示) [default:0]
  --cache CACHE         md5基于url的缓存目录(缓存可以避免重复加载,且会自动进行清理)
  --package PACKAGE     包裹名,最好是唯一的
  --title TITLE         应用的名称(网页标题)
  --version VERSION     覆盖预构建的版本路径 [default:0.6.0]
  --build               只进行构建,不运行测试服务器(默认可以在本机进行测试)
  --html                构建html,资源文件嵌入到html文件中
  --no_opt              关闭资源文件优化器(默认开启,此时可以压缩资源文件)
  --archive             生成build/web.zip文件,用于直接上传到itch.io
  --icon ICON           应用图标文件(png/ico,最小32x32)
  --cdn CDN             本地缓存网站(用于缓存一些必要的资源) [default:https://pygame-web.github.io/archives/0.6/]
  --template TEMPLATE   index.html样式模板 [default:default.tmpl]
  --ssl SSL             启用ssl加密(server.pem and key.pem)
  --port [PORT]         指定备用端口 [default: 8000]

下面将以一个程序为例展示部分选项的作用。

5 示例

程序展示

在开始打包前,先展示一下本次打包使用的程序示例,如下所示:


import asyncio
import pygame as pg
from functools import lru_cache

@lru_cache()
# 用于缓存函数的返回值:
# 程序中第二次调用该函数时,如果参数和原先一样,
# 那么直接返回第一次调用该函数时存储的值
def load_image(name):
    return pg.image.load(name)

WIDTH = 700
HEIGHT = 300

load_image("assets/test.png")
# 在开始前加载素材文件
# 由于我的素材比较少,所以可以直接这么加载。有需要的也可以
# 通过os遍历整个素材文件夹然后加载所有内容。

async def main():
    pg.init()
    screen = pg.display.set_mode((WIDTH, HEIGHT))
    clock = pg.time.Clock()

    color = 0
    incr = 2

    while True:
        screen.fill((color, color, color))
        screen.blit(load_image("assets/test.png"), (0, 0))

        color += incr
        if not 0 <= color <= 255:
            incr = -incr
            color += incr
        
        for event in pg.event.get():
            if event.type == pg.QUIT:
                pg.quit()

        clock.tick(60)
        pg.display.flip()

        await asyncio.sleep(0)

if __name__ == "__main__":
    asyncio.run(main())

运行此程序后,可以看见屏幕上显示了一张图片test.png,同时背景迅速地由白色变成黑色,然后再由黑色变成白色。

下面展示这个程序文件夹的目录(有一个main.py,还有一个assets文件夹来存放素材文件):

easy_game
│  main.py
└─ assets
      │  icon.png
      └─ test.png

接下来,正式进入打包教程环节。

打包

首先启动cmd,如有虚拟环境将其激活。进入easy_game所在的文件夹,输入:


python -m pygbag --title EasyGame --can_close 1 --icon easy_game\assets\icon.png easy_game

这表示将标题设置为EasyGame;图标为assets目录下的icon.png;退出游戏时会弹出提示框确认是否关闭。最后的easy_game是游戏文件夹。

 

经过一系列处理后,末尾显示在http://localhost:8000/运行测试界面,在本机上可以输入该网址试试打包后的效果。如果想要退出测试,在cmd按Ctrl+C即可。

如果在打包时加上--build参数,则不会运行测试。

初次加载时速度非常慢,但是之后再次启动时加载速度会变快,这是由于缓存的原因。作者在本机测试时效果非常不好,一直无法加载出来,并且还有很多404错误,打开VPN后效果明显变好。

调试

pygbag提供了一个调试debug页面,运行服务器后,进入网址:

http://localhost:8000#debug

发布到itch

发布pygbag程序主要是发布到itch(一个游戏平台),在其他地方发布比较麻烦。

首先需要进入游戏文件夹中的build文件夹,将其中的web文件夹压缩为zip文件。(如果打包时加上--archive选项,那么这个压缩包会自动生成)

然后进入https://itch.io/,此处省略登录等一系列流程。点开右上角的箭头,选择Upload new project上传项目(itch是支持中文的,可自行设置)。

填写项目标题后,找到下面的上传文件部分,点击Upload files,并将web.zip上传。(itch上传文件效果不好,如果文件大,必须开VPN才能成功上传)

然后在上面设置项目类型为HTML,同时勾选web.zip文件旁边下面的This file will be played in the browser。

 

还可以在下方设置游戏运行时的一些选项。选项包括:是否能在手机上运行,是否自动加载,是否允许全屏显示,是否支持滚动条等等。

可以在下方设置该游戏状态为公开。

最后保存页面。这样就完成了上传工作。

打开页面后点击Run Game,等它下载一会儿,然后就会跳出一个加载进度条。如果跳出了这个进度条说明前面的Downloading已经没有问题了,只需要一点时间加载一些素材。如果很长时间都停留在Downloading界面,可以右击页面选择“检查”,选择“控制台”,看是否出现了很多的错误;如果有错误可以重新加载页面。

加载完成后游戏即可在浏览器上运行。

发布到github 

首先下载这个文件:https://pygame-web.github.io/wiki/pygbag/github.io/pygbag.yml

打开文件后,可以把16行关于pygbag打包的语句修改掉。 

然后把这个文件放到你的仓库中,位于[你的仓库名]/.github/workflows(注意github前面的那个点不要忘了)。

接下来进入设置,将“工作流权限”设为可读可写。 按照步骤点击。

然后运行动作。

出现绿色的勾表示运行成功。运行成功后,会生成一个分支gh_pages。

再返回设置页面,将分支设为gh_pages,并保存。

返回Actions,此时会多出一个pages build and deployment自动运行。等待它运行完毕。

再返回Settings - Page,如果网站部署完成会在上方显示网址,点Visit site即可运行游戏。

如果网站上只显示了README,说明pages build and deployment还没有运行完成就启动了网站。需等待其运行完成再重启网站