【Flutter&Flame游戏 - 拾肆】碰撞检测 | 之前代码优化

2022-06-19 16:26:54 浏览数 (1)

持续创作,加速成长!这是我参与「掘金日新计划 · 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 支持配置检测:

代码语言:javascript复制
lass TolyGame extends FlameGame with HasCollisionDetection,
复制代码

接下来让三类主要角色: HeroComponentMonsterBullet 混入 HasCollisionDetection 。确定碰撞区域,并处理碰撞逻辑。代码详见 【14/01】


1.子弹的处理逻辑

之前把子弹分为了静止 Bullet 的和运动 AnimBullet 的 ,这里把 Bullet 去掉了。一张图片也可以看做是一帧动画,只要不循环播放,先在 AnimBullet 中混入 CollisionCallbacks ,支持监听碰撞回调:

代码语言:javascript复制
class AnimBullet extends SpriteAnimationComponent with CollisionCallbacks {
复制代码

然后在 onLoad 中添加 Hitbox 碰撞区域,这里用矩形区域:

代码语言:javascript复制
void addHitbox(){
  ShapeHitbox hitbox = RectangleHitbox();
  hitbox.debugMode = true;
  add(hitbox);
}
复制代码

然后覆写 onCollision 监听碰撞,这里的处理逻辑是:当子弹类型是 hero 射的,碰撞物体是 Monster ,则说名主角的子弹命中的怪兽,消失即可,反正亦然。

代码语言:javascript复制
@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 ,支持监听碰撞回调:

代码语言:javascript复制
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 方法添加伤害。

代码语言:javascript复制
@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 ,支持监听碰撞回调:

代码语言:javascript复制
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 方法添加伤害。

代码语言:javascript复制
@override
void onCollision(Set<Vector2> intersectionPoints, PositionComponent other) {
  super.onCollision(intersectionPoints, other);
  if (other is AnimBullet&&other.type==BulletType.hero) {
      loss(other.attr);
  }
}
复制代码

其实 MonsterHeroComponent 整体结构是类似的。都具有发射子弹、减少生命值、基础属性等特点。但也有很多不同点,比如主角是用户的主动操作,怪物是系统的固定行为。以后可以考虑进一步进行抽象,求同存异来,优化逻辑。

最后把之前在 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);
}
复制代码

当将RectangleHitboxanchor 指定为 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

0 人点赞