持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第 15 天,点击查看活动详情
前言
这是一套 张风捷特烈 出品的 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 游戏 - 贰贰】菜单、字体和浮层
未完待续
~
0. 前情回顾
之前的 矩形域
和 中心点
的包含关系,这样校验有很大的误差。上一篇介绍了 CollisionCallbacks
的使用,本篇就来基于 CollisionCallbacks
对之前的代码进行优化。首先让 TolyGame
混入 HasCollisionDetection
支持配置检测:
lass TolyGame extends FlameGame with HasCollisionDetection,
复制代码
接下来让三类主要角色: HeroComponent
、Monster
、Bullet
混入 HasCollisionDetection
。确定碰撞区域,并处理碰撞逻辑。代码详见 【14/01】
1.子弹的处理逻辑
之前把子弹分为了静止 Bullet
的和运动 AnimBullet
的 ,这里把 Bullet
去掉了。一张图片也可以看做是一帧动画,只要不循环播放,先在 AnimBullet
中混入 CollisionCallbacks
,支持监听碰撞回调:
class AnimBullet extends SpriteAnimationComponent with CollisionCallbacks {
复制代码
然后在 onLoad
中添加 Hitbox
碰撞区域,这里用矩形区域:
代码语言:javascript复制void addHitbox(){
ShapeHitbox hitbox = RectangleHitbox();
hitbox.debugMode = true;
add(hitbox);
}
复制代码
然后覆写 onCollision
监听碰撞,这里的处理逻辑是:当子弹类型是 hero
射的,碰撞物体是 Monster
,则说名主角的子弹命中的怪兽,消失即可,反正亦然。
@override
void onCollision(Set<Vector2> intersectionPoints, PositionComponent other) {
super.onCollision(intersectionPoints, other);
if (type == BulletType.hero && other is Monster) {
removeFromParent();
}
if (type == BulletType.monster && other is HeroComponent) {
removeFromParent();
}
}
复制代码
2. HeroComponent 的处理逻辑
先看 HeroComponent
中的处理,先混入 CollisionCallbacks
,支持监听碰撞回调:
class HeroComponent extends PositionComponent with HasGameRef, CollisionCallbacks
复制代码
然后在 onLoad
中添加 Hitbox
碰撞区域,这里用矩形区域:
代码语言:javascript复制void addHitbox(){
ShapeHitbox hitbox = RectangleHitbox();
hitbox.debugMode = true;
add(hitbox);
}
复制代码
然后覆写 onCollision
监听碰撞,这里的处理逻辑是当碰撞物体类型是 AnimBullet
,且类型为 BulletType.monster
时,就表示 HeroComponent
已经中弹了,通过 loss
方法添加伤害。
@override
void onCollision(Set<Vector2> intersectionPoints, PositionComponent other) {
super.onCollision(intersectionPoints, other);
if (other is AnimBullet&&other.type==BulletType.monster) {
loss(other.attr);
}
}
复制代码
3. Monster 的处理逻辑
同样,先混入 CollisionCallbacks
,支持监听碰撞回调:
class Monster extends SpriteAnimationComponent with CollisionCallbacks,
复制代码
然后在 onLoad
中添加 Hitbox
碰撞区域,这里用矩形区域:
代码语言:javascript复制void addHitbox(){
ShapeHitbox hitbox = RectangleHitbox();
hitbox.debugMode = true;
add(hitbox);
}
复制代码
然后覆写 onCollision
监听碰撞,这里的处理逻辑是当碰撞物体类型是 AnimBullet
,且类型为 BulletType.hero
时,就表示 Monster
已经中弹了,通过 loss
方法添加伤害。
@override
void onCollision(Set<Vector2> intersectionPoints, PositionComponent other) {
super.onCollision(intersectionPoints, other);
if (other is AnimBullet&&other.type==BulletType.hero) {
loss(other.attr);
}
}
复制代码
其实 Monster
和 HeroComponent
整体结构是类似的。都具有发射子弹、减少生命值、基础属性等特点。但也有很多不同点,比如主角是用户的主动操作,怪物是系统的固定行为。以后可以考虑进一步进行抽象,求同存异来,优化逻辑。
最后把之前在 TolyGame#update
中校验的一堆逻辑删掉即可。从这里可以看出,CollisionCallbacks
的优势是: 可以给让构件监听到自身的碰撞事件,从而主动处理碰撞逻辑;而之前那样,就像是一个 监工
,在不断看着有没有人碰撞,构件本身是被动的。
4. 区域调整
由于图片的关系,可能碰撞区域会比较大,如果我们只想取区域中的某一块,而非整体区域,该怎么做呢?
默认情况下,RectangleHitbox
会占据整个构件区域,但其构造方法中可以指定一些尺寸和位置属性。
下面我们来结合上一篇中的碰撞试针,来看一下如何对碰撞区域进行调整,代码详见 【14/02】。下图中将矩形区域的宽变成角色尺寸的 0.5
倍,高变为 0.8
倍。可以看出默认情况下,矩形区域的左上角和构件的左上角对齐:
https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/2e57f9d9d09245429a8e3c910f6f3c9a~tplv-k3u1fbpfcp-zoom-in-crop-mark:1304:0:0:0.awebp?
代码语言:javascript复制void addHitbox(){
Vector2 boxSize = Vector2(size.x*0.5, size.y*0.8);
ShapeHitbox hitbox = RectangleHitbox(
size: boxSize,
);
hitbox.debugMode = true;
add(hitbox);
}
复制代码
当将RectangleHitbox
的 anchor
指定为 Anchor.center
时,可以看出此时矩形的中心和构件的左上角对齐。
代码语言:javascript复制void addHitbox(){
Vector2 boxSize = Vector2(size.x*0.5, size.y*0.8);
ShapeHitbox hitbox = RectangleHitbox(
size: boxSize,
anchor: Anchor.center,
);
hitbox.debugMode = true;
add(hitbox);
}
复制代码
然后只有通过 position
进行偏移 size/2
即可和构件的中心点对齐,可以看出此时矩形区域还需要向下微调。
代码语言:javascript复制void addHitbox(){
Vector2 boxSize = Vector2(size.x*0.5, size.y*0.8);
ShapeHitbox hitbox = RectangleHitbox(
size: boxSize,
anchor: Anchor.center,
position: size/2
);
hitbox.debugMode = true;
add(hitbox);
}
复制代码
主要,偏移的微调最好用构件的尺寸分率计算得到,不要直接写死多少数值,不然在构建进行放大时,肯定会出问题。如下的操作,就可以得到一个比较满意的碰撞区域:
https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/fe691155ce5f4674ac3ad3482cd5e060~tplv-k3u1fbpfcp-zoom-in-crop-mark:1304:0:0:0.awebp?
代码语言:javascript复制void addHitbox(){
Vector2 boxSize = Vector2(size.x*0.5, size.y*0.8);
double offsetFixY = -size.y*0.11;
ShapeHitbox hitbox = RectangleHitbox(
size: boxSize,
anchor: Anchor.center,
position: size/2-Vector2(0,offsetFixY)
);
hitbox.debugMode = true;
add(hitbox);
}
复制代码
其他的构建,也可以用类似的方式来优化碰撞区域。本文介绍了一下如通过 CollisionCallbacks
来优化之前代码中的碰撞检测逻辑。一般的休闲游戏的重头戏就是对碰撞的检测和逻辑处理,可以说这点还是非常实用的。那本文就到这里,明天见 ~
@张风捷特烈 2022.06.08 未允禁转
我的 掘金主页
: 张风捷特烈我的 B站主页
: 张风捷特烈我的 github 主页
: toly1994328