[Flutter&Flameに基づく航空機戦争の開発ノート]弾丸のアップグレードと消耗品

序文

敌机Component復興後、基本的に航空機戦の基本能力が形成されます。この記事では、アップグレードを行い子弹Component、ゲームに弾丸の小道具战机Componentを追加し、それとの衝突対処します。

著者はこのシリーズの記事を次のコラムに含めており、興味のある学生は次の記事を読むことができます。

Flutter&Flameに基づく航空機戦争の開発ノート

弾丸の特徴

Bullet1フロントはクラスの単純な定義であり子弹Component、既存の属性に従って、箇条書きを定義でき父类Bulletます。ここで抽象クラスを引き続き使用します。

abstract class Bullet extends SpriteAnimationComponent with CollisionCallbacks {
  Bullet({required this.speed, required this.attack})
      : super(size: Vector2(5, 11));

  double speed;
  int attack;

  Future<SpriteAnimation> bulletAnimation();

  @override
  Future<void> onLoad() async {
    animation = await bulletAnimation();

    add(MoveEffect.to(
        Vector2(position.x, -size.y), EffectController(speed: speed),
        onComplete: () {
      removeFromParent();
    }));
    
    add(RectangleHitbox());
  }

  void loss() {
    removeFromParent();
  }
}
复制代码

定義は比較的簡単です

  • 弾丸には独自の速度speedとダメージがありattackます。
  • 修正されsize = 5 * 11ました。
  • 弾丸ごとにテクスチャが異なるため、bulletAnimation()サブクラスによってロードされる抽象メソッドを定義します。
  • 前述の以前のコントロールムーブメントをMoveEffect置き換えるために使用します。s = v * tここでは、初期位置(通常战机Componentはヘッド)から画面の上部に移動します
  • loss()敌机Componentとの衝突が発生すると、メソッドが呼び出されます。

上記のリストは、現在の弾丸の一般的な特徴です。

弾丸をアップグレードする

ゲームでは、小道具を入手することで、短時間で強化された弾丸を入手战机Componentできます。ここで表現类Bullet2

class Bullet2 extends Bullet {
  Bullet2({required super.speed, required super.attack});

  @override
  Future<SpriteAnimation> bulletAnimation() async {
    List<Sprite> sprites = [];
    sprites.add(await Sprite.load('bullet/bullet2.png'));
    final SpriteAnimation spriteAnimation =
        SpriteAnimation.spriteList(sprites, stepTime: 0.15);
    return spriteAnimation;
  }
}
复制代码

ここでは、別のテクスチャが弾丸のスタイルとして読み込まれます。

最終的な効果を見てみましょう

VID_20220711191853_.gif

弾薬供給

如上面的效果,游戏中会随机生成子弹补给。这里定义一个抽象类Supply补给Component的效果是匀速向下移动并带有小幅度的晃动

  • 匀速向下移动继续采用MoveEffect的方式。
  • 小幅度的晃动需要使用RotateEffect做一个小幅度的旋转。由于需要一个吊起来来回晃动的效果,所以我们还需要将锚点设置为topCenter,令旋转支点为上方居中的位置。

故最后的逻辑为

abstract class Supply extends SpriteComponent with HasGameRef {
  Supply({position, size})
      : super(position: position, size: size, anchor: Anchor.topCenter);

  @override
  Future<void> onLoad() async {
    add(MoveEffect.to(
        Vector2(position.x, gameRef.size.y), EffectController(speed: 40.0),
        onComplete: () {
      removeFromParent();
    }));

    add(RotateEffect.by(15 / 180 * pi,
        EffectController(duration: 2, reverseDuration: 2, infinite: true)));

    add(RectangleHitbox()..debugMode = true);
  }
}
复制代码
  • 移动距离是从屏幕最上方向屏幕最下方移动
  • 旋转角度是弧度,这里是(15 / 180)pi,需要来回晃动,所以这里设置了正反方向的持续时间且效果是无限循环的
  • 最后需要添加RectangleHitbox,作为与战机Component的碰撞检测。ps:碰撞检测的逻辑在子类实现,这里目前是类BulletSupply

子类的逻辑几乎只有碰撞检测,这个在下文会讲到,先来看看这个实现的效果 Record_2022-07-11-17-25-45_13914082904e1b7ce2b619733dc8fcfe_.gif

升级与恢复

子弹补给Component中,碰撞检测与战机Component发生碰撞后

  • 会触发其upgradeBullet()方法,此时会变更bulletType,即子弹类型。
  • 添加一个0.15sColorEffect,作为子弹升级的反馈
  • 弾丸が通常のタイミングに戻るのでTimer、タイミングを合わせて1つをオンにしますps:設定自動オープンを許可しないと呼ばれると、進行状況がリセットされます。これは、継続的に供給を取得するタイミングがリセットされることを理解できます。5s_bulletUpgradeTimerautoStart = falsestart()
// class BulletSupply
@override
void onCollisionStart(
    Set<Vector2> intersectionPoints, PositionComponent other) {
  super.onCollisionStart(intersectionPoints, other);
  if (other is Player) {
    other.upgradeBullet();
    removeFromParent();
  }
}

// class Player
int bulletType = 1;

_bulletUpgradeTimer = Timer(5, onTick: _downgradeBullet, autoStart: false);

void upgradeBullet() {
  bulletType = 2;
  add(ColorEffect(Colors.blue.shade900, const Offset(0.3, 0.0),
      EffectController(duration: 0.15)));

  _bulletUpgradeTimer.start();
}

void _downgradeBullet() {
  bulletType = 1;
}

// timer.dart
/// Start the timer from 0.
void start() {
  reset();
  resume();
}
复制代码

战机Component弾丸を発射するため类Playerのタイマーがあるため、弾丸の種類をbulletType変更した後、次の弾丸の発射のロジックを変更する必要があります

// class Player
void _addBullet() {
  if (bulletType == 2) {
    final Bullet2 bullet2 = Bullet2(speed: 400, attack: 2);
    bullet2.priority = 1;
    bullet2.position = Vector2(position.x + size.x / 2 - 10, position.y + 10);

    final Bullet2 bullet2a = Bullet2(speed: 400, attack: 2);
    bullet2a.priority = 1;
    bullet2a.position =
        Vector2(position.x + size.x / 2 + 10, position.y + 10);

    gameRef.add(bullet2);
    gameRef.add(bullet2a);
  } else {
    final Bullet1 bullet1 = Bullet1(speed: 200, attack: 1);
    bullet1.priority = 1;
    bullet1.position = Vector2(position.x + size.x / 2, position.y);
    gameRef.add(bullet1);
  }
}
复制代码

供給生成

それを覚えてい敌机生成器EnemyCreatorますか?ここで一時的に供給を生成するタイミングを介して

// class EnemyCreator
void _createEnemy() {
  final width = gameRef.size.x;
  double x = _random.nextDouble() * width;
  final double random = _random.nextDouble();
  if (random < 0.05) {
    final size = Vector2(60, 75);
    if (width - x < size.x / 2) {
      x = width - size.x / 2;
    } else if (x < size.x / 2) {
      x = size.x / 2;
    }
    final enemySupply =
        BulletSupply(position: Vector2(x, -size.y), size: size);

    add(enemySupply);
    return;
  }
  。。。
复制代码

补给Component'のアンカーポイントがに変更されたためtopCenterposition''の位置もに移動されtopCenterていることに注意してくださいここでの境界計算は、この状況に応じて調整する必要があります。

やっと

この記事では、弾丸の種類の拡張、弾丸の供給の効果と生成、および战机Component弾丸のアップグレードのロジックを記録します。これまでのところ、航空機戦争の機能は基本的に実現されており、残りの機能は追加の小道具、スコアリングシステム、健康価値システムなどです。今後は、公式bloc例を参考にプロジェクト全体の構造を変更することを検討していきます。

おすすめ

転載: juejin.im/post/7119401685138538510