【Flutter&Flame游戏 - 拾贰】探索构件 | 角色管理

2022-06-19 16:22:08 浏览数 (1)

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


前言

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

  • 【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. 管理怪兽

前面把 Monster 全写在 TolyGame 中,看起来很杂乱。一个场景中可能存在多个怪兽,比如下面六个小怪和一个 Boss 。我们可以通过自定义一个 MonsterManager 构件对这些怪兽进行管理:代码详见【12/01】


MonsterManager 中通过入参将小怪和 Boss 的序列帧传入,这样可以中使用时自定义序列帧:

代码语言:javascript复制
class MonsterManager extends PositionComponent with HasGameRef {
  final SpriteSheet bossSpriteSheet;
  final SpriteSheet stoneSpriteSheet;
  MonsterManager({
    required this.bossSpriteSheet,
    required this.stoneSpriteSheet,
  }):super(anchor: Anchor.center);
复制代码

onLoad 方法中,通过 createBoss 方法创建并添加 Boss 构件,然后通过 createStoneMonster 方法在左右分别遍历三个小怪。创建和添加 Monster 的方式和前面是一样的,这里就不赘述了,详见源码。

代码语言:javascript复制
@override
Future<void> onLoad() async {
  createBoss();
  int lineCount =3;
  double step = gameRef.size.y/lineCount ;
  for(int i=1;i<=lineCount;i  ){
    final double pY = i*step- 30;
    final double pX = gameRef.size.x - 200;
    createStoneMonster(Vector2(pX, pY));
  }
  for(int i=1;i<=lineCount;i  ){
    final double pY = i*step- 30;
    final double pX = 150;
    createStoneMonster(Vector2(pX, pY));
  }
}
复制代码

2. 怪兽发射子弹

现在怪物站在那傻乎乎的被打很不公平,下面看看如何让怪兽发射子弹。这里用来两个序列帧动画作为子弹的资源,如下图所示:代码详见【12/02】

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

其实本质上,怪物发射子弹和主角发射子弹本质上是一样的。不同点在于,主角子弹发送是用户控制的,怪物一般是定时发射子弹。另外,要区分一下子弹的类型,是怪物发射的,还是主角发射的。之前角色的弓箭是静态图片,这里可以定义一个 AnimBullet 来支持序列帧的子弹:

代码语言:javascript复制
enum BulletType{
  hero,
  monster
}

class AnimBullet extends SpriteAnimationComponent {
  double speed = 200;
  final double maxRange;
  final BulletType type;
  final bool isLeft;

  AnimBullet({
    required SpriteAnimation animation,
    required this.maxRange,
    required this.type,
    required this.speed,
    this.isLeft = true,
  }) : super(animation : animation);
  
  // 略同...
复制代码

然后看一下如何通过 Timer 来定时不断发射子弹,这里的 TimerFlame 中封装的,不是 Flutter 自带的。如下 Monster 的各个生命周期中对 Timer 进行相关处理:onLoad 方法中初始化 _timer 对象,隔 3 s 钟触发一次 addBullet 方法添加子弹。onMount 方法中开启 _timeronRemove 中停止 _timer

代码语言:javascript复制
---->[Monster]----
late Timer _timer;

@override
Future<void> onLoad() async {
  _timer = Timer(3, onTick: addBullet, repeat: true);
}

@override
void onMount() {
  super.onMount();
  _timer.start();
}

@override
void update(double dt) {
  super.update(dt);
  _timer.update(dt);
}

@override
void onRemove() {
  super.onRemove();
  _timer.stop();
}
复制代码

如下是 addBullet 方法,和之前主角发射子弹的逻辑基本一致:

代码语言:javascript复制
// 添加子弹
void addBullet() {
  AnimBullet bullet = AnimBullet(
    type: BulletType.monster,
    animation: bulletSprite,
    maxRange: attr.attackRange,
    speed: attr.attackSpeed,
    isLeft: isLeft,
  );
  bullet.size = bulletSize;
  bullet.anchor = Anchor.center;
  bullet.priority = 1;
  priority = 2;
  bullet.position = position - Vector2(0, size.y/2);
  gameRef.add(bullet);
}
复制代码

3.怪兽发射的命中

如下所示,在怪物发射的子弹命中主角时,主角也会受到伤害。生命值降低,并且显示伤害数值:代码详见【12/03】

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

目前仍是校验 矩形域中心点 的包含关系来判定是否命中。如下,在 update 中通过 _checkAttackHero 校验是否命中,命中时 player 触发 loss 方法掉血。

代码语言:javascript复制
---->[TolyGame#update]----
@override
void update(double dt) {
  super.update(dt);
  _checkAttackMonster();
  _checkAttackHero();
}
  
void _checkAttackHero() {
  final Iterable<AnimBullet> bullets = children
      .whereType<AnimBullet>()
      .where((AnimBullet e) => e.type == BulletType.monster);
  for (AnimBullet bullet in bullets) {
    if (bullet.shouldRemove) {
      continue;
    }
    if (player.containsPoint(bullet.absoluteCenter) ||
        bullet.containsPoint(player.absoluteCenter)) {
      bullet.removeFromParent();
      player.loss(bullet.attr);
      break;
    }
  }
}
复制代码

到这里,我们已经完成了 主角怪物 间的基本交互,也基本上对 Component 有了较深的理解。接下来将进一步探讨 碰撞检测 相关的知识,毕竟现在靠的是 矩形域中心点 的包含关系,并不是非常准确。那本文就到这里,明天见 ~

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

0 人点赞