【Flutter&Flame游戏 - 拾壹】探索构件 | Component 使用细节

2022-06-19 16:20:10 浏览数 (1)

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


前言

这是一套 张风捷特烈 出品的 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.关于 Compoent 树

如下图场景,每个显示的物体都是 Component ,它们形成一个树形结构。代码见 【11/01】

各种角色通过 add 方法添加到树中,此时的树型结构如下:

现在有个问题:因为血条和血量是被加入到 Adventurer 构件中的,所以 Adventurer 的变换行为也会引起血条的变换。如下角色沿 Y 轴镜像,可以看到血条及文字也发生了镜像,这并不是我们所期望的。

那如何解决呢?思路很简单,既然 Adventurer 有单独镜像的需求,那就不能是血条的父级。将两者从父子关系变为兄弟关系即可,这里将血条封装为 LifeComponent 构建,和 Adventurer 一起存在于 HeroComponent 中:


2.角色移动中的镜像反转

现在想实现如下效果:如果触点在角色左侧,角色会镜像反转到左侧,反之,镜像反转到右侧。这样的目的是为了角色可以选择攻击的方向,比如面向左侧攻击左侧怪物:代码见 【11/02】

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


因为这里只有左右反转,在 HeroComponent 中定义一个 isLeftbool 值用于记录状态。如果需要支持其他方向,比如上、下、左上、右下等,可以通过枚举来维护。

代码语言:javascript复制
---->[HeroComponent]----
bool isLeft = true;
late Adventurer adventurer;
late LifeComponent lifeComponent;
复制代码

在点击屏幕时,触发 toTarget 方法,在开始可以通过 _checkFlip 方法来对 isLeft 属性进行维护,已经在需要反转是通过 flip 反转角色:

代码语言:javascript复制
---->[HeroComponent#toTarget]----
void toTarget(Vector2 target) {
  _checkFlip(target);
  // 略同...
}

void _checkFlip(Vector2 target){
  if (target.x < position.x) {
    if (isLeft) {
      flip();
      isLeft = false;
    }
  }
  if (target.x > position.x) {
    if (!isLeft) {
      flip();
      isLeft = true;
    }
  }
}
复制代码

用于只想要让主角反转,所以在 flip 中,执行 adventurer.flip 即可。这样就不会影响血条的显示:

代码语言:javascript复制
void flip({
  bool x = false,
  bool y = true,
}) {
  adventurer.flip(x: x, y: y);
}
复制代码

代码语言:javascript复制
---->[HeroComponent#flip]----
void flip({
  bool x = false,
  bool y = true,
}) {
  adventurer.flip(x: x, y: y);
}
复制代码

另外关于反转,还需要注意子弹的发射方向。因为前面的子弹总是向右侧发射的,如果面朝左侧,应该向左运动,如下所示:

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

处理起来也比较简单,根据 isLeft 确实向左还是向右发射即可,如下tag1

代码语言:javascript复制
---->[Bullet]----
@override
void update(double dt) {
  super.update(dt);
  Vector2 ds = Vector2(isLeft ? 1 : -1, 0) * speed * dt; // tag1
  _length  = ds.length;
  position.add(ds);
  if (_length > maxRange) {
    _length = 0;
    removeFromParent();
  }
}
复制代码

3. 关于属性的维护

前面为了方便演示,对于角色的属性,写的比较零散,比如速度、攻击力等。在这里既然可以封装了 HeroComponent 来维护主角类。就可以定义一个 HeroAttr 类来维护主角的属性,如下所示:

代码语言:javascript复制
class HeroAttr {
  double life; // 生命值
  double speed; // 速度
  double attackSpeed; // 攻击速度
  double attackRange; // 射程
  double attack; // 攻击力
  double crit; // 暴击率
  double critDamage;  // 暴击伤害

  HeroAttr({
    required this.life,
    required this.speed,
    required this.attackSpeed,
    required this.attackRange,
    required this.attack,
    required this.crit,
    required this.critDamage,
  });
}
复制代码

这样在构建 HeroComponent 时,传入 HeroAttr 对象来确定该对象的属性信息。

代码语言:javascript复制
---->[TolyGame#onLoad]----
final HeroAttr heroAttr = HeroAttr(
  life: 3000,
  speed: 100,
  attackSpeed: 200,
  attackRange: 200,
  attack: 50,
  crit: 0.75,
  critDamage: 1.5,
);
player = HeroComponent(attr: heroAttr);
add(player);
复制代码

这样在怪物损失生命值,可以根据 HeroAttr 的属性进行计算:

代码语言:javascript复制
---->[Liveable]----
void loss(HeroAttr attr) {
  double point = attr.attack;
  double crit = attr.crit;
  double critDamage = attr.critDamage;
  bool isCrit = _random.nextDouble() < crit;
  if (isCrit) {
    point = point * critDamage;
  }
  _damageText.addDamage(-point.toInt(), isCrit: isCrit);
}
复制代码

添加子弹时,可以根据 HeroAttr 的属性信息确定攻速和射程:


本篇,我们继续拓展了角色的功能,知道父级构件的变换会影响子级组件,所以在使用构件时需要注意构件间的关系。另外通过 HeroAttr 封装了角色信息,这样通过 HeroComponent 就可以添加多个主角节点,就可以双人模式打怪。

到这里,可以看到 TolyGame 中非常乱,下一章我来介绍一下,如何对多个角色和怪物进行管理,包括怪物的生成、发射子弹、命中主角等。那本文就到这里,明天见 ~

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

0 人点赞