本文主要介绍了如何使用 pygame 和 pygbag 搭建游戏框架,使其可以直接在浏览器网页运行。
pygame 网页框架
https://github.com/Newverse-Wiki/Code-for-Blog/tree/main/Pygame-On-Web/Skeleton
总览
本文给出网页运行 pygame 的基本框架,后续程序仅需考虑游戏内容,无需关注网页适配等问题。
项目代码可从上述 GitHub - Skeleton 链接下载。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
# 项目结构
$ tree Skeleton
Skeleton
├── build
│ └── web
│ ├── favicon.png
│ ├── index.html
│ └── skeleton.apk
├── debug.py
├── game.py
└── main.py
# python 运行
$ python Skeleton/main.py
# pygbag 打包
$ pygbag Skeleton
|
build/web
目录下为 pygbag 打包好的网页项目,将该目录下的所有内容上传至网站服务器即可完成部署。
main.py
pygbag 打包游戏内容时要求游戏以 main.py
为入口
将涉及到与 asyncio 相关的代码均放置在 main.py
中,
后续代码中无序考虑 asyncio 异步处理、等待等问题。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
"""pygbag 打包游戏内容时要求游戏以 main.py 为入口
将涉及到与 asyncio 相关的代码均放置在 main.py 中,
后续代码中无序考虑 asyncio 异步处理、等待等问题。
"""
# 网页运行 python 需要引入 asyncio
import asyncio
# 游戏主代码存放在 game.py 中,作为模块引入
from game import Game
# main() 主函数需要由 async 定义
async def main():
# 游戏主体为 Game 类的实例
# 初始化时确定游戏界面的尺寸、刷新率
game = Game((800, 600), FPS = 60)
# 游戏的主循环(while True)所在函数的调用需有 await
# 相应的游戏主循环所在函数也需要由 async 定义
await game.start()
if __name__ == "__main__":
# 经由 asyncio.run() 调用 main() 主函数
asyncio.run(main())
|
game.py
Game 类承载游戏主循环:Game(dims, FPS)
定义游戏界面的尺寸 dims
,游戏帧数 FPS
,控制游戏流程。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
|
# 引入 pygame 模块
import pygame
# 引入 asyncio 模块,game.py 中仅需 2 行与之相关的代码
import asyncio
# 引入 debug 函数,方便在游戏界面上输出调试信息
from debug import debug
class Game:
"""Game 类承载游戏主循环
Game(dims, FPS)
定义游戏界面的尺寸 dims,游戏帧数 FPS,控制游戏流程
"""
def __init__(self, dims, FPS = 60):
self.dims = dims
self.FPS = FPS
# 初始化pygame,预定义各种常量
pygame.init()
# 游戏主循环所在函数需要由 async 定义
async def start(self):
# 初始化游戏界面(screen):尺寸、背景色等
screen = pygame.display.set_mode(self.dims)
screen_width, screen_height = self.dims
screen_color = 'Black'
# 初始化游戏时钟(clock),由于控制游戏帧率
clock = pygame.time.Clock()
# 游戏运行控制变量(gamen_running)
# True:游戏运行
# False:游戏结束
game_running = True
# 游戏主循环
while game_running:
# 按照给定的 FPS 刷新游戏
# clock.tick() 函数返回上一次调用该函数后经历的时间,单位为毫秒 ms
# dt 记录上一帧接受之后经历的时间,单位为秒 m
# 使用 dt 控制物体运动可以使游戏物理过程与帧率无关
dt = clock.tick(self.FPS) / 1000.0
# 使用 asyncio 同步
# 此外游戏主体代码中不需要再考虑 asyncio
await asyncio.sleep(0)
# 游戏事件处理
# 包括键盘、鼠标输入等
for event in pygame.event.get():
# 点击关闭窗口按钮或关闭网页
if event.type == pygame.QUIT:
game_running = False
# 以背景色覆盖刷新游戏界面
screen.fill(screen_color)
# 调用 debug 函数在游戏界面左上角显示游戏帧率
debug(f"{clock.get_fps():.1f}", color = 'green')
# 将游戏界面内容输出至屏幕
pygame.display.update()
# 当 game_running 为 False 时,
# 跳出游戏主循环,退出游戏
pygame.quit()
|
debug.py
调试模块:debug(info, x = 10, y = 10, color = 'white')
将调试信息 info
,以字符串格式输出至游戏界面 (x, y)
位置,可以自定义字体颜色 color
。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
|
""" 调试模块
debug(info, x = 10, y = 10, color = 'white')
将调试信息 info,以字符串格式输出至游戏界面 (x, y) 位置
可以自定义字体颜色 color
"""
# 引入 pygame 模块
import pygame
# 初始化pygame,预定义各种常量
pygame.init()
# 调用 pygame 内置默认字体
font = pygame.font.Font(None, 30)
def debug(info, x = 10, y = 10, color = 'white'):
# 获取游戏界面
screen = pygame.display.get_surface()
# 渲染调试信息
debug_surf = font.render(str(info), True, color)
# 给定调试信息输出位置
debug_rect = debug_surf.get_rect(topleft = (x, y))
# 将调试信息背景设置为黑色,以覆盖游戏界面中的其他元素
pygame.draw.rect(screen, 'black', debug_rect)
# 将调试信息输出至游戏界面
screen.blit(debug_surf, debug_rect)
|
【参考】: