一个用 AI 驱动的视觉小说 + 开放世界 RPG 引擎,个人独立开发,边玩边做。
我一直想玩那种"故事会随着我的选择真正改变"的游戏,但市面上的要么剧本固定、要么 AI 生成的内容又烂又慢。所以自己动手做了这个。
它目前能做的事情:
- 视觉小说模式 — 用 Ink 脚本写剧情,AI 动态续写下一章,每次读档都会有新内容
- 开放世界模式 — AI 实时生成区域描述、NPC 对话、任务,探索的地方越来越多
- 战斗系统 — 基于自研 BattleDSL,LLM 只输出约 100 tokens 的 JSON 配置,由前端 Runtime 渲染成 Phaser 3 小游戏(回合制/QTE/弹反/连击/生存五种机制)
- 预加载 — 玩家进入区域后,后台静默预生成相邻区域和战斗缓存,尽量把"等 AI"的感知时间压到最低
- 多人联机 — 支持旁观者模式和共创模式,好友可以实时观看你的游戏进度,或共同影响故事走向
| 层 | 技术 |
|---|---|
| 前端 | Vite + 原生 JS,Phaser 3(游戏场景) |
| 剧本 | inkjs(Ink 脚本运行时) |
| 后端 | Express + better-sqlite3 |
| 实时通信 | WebSocket(多人联机) |
| AI | 智谱 GLM API(可以换成其他 OpenAI 兼容接口) |
| 部署 | Docker |
src/
ai/
gameSceneAgent.js # BattleDSL 生成器,LLM 只输出轻量 JSON
WorldAgent.js # 开放世界区域/NPC/任务生成
generate.js # 故事/章节生成
engine/
GameController.js # 视觉小说核心控制器
InkEngine.js # inkjs 封装
Renderer.js # 对话/选项渲染
game/
BattleDSLRuntime.js # DSL → Phaser Scene 编译器
WorldController.js # 开放世界控制器(区域切换/战斗/NPC)
GameLauncher.js # Phaser 沙箱启动器
multiplayer/
RoomManager.js # 服务端房间管理器(WebSocket)
MultiplayerClient.js # 前端 WebSocket 客户端
db/
database.js # SQLite 数据层
server.js # Express API 服务器
stories/ # 内置故事(.ink + .world.json)
需要: Node.js 18+,一个 GLM 或 OpenAI 兼容的 API Key
# 安装依赖
npm install
# 开发模式(前端热更新)
npm run dev
# 编译 Ink 脚本 + 构建前端 + 启动服务器
npm run deploy服务器默认跑在 http://localhost:3000/mvp。
环境变量(可选,不配也能跑开发环境):
PORT=3000
JWT_SECRET=换成你自己的随机字符串
DATA_DIR=./data # SQLite 数据库目录
STORIES_DIR=./stories # 内置故事目录
# 先构建前端
npm run build:prod
# 构建镜像
docker build -t ai-rpg .
# 跑起来
docker run -d \
-p 3000:3000 \
-v $(pwd)/data:/rpg/data \
-v $(pwd)/stories:/rpg/stories \
-e JWT_SECRET=你的密钥 \
ai-rpg注册入口已关闭(避免滥用 API),账号由管理员在 src/db/database.js 的 BUILTIN_USERS 数组里内置。VIP 账号可以:
- 用自己的 API Key 生成故事/续写章节
- 保存进度、多端读档
- 把自己的故事设为公开,让其他人也能玩
游客可以直接玩所有公开的故事,不需要登录。
这是我自己设计的一个轻量描述语言,解决"LLM 生成战斗场景太慢、token 消耗太多"的问题。
LLM 只需要输出这样一段 JSON(约 80-120 tokens):
{
"type": "parry",
"theme": "cyber",
"arena": "rooftop",
"enemy": { "name": "机械守卫", "shape": "mech", "hp": 1.2, "atk": 1.0, "special": "shield" },
"win": "deplete",
"open": "你欠债的日子到头了",
"win_line": "系统已瓦解",
"lose_line": "连接中断…"
}前端 BattleDSLRuntime.js 拿到这段 JSON,组合出完整的 Phaser 3 战斗场景。相比之前让 LLM 直接写 Phaser JS 代码(约 2700 tokens),token 消耗降低了大约 87%,而且不会出代码语法错误了。
进入区域后会在后台静默预生成几个战斗 DSL 存入数据库,触发战斗时直接命中缓存,延迟 < 50ms。
支持两种联机模式:
- 房主正常游玩,其他玩家实时观看游戏进度
- 观看者可以发送弹幕评论
- 房主广播所有游戏事件(区域切换、战斗、对话等)
- 新加入的观看者通过状态快照立即同步
- 两位玩家共同影响故事走向
- 遇到选择时,双方投票决定
- 若达成共识,直接采用;若分歧,随机选一个
- LLM 整合双方选择,生成更丰富的剧情分支
- 切换到"多人模式"标签页
- 选择模式(旁观者/共创)
- 创建房间并分享房间码给好友
- 好友输入房间码加入
- 基于 WebSocket 的低延迟双向通信
- 房主作为权威源,广播所有事件
- 服务端
RoomManager管理房间生命周期 - 前端
MultiplayerClient封装连接与消息处理
stories/ 里放了一些自己写的测试故事:
demo— 功能演示shadowblade— 剑与魔法霓虹断路— 赛博朋克屠龙者之烬— 奇幻史诗- 还有一些奇奇怪怪的...
都可以在游客模式直接玩,不需要账号。
- 移动端适配还很糙
- AI 生成的故事偶尔会有逻辑断层(Ink 脚本修复还不够完善)
- 开放世界地图可视化还没做
- 战斗 DSL 的
survive模式体验还不够好
简单说:可以自由使用和修改,但如果你把改过的版本作为服务提供给别人用,需要开放源代码。
个人项目,能跑就行。有 Bug 是正常的。