【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);
}
相关推荐
孟无岐9 小时前
【Laya】下雪环境效果
·laya·环境效果
孟无岐1 天前
AI小游戏·极简2048
源码·小游戏·2048·laya·ai游戏开发
luoluoal1 天前
基于python的web渗透测试工具(源码+文档)
python·mysql·django·毕业设计·源码
WangYaolove13141 天前
基于信息安全领域中语义搜索引擎的设(源码+文档)
python·django·毕业设计·源码·计算机源码
WangYaolove13141 天前
基于Python的登录网站验证码的生成与识别系统(源码+文档)
python·mysql·django·毕业设计·源码
千寻技术帮2 天前
10399_基于SpringBoot的智慧养老院管理系统
java·spring boot·后端·源码·安装·代码
孟无岐2 天前
【Laya】HttpRequest 网络请求
网络·typescript·游戏引擎·游戏程序·laya
孟无岐2 天前
AI小游戏·极简贪食蛇
源码·小游戏·laya·ai游戏开发·贪食蛇