开发初衷

源海世界最初只是一个念头:能不能做一个"活着"的文字世界?

市面上的文字游戏大多是静态的——NPC 永远站在原地,对话树固定不变,世界等待玩家推进而非自行运转。我想做一个不同的尝试:NPC 有自己的记忆,会遗忘、会偏移;世界有潮汐涨落,夜晚的危险与机遇并存;玩家的身份是隐藏的,你需要通过探索来认识自己。

于是有了三层宇宙的设定——黄天在上、赤原在中、渊境在底。玩家在赤原醒来,却不知道自己来自哪一层。这个"不知道"就是整个游戏的探索驱动力。

项目历程

从单人原型到全栈联机、从云端网页到本地胖客户端——15 个关键里程碑,横跨架构、玩法、性能三大维度。

详细的版本迭代日志请查看 开发随笔 页面,那里有完整的 15 节点时间线和每次更新的技术细节。

2026-06
原型起步期
Flask + WebSocket 后端骨架、潮汐机制落地、NPC 记忆衰减、背包/地图/公告四大系统、双端管理后台
2026-07 初
玩法爆发期
回合制战斗、隐藏身份系统、寻潮人支线、纪元漫游终局、创意工坊、GAL 剧情引擎、沙盒探索框架
2026-07-04
胖客户端重构
Electron 本地胖客户端、单机/联机双模式统一、NSIS 安装包 + 便携版、v2.0.0 正式发布

设计理念

"活着"的世界

NPC 不是数据表里的一行记录。他们有记忆,会遗忘;有性格,会偏移。潮汐时段他们会主动靠近你,平潮时段他们的行为会越来越随机。你今天交好的朋友,明天可能只是个陌生人。

"隐藏"的驱动力

隐藏身份是核心设计。玩家不知道自己是黄天原生还是中心者,只能通过行为反馈推测。GAL 剧情中,16 项隐藏属性在暗中影响分支走向——你不知道为什么走到这个结局,这本身就是探索的动力。

"呼吸"的节奏

潮汐系统让世界有了呼吸感。白天是相对安全的探索时间,夜晚是危险与机遇并存的潮汐时段。玩家需要规划自己的行动节奏,而不是无脑推进。

"离线优先"的架构

游戏不应该是"没网就不能玩"的。胖客户端架构将所有游戏资源本地化部署——页面、剧情素材、静态资源一次性安装到本地,弱网甚至断网环境下依然可以流畅游玩单机模式。

技术栈

Python Flask WebSocket SQLite Redis PostgreSQL ECS 架构 DeepSeek AI 原生 JS Chart.js PixiJS Tiled Electron PyInstaller pywebview WebView2 Nginx Git Windows Server

架构设计思路

后端:Flask + WebSocket + SQLite

选择 Flask 而非 Django/FastAPI,是因为项目体量不需要重型框架,Flask 的蓝图机制足够模块化。WebSocket 用 flask-sock 轻量实现,不走 Socket.IO 那套复杂协议。后期引入 Redis 做消息队列和缓存,PostgreSQL 做分析查询。

数据库选 SQLite 而非 PostgreSQL——单人项目不需要并发写入,SQLite 单文件部署最省心。表结构按 MySQL 规范设计,将来迁移只换驱动。实际运行中 SQLite 在 50+ 并发下表现稳定,未出现锁竞争瓶颈。

前端:三栏布局 + 模块化 JS + PixiJS 2D 地图

没有用 Vue/React——文字游戏不需要复杂的状态管理。纯原生 JS + 模块化拆分,config.js 统一管理 API 地址和 Token,各功能模块独立 init。沙盒探索部分引入 PixiJS 渲染 2D 瓦片地图,Tiled 编辑器制作地图数据。GAL 剧情树使用纯 SVG + 贝塞尔曲线绘制,支持拖拽排版和子树递归渲染。

AI 对话:DeepSeek 接入

NPC 对话通过 DeepSeek API 实现。每次对话拼接 Prompt 包含:NPC 人设 + 记忆数据 + 双层人格约束 + 潮汐上下文 + 好感度修正。AI 缓存 5 分钟有效,同角色 1 秒内不重复调用。后期增加三层防护(Prompt 过滤 + 后处理 + 日志审计),25 个 meta 黑名单。

客户端:Electron + PyInstaller + NSIS

统一客户端基于 Electron 构建,内嵌 Flask 本地服务器提供离线单机模式。Python 后端通过 PyInstaller 打包为独立可执行文件。Windows 安装包使用 NSIS 制作,支持安装版和便携版双渠道发布。本地管理后台采用 pywebview + pystray 技术栈,底层调用 Edge WebView2 渲染。

技术踩坑记录

WebSocket 鉴权

最初尝试在 HTTP 握手阶段做 JWT 鉴权,但 flask-sock 不支持自定义 header。最终方案:连接建立后第一条消息发送 {type: 'auth', token: '...'},服务端验证后才加入频道。

NPC 记忆序列化

NPC 的 memory_data 是 JSON 嵌套结构,衰减时需要深拷贝再裁剪。直接 json.loads → 修改 → json.dumps 性能尚可,但要注意 SQLite 的 TEXT 字段有长度限制,记忆数据需要做容量控制。后期引入 200 字 Token 控制,超出部分按优先级裁剪。

部署备案问题

最初计划用域名 + HTTPS 部署,但个人主体无法备案游戏类网页。最终方案:游戏服务走公网 IP + 端口直连(9000),静态内容站独立部署(80/443)按个人博客备案。两套站点完全隔离,规避了备案限制。

Electron 离线客户端打包

PyInstaller 打包 Python 后端时遇到多个陷阱:hidden-import 命名不一致导致模块缺失、中文路径下 extract-zip 静默失败、frozen 模式下路径解析异常。最终通过 spec 文件精确配置 + .NET 手动解压 + 运行时路径检测三层防护解决。

Windows .bat 文件编码陷阱

CMD 使用 GBK 解析 .bat 文件,UTF-8 编码的中文会被当成命令执行导致启动失败。所有含中文的 .bat 启动脚本必须保存为 GBK 编码,chcp 65001 无法可靠修复此问题。

暗色主题适配

游戏前端从浅色迁移到暗黑悬疑风格时,遇到大量 CSS 变量覆盖问题。最终用 dark-core.css 定义 21 个令牌的 CSS 变量体系 + dark-pages.css 做页面级覆盖,body:not(.player-page) 隔离两套后台风格。8 层径向渐变实现深海背景 + 毛玻璃全局效果。

工程化实践

关于本站

做游戏是一件孤独但有趣的事。写下这些设定和踩坑记录,既是给自己留档,也是给同样在独立开发路上的人一点参考。

如果你看到了这里,感谢你的耐心。

玩家反馈