前言

持续前面的内容,本文首要记载根据Flame完成飞机大战敌机创立及生成器的基本内容

笔者将这一系列文章收录到以下专栏,欢迎有兴趣的同学阅览:

根据Flutter&Flame 的飞机大战开发笔记

敌机构建

创立类Enemy1承继SpriteAnimationComponent,和之前的Player类一样,只是这个是代表敌机的Component

class Enemy1 extends SpriteAnimationComponent {
  Enemy1({required Vector2 initPosition, required Vector2 size})
      : super(position: initPosition, size: size);
  @override
  Future<void> onLoad() async {
    List<Sprite> sprites = [];
    sprites.add(await Sprite.load('enemy/enemy1.png'));
    final spriteAnimation = SpriteAnimation.spriteList(sprites, stepTime: 0.15, loop: false);
    animation = spriteAnimation;
    add(RectangleHitbox()..debugMode = true);
  }

ps:这儿持续使用SpriteAnimationComponent,只不过只有一帧。 最后我们在Game层的onLoad添加一个敌机Component,先假定敌机的方位在(50,50),巨细为50 * 50。当然这儿还有前文的Player

// class Game
@override
Future<void> onLoad() async {
  final ParallaxComponent parallax = await loadParallaxComponent(
      [ParallaxImageData('background.png')]);
  add(parallax);
  final player = Player(
      initPosition: Vector2(size.x / 2, size.y * 0.75),
      size: Vector2(75, 100));
  add(player);
  final enemy = Enemy1(initPosition: Vector2(50, 50), size: Vector2(50, 50));
  add(enemy);
}
【基于Flutter&Flame 的飞机大战开发笔记】敌机生成器

主动移动

飞机大战中敌机会主动从屏幕上方移动到屏幕下方直至移出界面。这儿先简略使用Flame屏幕刷新来更新敌机Component的position

// class Enemy1
@override
void update(double dt) {
  super.update(dt);
  Vector2 ds = Vector2(0, 1) * 100 * dt;
  position.add(ds);
  if (position.y > gameRef.size.y) {
    removeFromParent();
  }
}

每个Component都有一个update回调,假如打印回调的dt可以发现,基本在16.66s左右,即1s会回调60次左右。这样符合刷新率60fps。依赖这个就可以计算出每一帧敌机位移的距离,继而更新position

  • 计算位移:这儿运用小学的知识:s = v * t。即速度乘以时刻,速度这儿先简略定义100。由所以带方向的位移这儿还需求转换成Vector2类型Vector2(0, 1)表明向y轴正方向移动。ps:Flame中坐标系y轴向下,所以往下为正方向,这个跟移动端界面的坐标类似。
  • 边界问题:若位移后position超越可显现规模,视为移出屏幕,此刻需求将敌机Component父Component中移除。这儿使用gameRef获取到最上层的Game目标,它的size即为游戏可显现规模
【基于Flutter&Flame 的飞机大战开发笔记】敌机生成器

主动生成器

实践场景中,敌机应该是随机在屏幕上方生成,然后再进行主动移动的。所以这儿就需求一个生成器(亦或者是管理器,因为也不止一个种类的敌机)。

所以这儿需求新建一个Component专门用作生成器。

class EnemyCreator extends PositionComponent with HasGameRef {
  late Timer _createTimer;
  final Random _random = Random();
  @override
  Future<void> onLoad() async {
    _createTimer = Timer(1, onTick: _createEnemy, repeat: true);
  }
  @override
  void onMount() {
    super.onMount();
    _createTimer.start();
  }
  @override
  void update(double dt) {
    super.update(dt);
    _createTimer.update(dt);
  }
  @override
  void onRemove() {
    super.onRemove();
    _createTimer.stop();
  }
  void _createEnemy() {
    final width = gameRef.size.x;
    double x = _random.nextDouble() * width;
    final Vector2 size = Vector2(50, 50);
    if (width - x < 50) {
      x = width - 50;
    }
    final enemy1 = Enemy1(initPosition: Vector2(x, -size.y), size: size);
    add(enemy1);
  }
}

创立类EnemyCreator用于敌机生成

  • 承继自PositionComponent。其实上述的SpriteAnimationComponent也是承继于它。默认的position为(0,0),size为0 * 0。所以可以理解为这是一个不在Component树中绘制出来的Component
  • 使用Timer守时创立敌机,守时为1s。守时触发办法_createEnemy。从代码可以看出Timer也是依赖于update回调的。ps:onMount为Component被挂载到Component树的机遇,反之onRemove就是被移除的机遇
  • _createEnemy办法中会使用Game的宽度计算出一个随机的x坐标,然后由于要完成敌机从屏幕上方之外的方位从下移动的作用,所以将y坐标设置在-size.y的方位
  • 添加到Component树后,敌机Component就会执行前面的主动移动逻辑从上往下移动出屏幕。
【基于Flutter&Flame 的飞机大战开发笔记】敌机生成器