Appearance
四风归一
背景:风参数统一存储在 envState,但云/粒子/水面/布料各自独立读取、自行计算风力矢量——逻辑重复三次,水面和布料根本不响应风向。
过程:新增 wind-utils.ts 统一风力入口,补齐水面波向 uniform + 布料风场力,粒子改用统一函数。
外交官和 AI 站在观景台上,看着眼前的世界。
云正平稳地往东飘。细密的粒子从地面扬起,摇摇晃晃地往北飞。远处的湖面泛着波光,波浪排成固定的队列,从西往东推进。
布料呢?布料静止不动。
"你感觉到了吗?"外交官问。
"风,"AI 说,"感觉到了。"
"有几阵风?"
AI 看了看四周,犹豫了一下:
"一阵?"
"一阵,"外交官点头,"但你看看它们——云往东,粒子往北,水往西。一个风,三个方向。"
AI 又看了看,沉默了。
"那个布料呢?"
"布料没动,"外交官说,"它根本没感觉到风。"
四套模式
他们从观景台走下来,一个一个地检查。
云。 "云漂移有一套自己的风计算公式——windDirection × windSpeed × factor,"外交官翻开代码,"乘完直接用在云的坐标偏移上。"
粒子。 "粒子偏转也有一套——同样是 windDirection × windSpeed × factor,乘完作为粒子的加速度方向。"
水面。 "水面最离谱——"外交官停在一段代码前面,"波浪方向是硬编码的。"
AI 凑过去看:
glsl
const vec2 WAVE_DIR[4] = vec2[](
vec2(1.0, 0.0), // 东
vec2(0.7, 0.7), // 东北
vec2(0.0, 1.0), // 北
vec2(-0.7, 0.7) // 西北
);"这不是风向,"AI 说,"这是……四个固定方向。"
"对。水温感调控的滑条连着 UI——想调成什么方向都行,滑条随便拖——但波浪根本就不读它。你拖了也白拖。"
"那布料呢?"
"布料没有风,"外交官说,"XPBD 求解器给每个粒子算的是位置约束和碰撞。风?不存在的。"
AI 看着这四个子系统:
"所以一阵风到了这个世界,变成了四种不同的力?"
"一阵风,四种解释。"
诊断
"为什么会这样?"AI 问。
外交官靠在一棵树上:
"因为风是环境里唯一一个'广播式'的状态——一个输入,四个独立的子系统都要响应:云要漂、粒子要偏、水面要起浪、布料要飘动。"
"但它们的代码是分开写的。写云的人想着云,写粒子的人想着粒子。没有人想过'这四个东西应该被同一阵风吹'。"
"那架构上呢?怎么治?"
"架构上有一个蠢方案和一个不蠢的方案。"
否决的方案
"蠢方案是什么?"
"加一个全局事件总线——'风变了,通知所有人'——专门做风的中转。"
typescript
globalPhysics.on('wind:change', (dir) => {
clouds.updateWind(dir);
particles.updateWind(dir);
water.updateWaveDir(dir);
cloth.updateWindForce(dir);
});"这不挺好的?清晰,解耦。"
"是挺好的,但多了一层,"外交官说,"我们已经有一套完整的链路了:envState 存状态,setEnvState() 统一写入口,每帧有 ensureEnvUpdateObserver() 统一循环。再加一层事件系统是重复——没有实质收益。"
"那实际怎么做的?"
三件事
"三件小事。第一件——统一一个入口。"
他在一个新的文件里写了一小段代码:
typescript
// wind-utils.ts
export function getWindVector(): Vector3 {
return windDirection.scale(windSpeed);
}"所有子系统统一调 getWindVector()。不再各自算一遍 dir[0] * speed * factor。"
"第二件——把水面接上。"
他从 GLSL 里删掉了那四行硬编码,加了一个 uniform:
glsl
uniform vec2 uWindDir[4];在 JS 端加了一个 computeWaveDirs() 函数——根据风向生成四个波向:主方向与风向对齐,三个副方向做微小偏移(+0.3, -0.2, +0.1 弧度):
"风向变了,波向马上跟着变。"
"第三件——给布料加风。"
在 XPBD 求解器的粒子循环里,对没有被固定的粒子施加风场位移:
typescript
particle.p += windVector * 0.2 * dt * random(0.6, 1.4);"0.2 是布料的惯性系数——布料太轻,风大了吹飞;太重,风小了没反应。0.2 刚好。"
"那个 random 呢?"
"所有粒子都朝同一个方向飘?那看起来像一块布被风吹得平移——不自然。加上随机因子,每个粒子的偏移量有差异,布料才会起皱。"
调试
外交官把四系统都接上了,走到控制台前,拖动了风向角的滑条。
45 度。
云开始往东北飘。 粒子跟着往东北偏转。 水面的波浪方向开始旋转——主波向对齐了风向,三个副波向散开在周围。
布料鼓了起来,裙摆微微向北摆动。
"四系统同步响应,"AI 说。
"四系统同步响应,"外交官重复了一遍,"第一次。"
他又拖了拖滑条,从 45 度拖到 90 度。云转了。粒子转了。水面转了。布料转了。
"它们本来就应该一起转的——它们被同一阵风吹。"
最小的改动
"一共改了多少代码?"
"加起来不到 80 行,"外交官说,"没有架构变更,没有新增依赖。一个工具函数加三个接入口。"
"但效果特别明显。"
"对。因为问题不在代码量——问题是四个子系统不共享一个东西,它们各自造了一个。"
"统一入口这件事,说起来简单,做起来……"
"做起来也不难,"外交官接话,"难的是意识到'它们需要的其实是同一阵风'。"
教训:风是环境里唯一一个"广播式"的状态——一个输入驱动四个独立子系统。它们各自实现了一遍风力计算,就出了问题。统一入口 + 补齐缺口,比加一层事件系统更轻。有时候问题的解法不是加东西——是把重复的东西合并成一个。