weathr雨滴模拟全解:从风向量分解到地面溅落,快速看懂终端天气动画的终端物理
【免费下载链接】weathra terminal weather app with ascii animation项目地址: https://gitcode.com/gh_mirrors/wea/weathr
weathr 是一款开源的终端天气应用(terminal weather app with ASCII animation),用实时天气数据驱动 ASCII 动画。本文带你从源码层面拆解它最有物理感的部分——raindrops.rs 里的雨滴模拟:风向角如何分解成雨幕的斜度、四层雨量等级如何决定雨滴密度与字符、以及地面溅落动画的 3 帧时序。全程以参数和图表演示为主,零基础也能看懂。
🌧️ 雨幕从哪里来:RainIntensity 的 4 个等级
雨滴系统不直接读降水量,而是先经过一次"翻译":types.rs 中的rain_intensity()把天气类型映射为 4 个等级——毛毛雨、小雨、大雨、暴雨。
| 等级 | 触发天气 | 雨滴数量(相对终端宽度) | 基础风速 wind_x | 下落速度 |
|---|---|---|---|---|
| 毛毛雨 Drizzle | Drizzle | 宽度 ÷ 4 | 0.05 | 0.2 ~ 0.4 |
| 小雨 Light | Rain、RainShowers | 宽度 ÷ 2 | 0.1 | 0.4 ~ 0.7 |
| 大雨 Heavy | FreezingRain、Thunderstorm | 宽度 × 1 | 0.15 | 0.6 ~ 0.9 |
| 暴雨 Storm | ThunderstormHail | 宽度 × 1.5 | 0.8 | 1.2 ~ 1.8 |
数量公式与速度参数分别定义在 raindrops.rs 和 raindrops.rs。注意一个细节:雨滴不是一下子铺满屏幕的,而是每帧按 1/2/5 的"生成速率"逐渐补满目标数量,所以开场会有雨幕渐浓的过渡感。
🌬️ 风向角分解:把 225° 变成雨幕斜度
这是全篇最"物理"的一步。气象学里的风向是风来的方位角(0°=北,90°=东),而屏幕只需要一个横向漂移速度。set_wind 的分解公式是:
wind_x = (风速 ÷ 40) × sin(−风向角)
为什么这样对?拿三个数字验证一下:
- 北风 0°:sin(0) = 0 → 雨垂直下坠,左右没有漂移,符合"正对吹来的风不改变水平位移"的直觉;
- 东风 90°:sin(−90°) = −1 → 雨被往左吹,正对着从东边来的风;
- 西南风 225°(模拟模式默认值,见 app.rs):sin(−225°) ≈ 0.71 → 向右飘。
风速除以一个 40 的"归一化常数",意味着40 km/h 就是满偏:再大的风也只是逼近极限斜度,雨幕不会失控飞离屏幕。
而渲染阶段的判据同样精巧:横向速度 |speed_x| 超过 0.5 时,字符强制切换为\(向右)或/(向左)(raindrops.rs)。所以"雨滴斜度"其实是被速度阈值驱动的状态机,而不是直接画直线——大风吹过时你会看到整片雨幕"齐刷刷"斜过去。
🪟 双层深度:z_index 制造的前景/背景雨
spawn_drop 里给每颗雨滴随机分配 z_index 0 或 1,相当于远近两层:
| 层级 | 速度 | 颜色 | 角色 |
|---|---|---|---|
| 远景 z=0 | 较慢 | DarkGrey | 背景薄雨 |
| 近景 z=1 | 较快 | 青色/白色 | 前景清晰雨丝 |
而且只有近景雨滴才允许触发地面溅落。远景那层是灰色的"慢速雨",近景是亮的"快速雨"——两层叠加就产生了终端里难得的纵深错觉。另外每颗雨滴还会叠加一点随机扰动(纵向 ±20%、横向 ±10%),让雨丝彼此错开,避免"整齐划一"的机械感。
💦 地面溅落:3 帧 1 秒的白色涟漪
雨滴的下落循环在 update:每帧 y 加纵向速度、x 加横向速度;一旦到达地面行(终端高度 −1),先按等级概率(10% / 30% / 60%)掷骰子决定是否生成 Splash,然后雨滴本体销毁。
Splash 本体只是一个(x, y, timer, max_timer)结构,生命只有 3 帧,渲染时在原地依次打出:
帧0: '.' → 帧1: 'o' → 帧2: 'O' → 帧3: ' '(消散)一个字符从小圆点滚到大圆圈,就是那圈水花(render)。为防止大雨天溅落刷屏,MAX_SPLASHES = 100 做了硬上限,超出就从队首丢弃最老的溅落。最终这些字符由 TerminalRenderer 的双缓冲机制合帧输出——只把变化的格子写入终端,所以满屏雨滴也依然流畅。
⚙️ 事件驱动:风与雨量如何实时灌进雨幕
雨滴系统并不是死循环里硬编码参数,而是挂在动画框架的事件总线上(system.rs 定义的AnimationSystemtrait):
- 天气数据到达 → AnimationManager 广播
on_rain_intensity与on_wind→ 雨滴系统更新等级与 wind_x; - 终端窗口缩放 →
on_resize自动清理越界的雨滴与溅落; - 是否激活 → 由 is_active 判断当前条件是否在降雨或雷暴。
这就是为什么你在 HUD 上看到风速从 10 km/h 跳到 45 km/h 时,雨幕会立刻整体斜过来——参数是流式更新的。
🖥️ 动手体验:3 条命令看完全部效果
# 模拟普通降雨 weathr --simulate rain # 模拟雷暴(Storm 级 + 45 km/h 大风,雨丝明显斜飞) weathr --simulate thunderstorm # 对比场景:降雪 / 晴朗 weathr --simulate snow weathr --simulate clear运行后按q即可退出。下面的夜景与降雪演示和雷暴雨幕放在一起,能直观感受到同一套框架下不同粒子系统(雨 / 雪 / 星月)的差异:
📌 小结
weathr 的雨滴模拟用不到 300 行代码串起了三个物理概念:向量分解(风向角 → 横向漂移)、状态阈值(速度超 0.5 才斜雨)、有限寿命粒子(3 帧溅落)。如果你也想给终端项目加一点"物理感",这套"归一化风速 + 阈值字符切换 + 寿命计时器"的组合拳非常值得参考。
【免费下载链接】weathra terminal weather app with ascii animation项目地址: https://gitcode.com/gh_mirrors/wea/weathr
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考