← 返回 DacAI Lab

实践记录

几个月后,我重新做了一遍“电子鱼”

从几个月前依赖自然语言反复试错的单文件 Demo,到重新定义视觉、声音与验证边界的 Electronic Fish 2.0。

AI 协作Web个人网站Codex项目复盘

今年一月,我做过一个很简单的“电子鱼塘”。

逻辑不复杂:环境保持安静,鱼会慢慢成长;出现明显声音,鱼会受到影响。当时我几乎不会写前端代码,主要做法就是把需求告诉 Gemini,让它生成代码,我负责运行、看效果,再把遇到的问题继续用自然语言描述给它。

最后做出来的是一个本地 HTML 文件,下载解压以后双击就能运行。

后来公众号一直没怎么打理,这个项目也就放在那里了。

前几天我偶然打开很久没看的公众号后台,看到有人还在以前那篇文章下面问源码怎么获取。我才想起来,自己几个月前还做过这么一个东西。

于是把旧版本重新翻了出来。

第一反应是:确实挺简陋的。

但我不觉得它是一个失败项目。至少在当时,它已经真的跑起来了,也实现了我最初想要的玩法。

当时我是怎么做的

V1 最开始甚至连鱼的正式素材都没有。

我先让 Gemini 按需求写出一个能运行的版本:保持安静一段时间后鱼会成长,屏幕显示专注时间和下一次成长倒计时,有声音时鱼会受到影响。为了减少环境底噪带来的误触发,当时还尝试用音频频段去近似判断人声。

功能跑起来以后,我再去找鱼的素材,然后继续往里面替换。

也正是在这个阶段,出现了一个很典型的问题。

换完素材后,鱼的视觉朝向和实际运动方向反了。鱼头看起来朝右,实际却往左移动;反过来也一样。

当时我在文章里很直观地形容成“所有的鱼都是倒着游的,像极了倒放”。

V1 的真实历史素材:鱼头视觉朝向与实际位移方向相反。视频点击后才会加载播放。

后来重新核对逐帧运动,也确认它确实不是错觉,而是鱼头朝向和位移方向不一致。

我当时并没有去读代码找是哪一段算法出了问题,而是继续告诉 AI:

“鱼倒着游了,改一下。”

类似的问题还有水草。最初的水草不是摆动,而是整片左右平移。

整个开发过程基本就是:

我提需求 → AI 改代码 → 我运行 → 发现问题 → 再反馈。

很原始,但有效。

后来 V1 也已经有了成长事件和状态反馈。

V1 运行时出现新鱼诞生、全体成长提示及专注时间、鱼群状态和倒计时
V1 当年的真实运行反馈;这张图不能单独证明鱼群数量在这一刻增加。

这张当年的运行画面里,已经能看到“新鱼诞生!全体成长!”提示,以及专注时间、鱼群状态和下一次成长倒计时。它说明 V1 的成长反馈机制已经实际运行起来,但我不把这张图当成“这一刻确实新增了一条鱼”的证据——保存下来的动画里,鱼群数字并没有同步上升。

这次我没有继续修 V1

重新打开旧版本以后,我一开始也想过,是不是换一下背景、重新做几条鱼、把界面修一修就算了。

很快就放弃了这个思路。

因为真正让我觉得旧的,不只是画面。

V1 更像一个“已经能工作的 Demo”:所有东西都挤在一个页面里,素材风格不统一,鱼的运动比较简单,环境声音判断也更接近单纯的阈值控制。

如果只是给它换一套漂亮素材,本质上还是原来的项目。

所以这一次我没有在旧代码上继续装修,而是重新做了一版。

从旧 ZIP 原样恢复并直接运行的 Electronic Fish V1
V1:旧 ZIP 原样恢复,直接运行当年的 cyberfish.html,保留 20 条初始鱼。
Electronic Fish 2.0 当前正式版本的默认初始状态
V2:当前正式版本的默认初始状态;真实麦克风完成环境校准,未填充居民或模拟奖励。

左边是从旧 ZIP 原样恢复出来的 V1。没有改代码,直接运行当年的 cyberfish.html,20 条初始鱼仍然可以正常工作。

右边是现在的 Electronic Fish 2.0,使用当前正式版本、默认初始状态和真实麦克风完成环境校准,没有人工填充居民,也没有使用开发测试数据。

两张图放在一起以后,差别基本不用解释。

先决定鱼塘应该是什么样

V1 当年做素材的方法很直接:需要鱼,就去找几张鱼图。

这一次我先确定了一个视觉目标。

我想要的不是卡通小游戏式的蓝色背景,而是更接近一个安静的水族箱:水草、沉木、石头和沙地形成固定环境,鱼是画面里的居民,而不是贴在背景上移动的图标。

Electronic Fish 2.0 的半写实水族箱视觉方向参考
V2 的视觉方向参考,不是最终产品截图。

这张只是当时确定视觉方向时使用的参考图,不是最终产品截图。

方向确定以后,再按同一套风格制作实际生产素材。

最初有灯鱼、神仙鱼、孔雀鱼,后面又增加了金橙色观赏鱼、鼠鱼、樱花虾和斑马螺。

不同生物也不再只是“换了一张图片”。

鱼需要在水体里游动,鼠鱼的位置更接近底层,虾和螺又有完全不同的运动节奏。素材真正放进环境以后,还需要重新检查尺寸、遮挡和视觉密度。

金橙鱼、鼠鱼、樱花虾和斑马螺在环境底图中的第二轮素材集成测试
第二轮生态素材集成测试,用于检查尺寸、遮挡与视觉密度;不是正式运行 UI。

这一步让我明显感觉到,这次已经不像几个月前那样是在“给网页加几张鱼”。

更接近在做一个很小的完整项目。

声音也不再只是一个阈值

V1 右下角有一个滑块,用来手动调整环境声音触发阈值。

这种方式简单,也能工作,但不同房间、不同麦克风、不同设备的底噪差别很大。

V2 启动以后,会先听一小段当前环境声音,建立一个基线,再根据声音相对这个基线的变化判断当前状态。项目里也区分了短暂声响和持续噪声,而不是听到一次声音就立刻中断成长。

短暂的动静会让鱼受到惊吓;只有持续达到更高程度的噪声,成长计时才会暂停。恢复安静以后,再慢慢回到正常状态。

我还保留了三档敏感度,以及不同的成长节奏。

Electronic Fish 2.0 的声音敏感度、成长节奏与减少动态效果设置界面
V2 正式设置界面,展示空存档下的默认选项。

这些都不是为了把功能堆得更多。

主要目的还是解决 V1 当时很实际的问题:同一个固定阈值,在不同环境里并不好用。

鱼还是会出问题

有点巧的是,两次做电子鱼,最后都在“鱼怎么游”这件事上出了问题。

V1 是鱼的素材朝向和实际移动方向相反。

V2 则是另一种问题。

最初实现运动时,鱼的整体旋转角度直接跟速度方向绑定。鱼往右游时问题不大,一旦逐渐往左转,角度接近 180°,整张鱼图也会跟着翻过去。

看起来就像鱼突然侧翻甚至上下颠倒。

这次没有留下修复前的截图,所以我也没有为了写文章重新制造一个错误现场。现有的运动测试图都是修复之后的结果。

最后的处理方式是把两个概念拆开:

左右朝向只负责决定鱼面向哪边;上下运动只允许很小幅度的俯仰。

鱼往左时做水平镜像,而不是让整张图片旋转 180°。

听起来只是一个小问题,但真正做的时候,这类小问题非常多。

AI 可以很快给出一套实现,但画面看起来对不对,通常还是要实际跑起来以后才能发现。

几个月后,变化最大的不是代码

如果只看最终画面,V1 和 V2 最大的区别似乎只是:

一个比较简陋,一个比较完整。

但重新做完以后,我觉得变化更明显的是自己的工作方式。

几个月前,我更多是在这样使用 AI:

我想要一个功能 → AI 给我代码 → 我复制下来 → 运行 → 出问题 → 再问 AI。

代码、素材和文件基本都需要我自己在中间搬来搬去。

这一次,我很少再直接问“这段代码怎么写”。

更多时间是在决定:

这个项目到底要不要重做;

哪些旧逻辑应该保留;

鱼塘应该长什么样;

需要哪些鱼;

声音应该怎么判断;

什么算 Bug;

哪些功能值得继续做,哪些做到这里就够了。

这些问题确定以后,我在 ChatGPT 里继续讨论设计和取舍,再把具体实现、运行测试和修改交给 Codex。

最后还专门把 V1 原样恢复了一次,用相同尺寸重新截图,再检查旧的 GIF、测试图和生产素材到底能证明什么,哪些不能证明。

这已经和几个月前“让 AI 帮我写一个网页”不太一样了。

AI 做的事情更多了,我自己反而更少直接碰代码。

但需要我决定的事情并没有变少。

只是从“这行代码怎么改”,慢慢变成了“这个东西到底应该怎么做”。

Electronic Fish 2.0 现在已经放到了我的个人网站:

https://play.zhuzg.com/aquarium/

这次没有再把它做成一个需要下载到电脑、本地双击运行的 HTML 文件。

打开网页,允许麦克风以后,就可以直接开始养。

至于后面会不会继续增加新的鱼或者玩法,目前没有计划。

先让这个鱼塘安静地待在那里。