wasm+pygbag让你在网页上也能运行Python代码:【贪吃蛇游戏】
引言
最近小伙伴告诉我一种新的方法,可以使用wasm来使浏览器网页能够运行Python代码。这一下子激起了我的兴趣,因为这意味着用户无需安装Python环境就能直接运行我的demo,这真是太方便了。所以,我们的主要目标今天就是让网页能够直接运行我的贪吃蛇游戏。贪吃蛇游戏其实很简单,因为Python有一个很棒的pygame库可以供我们使用。所以编写起来也不会太复杂。废话不多说,让我们开始吧。
何为wasm
全称为WebAssembly ,简称为Wasm,是一种能够在web上加载非常快速的一种格式。它可以被视为HTML、JS等其他表达形式的一种补充。对于我来说,这就是它的简单定义。然而,我们今天的任务并不是去介绍Wasm,而是探讨如何实现Python在web中直接运行的方法。
当然有兴趣的同学可以直接去看官方网站:https://webassembly.org/
emscripten
我们现在已经了解到,WebAssembly可以在web上运行,但我想知道如何将我的Python代码转换成WebAssembly。当然,肯定有相应的工具可供使用。在网上,最常被提及的工具就是emscripten,可以说是WebAssembly的核心工具。它的主要功能是将其他高级语言编译成WebAssembly。然而,我在本地尝试过后发现,emscripten无法直接将Python代码转换成WebAssembly格式。你需要使用其他工具先将Python转换成其他高级语言如C语言,然后再使用emscripten将其转换成WebAssembly。如果你知道其他更好的方法,可以在下方提出来,非常感谢。
我就不实验了,毕竟后台报错,如果你有兴趣可以看看emscripten官方网站:https://emscripten.org/
在网站的顶部导航栏中,找到并点击 "Get Started"(开始使用)。
pyodide
如果你尝试过在Web上运行Python代码,那你肯定了解到pyodide方案,它确实是一个功能强大的工具。然而,它也存在明显的缺点,例如它所支持的第三方库非常有限,而且加载速度也很慢。尽管如此,它仍然是最便捷的选择,因为你可以直接在HTML中编写代码,而不需要额外的工具。唯一需要注意的是需要引用它的JavaScript文件。test.html代码示例如下:
<script src="https://cdn.jsdelivr.net/pyodide/v0.18.1/full/pyodide.js"></script>
<script type="text/javascript">
loadPyodide({ indexURL : "https://cdn.jsdelivr.net/pyodide/v0.18.1/full/" }).then((pyodide) => {
pyodide.runPython(`
def hello_world():
return "Hello, World!"
print(hello_world())
`);
});
</script>
对于我们来说,使用pyodide是相对简单的。只需点击文件后,浏览器就能正常运行其中的Python代码。但是要直接使用Python的pygame库是不可能的。不过,一些简单的代码还是可以运行的。那么,是否还有其他解决方案呢?答案是肯定的。

pygbag
开发人员在寻找解决方案时,最好的资源就是Github了。几乎所有开源的代码仓库都可以在那里找到,只要你能想到的,几乎都能找到。我也是通过搜索找到了一个解决方案,真的有一个开源的第三方库叫做pygbag。虽然这个库很新,但它是由Python官方支持的。只是要依靠官方文档和浏览器去寻找示例代码。连gpt这样的人工智能模型都不知道有这么一个东西存在。此外,pygbag专门集成了pygame,可以直接将Python代码编译成wasm,在浏览器中运行。它还有官方开发人员制作的游戏可供参考,当然如果你也制作了游戏,也可以上传到这里。
官方Github地址:https://github.com/pygame-web/pygbag
官方游戏首页:https://itch.io/games/tag-roguelike
他的宗旨也很简单:Python WebAssembly for everyone ( packager + test server ),但是他也有一些编码要求,我们先来实现一个贪吃蛇游戏吧。
贪吃蛇游戏
在开始使用pygbag三方库之前,我们需要确保已经在本地实现了贪吃蛇游戏。现在,请跟着我一起按照以下步骤进行操作。
安装 Pygame
命令如下:pip install pygame
Pygame是一套专门用于编写游戏的Python模组,它在SDL库的基础上添加了各种游戏功能的实现。借助Python语言的灵活性,你可以快速、轻松地创建各种类型的游戏。正如之前所提到的,Python拥有丰富的库和模组,我们只需直接使用它们,而不必重复造轮子。
我已经为你写好了贪吃蛇游戏的代码,你可以直接使用。这是一个大家都很熟悉的游戏,所以没有太多需要解释的。
import pygame
import random
pygame.init()
screen = pygame.display.set_mode((1280, 720))
clock = pygame.time.Clock()
running = True
dt = 0
player_pos = [pygame.Vector2(screen.get_width() / 2, screen.get_height() / 2)]
player_speed = 300
player_radius = 30
food_pos = pygame.Vector2(random.randint(0, screen.get_width()), random.randint(0, screen.get_height()))
food_radius = 15
direction = pygame.Vector2(0, 0)
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
screen.fill("black")
keys = pygame.key.get_pressed()
if keys[pygame.K_w] or keys[pygame.K_UP]:
direction = pygame.Vector2(0, -1)
if keys[pygame.K_s] or keys[pygame.K_DOWN]:
direction = pygame.Vector2(0, 1)
if keys[pygame.K_a] or keys[pygame.K_LEFT]:
direction = pygame.Vector2(-1, 0)
if keys[pygame.K_d] or keys[pygame.K_RIGHT]:
direction = pygame.Vector2(1, 0)
# Move the player's head
player_pos[0] += direction * player_speed * dt
# Check if player eats the food
if player_pos[0].distance_to(food_pos) < player_radius + food_radius:
player_pos.append(player_pos[-1].copy()) # Add new segment to the player
food_pos = pygame.Vector2(random.randint(0, screen.get_width()), random.randint(0, screen.get_height())) # Generate new food
# Move the player's body
for i in range(len(player_pos)-1, 0, -1):
player_pos[i] = player_pos[i-1].copy()
pygame.draw.circle(screen, "white", food_pos, food_radius) # Draw the food
for pos in player_pos:
pygame.draw.circle(screen, "red", pos, player_radius) # Draw the player
pygame.display.flip()
dt = clock.tick(60) / 1000
pygame.quit()
启动命令就是python main.py。运行效果如下:我想要说明的是,我只是简单地实现了一下,并没有进行太多的校验。另外,值得注意的是,尽管我吃完食物后,并没有在身体上感受到太大的变化,但当我吃了很多食物之后,你就可以看到明显的变化了。

pygbag改造
使用 pygbag 将 pygame 制作的游戏打包,使游戏可在浏览器中直接运行。 pygbag 的使用可参考 Pygbag Wiki。官方文档:https://pygame-web.github.io/
使用 pip 安装 pygbag:pip install pygbag
使用 pygbag 打包游戏前,待打包的目录下的游戏代码文件须名为 main.py。 然后仅需对游戏代码做简易修改,修改后代码如下:
import pygame
import random
import asyncio
pygame.init()
screen = pygame.display.set_mode((1280, 720))
clock = pygame.time.Clock()
running = True
dt = 0
player_pos = [pygame.Vector2(screen.get_width() / 2, screen.get_height() / 2)]
player_speed = 300
player_radius = 30
food_pos = pygame.Vector2(random.randint(0, screen.get_width()), random.randint(0, screen.get_height()))
food_radius = 15
direction = pygame.Vector2(0, 0)
async def main():
global screen, clock, running, dt ,player_pos ,player_speed,player_radius,food_pos,food_radius,direction
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
screen.fill("black")
keys = pygame.key.get_pressed()
if keys[pygame.K_w] or keys[pygame.K_UP]:
direction = pygame.Vector2(0, -1)
if keys[pygame.K_s] or keys[pygame.K_DOWN]:
direction = pygame.Vector2(0, 1)
if keys[pygame.K_a] or keys[pygame.K_LEFT]:
direction = pygame.Vector2(-1, 0)
if keys[pygame.K_d] or keys[pygame.K_RIGHT]:
direction = pygame.Vector2(1, 0)
# Move the player's head
player_pos[0] += direction * player_speed * dt
# Check if player eats the food
if player_pos[0].distance_to(food_pos) < player_radius + food_radius:
player_pos.append(player_pos[-1].copy()) # Add new segment to the player
food_pos = pygame.Vector2(random.randint(0, screen.get_width()), random.randint(0, screen.get_height())) # Generate new food
# Move the player's body
for i in range(len(player_pos)-1, 0, -1):
player_pos[i] = player_pos[i-1].copy()
pygame.draw.circle(screen, "white", food_pos, food_radius) # Draw the food
for pos in player_pos:
pygame.draw.circle(screen, "red", pos, player_radius) # Draw the player
pygame.display.flip()
dt = clock.tick(60) / 1000
await asyncio.sleep(0)
pygame.quit()
if __name__ == '__main__':
# 使用 asyncio.run() 调用主函数 main()
asyncio.run(main())
可以看到,基本上必须引入另一个包:导入 asyncio。剩下的就没啥了。我们再来启动下。
启动命令需要修改下:python -m pygbag pyGame,这里注意下,pygbag打包的是整个目录,所以不能像Python那样启动,所以我基本上都是回退到父级目录后,在进行终端控制台打包。命令会直接在本地启动游戏服务。你稍等片刻。

然后直接字啊浏览器查看URL地址:http://localhost:8000/,仍然是稍等片刻,让他加载一下。这时候,你就可以看到浏览器的游戏界面了,如下:

总结
经过努力,我成功完成了任务。如果你有兴趣,也可以将你的游戏上传到官方网站,但作为示例,我并不打算上传。不过,我已经提供了源代码给你,所以你可以直接复制粘贴并运行它。虽然Python现在可以直接在web端使用,但我个人不太喜欢这种方式。
wasm+pygbag让你在网页上也能运行Python代码:【贪吃蛇游戏】的更多相关文章
- 【C语言项目】贪吃蛇游戏(上)
目录 00. 目录 01. 开发背景 02. 功能介绍 03. 欢迎界面设计 3.1 常用终端控制函数 3.2 设置文本颜色函数 3.3 设置光标位置函数 3.4 绘制字符画(蛇) 3.5 欢迎界面函 ...
- android系统上编写、运行C#代码
最近找到个好玩的APP,C#Shell (Compiler REPL),可以在安卓系统上编写和运行C#代码,配合sqlite数据库,写了个小爬虫,运行还不错: 运行一些小爬虫或者定时任务可以用这个,毕 ...
- react如何在网页上编辑并运行代码?
最近想做个能在网站,能在网页上运行代码,并且保存这个组件,看了一下element-react的组件和官方的实例,发现都是可以编辑运行的,因为之前没有这方面的经验,所以看下各位大佬能不能给点意见
- 上传自己的Python代码到PyPI
一.需要准备的事情 1.当然是自己的Python代码包了: 2.注册PyPI的一个账号. 二.详细介绍 1.代码包的结构: application \application __init__.py m ...
- 【已解决】ERR_BLOCKED_BY_XSS_AUDITOR:Chrome 在此网页上检测到了异常代码:解决办法
工作中,用Selenium自动化填表并获取结果时,程序一直安静的读取数据库,网页填表,获取结果,存库,但跑着跑着突然报错了. 排查后,原来不是Selenium的问题,是数据比较特殊,带了个双引号,如下 ...
- 【BZOJ-4213】贪吃蛇 有上下界的费用流
4213: 贪吃蛇 Time Limit: 5 Sec Memory Limit: 64 MBSubmit: 58 Solved: 24[Submit][Status][Discuss] Desc ...
- “此网页上的某个 Web 部件或 Web 表单控件无法显示或导入。找不到该类型,或该类型未注册为安全类型。”
自从vs装了Resharper,看见提示总是手贱的想去改掉它.于是乎手一抖,把一个 可视web部件的命名空间给改了. 喏,从LibrarySharePoint.WebPart.LibraryAddEd ...
- [moka同学收藏]网页上的“返回上一页”的几种实现代码
我们在制作网页的时候,经常在网页上要用到"返回上一页"的功能.这一功能在制作网页的时候会有多种编码方法,在此,笔者将比较常用的几种编码写作方法在下面列出来,供各位技术人员参考使用. ...
- 使用chrome查看网页上效果的实现方式
使用chrome查看网页上效果的实现方式 chrome是一个极为强大的工具,很多时候,我们不知道一个效果怎么实现的,我们完全可以找到响应的网页,然后找到其html文件,和js文件,查看源码,获得其实现 ...
- css015 定位网页上的元素
css015 定位网页上的元素 一. 定位属性的功能 1. 四中类型的定位 Position: absolute relative fixed static a. 绝对定位 绝对定 ...
随机推荐
- oracle:ORA-14765建索引阻塞创建分区及处理步骤
在生产库建立一个索引,报ORA-14765创建索引时不能创建分区,也就是索引的创建阻塞分区的建立. 处理步骤: 1.与开发人员沟通昨天下午在Tbl_Waste表上建索引,一直未返回成功,定位问题SQL ...
- Insert a scratch project into a ppt (MSPowerPoinT file)在powerpoint中播放Scratch动画
Insert a scratch project into a ppt (MSPowerPoinT file)在powerpoint中播放Scratch动画 Contributed by liu pe ...
- RatingBar android 自定义 评级 星星
资源下载地址 <!-- xml 中的使用 --> <RatingBar android:id="@+id/ratingBar" android:layout_wi ...
- 《最新出炉》系列初窥篇-Python+Playwright自动化测试-18-处理鼠标拖拽-上篇
1.简介 本文主要介绍两个在测试过程中可能会用到的功能:在selenium中宏哥介绍了Actions类中的拖拽操作和Actions类中的划取字段操作.例如:需要在一堆log字符中随机划取一段文字,然后 ...
- 03-11gR2单机通过RMAN恢复到RAC(未验证)
1.在单机上做一个完全备份,并将备份集拷贝到RAC的第一个节点上. 2.强行启动到nomount 3.恢复spfile 4.创建pfile,修改pfile,重建spfile #####修改contro ...
- 14.8 Socket 一收一发通信
通常情况下我们在编写套接字通信程序时都会实现一收一发的通信模式,当客户端发送数据到服务端后,我们希望服务端处理请求后同样返回给我们一个状态值,并以此判断我们的请求是否被执行成功了,另外增加收发同步有助 ...
- Java开发面试--群面专区
目录 一.群面背景 二.群面流程 三.群面角色 四.群面细节 五.群面礼仪 六.群面话术 七.个人演讲 八.群面题型 群面也称无领导小组~ 候选人们被要求在一个相对自由的环境中展示他们的能力,并在没有 ...
- CSP 初赛复习
想要做一些不需要思考也算不得摆烂的事,但发现很难找到符合上述要求的学习内容. 突然想到还剩两天就 CSP 初赛了.虽然在 LN 想过不了初赛纯属搞笑,但为了不让自己的分数太难看还是简单复习一下. 没有 ...
- Java Web程序在Tomcat上是如何运行的
https://blog.csdn.net/fuzhongmin05/article/details/104379514 一个JVM是一个进程,JVM上跑Tomcat,Tomcat上可以部署多个应用. ...
- JVM-JVM如何加载类
一.Java 语言的类型可以分为两大类: 基本类型(primitive types) 引用类型(reference types):类.接口.数组类和泛型参数(泛型参数会在编译中被擦除),因此Java虚 ...