HarmonyOS游戏开发:火焰粒子效果与实时渲染

举报
Jack20 发表于 2026/06/22 21:16:02 2026/06/22
【摘要】 HarmonyOS游戏开发:火焰粒子效果与实时渲染📌 核心要点:基于粒子系统模拟火焰效果,掌握火焰颜色渐变、温度映射、性能优化及篝火/爆炸等实战场景。 一、背景与动机火焰,大概是游戏特效中最"迷人"的存在了。从RPG游戏里篝火旁的温暖光晕,到动作游戏中华丽的爆炸火光,再到策略游戏中燃烧的城池——火焰效果无处不在,却又极难做好。为什么难?因为火焰不是"一个东西",它是成百上千个微小粒子的集...

HarmonyOS游戏开发:火焰粒子效果与实时渲染

📌 核心要点:基于粒子系统模拟火焰效果,掌握火焰颜色渐变、温度映射、性能优化及篝火/爆炸等实战场景。


一、背景与动机

火焰,大概是游戏特效中最"迷人"的存在了。从RPG游戏里篝火旁的温暖光晕,到动作游戏中华丽的爆炸火光,再到策略游戏中燃烧的城池——火焰效果无处不在,却又极难做好。

为什么难?因为火焰不是"一个东西",它是成百上千个微小粒子的集体行为。每个粒子有自己的位置、速度、颜色、大小、生命周期,而且这些属性还在不断变化。更关键的是,火焰的"真实感"来自于这些粒子的随机性协调性——太规律了像假火,太随机了像噪点。

在HarmonyOS游戏开发中,火焰效果的实现主要依赖Canvas自定义绘制粒子系统。没有现成的"火焰组件"给你拖拽使用,一切都要从粒子物理开始搭建。但别担心,一旦你理解了火焰的粒子模型,实现起来其实并不复杂——甚至可以说,火焰效果是粒子系统最经典的入门案例。

今天咱们就从零开始,一步步搭建一个完整的火焰粒子系统,最终实现篝火和爆炸两种实战效果。


二、核心原理

2.1 火焰的粒子模型

火焰的本质是什么?是燃烧产生的高温气体和微小颗粒。从粒子系统的角度看,火焰可以建模为:

  1. 发射器:在火焰底部持续产生新粒子
  2. 粒子运动:粒子向上运动,受随机扰动影响左右飘摆
  3. 温度衰减:粒子越往上温度越低
  4. 颜色映射:温度高→白/黄色,温度低→橙色→红色→暗红→透明
  5. 大小变化:粒子先膨胀后收缩
  6. 生命周期:粒子到达一定高度后消亡
graph TD
    A[火焰发射器]:::primary --> B[生成新粒子]:::info
    B --> C[设置初始属性]:::info
    C --> D{每帧更新}:::warning
    D --> E[更新位置:向上+随机扰动]:::success
    D --> F[更新温度:随时间衰减]:::success
    D --> G[更新颜色:温度→颜色映射]:::success
    D --> H[更新大小:先膨胀后收缩]:::success
    D --> I[更新透明度:随生命衰减]:::success
    E --> J{生命结束?}:::warning
    F --> J
    G --> J
    H --> J
    I --> J
    J -->|| D
    J -->|| K[移除粒子]:::error
    
    classDef primary fill:#4CAF50,stroke:#388E3C,color:#fff
    classDef warning fill:#FF9800,stroke:#F57C00,color:#fff
    classDef info fill:#2196F3,stroke:#1976D2,color:#fff
    classDef success fill:#9C27B0,stroke:#7B1FA2,color:#fff
    classDef error fill:#F44336,stroke:#D32F2F,color:#fff

2.2 火焰颜色与温度映射

火焰的颜色由温度决定,这是物理学的基本事实。在粒子系统中,我们用**温度值(0~1)**来控制颜色渐变:

温度范围 颜色 RGB近似值 视觉效果
1.0 ~ 0.8 白/亮黄 (255, 255, 200) 火焰核心,最亮
0.8 ~ 0.6 黄色 (255, 220, 50) 明亮火焰
0.6 ~ 0.4 橙色 (255, 150, 20) 火焰主体
0.4 ~ 0.2 红色 (220, 50, 10) 火焰边缘
0.2 ~ 0.0 暗红/透明 (100, 20, 5) 烟雾余烬

2.3 粒子属性结构

// 火焰粒子属性
interface FireParticle {
  x: number           // X坐标
  y: number           // Y坐标
  vx: number          // X方向速度
  vy: number          // Y方向速度(向上为负)
  temperature: number // 温度 0~1
  size: number        // 粒子大小
  life: number        // 剩余生命 0~1
  maxLife: number     // 初始生命值
  turbulence: number  // 扰动强度
}

三、代码实战

3.1 基础用法——简单火焰粒子

先从最简单的火焰开始:一个持续燃烧的小火苗。

// 简单火焰粒子效果
@Component
struct SimpleFireDemo {
  private settings: RenderingContextSettings = new RenderingContextSettings(true)
  private context: CanvasRenderingContext2D = new CanvasRenderingContext2D(this.settings)
  private particles: FireParticle[] = []
  private animationId: number = -1

  // 粒子接口
  interface FireParticle {
    x: number
    y: number
    vx: number
    vy: number
    temperature: number
    size: number
    life: number
    maxLife: number
    turbulence: number
  }

  // 生成新粒子
  private emitParticle(centerX: number, centerY: number): void {
    const particle: FireParticle = {
      x: centerX + (Math.random() - 0.5) * 20,  // 随机偏移
      y: centerY,
      vx: (Math.random() - 0.5) * 2,             // 随机水平速度
      vy: -(Math.random() * 3 + 2),               // 向上速度
      temperature: 1.0,                            // 初始温度最高
      size: Math.random() * 8 + 4,                // 随机大小
      life: 1.0,                                   // 初始生命满
      maxLife: Math.random() * 0.5 + 0.5,         // 随机生命长度
      turbulence: Math.random() * 2 + 1           // 随机扰动强度
    }
    this.particles.push(particle)
  }

  // 温度到颜色映射
  private temperatureToColor(temp: number, alpha: number): string {
    let r: number, g: number, b: number
    if (temp > 0.8) {
      // 白/亮黄
      r = 255
      g = 255 - Math.round((1 - temp) * 5 * 55)
      b = 200 - Math.round((1 - temp) * 5 * 180)
    } else if (temp > 0.6) {
      // 黄色
      r = 255
      g = 220 - Math.round((0.8 - temp) * 5 * 70)
      b = 50 - Math.round((0.8 - temp) * 5 * 40)
    } else if (temp > 0.4) {
      // 橙色
      r = 255
      g = 150 - Math.round((0.6 - temp) * 5 * 100)
      b = 20 - Math.round((0.6 - temp) * 5 * 15)
    } else if (temp > 0.2) {
      // 红色
      r = 220 - Math.round((0.4 - temp) * 5 * 120)
      g = 50 - Math.round((0.4 - temp) * 5 * 30)
      b = 10
    } else {
      // 暗红
      r = 100 - Math.round((0.2 - temp) * 5 * 80)
      g = 20
      b = 5
    }
    return `rgba(${r},${g},${b},${alpha})`
  }

  // 更新粒子状态
  private updateParticles(): void {
    for (let i = this.particles.length - 1; i >= 0; i--) {
      const p = this.particles[i]
      // 更新位置
      p.x += p.vx + (Math.random() - 0.5) * p.turbulence
      p.y += p.vy
      // 温度随生命衰减
      p.life -= 0.02
      p.temperature = Math.max(0, p.life / p.maxLife)
      // 大小先膨胀后收缩
      const lifeRatio = 1 - p.life / p.maxLife
      p.size = p.size * (lifeRatio < 0.3 ? 1.02 : 0.98)
      // 移除死亡粒子
      if (p.life <= 0) {
        this.particles.splice(i, 1)
      }
    }
  }

  // 渲染粒子
  private renderParticles(): void {
    this.context.clearRect(0, 0, 400, 600)
    // 使用混合模式让火焰更亮
    this.context.globalCompositeOperation = 'lighter'

    for (const p of this.particles) {
      const alpha = Math.min(1, p.life * 1.5)
      const color = this.temperatureToColor(p.temperature, alpha)
      this.context.beginPath()
      this.context.arc(p.x, p.y, p.size, 0, Math.PI * 2)
      this.context.fillStyle = color
      this.context.fill()
    }

    this.context.globalCompositeOperation = 'source-over'
  }

  // 动画循环
  private startAnimation(): void {
    const animate = () => {
      // 每帧发射3-5个新粒子
      const emitCount = Math.floor(Math.random() * 3) + 3
      for (let i = 0; i < emitCount; i++) {
        this.emitParticle(200, 450)
      }
      this.updateParticles()
      this.renderParticles()
      this.animationId = requestAnimationFrame(animate)
    }
    animate()
  }

  build() {
    Column() {
      Canvas(this.context)
        .width(400)
        .height(600)
        .backgroundColor('#1a1a2e')
        .onReady(() => {
          this.startAnimation()
        })
    }
    .width('100%')
    .height('100%')
    .justifyContent(FlexAlign.Center)
    .backgroundColor('#0a0a1a')
  }

  aboutToDisappear() {
    if (this.animationId !== -1) {
      cancelAnimationFrame(this.animationId)
    }
  }
}

3.2 进阶用法——火焰颜色渐变与温度映射优化

上面的简单火焰有个问题:颜色过渡不够平滑,看起来像"一层一层的"。我们用渐变径向填充来优化。

// 进阶火焰:使用径向渐变让颜色过渡更自然
@Component
struct AdvancedFireDemo {
  private settings: RenderingContextSettings = new RenderingContextSettings(true)
  private context: CanvasRenderingContext2D = new CanvasRenderingContext2D(this.settings)
  private particles: FireParticle[] = []
  private animationId: number = -1

  // 使用径向渐变绘制单个粒子
  private drawParticleWithGradient(p: FireParticle): void {
    const alpha = Math.min(1, p.life * 1.5)
    const radius = p.size

    // 创建径向渐变:中心亮,边缘暗
    const gradient = this.context.createRadialGradient(
      p.x, p.y, 0,           // 内圆中心
      p.x, p.y, radius       // 外圆半径
    )

    // 根据温度设置渐变色
    const coreColor = this.temperatureToColor(p.temperature, alpha)
    const edgeColor = this.temperatureToColor(p.temperature * 0.3, alpha * 0.3)

    gradient.addColorStop(0, coreColor)
    gradient.addColorStop(0.4, this.temperatureToColor(p.temperature * 0.7, alpha * 0.8))
    gradient.addColorStop(1, edgeColor)

    this.context.beginPath()
    this.context.arc(p.x, p.y, radius, 0, Math.PI * 2)
    this.context.fillStyle = gradient
    this.context.fill()
  }

  // 温度到颜色映射(同基础用法,此处省略)
  private temperatureToColor(temp: number, alpha: number): string {
    // ... 同上
    return `rgba(255,${Math.round(150 * temp + 50)},${Math.round(20 * temp)},${alpha})`
  }

  // 渲染所有粒子(使用渐变)
  private renderParticles(): void {
    this.context.clearRect(0, 0, 400, 600)
    this.context.globalCompositeOperation = 'lighter'

    // 按温度从低到高排序,低温粒子先绘制
    const sorted = [...this.particles].sort((a, b) => a.temperature - b.temperature)
    for (const p of sorted) {
      this.drawParticleWithGradient(p)
    }

    this.context.globalCompositeOperation = 'source-over'
  }

  build() {
    Column() {
      Canvas(this.context)
        .width(400)
        .height(600)
        .backgroundColor('#1a1a2e')
        .onReady(() => {
          // 启动动画循环(同基础用法)
        })
    }
    .width('100%')
    .height('100%')
    .justifyContent(FlexAlign.Center)
  }
}

3.3 完整示例——篝火与爆炸效果

将火焰粒子系统扩展为两种实战效果:持续燃烧的篝火和瞬间爆发的爆炸。

// 完整实战:篝火 + 爆炸效果
interface FireParticle {
  x: number
  y: number
  vx: number
  vy: number
  temperature: number
  size: number
  life: number
  maxLife: number
  turbulence: number
  type: 'campfire' | 'explosion'  // 粒子类型
}

@Component
struct FireEffectDemo {
  private settings: RenderingContextSettings = new RenderingContextSettings(true)
  private context: CanvasRenderingContext2D = new CanvasRenderingContext2D(this.settings)
  private particles: FireParticle[] = []
  private animationId: number = -1
  @State modeText: string = '篝火模式'

  // 篝火粒子发射
  private emitCampfireParticles(): void {
    const centerX = 200
    const centerY = 400
    const count = Math.floor(Math.random() * 4) + 3
    for (let i = 0; i < count; i++) {
      this.particles.push({
        x: centerX + (Math.random() - 0.5) * 30,
        y: centerY + (Math.random() - 0.5) * 10,
        vx: (Math.random() - 0.5) * 1.5,
        vy: -(Math.random() * 2.5 + 1.5),
        temperature: 0.8 + Math.random() * 0.2,
        size: Math.random() * 10 + 5,
        life: 1.0,
        maxLife: Math.random() * 0.6 + 0.4,
        turbulence: Math.random() * 1.5 + 0.5,
        type: 'campfire'
      })
    }
  }

  // 爆炸粒子发射
  private emitExplosionParticles(centerX: number, centerY: number): void {
    const count = 80  // 爆炸一次产生大量粒子
    for (let i = 0; i < count; i++) {
      const angle = Math.random() * Math.PI * 2
      const speed = Math.random() * 6 + 2
      this.particles.push({
        x: centerX,
        y: centerY,
        vx: Math.cos(angle) * speed,
        vy: Math.sin(angle) * speed,
        temperature: 1.0,  // 爆炸初始温度最高
        size: Math.random() * 12 + 3,
        life: 1.0,
        maxLife: Math.random() * 0.4 + 0.2,  // 爆炸粒子生命更短
        turbulence: Math.random() * 3 + 1,
        type: 'explosion'
      })
    }
  }

  // 更新粒子
  private updateParticles(): void {
    for (let i = this.particles.length - 1; i >= 0; i--) {
      const p = this.particles[i]
      // 位置更新
      p.x += p.vx + (Math.random() - 0.5) * p.turbulence
      p.y += p.vy

      if (p.type === 'campfire') {
        // 篝火粒子:向上减速
        p.vy *= 0.99
        p.vx *= 0.98
      } else {
        // 爆炸粒子:受重力影响向下
        p.vy += 0.1
        p.vx *= 0.97
        p.vy *= 0.97
      }

      // 生命和温度衰减
      p.life -= 0.02
      p.temperature = Math.max(0, p.life / p.maxLife)

      // 大小变化
      const lifeRatio = 1 - p.life / p.maxLife
      if (p.type === 'explosion') {
        p.size *= (lifeRatio < 0.2 ? 1.05 : 0.96)  // 爆炸先膨胀后收缩
      } else {
        p.size *= (lifeRatio < 0.3 ? 1.01 : 0.99)
      }

      // 移除死亡粒子
      if (p.life <= 0) {
        this.particles.splice(i, 1)
      }
    }
  }

  // 温度到颜色映射
  private temperatureToColor(temp: number, alpha: number): string {
    let r: number, g: number, b: number
    if (temp > 0.8) {
      r = 255; g = 255 - Math.round((1 - temp) * 5 * 55); b = 200
    } else if (temp > 0.6) {
      r = 255; g = 220 - Math.round((0.8 - temp) * 5 * 70); b = 50
    } else if (temp > 0.4) {
      r = 255; g = 150 - Math.round((0.6 - temp) * 5 * 100); b = 20
    } else if (temp > 0.2) {
      r = 220 - Math.round((0.4 - temp) * 5 * 120); g = 50; b = 10
    } else {
      r = 100 - Math.round((0.2 - temp) * 5 * 80); g = 20; b = 5
    }
    return `rgba(${r},${g},${b},${alpha})`
  }

  // 渲染
  private render(): void {
    this.context.clearRect(0, 0, 400, 600)
    this.context.globalCompositeOperation = 'lighter'

    // 绘制环境光晕(篝火模式下)
    if (this.modeText === '篝火模式') {
      const glowGradient = this.context.createRadialGradient(200, 400, 10, 200, 400, 150)
      glowGradient.addColorStop(0, 'rgba(255,150,50,0.15)')
      glowGradient.addColorStop(0.5, 'rgba(255,100,20,0.05)')
      glowGradient.addColorStop(1, 'rgba(255,50,0,0)')
      this.context.fillStyle = glowGradient
      this.context.fillRect(0, 0, 400, 600)
    }

    // 绘制粒子
    const sorted = [...this.particles].sort((a, b) => a.temperature - b.temperature)
    for (const p of sorted) {
      const alpha = Math.min(1, p.life * 1.5)
      const gradient = this.context.createRadialGradient(p.x, p.y, 0, p.x, p.y, p.size)
      gradient.addColorStop(0, this.temperatureToColor(p.temperature, alpha))
      gradient.addColorStop(0.5, this.temperatureToColor(p.temperature * 0.6, alpha * 0.6))
      gradient.addColorStop(1, this.temperatureToColor(p.temperature * 0.2, 0))
      this.context.beginPath()
      this.context.arc(p.x, p.y, p.size, 0, Math.PI * 2)
      this.context.fillStyle = gradient
      this.context.fill()
    }

    this.context.globalCompositeOperation = 'source-over'

    // 绘制篝火木柴(装饰)
    if (this.modeText === '篝火模式') {
      this.context.fillStyle = '#5D4037'
      this.context.save()
      this.context.translate(200, 420)
      this.context.rotate(-0.3)
      this.context.fillRect(-40, -5, 80, 10)
      this.context.restore()
      this.context.save()
      this.context.translate(200, 420)
      this.context.rotate(0.3)
      this.context.fillRect(-40, -5, 80, 10)
      this.context.restore()
    }
  }

  // 动画循环
  private startAnimation(): void {
    const animate = () => {
      if (this.modeText === '篝火模式') {
        this.emitCampfireParticles()
      }
      this.updateParticles()
      this.render()
      this.animationId = requestAnimationFrame(animate)
    }
    animate()
  }

  build() {
    Column() {
      // 模式切换
      Row({ space: 16 }) {
        Button('篝火模式')
          .onClick(() => {
            this.modeText = '篝火模式'
            this.particles = []
          })
          .backgroundColor(this.modeText === '篝火模式' ? '#FF5722' : '#666666')

        Button('爆炸模式')
          .onClick(() => {
            this.modeText = '爆炸模式'
            this.particles = []
          })
          .backgroundColor(this.modeText === '爆炸模式' ? '#FF5722' : '#666666')
      }
      .margin({ bottom: 16 })

      Canvas(this.context)
        .width(400)
        .height(500)
        .backgroundColor('#0a0a1a')
        .borderRadius(12)
        .onReady(() => {
          this.startAnimation()
        })
        .onClick((event) => {
          // 爆炸模式下点击触发爆炸
          if (this.modeText === '爆炸模式') {
            this.emitExplosionParticles(event.x, event.y)
          }
        })

      Text(this.modeText === '篝火模式' ? '持续燃烧的篝火' : '点击画布触发爆炸')
        .fontSize(14)
        .fontColor('#999999')
        .margin({ top: 8 })
    }
    .width('100%')
    .height('100%')
    .justifyContent(FlexAlign.Center)
    .backgroundColor('#1a1a2e')
  }

  aboutToDisappear() {
    if (this.animationId !== -1) {
      cancelAnimationFrame(this.animationId)
    }
  }
}

四、踩坑与注意事项

坑点1:Canvas的globalCompositeOperation性能陷阱

lighter混合模式能让火焰看起来更亮更真实,但它的性能开销是source-over的3-5倍。当粒子数量超过200时,帧率可能骤降。

优化方案:限制粒子总数在150以内;或者只对高温粒子使用lighter,低温粒子使用source-over

坑点2:径向渐变创建过多导致卡顿

每个粒子都创建一个createRadialGradient,当粒子数量多时,渐变对象的创建和销毁会带来显著的GC压力。

优化方案:预创建一组渐变模板(比如10个温度等级),粒子根据温度选择最近的模板,而不是每帧都创建新渐变。

坑点3:粒子数组的splice操作性能差

this.particles.splice(i, 1)在数组中间删除元素,时间复杂度O(n)。当粒子数量多时,频繁的splice会导致帧率波动。

优化方案:使用双缓冲策略——维护两个数组,每帧将存活粒子复制到新数组,避免splice操作。

// 双缓冲策略
private updateParticles(): void {
  const alive: FireParticle[] = []
  for (const p of this.particles) {
    // 更新粒子...
    if (p.life > 0) {
      alive.push(p)
    }
    // 不再splice,直接丢弃死亡粒子
  }
  this.particles = alive
}

坑点4:requestAnimationFrame不自动取消

组件销毁时如果不取消requestAnimationFrame,回调会继续执行,访问已销毁的组件上下文导致崩溃。

必须aboutToDisappear中取消动画。

坑点5:Canvas的onReady只触发一次

Canvas.onReady回调只在Canvas首次准备好时触发一次。如果你在onReady中启动了动画循环,切换页面再回来时动画不会自动重启。

解决:在onPageShowonVisibleAreaChange中重新启动动画。

坑点6:火焰效果在深色和浅色背景下的表现差异

火焰效果在深色背景下看起来很好,但在浅色背景下几乎看不到——因为lighter混合模式在浅色背景上效果很弱。

解决:根据背景色动态调整粒子的alpha值和混合模式。浅色背景下使用source-over并增加alpha。

坑点7:爆炸效果的一次性粒子太多

爆炸瞬间产生80个粒子,加上渐变绘制,首帧可能需要30-40ms的渲染时间,导致明显的卡顿。

优化方案:分帧发射——首帧发射30个核心粒子,后续2-3帧各发射15-20个外围粒子,让爆炸"展开"而不是"炸开"。


五、HarmonyOS 6适配说明

API差异

API HarmonyOS 5.0 HarmonyOS 6.0 迁移建议
Canvas绘制 CanvasRenderingContext2D CanvasRenderingContext2D + GPUContext 可选GPU加速上下文
requestAnimationFrame 返回number 返回number + 支持优先级 可指定动画帧优先级
粒子系统 无内置 ParticleEmitter组件 简单场景可用内置组件
Canvas分辨率 默认物理分辨率 canvas.resolutionScale 可控制绘制分辨率
离屏Canvas OffscreenCanvas OffscreenCanvas + WebWorker 支持Worker中绘制

行为变更

  • Canvas GPU加速默认开启:HarmonyOS 6中Canvas默认使用GPU渲染,不再需要手动设置RenderingContextSettings(true)
  • 粒子数量限制:单个Canvas同时绘制的粒子数量建议不超过300,超过后系统可能自动降帧
  • 渐变对象缓存createRadialGradient创建的渐变对象会被自动缓存复用,相同参数不会重复创建
  • requestAnimationFrame精度:回调时间戳精度从ms级提升到μs级,便于更精确的物理计算

适配代码

// HarmonyOS 6适配:使用内置ParticleEmitter和GPU加速
@Component
struct Hmos6FireDemo {
  build() {
    Stack() {
      // 方式1:使用内置ParticleEmitter(简单场景)
      ParticleEmitter({
        // 火焰发射器配置
        emitter: {
          emitRate: 30,           // 每秒发射30个粒子
          lifetime: 1500,         // 粒子生命1.5秒
          position: { x: 200, y: 400 },
          shape: 'circle',
          radius: 15
        },
        particle: {
          color: {
            from: '#FFFFC8',      // 初始颜色:亮黄
            to: '#FF3300'         // 结束颜色:红色
          },
          scale: {
            from: 1.0,
            to: 0.2
          },
          opacity: {
            from: 1.0,
            to: 0.0
          },
          velocity: {
            x: { from: -20, to: 20 },
            y: { from: -80, to: -30 }
          },
          acceleration: {
            y: -10               // 向上加速
          }
        }
      })
      .width(400)
      .height(500)

      // 方式2:Canvas + GPU加速(复杂场景)
      // Canvas(this.context)
      //   .width(400)
      //   .height(500)
      //   .resolutionScale(0.75)  // HarmonyOS 6: 降低绘制分辨率提升性能
    }
  }
}

六、总结

维度 评价
学习难度 ⭐⭐⭐⭐⭐
使用频率 ⭐⭐⭐
重要程度 ⭐⭐⭐⭐

火焰效果是粒子系统的"Hello World"——它涵盖了粒子发射、属性更新、颜色映射、生命周期管理、混合模式等所有核心概念。掌握了火焰,其他粒子效果(烟雾、水流、星尘)都是换汤不换药。

核心要点回顾:

  1. 火焰的本质是粒子的集体行为——发射器产生粒子,粒子向上运动并衰减
  2. 温度映射是火焰的灵魂——从白/黄到橙/红再到暗红/透明,颜色渐变决定真实感
  3. 径向渐变让粒子更柔和——中心亮边缘暗,比纯色填充真实10倍
  4. lighter混合模式是火焰的"光"——让粒子叠加产生明亮的火焰核心
  5. 性能优化要关注粒子数量和渐变创建——双缓冲、预创建模板、分帧发射
  6. HarmonyOS 6的ParticleEmitter让简单场景的实现更简单,复杂场景仍需Canvas自定义

火焰效果就像烹饪中的"火候"——同样的食材(粒子),火候不同(参数配置),出来的效果天差地别。多调多试,找到那个"刚刚好"的参数组合,你的火焰就能"烧"出真实感。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。