【Flutter&Flame游戏 - 拾壹】探索构件 C

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


前言

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

第一季完结,谢谢支持 ~


1.关于 Compoent 树

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

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

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

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


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

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


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

1
2
3
4
dart复制代码---->[HeroComponent]----
bool isLeft = true;
late Adventurer adventurer;
late LifeComponent lifeComponent;

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
dart复制代码---->[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 即可。这样就不会影响血条的显示:

1
2
3
4
5
6
dart复制代码void flip({
bool x = false,
bool y = true,
}) {
adventurer.flip(x: x, y: y);
}

1
2
3
4
5
6
7
dart复制代码---->[HeroComponent#flip]----
void flip({
bool x = false,
bool y = true,
}) {
adventurer.flip(x: x, y: y);
}

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

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

1
2
3
4
5
6
7
8
9
10
11
12
dart复制代码---->[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 类来维护主角的属性,如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
dart复制代码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 对象来确定该对象的属性信息。

1
2
3
4
5
6
7
8
9
10
11
12
dart复制代码---->[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 的属性进行计算:

1
2
3
4
5
6
7
8
9
10
11
dart复制代码---->[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 中非常乱,下一章我来介绍一下,如何对多个角色和怪物进行管理,包括怪物的生成、发射子弹、命中主角等。那本文就到这里,明天见 ~

\

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

0%