【Flutter&Flame游戏 - 拾陆】粒子系统 | 粒子的种类

2022-06-19 16:33:09 浏览数 (1)

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第 17 天,点击查看活动详情


前言

这是一套 张风捷特烈 出品的 Flutter&Flame 系列教程,发布于掘金社区。如果你在其他平台看到本文,可以根据对于链接移步到掘金中查看。因为文章可能会更新、修正,一切以掘金文章版本为准。本系列源码于 【toly_game】 ,如果本系列对你有所帮助,希望点赞支持,本系列文章一览:

  • 【Flutter&Flame 游戏 - 壹】开启新世界的大门
  • 【Flutter&Flame 游戏 - 贰】操纵杆与角色移动
  • 【Flutter&Flame 游戏 - 叁】键盘事件与手势操作
  • 【Flutter&Flame 游戏 - 肆】精灵图片加载方式
  • 【Flutter&Flame 游戏 - 伍】Canvas 参上 | 角色的血条
  • 【Flutter&Flame 游戏 - 陆】暴击 Dash | 文字构件的使用
  • 【Flutter&Flame 游戏 - 柒】人随指动 | 动画点触与移动
  • 【Flutter&Flame 游戏 - 捌】装弹完毕 | 角色武器发射
  • 【Flutter&Flame 游戏 - 玖】探索构件 | Component 是什么
  • 【Flutter&Flame 游戏 - 拾】探索构件 | Component 生命周期回调
  • 【Flutter&Flame 游戏 - 拾壹】探索构件 | Component 使用细节
  • 【Flutter&Flame 游戏 - 拾贰】探索构件 | 角色管理
  • 【Flutter&Flame 游戏 - 拾叁】碰撞检测 | CollisionCallbacks
  • 【Flutter&Flame 游戏 - 拾肆】碰撞检测 | 之前代码优化
  • 【Flutter&Flame 游戏 - 拾伍】粒子系统 | ParticleSystemComponent
  • 【Flutter&Flame 游戏 - 拾陆】粒子系统 | 粒子的种类
  • 【Flutter&Flame 游戏 - 拾柒】构件特效 | 了解 Effect 体系
  • 【Flutter&Flame 游戏 - 拾捌】构件特效 | ComponentEffect 一族
  • 【Flutter&Flame 游戏 - 拾玖】构件特效 | 了解 EffectController 体系
  • 【Flutter&Flame 游戏 - 贰拾】构件特效 | 其他 EffectControler
  • 【Flutter&Flame 游戏 - 贰壹】视差组件 | ParallaxComponent
  • 【Flutter&Flame 游戏 - 贰贰】菜单、字体和浮层
  • 未完待续 ~

1. 粒子种类总览

在上一篇中,我们知道了如何使用 ParticleSystemComponent 构建显示粒子。这篇文章将详细介绍一下 Flame 中内置的粒子种类。Particle 抽象类共有如下十几种实现类:

其中 SingleChildParticle 一族在构造时需要指定一个 Particle ,主要是为指定粒子进行动画变换,比如 移动旋转缩放 等。

另外,我们可以指定 SpriteSpriteAnimationImage 等图片资源,作为粒子。也可以使用 ComponentParticle 将一个构建作为粒子,下面通过粒子来详细认识一下。


2. 通过粒子实现点击时序列帧

在 第七篇 中,我们实现了如下的点触序列帧播放,那时是通过自己维护 SpriteAnimationComponent 完成的。它在生成后,会在一定时间内消失,而且拖动时产生大量的单体,很适合通过粒子来处理。

https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/55bcfa9d153d44b7a079eda7f5dd2f14~tplv-k3u1fbpfcp-zoom-in-crop-mark:1304:0:0:0.awebp?

如下,通过 SpriteAnimationParticle 携带 SpriteAnimation ,可以实现相同的效果。默认情况下 SpriteAnimationParticle 的生命时长和动画一致,也就是序列帧播放完毕就消失。代码详见 【16/01】

代码语言:javascript复制
void createIndicator(Vector2 position) {
  SpriteAnimation indicator = SpriteAnimation.spriteList(
    sprites,
    stepTime: 1 / 15,
    loop: false,
  );
  
  // 创建 Particle 对象
  Particle particle = SpriteAnimationParticle(
    animation: indicator,
    size: Vector2(30, 30),
  );
  // 创建 ParticleSystemComponent 构件
  final ParticleSystemComponent psc = ParticleSystemComponent(
    particle: particle,
    position: position,
    anchor: Anchor.center,
  );
  // 添加 ParticleSystemComponent 构件
  add(psc);
}
复制代码

3. 随机加速粒子

下面来看一下 AcceleratedParticle ,它可以指定速度和加速度让粒子进行运动。比如下面,在点击时,随机产生 第一象限Verctor2 作为速度和加速度。这样就会产生在第一象限产生随机的加速粒子。代码详见 【16/02】

: Y 轴正方向向下,所以视右下方位第一象限。

https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/874011309e254b36aca23ed10b74f0f6~tplv-k3u1fbpfcp-zoom-in-crop-mark:1304:0:0:0.awebp?

Vector2random 方法,可以生成 (0, 0) ~ (1, 1) 的随机点位,如下的 randomVector 的作用是生成 (0, 0) ~ (300, 300) 的随机点位:

代码语言:javascript复制
final Random _random = Random();

Vector2 randomVector() {
  Vector2 base = Vector2.random(_random);
  return base * 300;
}
复制代码

如下 createParticle 方法中,通过 Particle.generate 一次生成 10 个加速粒子,并且速度和加速度随机:

代码语言:javascript复制
void createParticle(Vector2 position) {
  CircleParticle circleParticle = CircleParticle(
    radius: 1,
    paint: Paint()..color = Colors.white,
  );
  
  Particle particle = Particle.generate(
    count: 10,
    lifespan: 1,
    generator: (i) =>
        AcceleratedParticle(
            child: circleParticle,
            acceleration: randomVector(),
            speed: randomVector(),
            position: position),
  );
  
  final ParticleSystemComponent psc =
  ParticleSystemComponent(particle: particle);
  add(psc);
}
复制代码

4. 控制随机粒子的方向

虽然粒子是随机的,但是我们也可以进行控制。比如如何让粒子四散,也就是速度和加速度的矢量可以随机在四个象限中出现。另外加了一点小优化,粒子颜色每次都随机生成:【16/03】

https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/ce58c6594cbb4d94a74d98dc0587e12c~tplv-k3u1fbpfcp-zoom-in-crop-mark:1304:0:0:0.awebp?

其实这是一道简单的数学题, base(0, 0) ~ (1, 1) ,表示只能在第一象限取值。如下左图,浅蓝色是随机点位取值范围;其实只要简单地对取值范围平移一下,就可以取 (-0.5, -0.5) ~ (0.5, 0.5) ,也就是取值范围可以包含四个象限的坐标。

代码语言:javascript复制
Vector2 randomVector() {
  Vector2 base = Vector2.random(_random); //  (0, 0) ~ (1, 1)
  Vector2 fix = Vector2(-0.5,-0.5);
  base = base   fix; //  (-0.5, -0.5) ~ (0.5, 0.5)
  return base * 200;
}
复制代码

如果只想生成 X 轴下方的矢量,向左偏移 0.5 即可:

https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/b71b7dbe4557482cb68e7dcd02fb4618~tplv-k3u1fbpfcp-zoom-in-crop-mark:1304:0:0:0.awebp?

代码语言:javascript复制
Vector2 base = Vector2.random(_random); //  (0, 0) ~ (1, 1)
Vector2 fix = Vector2(-0.5,0); 
base = base   fix; // (-0.5, 0) ~ (0.5, 1)
return base * 200;
复制代码

SingleChildParticle 一族的使用方式非常类似,就是给一个例子添加变换的效果,这里就不一一介绍了。


5. 为主角子弹添加爆炸粒子

粒子可以用在子弹消失时,产生爆炸效果。比如下面所示,可以在主角子弹打到怪物身上消失后,产生上面的爆炸粒子的效果:代码详见 【16/04】

https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/6e72c3001a324a07ada6e6b521b70a4b~tplv-k3u1fbpfcp-zoom-in-crop-mark:1304:0:0:0.awebp?

代码实现很简单,如下 tag1 处,是主角子弹碰到怪物时的场景。通过 createParticle 创建粒子即可:

代码语言:javascript复制
@override
void onCollision(Set<Vector2> intersectionPoints, PositionComponent other) {
  super.onCollision(intersectionPoints, other);
  if (type == BulletType.hero && other is Monster) {
    removeFromParent();
    createParticle(position Vector2(size.x/2,0)); // tag1
  }
  if (type == BulletType.monster && other is HeroComponent) {
    removeFromParent();
  }
}
复制代码

6. 其他类型粒子

还有几个粒子源码比较简单,就直接看下它们的源码实现。ComponentParticle 中必须传入一个 Component ,前面说过 Particle 最核心的就是对绘制的实现。如下,在 render 方法中,直接使用 Componentrender 方法:


ComputedParticle 源码更简单,它将 Canvas 作为 ParticleRenderDelegate 的回调参数,暴露给使用者,在 render 方法中触发回调。也就是说 ComputedParticle 可以通过 renderer 方法把绘制权交给使用者。


ComposedParticle 中必须传入 Particle 列表,在 renderupdate 中遍历子粒子列表,执行绘制和更新。

可以看出,这三者的源码都非常简单,都是借用它物来完成自身的功能,相当于一层包裹,简化使用。


ImageParticle 也非常简单,就是绘制 ui 包中的 Image 对象而已:


总的来看 Particle 就是一个 单体 ,它的核心是绘制方法。到这里,我们也对粒子系统有了较深的理解。粒子相对于构建来说,感觉更加轻量。比如一个 ParticleSystemComponent 可以渲染 100 个粒子,这对比与:把 100 个粒子都作为 Component 加入场景中,高低立判。那本文就到这里,明天见 ~

  • @张风捷特烈 2022.06.11 未允禁转
  • 我的 掘金主页 : 张风捷特烈
  • 我的 B站主页 : 张风捷特烈
  • 我的 github 主页 : toly1994328

0 人点赞