【Laya】下雨环境效果

下雨环境效果

一个基于 LayaAir 的 2D 下雨粒子特效系统,包含雨滴下落、地面溅射效果。

效果预览

快速使用

方式一:IDE 中挂载脚本

  1. 在 IDE 中创建一个 2D Sprite 节点
  2. 添加 RainEnvironmentScript 组件
  3. (可选)在属性面板中拖入雨滴纹理图片
  4. 在属性面板中调整参数

方式二:代码中使用

typescript 复制代码
import { RainEnvironmentScript } from "./environment/RainSystem";

// 添加下雨脚本
const rainScript = sprite.addComponent(RainEnvironmentScript);

// 动态调整参数
rainScript.setRainIntensity(10);     // 设置雨量
rainScript.setRainSpeed(15, 25);     // 设置速度
rainScript.setWind(-3);              // 设置风力

配置参数

参数 类型 说明 默认值
rainTexture Texture 雨滴纹理图片(可选,不设置则使用默认线条) null
rainIntensity Number 雨量大小 (0-20) 4
minSpeed Number 最小下落速度 8
maxSpeed Number 最大下落速度 14
wind Number 风力(负数向左,正数向右) -1
minLength Number 最小雨滴长度 10
maxLength Number 最大雨滴长度 25
enableSplash Boolean 是否启用地面溅射 true
splashRate Number 溅射频率 (0-1) 0.3

雨量参考

效果 雨量 速度范围
毛毛雨 1-3 3-6
小雨 3-6 6-10
中雨 6-10 8-14
大雨 10-15 12-20
暴雨 15-20 18-30

实现原理

核心架构

复制代码
RainEnvironmentScript (主脚本)
    │
    ├── RainSystem (雨滴系统)
    │       └── RainDrop[] (雨滴粒子数组)
    │
    └── SplashSystem (溅射系统)
            └── SplashParticle[] (溅射粒子数组)

1. 雨滴粒子 (RainDrop)

每个雨滴是一个独立的粒子对象,支持纹理和默认绘制两种模式:

typescript 复制代码
class RainDrop {
    sprite: Laya.Sprite;        // 精灵对象
    texture: Laya.Texture;      // 纹理(可选)
    vx: number;                 // X方向速度(风力)
    vy: number;                 // Y方向速度(下落)
    length: number;             // 雨滴长度
    scale: number;              // 缩放比例
    inUse: boolean;             // 使用状态
}

渲染方式:根据是否有纹理选择绘制方式

typescript 复制代码
// 有纹理时:使用 drawTexture 绘制 PNG 图片
if (this.texture) {
    this.sprite.graphics.drawTexture(
        this.texture,
        0, 0,
        this.texture.width,
        this.texture.height
    );
    this.sprite.pivotX = this.texture.width / 2;
    this.sprite.pivotY = this.texture.height / 2;
}
// 无纹理时:使用 drawLine 绘制默认线条
else {
    this.sprite.graphics.drawLine(0, 0, 0, this.length, "#aaddff", 2);
}

纹理绑定 :使用 @property({type: Laya.Texture}) 装饰器实现 IDE 拖拽绑定

typescript 复制代码
@property({type: Laya.Texture, tooltip: "雨滴纹理(拖入图片资源)"})
rainTexture: Laya.Texture | null = null;

2. 雨滴系统 (RainSystem)

职责:管理所有雨滴粒子的创建、更新和销毁

2.1 雨滴生成

每帧根据 intensity 参数生成新雨滴:

typescript 复制代码
update() {
    const texture = this.texture;
    // 产生新雨滴
    for (let i = 0; i < this.intensity; i++) {
        const x = Math.random() * (this.stageWidth + 100) - 50;
        const y = -20;
        const drop = RainDrop.acquire(x, y, this.config, texture);
        this.drops.push(drop);
        this.container.addChild(drop.sprite);
    }

    // 更新现有雨滴...
}

3. 溅射系统 (SplashSystem)

职责:管理地面溅射水花效果

typescript 复制代码
class SplashParticle {
    init(x: number, y: number): void {
        // 绘制小水珠
        this.sprite.graphics.drawCircle(0, 0, 2, "#cceeff");
        this.sprite.pos(x, y);
        this.sprite.alpha = 1;

        // 向上溅射
        const angle = -Math.PI / 2 + (Math.random() - 0.5);
        const speed = 1 + Math.random() * 2;
        this.vx = Math.cos(angle) * speed;
        this.vy = Math.sin(angle) * speed;

        this.life = 10 + Math.random() * 10;
    }

    update(): boolean {
        this.life--;
        if (this.life <= 0) {
            this.recycle();
            return false;
        }

        this.sprite.x += this.vx;
        this.sprite.y += this.vy;
        this.vy += 0.2;  // 重力
        this.sprite.alpha = this.life / 20;  // 淡出

        return true;
    }
}

4. 参数配置系统

使用接口定义配置,支持灵活扩展:

typescript 复制代码
interface RainDropConfig {
    minSpeed: number;      // 最小速度
    maxSpeed: number;      // 最大速度
    minLength: number;     // 最小长度
    maxLength: number;     // 最大长度
    windX: number;         // 风力
}

// 默认配置 + 自定义覆盖
this.config = {...DEFAULT_RAIN_CONFIG, ...config};

5. 性能优化

5.1 对象池

使用 LayaAir 内置的对象池复用粒子对象:

typescript 复制代码
// 获取雨滴
static acquire(x: number, y: number, config: RainDropConfig, texture: Laya.Texture | null): RainDrop {
    let drop = Laya.Pool.getItemByClass("RainDrop", RainDrop);
    if (!drop) {
        drop = new RainDrop();
    }
    drop.init(x, y, config, texture);
    return drop;
}

// 回收雨滴
recycle(): void {
    this.inUse = false;
    this.sprite.visible = false;
    this.sprite.graphics.clear();
    this.texture = null;
    if (this.sprite.parent) {
        this.sprite.parent.removeChild(this.sprite);
    }
    Laya.Pool.recover("RainDrop", this);
}

优点

  • 减少对象创建和销毁的开销
  • 降低垃圾回收压力
  • 提高帧率稳定性
5.2 批量更新

使用反向循环遍历,安全删除元素:

typescript 复制代码
for (let i = this.drops.length - 1; i >= 0; i--) {
    const alive = this.drops[i].update(this.stageHeight);
    if (!alive) {
        this.drops.splice(i, 1);
    }
}

API 参考

RainEnvironmentScript

方法 说明
setRainIntensity(value) 设置雨量 (0-20)
getRainIntensity() 获取当前雨量
setRainSpeed(min, max) 设置下落速度范围
setWind(windX) 设置风力

RainSystem

方法 说明
setIntensity(value) 设置雨量
getIntensity() 获取当前雨量
setSpeed(min, max) 设置速度
setWind(windX) 设置风力
setLength(min, max) 设置雨滴长度
destroy() 销毁系统

RainDrop

静态方法 说明
RainDrop.acquire(x, y, config, texture) 从对象池获取雨滴
RainDrop.clearPool() 清空雨滴对象池

SplashParticle

静态方法 说明
SplashParticle.acquire(x, y) 从对象池获取溅射粒子
SplashParticle.clearPool() 清空溅射对象池

使用场景

游戏中的下雨场景

typescript 复制代码
// 在游戏场景中添加下雨效果
const rainScript = scene2D.addComponent(RainEnvironmentScript);
rainScript.setRainIntensity(12);
rainScript.setRainSpeed(12, 20);
rainScript.setWind(-2);

动态天气变化

typescript 复制代码
class WeatherSystem extends Laya.Script {
    private rain!: RainEnvironmentScript;

    async onAwake() {
        this.rain = this.owner.addComponent(RainEnvironmentScript);

        // 模拟天气变化:从小雨到大雨
        Laya.timer.loop(5000, this, this.changeWeather);
    }

    changeWeather() {
        const intensity = Math.random() * 15;
        this.rain.setRainIntensity(intensity);
        this.rain.setWind((Math.random() - 0.5) * 4);
    }
}

扩展建议

添加闪电效果

typescript 复制代码
private flash(): void {
    const originalColor = Laya.stage.bgColor;
    Laya.stage.bgColor = "#555577";
    Laya.timer.once(50, this, () => {
        Laya.stage.bgColor = originalColor;
    });
}

// 在 onUpdate 中随机触发(大雨时)
if (Math.random() < 0.005 && this.rainIntensity > 10) {
    this.flash();
}

添加风向变化

typescript 复制代码
private updateWind(): void {
    // 缓慢变化风向
    const time = Laya.timer.currTimer * 0.0001;
    const wind = Math.sin(time) * 2;
    this.rainSystem.setWind(wind);
}

添加雨滴音效

typescript 复制代码
// 根据雨量播放不同音效
setRainIntensity(value: number) {
    this.rainIntensity = value;
    if (value > 10) {
        // Laya.SoundManager.playSound("sounds/heavy_rain.mp3", 0);
    } else if (value > 5) {
        // Laya.SoundManager.playSound("sounds/moderate_rain.mp3", 0);
    } else if (value > 0) {
        // Laya.SoundManager.playSound("sounds/light_rain.mp3", 0);
    }
}

夜晚下雨效果

typescript 复制代码
async onAwake() {
    // 深蓝色夜空
    Laya.stage.bgColor = "#0a1520";

    // 添加水坑反射效果
    const puddle = new Laya.Sprite();
    puddle.graphics.drawCircle(0, 0, 30, "rgba(100, 150, 200, 0.3)");
    puddle.pos(Laya.stage.width / 2, Laya.stage.height - 50);
    this.owner.addChild(puddle);
}
相关推荐
暮色妖娆丶6 天前
Spring 源码分析 Lifecycle Bean
spring boot·spring·源码
luoluoal14 天前
基于python的医疗知识图谱问答系统(源码+文档)
python·mysql·django·毕业设计·源码
2501_9371454115 天前
神马影视 8.8 2026 版:电视 / 戏曲 / 动画全品类源码系统解析
源码·电视盒子·源代码管理·刷机包
EnzoRay15 天前
apk的安装过程
源码
源码宝17 天前
无需从零开发:医院智慧随访系统完整源码,一键快速部署
java·开源·源码·随访·随访系统源码·医院随访
千寻技术帮18 天前
10352_基于Springboot的房屋销售平台
java·spring boot·mysql·vue·源码·代码·远程
工业互联网专业18 天前
基于协同过滤算法的招聘信息推荐系统 _django+spider
python·django·毕业设计·源码·课程设计·协同过滤·spider
luoluoal19 天前
基于python的医疗问句中的实体识别算法的研究(源码+文档)
python·mysql·django·毕业设计·源码
暮色妖娆丶20 天前
Spring 源码分析 BeanFactoryPostProcessor
spring boot·spring·源码