
1. 雪天效果为什么是2D游戏里最容易被做砸的气氛杀手做2D游戏的人应该都有过这种体验美术花了两周画了一张特别有氛围的场景结果你往里面加了一个雪天粒子整个画面瞬间从寂静岭美学变成劣质屏保。我这次在《暗冬》项目里做雪天效果前前后后推翻了三版方案才把那种下雪了的感觉真正做出来。先说结论2D雪天效果的难点从来不在于让白色小点往下掉而在于三件容易被忽略的事——层次感、透感、和场景的交互感。这三件事做不到位雪就是贴在屏幕上的纸屑而不是飘在空气中的天气。这一期DEV LOG.05就专门记录我做2D雪天效果的完整过程从粒子系统设计、视差层级、着色器调优到暗黑风格场景里雪天氛围的配合全部拆开讲。如果你也在做俯视角或横版2D游戏想在场景里加入自然的雪天效果这篇文章可以直接当成一份踩坑参考。先说清楚我在做什么项目背景。《暗冬》是一款俯视角2D暗黑风生存游戏场景素材走的是低明度、高对比、手绘质感路线。这个底子决定了雪天效果不能走清新治愈路线而要走冷冽、压抑、能见度被压缩的路线。同一套粒子参数放在明亮卡通风格里成立放到暗黑素材上就会显得脏、假、跳戏。所以我后面所有参数调整都是在暗黑类素材配合度这个前提下做的这一点先明确免得你直接抄作业抄岔了。2. 先拆解需求2D雪天到底需要哪几个系统同时工作在动手写任何代码之前我先把雪天这个词拆成了五个需要独立实现的子系统。这一步特别重要因为如果你脑子里的下雪效果只是一个粒子发射器那后面百分之百会被各种细节追着打。2.1 粒子层视觉上的雪花飘落这是最表面的一层也是大多数人理解的下雪。但雪花粒子不是简单地给一个向下速度就完事。真实的雪和雨最大的区别在于雪几乎没有固定的下落轨迹它受风力、气流和自身重力的综合影响会飘、会回旋、会突然斜着走一段再落下来。这部分我放在第三大节专门讲因为参数调试的坑基本都集中在这里。2.2 视差层距离感与纵深感2D场景里要建立雪花在空中的感觉靠的是多个不同速度、不同大小、不同透明度的粒子层。远景的雪应该小而密、移动慢、透明度低近景的雪应该大而疏、移动快、透明度高。这就是经典的视差滚动的天气版。如果没有分层雪花全部一个速度往下掉画面会表现得非常平像是一张贴图在下滑。2.3 场景响应层积雪与天气的痕迹真正让雪天效果落地的是场景本身对天气的反馈。这一步在2D游戏里往往被砍掉因为工作量大但它是氛围感的关键。我指的是地面有没有渐变的积雪覆盖屋顶、树冠这些遮挡物边缘有没有雪线角色踩过的地方要不要留下脚印或者融雪痕迹《暗冬》的地面是拼接瓦片地图所以我在场景响应层上采用了一个比较取巧的方案用一张全屏渐变的雪量遮罩叠加在场景层之上通过世界坐标和高度图决定不同区域的积雪程度。不是每个瓦片都画积雪版本而是用遮罩地形判断来模拟。这个方案不是物理精确的但视觉表现足够而且性能开销小——后面我会把实现思路展开说。2.4 环境光照层雪天的光色变化下雪天不等于变暗。实际上雪天因为云层对光线的漫反射整体的环境亮度对比晴天会有一种特殊的均匀感——阴影变得柔和高光变少整个画面有一种被磨砂玻璃罩住的感觉。在暗黑风格游戏里这个光色变化要和美术风格做一次融合不能一刀切地调亮画面。我的做法是加了一个环境色偏移的Lerp根据天气强度在正常环境色和雪天色之间过渡。雪天色的参数是色温偏冷向蓝色方向偏移、对比度降低、暗部提亮一点点但保持暗调氛围。2.5 音效与辅助反馈层视觉之外雪天的氛围还靠听觉。这个我没有做特别复杂的处理只做了两件事下雪时叠加一层低通滤波的环境底噪让人声和风声变得闷一些角色移动时如果处于积雪状态脚步声切换为踩雪音效。前者用引擎的Audio Mixer就可以搞定后者需要和角色状态机联动逻辑不复杂但很提升沉浸感。五个子系统理清楚之后我对雪天效果的认知从一个粒子特效升级成了一个轻量级天气系统。接下来每一层的设计和调优都有了明确的边界。3. 三层雪花粒子的设计参数从纸屑感到空气感的关键差异这是雪天效果里最核心也最磨人的部分。我第二版方案失败就失败在这里——所有粒子混在一层里最终效果就是屏幕上一堆白色噪点在匀速下落毫无生气。3.1 近景、中景、远景三层参数表我最终采用的是三层粒子方案每层独立发射、独立渲染、独立调参。下面这张表是调完之后的最终参数平台是Unity的URP管线2D渲染粒子系统用的是Particle System组件。如果你用的是Godot或者自研引擎参数含义一样照抄思路即可。参数项远景层中景层近景层粒子数量40018060初始大小0.08~0.150.25~0.450.8~1.6下落速度0.3~0.60.8~1.52.0~3.5水平摆动幅度0.20.61.2摆动频率0.50.81.1透明度0.15~0.30.4~0.60.7~0.95视差系数0.20.61.0Sort Order01020这个表的逻辑是离镜头越近的雪花体量越大、速度越快、透明度越高、受风影响越明显。远层雪花小而密负责铺满画面营造雪很大的第一印象近层雪花大而疏速度很快地从镜头前划过给玩家一种雪在和我之间落下的临场感。3.2 水平摆动的正弦波实现细节雪花和雨滴最大的物理区别在于雪花下落时受到空气阻力的影响远大于重力所以它的运动近似于随风飘落而不是垂直砸落。我的实现方式是在粒子的自定义更新脚本里给速度向量叠加一个随时间变化的正弦扰动。具体思路是给每个粒子分配一个随机初始相位0到2π之间这个相位决定它进入水平摆动周期时的起点避免所有雪花同步左右飘看起来像整齐划一的波浪舞。水平速度vx sin(time * frequency phase) * amplitude其中frequency和amplitude从上面的参数表按层读取。为了让飘动更自然我在正弦波上叠加了一次Perlin噪声让摆动幅度在短时间内有微小的不规则变化而不是严格的周期运动。叠加噪声之后雪花轨迹就变得活了——偶尔停一下、偶尔抖一下、偶尔被一阵小风吹得斜着走两步。这个细节如果不做三层粒子做得再好看起来也只是一批有大小差别的纸屑在整齐地左右晃。3.3 为什么近景雪花必须加大模糊而不是加高透明度这是一个我从其他开发者那里学到、自己验证过好用的技巧。近景雪花因为离镜头近在真实摄影里是对焦范围之外的所以应该是模糊的。很多人做近景雪花时单纯把透明度调高、把粒子调大效果却很突兀——一大片半透明的大白斑在屏幕边缘快速移动很抢视线。正确方向是给近景雪花叠加一个径向模糊效果。在2D粒子里简单做法是给近景粒子使用一张边缘羽化的软圆形贴图并且加大粒子在屏幕边缘时的模糊量。我直接给近景粒子挂了一个带模糊的材质变体视觉上雪花从屏幕上方飘下来时边缘是柔的不是锋利的小圆点。这一项做完之后整体画面的临场感提升了一个档次。3.4 发射区域和生命周期看不见的边界处理还有一个细节容易踩坑粒子发射区域的边界。俯视角2D游戏里如果发射区域严格等于屏幕范围雪花只会从屏幕顶端出现到屏幕底端消失——这本身没错但在边缘处会出现突然生出和消失的硬切感玩家稍微注意就能看到。解决方法是把发射区域扩大为屏幕大小的1.5倍上方多出半个屏幕的高度下方也多出半个屏幕的高度。这样雪花从屏幕外进入视野时已经在飘落状态而不是在屏幕边缘凭空生成飘出屏幕底部后还要多飞一段距离才回收视觉上没有到边缘被吞掉的断裂感。这个处理同时也兼容了摄像机平滑移动的情况——当视角移动时新进入屏幕区域的雪花已经在空中了不会出现一片空白然后渐渐被新粒子填满的掉帧感。4. 着色器与渲染顺序暗黑风格素材下的雪天融合方案4.1 雪花的渲染层级和混合模式2D游戏里雪花的渲染层级是一个很容易搞出事故的地方。如果雪花渲染在场景之下那地面不透明物体就会遮挡它视觉上雪花只能出现在地面以下显然是错的如果雪花渲染在场景之上那角色、NPC、建筑就全部被雪花盖住下雪变成了隔着毛玻璃看游戏。我最终采用的层级方案是这样的远景雪花渲染在远景层和场景层之间Sort Order为0。它需要被地形和建筑物遮挡因为远景的雪本来就该在场景物件后面。中景雪花渲染在场景层之上、角色层之下Sort Order为10。这样角色走在雪中时雪花既可以被建筑遮挡穿过建筑侧面时又不会被角色本身挡住——角色是从雪中走出来的而不是站在雪花后面。近景雪花渲染在UI层之下、所有游戏对象之上Sort Order为20。它就是要从角色面前掠过故意制造镜头前有东西飞过的距离感。关于混合模式我用的是Additive混合也就是加法混合颜色叠加后整体变亮非常适合雪花这种自发光感不强的半透明物体。但要注意在暗黑风格素材中Additive混合如果粒子颜色是纯白会在暗色场景里产生明显的发光感像荧光雪反而出戏。我的调节方式是把雪花粒子的主色从纯白255, 255, 255调整为一个偏冷的灰白色200, 215, 230同时把粒子贴图的Alpha曲线做成边缘羽化而非硬边。暗黑场景本身明度低灰白色雪花既有雪的辨识度又不会因为过亮而跳出场景的明暗系统。4.2 地面积雪遮罩用置灰Lerp代替逐瓦片重绘接下来是场景响应层里最重要的一块——积雪遮罩。我的具体实现分成三步准备一张全屏后处理材质输入是场景的渲染纹理RenderTexture和一张高度/权重图Weight Map。高度图用来判断哪些区域更容易积雪比如屋顶、地面、树干顶部需要美术在场景制作时额外标注一张灰度图白色代表容易积雪黑色代表不积雪。在后处理Shader里对场景像素进行采样根据权重图的值在原始颜色和积雪颜色之间做Lerp。积雪颜色我设为235, 238, 245并额外叠加一层细小的噪声扰动让积雪边缘不至于太规则看起来像被风刮过的自然雪线。为了让积雪有逐渐累积的动态过程而不是瞬间铺满我把Lerp系数做成一个随时间增长的全局变量。雪下得越久积雪覆盖比例越高。我设定的完整积雪时间是120秒从完全无雪到积雪饱和度拉满这个过程在暗黑类素材上表现非常自然。这套方案的关键在于权重图的制作。如果不想额外画图也可以程序化生成拿场景的地形高度数据和障碍物分布在初始化时算出一张粗略的权重图。但我的经验是程序生成的效果终究不如美术手动标注来得准确因为美术知道哪些地方雪应该积起来才好看。4.3 暗黑类素材的明度匹配问题这里专门聊聊暗黑风格素材和雪天效果配合时的坑。暗黑类素材的特点是大面积深色、高对比、纹理细节丰富但明度整体偏低。在这种素材上叠加白色的粒子和积雪最大的视觉矛盾是白会非常刺眼破坏暗色的整体氛围。我的处理思路是降白保冷所有积雪相关的白色都往冷色方向偏移同时降低纯度。具体数值上我用了Hue偏移8度往蓝方向Saturation降到15%左右Value/Lightness控制在85%左右。经过这样的处理雪在画面上看起来是暗夜里的冷光而不是一张白纸贴在脸上。另外一点暗黑类素材的轮廓光通常很强如果你把雪花粒子放在这种轮廓光区域粒子会被高光吃掉变得几乎不可见。我在场景里手动标注了几个强光源区域给粒子系统加了遮挡剔除逻辑当粒子进入高光区域时自动把粒子透明度降到正常值的40%以下离开区域后恢复。这样不会出现人物站在月光下雪却不见了的怪异感。5. 性能调优2D粒子系统在低端机上的实战策略雪天效果做出来好看没有用得跑得动才算数。我测试的最低配设备是2019年的千元级安卓手机在1920x1080分辨率下粒子系统如果不开任何优化帧率会掉到20帧以下。这一节把优化过程完整列出来。5.1 颗粒数预算和分层统计先给粒子数量定一个硬性预算全屏同时存活的粒子数不超过800个。这个数字是我在目标设备上反复测试出来的一个平衡点800个以下帧率在45-60之间波动800个以上帧率开始明显下跌1000个以上直接掉到30帧附近。在800个预算里我重新分配了三层数量远景500、中景250、近景50。你没有看错我把近景从60砍到了50。原因很简单近景粒子尽管数量少但因为个体大、透明度高、带有模糊材质它的填充率开销比远景一枚小粒子多得多。所以在预算紧张时砍近景数量是性价比最高的选择视觉影响也不明显。5.2 粒子池化与GC控制Unity的Particle System在粒子生命周期结束时会回收对象但如果频繁创建和销毁粒子对象会产生大量的GC Alloc在手机上表现为周期性卡顿。解决方式是从项目一开始就建立一个全局的粒子池预先创建好所有粒子系统实例需要时激活不需要时禁用而不是Instantiate和Destroy。我给的粒子池设计池大小三层各预留最大数量2倍的实例也就是远景1000、中景500、近景100避免极端天气时粒子数突增导致池不够用。激活策略从池中取实例时重置它的startLifetime、startSize、startRotation、velocity等参数避免上次运行的残留数据污染新发射。回收策略粒子系统的main模块里打开stopAction为Disable这样粒子播放完自动失活池管理器订阅失活事件把实例重新放入可用列表。这套池化方案跑下来GC Alloc从原来的每帧平均3.2KB降到了接近0周期性卡顿也随之消失。5.3 遮挡剔除与区域级联俯视角游戏虽然不像3D那样有严格的遮挡关系但大量的粒子在屏幕外渲染依然是对GPU的浪费。我做了两重剔除第一重是屏幕包围盒剔除这个Unity自带打开粒子的Culling模式即可。注意一定要把发射器模拟空间设置为World否则粒子就算飞出屏幕只要发射器在屏幕内系统依然会模拟和渲染它产生不必要的开销。第二重是区域级联剔除这是针对性能的逻辑优化。我的地图分成了多个区块每个区块有一个天气影响系数。玩家在A区块时B区块的粒子系统直接暂停模拟只有玩家所在区块及相邻区块的粒子系统在运行。这种方法在需要全地图下雪的氛围需求下反而更实用——你可以用这种方法提升局部密度而不影响整体预算因为只有玩家周围的雪是真实落下的远处的雪可以用远景层的静态雪花贴图兜底。5.4 渲染纹理尺度优化除了粒子本身积雪后处理遮罩也是一个性能消耗点。全屏后处理如果在全分辨率下采样普通手机的压力不小。我把积雪后处理的RenderTexture分辨率设为了屏幕分辨率的1/4然后用双线性过滤放大回全屏。积雪效果本身就是大面积渐变对分辨率不敏感降采样后视觉差异几乎不可感知但性能开销直接从全屏后处理的1.0降到了0.16左右。6. 实测过程中翻过车的三个具体问题调参这种事理论再完备也要上机跑。下面三个问题是我在实测中真实遇到的每一个都花了我不少时间排查列出来给你省时间。6.1 雪花在角色头顶穿模我在第一版用中景层粒子时发现一个尴尬现象雪花从角色头顶飘过时会从帽子和头发中间穿过看起来像雪是假的没有体积概念。这个问题本质上是因为中景层雪花在角色层之下渲染但它和角色之间没有深度关系。我的解决方案是给角色增加一个头顶遮挡体积的碰撞判断。具体是用角色碰撞体的上表面画一条传感器检测到有粒子进入时将粒子的渲染层级临时提升到角色之上。听起来复杂实现起来其实只有几十行代码在粒子脚本里检测与角色碰撞体相交的粒子把它们单独加入一个前景粒子列表用另一个Sort Order渲染。效果非常明显——雪花飘到角色头顶上方时会自然地从帽子前沿滑落经过脸前时是在角色层之上的看起来就像真的从身体前面飘过而不是穿过角色身体。6.2 远景层积雪权重在特殊地形上表现错误我第一次做积雪遮罩时权重图用的是简单的灰度标注结果在山洞入口处翻了车。山洞内壁被标成了高权重雪直接积在了室内墙壁上看着特别违和。问题是权重图没有考虑到头顶是否有遮挡这个因素——室内不应该积雪即使它同样被标注为容易积。修正方案是引入一张光照探针图Light Probe Map把场景内的光照明暗和遮挡信息存进去。积雪权重 美术标注权重 × 光照探针的暴露度。室内因为光照极低暴露度接近0积雪权重被自动压到接近0露天区域光照充足暴露度接近1积雪权重保持原样。这个修正只增加了两个通道的乘算效果却非常自然。6.3 近景雪花透明度抖动一个让我调了大半天的细节近景雪花的透明度会随机抖动有时突然变透明像是闪烁。排查之后发现是Shader里_AlphaCutoff被粒子系统的SizeOverLifetime模块间接影响了——粒子在变大的过程中UV采样位置变化导致Alpha贴图边缘的透明区域被放大形成闪烁。解决方式是把粒子贴图的Alpha通道改为纯白把透明信息全部交给粒子系统本身的ColorOverLifetime曲线控制而不是依赖贴图本身的Alpha边缘。雪花贴图只需要画一个带羽化的圆形灰度图Alpha统一由引擎生成。这样近景雪花在放大和缩小的过程中透明度完全由曲线控制抖动问题彻底消失。7. 暗黑类素材的游戏里雪天还应该配合哪些环境细节最后聊一个偏设计向的话题。雪天效果在暗黑类素材游戏里其实是一个氛围放大器它放大的不只是冷还有危险感和孤立感。如果你也在做暗黑风格2D游戏下面这几个环境细节是我觉得值得一起做的。7.1 角色状态联动雪天影响移动与战斗下雪天不只是视觉事件还应该影响玩法。我在《暗冬》里做了两个联动雪天时角色移动速度降低5%翻滚距离缩短10%火属性伤害在雪天降低15%冰属性伤害提升10%。这些数值不大但玩家能明确感受到天气是有规则的系统而不只是背景板。7.2 生物行为变化视野与警觉性暗黑类游戏里怪物和NPC的行为在雪天也可以做差异化处理。我给怪物加了一个恶劣天气状态逻辑雪天时它们的视野范围缩短20%听觉距离保持不变但玩家在积雪上移动时发出的脚步声半径扩大30%。这样就产生了有趣的策略平衡——怪物看不到你但它们听到你的概率更高玩家需要在靠近和潜行之间做选择。7.3 音效的低通滤波与高频衰减音效方面雪天和雨天的处理一定要区分开。雨天是高频噪声多雪天则应该把环境底噪做成低频的闷响感。我做了两步处理一个低通滤波器把环境音中2kHz以上的频率衰减掉模拟雪的吸声效果再叠加一层细微的雪落音效——用白噪声经过一个低频随机调制音量控制在-40dB左右作为底噪存在。做完这一步再进游戏体验氛围感才算是真正闭环了画面上的冷、动作上的慢、听觉上的闷三者共同构成下雪天的完整体验。只看画面的话总觉得少了点什么但把这一层加上去之后整个暗黑雪天的沉浸感完全不一样。雪天效果做到这里我自认为已经达到了这一版DEMO的目标视觉上分层、行为上有反馈、性能上不掉链子。下一期DEV LOG计划把天气系统的昼夜循环加进去让雪天可以在傍晚和深夜呈现完全不同的两种氛围。如果你也在做类似的2D天气系统欢迎交流你们在暗黑素材配合上的处理思路。