粒子

Flame提供了一个基本而又强大且可扩展的粒子系统。 该系统的核心概念是 Particle 类,其行为与 ParticleComponent 非常相似。

ParticleBaseGame 中的最基本用法如下所示:

import 'package:flame/components/particle_component.dart';

// ... 

game.add(
     // 用[ParticleComponent]包装一个[Particle]
     // 将[Component]生命周期挂钩映射到[Particle]
     // 并嵌入一个销毁组件的触发器。
    ParticleComponent(
        particle: CircleParticle()
    )
);

当使用具有自定义 Game 实现的 Particle 时,请确保在每个游戏循环帧期间调用 Particleupdaterender

实现所需粒子效果的主要方法: 现有行为的组成 使用行为链(只是第一个的语法糖) *使用 ComputedParticle

定义粒子组件的方式与Flutter小部件的类似。 通过链式方式定义从底部到顶部的行为可以更流畅地实现相同的合成树。 计算粒子行为的实现完全委派给您的代码。 任何需求实现方法都可以与现有行为结合使用。

如下示例,使用以上三种方式实现了从 (0,0) 加速随机一个方向的圆圈。

Random rnd = Random();
Function randomOffset = () => Offset(
    rnd.nextDouble() * 200 - 100, 
    rnd.nextDouble() * 200 - 100,
);

// 组件
// 将粒子效果定义为一组嵌套
// 从上到下的行为,一个在另一个内部:
// ParticleComponent
//  > ComposedParticle
//    > AcceleratedParticle
//      > CircleParticle
game.add(
    ParticleComponent(
        particle: Particle.generate(
            count: 10,
            generator: (i) => AcceleratedParticle(
                acceleration: randomOffset(),
                child: CircleParticle(
                    paint: Paint()..color = Colors.red
                )
            )
        )
    )    
);
// 链式
// 与上述行为相同,但具有更流畅的API。
// 只有带有[SingleChildParticle] mixin 的 [Particles] 可以用作可链式操作。
game.add(
    Particle
        .generate(
            count: 10,
            generator: (i) => CircleParticle(paint: Paint()..color = Colors.red)
                .accelerating(randomOffset())
        )
        .asComponent()
);
// 计算粒子
// 所有行为均已明确定义。 提供更大的灵活性
// 与内置行为相比。
game.add(
    Particle
        .generate(
            count: 10,
            generator: (i) {
                final position = Offset.zero;
                final speed = Offset.zero;
                final acceleration = randomOffset();
                final paint = Paint()..color = Colors.red;

                return  ComputedParticle(
                    renderer: (canvas, _) {
                        speed += acceleration;
                        position += speed;
                        canvas.drawCircle(position, 10, paint);
                    }
                );
            }
        )
        .asComponent()
)

您可以在 此处 中找到更多使用不同内建粒子的示例。

生命周期

所有 Particle 他们都有 lifespan 参数。 一旦内部 Particle 达到使用寿命, ParticleComponent 将销毁。 Particle 内置了Flame Timer 跟踪 。通过 Particle 构造函数传入 lifespan:double 单位秒(精确到微秒)。

如下:

Particle(lifespan: .2); // 存活 200ms
Particle(lifespan: 4); // 存活 4s

也可以使用 setLifespan 方法重置 Particle 寿命,该方法接受 double 秒。

final particle = Particle(lifespan: 2);
// ... 在某个时间点之后
particle.setLifespan(2) // 从现在起将再存活2秒

在其生命周期内, Particle 会跟踪其存活时间并通过 progress (介于0.0到1.0之间)将其公开暴露到外部。 此值使用方式与Flutter中 AnimationControllervalue 类似。

final duration = const Duration(seconds: 2);
final particle = Particle(lifespan: duration.inMicroseconds / Durations.microsecondsPerSecond);

game.add(ParticleComponent(particle: particle));

// 将以0.1的步长打印0到1的值:0、0.1、0.2 ... 0.9、1.0
Timer.periodic(duration * .1, () => print(particle.progress));

使用链式方式可以将寿命传递到所有 Particle 的继承类中。

内置颗粒

Flame带有一些内置的 Particle 行为:

  • TranslatedParticle ,通过 Offset 平移 child
  • MovingParticle 通过两个预定义的 Offset 之间移动 child ,支持 Curve
  • AcceleratedParticle (加速粒子)允许基于物理的基本效果,例如重力或速度衰减
  • CircleParticle ,渲染各种形状和大小的圆圈
  • SpriteParticle ,在 Particle 内渲染 Flame Sprite
  • ImageParticle ,在 Particle 内渲染 dart:ui图片
  • ParticleParticle ,在Particle 内渲染 Flame Component
  • FlareParticle ,在 Particle 内渲染Flare动画

使用以上粒子示例 此处 所有实现都可以在Flame源中的粒子文件夹中找到。

平移粒子

Canvas 内只需将基本的 Particle 设置指定 Offset 即可。 不更改或更改其位置时请考虑使用 MovingParticleAcceleratedParticle 。 通过平移 Canvas 层可以达到相同的效果。

game.add(
    ParticleComponent(
        particle: TranslatedParticle(
            // Will translate child Particle effect to
            // the center of game canvas
            offset: game.size.center(Offset.zero),
            child: Particle(),
        )
    )
);

运动粒子

在其寿命期间,从 fromto之间移动子 Particle

通过 CurvedParticle 支持 Curve

game.add(
    ParticleComponent(
        particle: MovingParticle(
            // 会从一个角落移到另一个角落
            // 游戏画布
            from: game.size.topLeft(Offset.zero),
            to: game.size.bottomRight(Offset.zero),
            child: Particle(),
        )
    )
);

加速粒子

一个基本的物理粒子,它允许您指定其初始 position(位置), speed(速度) 和 acceleration (加速度),并让 update 循环完成其余的工作。 所有这三个都指定为 Offset , 您可以将其视为向量。 它对于基于物理的 "bursts" 特别有效,但不仅限于此。

Offset 值的单位为 px/s , 因此,Offset(0, 100) 的速度将使游戏中每隔一秒钟的子 Particle 移动设备的100个逻辑像素。

final rnd = Random();
game.add(
    ParticleComponent(
        particle: AcceleratedParticle(
            // 将在游戏画布的中心发射
            position: game.size.center(Offset.zero),
            // 具有随机的初始偏移速度(-100..100,0 ..- 100)
            speed: Offset(rnd.nextDouble() * 200 - 100, -rnd.nextDouble() * 100),
            // 向下加速,模拟 “重力”
            speed: Offset(0, 100),
            child: Particle(),
        )
    )
);

循环粒子

设置 Paint 渲染圆的一个 Particle ,通过的 Canvas 的偏移量为零。 结合 TranslatedParticleMovingParticleAcceleratedParticle 使用。

game.add(
    ParticleComponent(
        particle: CircleParticle(
            radius: game.size.width / 2,
            paint: Paint()..color = Colors.red.withOpacity(.5),
        )
    )
);

精灵粒子

允许您将 Flame 的 Sprite 嵌入到粒子效果中。 当从 SpriteSheet 中获取效果时使用。

game.add(
    ParticleComponent(
        particle: SpriteParticle(
          sprite: Sprite('sprite.png'),
          size: Position(64, 64),
        )
    )
);

图片粒子

在粒子树内渲染给定的 dart:ui 图像。

// 游戏初始化期间
await Flame.images.loadAll(const [
    'image.png'
]);

// ...

// 游戏循环中的某个地方
game.add(
    ParticleComponent(
        particle: ImageParticle(
          size: const Size.square(24),
          image: Flame.images.loadedFiles['image.png'],
        );
    )
);

动画粒子

嵌入 Flame AnimationParticle 。 默认情况下,对齐 AnimationstepTime,以便在 Particle 使用期限内完全播放。 可以使用 alignAnimationTime 参数覆盖此行为。

final spritesheet = SpriteSheet(
  imageName: 'spritesheet.png',
  textureWidth: 16,
  textureHeight: 16,
  columns: 10,
  rows: 2
);

game.add(
    ParticleComponent(
        particle: AnimationParticle(
          animation: spritesheet.createAnimation(0, stepTime: 0.1),
        );
    )
);

组件粒子

Particle 可让您将 Flame Component 嵌入到粒子效果中。 Component 可以有自己的 update 生命周期,可以在不同的效果树之间重复使用。 如果您要为某些 Component 的实例添加动态特性,请考虑将其直接添加到 game 中,而不要在中间添加 Particle

var longLivingRect = RectComponent();

game.add(
    ParticleComponent(
        particle: ComponentParticle(
          component: longLivingRect
        );
    )
);

class RectComponent extends Component {
    void render(Canvas c) {
        c.drawRect(
            Rect.fromCenter(center: Offset.zero, width: 100, height: 100), 
            Paint()..color = Colors.red
        );
    }

    void update(double dt) {
        /// 将由父级调用[Particle]
    }
}

Flare粒子

要在 Flame 中使用 Flare,请使用 flame_flare 包。

Flame 提供一个 FlareParticle 类,该类是 FlareActorAnimation 的容器,并向其子对象传播 updaterender 事件。

import 'package:flame_flare/flame_flare.dart';

// 游戏初始化期间
const flareSize = 32.0;
final flareAnimation = FlareActorAnimation('assets/sparkle.flr');
flareAnimation.width = flareSize; 
flareAnimation.height = flareSize;

// 游戏中的某个地方
game.add(
    ParticleComponent(
        particle: FlareParticle(flare: flareAnimation);
    )
);

计算粒子

可以在以下情况下为您提供帮助的 Particle

  • 默认功能不能达到您的需求时
  • 复杂效果优化
  • 用户自定义

创建后,将所有渲染委托给 ParticleRenderDelegate ,在每个帧上调用 执行必要的计算并将内容渲染在 Canvas

game.add(
    ParticleComponent(
        // 逐渐渲染一个圆圈
        // 在粒子寿命期间更改其颜色和大小
        particle: ComputedParticle(
            renderer: (canvas, particle) => canvas.drawCircle(
                Offset.zero,
                particle.progress * 10,
                Paint()
                  ..color = Color.lerp(
                    Colors.red,
                    Colors.blue,
                    particle.progress,
                  ),
            ),
        )
    )
)

嵌套

Flame 对粒子的实现遵循与Flutter部件相同的嵌套模式。 通过将行为片段封装在每个粒子中,然后将这些行为嵌套在一起以实现所需的视觉效果。

允许 Particle 相互嵌套的两个实体是: SingleChildParticleComposedParticle 类。

SingleChildParticle 可能会帮助您创建具有自定义行为的 Particles 。 例如,在每帧中随机定位它的子元素:

var rnd = Random();
class GlitchParticle extends Particle with SingleChildParticle {
    
    Particle child;

    GlitchParticle({
       this.child,
      double lifespan,
    }) : super(lifespan: lifespan);

    
    render(Canvas canvas)  {
        canvas.save();
        canvas.translate(rnd.nextDouble() * 100, rnd.nextDouble() * 100);

        // Will also render the child
        super.render();

        canvas.restore();
    }
}

ComposedParticle既可以单独使用,也可以在现有的 Particle 树中使用。

Flame中文站 all right reserved,powered by Gitbook最后修改: 2020-08-23 20:27:25

results matching ""

    No results matching ""