likes
comments
collection
share

【基于Flutter&Flame 的飞机大战开发笔记】子弹发射及碰撞检测

作者站长头像
站长
· 阅读数 67

前言

有了前面的基础,战机和敌机的环境已经搭建好了。故本文着重记录基于Flame实现飞机大战的战机子弹发射以及基于碰撞检测实现的子弹设计敌机和战机与敌机相遇场景

子弹Component

创建一个继承自SpriteAnimationComponent的类Bullet1作为子弹的实现类。这个和之前的一样都当是一个序列帧处理。

class Bullet1 extends SpriteAnimationComponent with CollisionCallbacks {
  double speed = 200;

  Bullet1() : super();

  @override
  Future<void> onLoad() async {
    List<Sprite> sprites = [];
    sprites.add(await Sprite.load('bullet/bullet1.png'));
    final SpriteAnimation spriteAnimation =
        SpriteAnimation.spriteList(sprites, stepTime: 0.15);
    animation = spriteAnimation;
  }    

为了让子弹Component运动起来,沿用之前敌机Component的做法。在update回调后更新position

@override
void update(double dt) {
  super.update(dt);
  Vector2 ds = Vector2(0, -1) * speed * dt;

  position.add(ds);
  if (position.y < 0) {
    removeFromParent();
  }
}

这个与之前类似,只是子弹是往y轴负方向移动至屏幕外的,所以这里是Vector2(0, -1)。同样也是s = v * t的体现。

子弹发射

这里需要令战机Component定时发射出子弹。还是使用之前讲到的Timer,间隔0.5s发射一次。

class Player extends SpriteAnimationComponent with HasGameRef, Draggable {
...
    late Timer _shootingTimer;
    
    @override
    Future<void> onLoad() async {
      ...
      _shootingTimer = Timer(0.5, onTick: _addBullet, repeat: true);
    }   
  • 定义_shootingTimer作为子弹Component的生成定时,与前面类似。
  • 定时触发方法_addBullet
void _addBullet() {
  final Bullet1 bullet1 = Bullet1();
  bullet1.size = Vector2(5, 11);
  bullet1.priority = 1;
  priority = 2;
  bullet1.position = Vector2(position.x + size.x / 2, position.y);
  gameRef.add(bullet1);
}

_addBullet方法新建一个子弹Component,这是上文创建的实体。需要注意的是

  • 由于子弹是相对于屏幕运动的,而不是战机Component。所以这里需要添加到Game也就是最外层
  • 后加入Component会之于前面的Component(类似帧布局),所以这里为了使效果更像是从战机发射出来的,需要调整两者的优先级
  • 子弹Component的初始位置会在战机Component上方的中心点Vector2(position.x + size.x / 2, position.y)。ps:这是默认战机Component的锚点在右上角的计算结果。

击中敌机及战机与敌机碰撞

要实现子弹击中敌机的效果,我们需要知道子弹Component敌机Component的相遇或者说是碰撞的时机。这里我们运用Flame提供的碰撞检测机制CollisionCallbacks)实现。

我们分别对类Enemy1Bullet1添加CollisionCallbacks的混入

class Bullet1 extends SpriteAnimationComponent with CollisionCallbacks {

class Enemy1 extends SpriteAnimationComponent
    with HasGameRef, CollisionCallbacks {

同时我们需要在两个Component上添加一个RectangleHitbox,这个在之前调试位置的时候有用到。碰撞检测的默认机制是通过两个RectangleHitbox进行的。如果玩过我的世界的同学应该对碰撞箱有所耳闻,这个其实也是类似。

@override
Future<void> onLoad() async {
  add(RectangleHitbox());
}

实现CollisionCallbacks中的onCollisionCallback,这个回调的是碰撞发生时的事件。以敌机Component为例:

// class Enemy1
@override
CollisionCallback<PositionComponent>? get onCollisionCallback =>
    (Set<Vector2> intersectionPoints, PositionComponent other) {
      if (other is Bullet1 || other is Player) {
        removeFromParent();
      }
    };

回调了一个PositionComponent对象,即为与其发生碰撞的Component。这里可以判断其类型是子弹Component战机Component时将敌机Component移除。ps:这里先默认敌机Component的生命值为1

ps:CollisionCallbacks还可以回调碰撞开始和碰撞结束事件:

// collision_callbacks.dart
/// [onCollision] is called in every tick when this object is colliding with
/// [other].
@mustCallSuper
void onCollision(Set<Vector2> intersectionPoints, T other) {
  onCollisionCallback?.call(intersectionPoints, other);
}

/// [onCollisionStart] is called in the first tick when this object starts
/// colliding with [other].
@mustCallSuper
void onCollisionStart(Set<Vector2> intersectionPoints, T other) {
  activeCollisions.add(other);
  onCollisionStartCallback?.call(intersectionPoints, other);
}

/// [onCollisionEnd] is called once when this object has stopped colliding
/// with [other].
@mustCallSuper
void onCollisionEnd(T other) {
  activeCollisions.remove(other);
  onCollisionEndCallback?.call(other);
}

Game这一层还需要添加HasCollisionDetection混入,这样碰撞检测才能开启并往下传递

class Game extends FlameGame with HasDraggables, HasCollisionDetection {

这样就能实现击中敌机及战机与敌机碰撞的效果了,其他地方的碰撞检测同理,这里就不一一赘述了。来看看效果吧

【基于Flutter&Flame 的飞机大战开发笔记】子弹发射及碰撞检测

转载自:https://juejin.cn/post/7117584272458219533
评论
请登录