【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);
}
相关推荐
只会写代码2 天前
一套开箱即用实体反射Lambda链式工具,彻底告别原生反射样板代码
java·程序员·源码
把马铃薯变成土豆14 天前
前端Stripe跨境支付对接感想
前端·源码
源码宝19 天前
拿来即改的云HIS源码:前端BootStrap+LayUI,后端SpringBoot,数据库MySQL+MyCat
java·源码·his系统·云技术·his源码·医院信息系统源码
幽络源小助理19 天前
2026最新写真图片视频打赏系统源码_全开源无加密_幽络源源码
开源·源码·php源码
幽络源小助理20 天前
苹果CMS觅知ART弹幕播放器_MizhiPlayer全新UI-幽络源源码网
开源·源码·php源码
程序猿阿越21 天前
AutoMQ源码(一)读、写、Compaction
java·后端·源码
贾艺驰22 天前
实战Android Framework: 新增一个系统服务
android·源码
VX_BYSJ834123 天前
【关注可白嫖源码】--17214基于Web的旅游信息交互网站设计与实现(案例分析)
源码·毕设·计算机毕业设计·大作业·毕设定制·程序定制·毕设代做
源码宝25 天前
智能随访系统源码,技术架构设计:Spring Boot + Vue.js + 微服务实战
java·人工智能·源码·随访系统·智能随访·随访系统成品源码
甜瓜看代码1 个月前
SystemUI 启动与组成机制
android·源码·源码阅读