【Laya】下雪环境效果

下雪环境效果

一个基于 LayaAir 的 2D 下雪粒子特效系统,支持纹理图片和默认绘制两种模式,包含雪花飘落、地面积雪效果。

效果预览

文件结构

复制代码
environment/
└── SnowSystem.ts    # 下雪效果完整实现

快速使用

方式一:IDE 中挂载脚本

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

方式二:代码中使用

typescript 复制代码
import { SnowEnvironmentScript } from "./environment/SnowSystem";

// 添加下雪脚本
const snowScript = sprite.addComponent(SnowEnvironmentScript);

// 动态调整参数
snowScript.setSnowIntensity(5);     // 设置雪量
snowScript.setSnowSpeed(0.5, 2);    // 设置速度
snowScript.setWind(1);              // 设置风力
snowScript.setSwing(30, 0.02);      // 设置摇摆幅度和速度

配置参数

参数 类型 说明 默认值
snowTexture Texture 雪花纹理图片(可选,不设置则使用默认圆形) null
snowIntensity Number 雪量大小 (0-15) 5
minSpeed Number 最小下落速度 0.5
maxSpeed Number 最大下落速度 2
wind Number 风力(负数向左,正数向右) 0
minScale Number 最小缩放 0.5
maxScale Number 最大缩放 1.2
enableAccumulation Boolean 是否启用地面积雪 true
accumulationRate Number 积雪频率 (0-1) 0.15
maxParticles Number 最大粒子数(影响性能) 800

雪量参考

效果 雪量 速度范围
小雪 1-3 0.5-1.5
中雪 3-6 0.5-2
大雪 6-10 0.5-2.5
暴雪 10-15 1-3

实现原理

核心架构

复制代码
SnowEnvironmentScript (主脚本)
    │
    ├── SnowSystem (雪花系统)
    │       └── SnowFlakeSprite[] (雪花粒子数组)
    │
    └── SnowAccumulationSystem (积雪系统)
            └── Sprite[] (积雪粒子数组)

1. 雪花粒子 (SnowFlakeSprite)

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

typescript 复制代码
class SnowFlakeSprite {
    sprite: Laya.Sprite;        // 精灵对象
    texture: Laya.Texture;      // 纹理(可选)
    vy: number;                 // Y方向速度(下落)
    scale: number;              // 缩放比例
    alpha: number;              // 透明度
    startX: number;             // 起始X位置(用于摇摆计算)
    time: number;               // 时间累加(用于正弦摇摆)
    swingAmount: number;        // 摇摆幅度
    swingSpeed: number;         // 摇摆速度
    windX: number;              // 风力
}

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

typescript 复制代码
// 有纹理时:使用 drawTexture 绘制 PNG 图片
if (this.texture) {
    this.sprite.graphics.drawTexture(
        this.texture,
        0, 0,
        this.texture.width,
        this.texture.height
    );
}
// 无纹理时:使用 drawCircle 绘制默认圆形雪花
else {
    const size = 8;
    this.sprite.graphics.drawCircle(size / 2, size / 2, size / 2 - 1, "rgba(255, 255, 255, 0.8)");
    this.sprite.graphics.drawCircle(size / 2 - 2, size / 2 - 2, size / 4, "rgba(255, 255, 255, 1)");
}

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

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

2. 雪花摇摆效果

雪花使用正弦函数实现左右摇摆,模拟真实飘落效果:

typescript 复制代码
update(stageHeight: number): boolean {
    // 更新时间和摇摆位置
    this.time += this.swingSpeed;
    const swingOffset = Math.sin(this.time) * this.swingAmount * 0.01;

    // 更新位置:风力 + 摇摆 + 下落
    this.startX += this.windX * 0.1;
    this.sprite.x = this.startX + swingOffset;
    this.sprite.y += this.vy;

    // 超出屏幕底部则移除
    if (this.sprite.y >= stageHeight + 20) {
        this.recycle();
        return false;
    }
    return true;
}

摇摆原理

  • time 随时间累加,作为正弦函数的输入
  • Math.sin(time) 产生 -1 到 1 的周期值
  • 乘以 swingAmount 控制摇摆幅度
  • swingSpeed 控制摇摆快慢

3. 雪花系统 (SnowSystem)

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

3.1 雪花生成

每帧根据 intensity 参数生成新雪花,但不超过最大粒子数:

typescript 复制代码
update() {
    const texture = this.texture;
    const toAdd = Math.min(this.intensity, this.maxParticles - this.flakes.length);
    for (let i = 0; i < toAdd; i++) {
        const x = Math.random() * (this.stageWidth + 100) - 50;
        const y = -20;
        const flake = SnowFlakeSprite.acquire(x, y, this.config, texture);
        this.flakes.push(flake);
        this.container.addChild(flake.sprite);
    }

    // 更新现有雪花...
}

4. 积雪系统 (SnowAccumulationSystem)

职责:管理地面积雪堆积效果,同样支持纹理和默认绘制:

typescript 复制代码
accumulate(x: number, y: number, scale: number, texture: Laya.Texture | null): void {
    const sprite = new Laya.Sprite();
    sprite.pos(x, y);
    sprite.scaleX = scale;
    sprite.scaleY = scale;
    sprite.alpha = 0.6;

    // 根据是否有纹理选择绘制方式
    if (texture) {
        sprite.graphics.drawTexture(texture, 0, 0, texture.width, texture.height);
    } else {
        // 使用 Graphics 绘制默认积雪
        const size = 6;
        sprite.graphics.drawCircle(size / 2, size / 2, size / 2, "rgba(255, 255, 255, 0.8)");
    }

    this.container.addChild(sprite);
    this.particles.push(sprite);

    // 5-8秒后自动消失
    Laya.timer.once(5000 + Math.random() * 3000, this, () => {
        // 移除积雪...
    });
}

5. 参数配置系统

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

typescript 复制代码
interface SnowFlakeConfig {
    minSpeed: number;      // 最小速度
    maxSpeed: number;      // 最大速度
    minScale: number;      // 最小缩放
    maxScale: number;      // 最大缩放
    windX: number;         // 风力
    swingAmount: number;   // 摇摆幅度
    swingSpeed: number;    // 摇摆速度
}

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

6. 性能优化

6.1 对象池

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

typescript 复制代码
// 获取雪花
static acquire(x: number, y: number, config: SnowFlakeConfig, texture: Laya.Texture | null): SnowFlakeSprite {
    let flake = Laya.Pool.getItemByClass("SnowFlakeSprite", SnowFlakeSprite);
    if (!flake) {
        flake = new SnowFlakeSprite();
    }
    flake.init(x, y, config, texture);
    return flake;
}

// 回收雪花
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("SnowFlakeSprite", this);
}

优点

  • 减少对象创建和销毁的开销
  • 降低垃圾回收压力
  • 提高帧率稳定性
6.2 最大粒子数限制

防止粒子数量无限增长导致性能下降:

typescript 复制代码
const toAdd = Math.min(this.intensity, this.maxParticles - this.flakes.length);
6.3 批量更新

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

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

API 参考

SnowEnvironmentScript

方法 说明
setSnowIntensity(value) 设置雪量 (0-15)
getSnowIntensity() 获取当前雪量
setSnowSpeed(min, max) 设置下落速度范围
setWind(windX) 设置风力
setSwing(amount, speed) 设置摇摆幅度和速度

SnowSystem

方法 说明
setIntensity(value) 设置雪量
getIntensity() 获取当前雪量
setSpeed(min, max) 设置速度
setWind(windX) 设置风力
setScale(min, max) 设置缩放范围
setSwing(amount, speed) 设置摇摆参数
setMaxParticles(value) 设置最大粒子数
getParticleCount() 获取当前粒子数
destroy() 销毁系统

SnowFlakeSprite

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

使用场景

游戏中的降雪场景

typescript 复制代码
// 在游戏场景中添加降雪效果
const snowScript = scene2D.addComponent(SnowEnvironmentScript);
snowScript.setSnowIntensity(8);
snowScript.setSnowSpeed(1, 2.5);

动态天气变化

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

    async onAwake() {
        this.snow = this.owner.addComponent(SnowEnvironmentScript);

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

    changeWeather() {
        const intensity = 2 + Math.random() * 10;
        this.snow.setSnowIntensity(intensity);
        this.snow.setWind((Math.random() - 0.5) * 4);
    }
}

扩展建议

添加雪花旋转效果

typescript 复制代码
class SnowFlakeSprite {
    private rotation: number = 0;
    private rotationSpeed: number = 0;

    init(...) {
        // 随机旋转速度
        this.rotationSpeed = (Math.random() - 0.5) * 0.05;
    }

    update() {
        this.rotation += this.rotationSpeed;
        this.sprite.rotation = this.rotation;
        // ...
    }
}

添加风向变化

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

夜晚降雪效果

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

    // 添加月亮
    const moon = new Laya.Sprite();
    moon.graphics.drawCircle(0, 0, 40, "#fffae0");
    moon.pos(Laya.stage.width - 100, 100);
    this.owner.addChild(moon);
}
相关推荐
孟无岐1 天前
AI小游戏·极简2048
源码·小游戏·2048·laya·ai游戏开发
孟无岐2 天前
【Laya】HttpRequest 网络请求
网络·typescript·游戏引擎·游戏程序·laya
孟无岐2 天前
AI小游戏·极简贪食蛇
源码·小游戏·laya·ai游戏开发·贪食蛇
孟无岐3 天前
【Laya】LocalStorage 本地存储
typescript·游戏引擎·游戏程序·laya
孟无岐5 天前
【Laya】Byte 二进制数据处理
网络·typescript·游戏引擎·游戏程序·laya
孟无岐5 天前
【Laya】ClassUtils 类反射工具
typescript·游戏引擎·游戏程序·laya
孟无岐5 天前
【Laya】Ease 缓动函数
typescript·游戏引擎·游戏程序·laya
孟无岐6 天前
【Laya】Scene3D 介绍
typescript·游戏引擎·游戏程序·laya
孟无岐6 天前
【Laya】Sprite3D 介绍
typescript·游戏引擎·游戏程序·laya