【基於Flutter&Flame 的飛機大戰開發筆記】敵機生成器

語言: CN / TW / HK

theme: cyanosis highlight: xcode


前言

繼續前面的內容,本文主要記錄基於Flame實現飛機大戰的敵機建立及生成器的基本內容

筆者將這一系列文章收錄到以下專欄,歡迎有興趣的同學閱讀:

基於Flutter&Flame 的飛機大戰開發筆記

敵機構建

建立類Enemy1繼承SpriteAnimationComponent,和之前的Player類一樣,只是這個是代表敵機的Component。 ```dart class Enemy1 extends SpriteAnimationComponent { Enemy1({required Vector2 initPosition, required Vector2 size}) : super(position: initPosition, size: size);

@override Future onLoad() async { List 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`。dart // class Game @override Future 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); } ```

自動移動

飛機大戰中敵機會自動從螢幕上方移動到螢幕下方直至移出介面。這裡先簡單利用Flame螢幕重新整理來更新敵機Component的positiondart // 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即為遊戲可顯示範圍

自動生成器

實際場景中,敵機應該是隨機在螢幕上方生成,然後再進行自動移動的。所以這裡就需要一個生成器(亦或者是管理器,因為也不止一個種類的敵機)。

所以這裡需要新建一個Component專門用作生成器。 ```dart class EnemyCreator extends PositionComponent with HasGameRef { late Timer _createTimer; final Random _random = Random();

@override Future 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`就會執行前面的自動移動邏輯從上往下移動出螢幕。