Vue + 纯 SVG 打造等距游戏世界:不依赖游戏引擎的终端学习游戏改造记
译注:本文编译自 dev.to 文章《I turned my terminal game into an isometric world (plain SVG + Vue, no game engine)》,作者为 DockerLinux 项目开发者。原文链接见文末。
DockerLinux 是一款免费的浏览器游戏,让玩家在模拟终端中学习 Linux 和 Docker。此前它的界面只是一个终端加一列关卡列表。而现在,每个关卡都变成了岛屿地图上的一栋建筑:在终端中完成一个关卡,通往下一关的路径就会亮起,像素企鹅 Byte 会沿着路径走过去。
整个实现没有使用 canvas,也没有游戏引擎。地图由 Vue 绘制 SVG,再由一个纯 JavaScript 小模块决定各元素的摆放位置。
地图即数据
整个世界由每个岛屿的几个数字确定性地一次性构建完成:
export const ISLANDS = [
{ id: 'linux', kind: 'linux', parts: [[9, 10, 5.6, 5.0]], levels: [1, 2, 3, 4, 5] },
{ id: 'harbour', kind: 'harbour', parts: [[23, 8, 6.4, 5.6]], levels: [6, 7, 8, 9, 10, 11], incidents: true },
// … Sandbox Atoll、Linux Highlands,以及两座仍在迷雾中的岛屿
]每个岛屿是共享瓦片网格上的一个或多个椭圆(中心点与半径均以瓦片为单位)。一个微小的哈希函数会让边缘产生抖动,使海岸线看起来不像完美的椭圆。但这是对瓦片坐标做哈希,而非使用 Math.random(),因此每位玩家看到的地图完全一致,测试也能对其进行校验。
接下来:
- 每个关卡都站在选定的瓦片上,并获得一栋与其主题匹配的小建筑(文件关卡是图书馆,Compose 关卡是起重机,权限关卡是金库……)。
- 路径在相邻关卡之间路由,使用网格上的小型 Dijkstra 算法。陆地成本低,转弯略有额外开销(让道路看起来笔直),海洋成本高但允许通行,木桥便由此而来。
- 空闲瓦片获得装饰:Linux 岛上有树木,港口有集装箱,高地上有山脉。
等距投影只需两行代码
// 网格 → 屏幕:64 × 44 瓦片的左上角
export const iso = (i, j) => ({ x: (i - j) * 32, y: (i + j) * 16 })按 i + j 的顺序绘制(从后到前),重叠关系便自然处理妥当。
相机就是 viewBox
平移和缩放从不移动任何元素,而是改变 SVG 的 viewBox:拖拽平移它,滚轮或双指捏合围绕指针缩放,"飞往某座岛屿"则通过 requestAnimationFrame 为其添加动画。所有元素都通过 SVG 属性定位,从不使用内联样式,因此严格的 Content-Security-Policy 得以保持。在 prefers-reduced-motion 下,相机会直接跳转而非飞行动画,Byte 也不会行走。
任务即数据(新增的 Linux Highlands)
五个新的高级关卡并不在公开的 JavaScript 包中。服务器以 JSON 形式将它们发送给已登录玩家,这意味着它们的任务检查不能是函数,而必须是数据:
{
id: 'hl_ps_kill', desc: 'Ask it to stop with kill and its PID',
check: { any: [
{ all: [{ ran: 'kill' }, { args: '(^|\\s)4242(\\s|$)' }] },
{ all: [{ ran: 'pkill' }, { args: 'miner' }] },
] },
}浏览器将每个规格编译成与手写关卡相同的 (ctx) => boolean。任何未知或格式错误的内容都会编译为"永不通过",因此来自网络的坏数据不会让游戏崩溃。(没错,该关卡有一个占用 97% CPU 的加密货币挖矿程序,它会礼貌地忽略 kill,你需要 kill -9。)
立即体验
👉 dockerlinux.com:17 个关卡,免费,支持英语和葡萄牙语。"世界"模式需要免费账户(邮箱 + 一次性验证码);所有免费关卡无需账户即可游玩。
目前仍有两座岛屿处于迷雾之中。如果你在构建一款 Linux/DevOps 学习游戏,你会往那里放什么?