序文
敌机Component
復興後、基本的に航空機戦の基本能力が形成されます。この記事では、アップグレードを行い子弹Component
、ゲームに弾丸の小道具战机Component
を追加し、それとの衝突に対処します。
著者はこのシリーズの記事を次のコラムに含めており、興味のある学生は次の記事を読むことができます。
弾丸の特徴
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;
}
}
复制代码
ここでは、別のテクスチャが弾丸のスタイルとして読み込まれます。
最終的な効果を見てみましょう
弾薬供給
如上面的效果,游戏中会随机生成子弹补给。这里定义一个抽象类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
。
子类的逻辑几乎只有碰撞检测,这个在下文会讲到,先来看看这个实现的效果
升级与恢复
在子弹补给Component
中,碰撞检测与战机Component
发生碰撞后
- 会触发其
upgradeBullet()
方法,此时会变更bulletType
,即子弹类型。 - 添加一个
0.15s
的ColorEffect
,作为子弹升级的反馈。 - 弾丸が通常のタイミングに戻るので
Timer
、タイミングを合わせて1つをオンにします。ps:設定、自動オープンを許可しない。と呼ばれると、進行状況がリセットされます。これは、継続的に供給を取得するタイミングがリセットされることを理解できます。5s
_bulletUpgradeTimer
autoStart = false
start()
// 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
'のアンカーポイントがに変更されたためtopCenter
、position
''の位置もに移動されtopCenter
ていることに注意してください。ここでの境界計算は、この状況に応じて調整する必要があります。
やっと
この記事では、弾丸の種類の拡張、弾丸の供給の効果と生成、および战机Component
弾丸のアップグレードのロジックを記録します。これまでのところ、航空機戦争の機能は基本的に実現されており、残りの機能は追加の小道具、スコアリングシステム、健康価値システムなどです。今後は、公式bloc
例を参考にプロジェクト全体の構造を変更することを検討していきます。