【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);
}
相关推荐
luoluoal1 天前
基于深度学习的web端多格式纠错系统(源码+文档)
python·mysql·django·毕业设计·源码
2501_937189231 天前
TV 电视影视大全:全场景高清观影技术解析
android·源码·源代码管理
2501_937154931 天前
TV 电视影视大全:全场景高清观影技术解析
android·源码·源代码管理·机顶盒
千寻技术帮2 天前
10333_基于SpringBoot的家电进存销系统
java·spring boot·后端·源码·项目·家电进存销
源码宝3 天前
云诊所系统(源码)采用SpringBoot+Vue.js架构,实现智能化药品库存预警管理
源码·源代码·门诊系统·药品·库存预警
千寻技术帮3 天前
10334_基于Web的文学书刊服务平台
mysql·ssm·源码·代码·文学书刊
luoluoal4 天前
基于opencv的疲劳检测系统(源码+文档)
python·mysql·django·毕业设计·源码
2501_937193144 天前
TV 电视影视大全:全终端流畅观影技术解析
android·源码·源代码管理·机顶盒
万岳软件开发小城6 天前
在线问诊系统源码实战:视频/图文问诊平台搭建步骤全解析
人工智能·开源·源码·在线问诊系统源码·问诊app开发·问诊软件开发·视频问诊小程序
WangYaolove13146 天前
基于opencv的疲劳检测系(源码+文档)
python·mysql·django·毕业设计·源码