これは、Flame エンジンを使用して単純なプラットフォーマー ゲームを作成する方法を学ぶ 4 部構成のシリーズの最終回です。 The Boy という名前のアニメーション化されたプレイヤー キャラクターを追加する方法、Tiled エディターを使用してスクロール可能なゲームレベルを作成する方法、衝突検出を使用して重力とジャンプを追加する方法 (パート 1、2、3 )は既にわかっています。
この部分では、キャラクターが収集できるコイン、プレイヤーが持っているコインの数を表示する HUD、およびすべてのコインが収集されたときに表示される勝利画面を追加します。
コインをスポーンする場所 (またはその他のゲーム オブジェクト) をゲームに伝える必要があります。ご想像のとおり、Tiled エディターを使用して、プラットフォームを追加したのと同様の方法で別のオブジェクト レイヤーを追加しますが、2 つの違いがあります。
プラットフォームでは、スプライト イメージをレベル データに焼き付けました。この方法はコインにはあまり適していません。プレイヤーがオブジェクトを収集したら、オブジェクトを削除したいからです。そのため、スポーン ポイントを追加して、コインがゲーム内のどこに出現するかを把握し、Flame コンポーネントを使用してレンダリングを行います。
プラットフォームには任意のサイズの長方形を使用しましたが、コインにはスポーン ポイントを 1 タイルのサイズになるように追加します。ただし、コインの列を次々と追加したい場合 (こんにちはマリオ)、ゲーム コードを変更し、コイン オブジェクトを追加するときにスポーン ポイントのサイズを考慮することで、簡単に行うことができます。しかし、このシリーズの目的のために、コインのスポーン ポイントは 1x1 であると想定しています。
Tiled エディターでレベルを開き、Coins という新しいオブジェクト レイヤーを作成します。次に、Rectangular ツールを使用して、マップ全体にいくつかのスポーン ポイントを追加し、ゲーム エンジンを使用してコイン コンポーネントを追加します。私のレベルは次のようになります。
私たちのゲームはかなり単純であり、これらの空の四角形がゲームの実行時にコインに変わることがわかっていることを付け加えておきます。しかし、さらにオブジェクト タイプを追加すると、それらを区別することが難しくなります。幸いなことに、Tiled には「Insert Tile」というツールがあり、すべてのオブジェクトに視覚的な合図を追加できますが、これらの画像はゲームでレンダリングされません。
レベルを保存して IDE に戻ります。 Coin
クラスを/objects/
フォルダーに追加しましょう。
class Coin extends SpriteAnimationComponent with HasGameRef<PlatformerGame> { late final SpriteAnimation spinAnimation; late final SpriteAnimation collectAnimation; Coin(Vector2 position) : super(position: position, size: Vector2.all(48)); @override Future<void> onLoad() async { spinAnimation = SpriteAnimation.fromFrameData( game.images.fromCache(Assets.COIN), SpriteAnimationData.sequenced( amount: 4, textureSize: Vector2.all(16), stepTime: 0.12, ), ); collectAnimation = SpriteAnimation.fromFrameData( game.images.fromCache(Assets.COIN), SpriteAnimationData.range( start: 4, end: 7, amount: 8, textureSize: Vector2.all(16), stepTimes: List.filled(4, 0.12), loop: false ), ); animation = spinAnimation; final hitbox = RectangleHitbox() ..collisionType = CollisionType.passive; add(hitbox); return super.onLoad(); } }
回転と収集のための 2 つの異なるアニメーションと、後でプレイヤーとの衝突をチェックするためのRectangleHitbox
があります。
次に、 game.dart
に戻り、 spawnObjects
メソッドを変更してコインを生成します。
final coins = tileMap.getLayer<ObjectGroup>("Coins"); for (final coin in coins!.objects) { add(Coin(Vector2(coin.x, coin.y))); }
ゲームを実行すると、追加されたコインが表示されます:
coin.dart
に戻り、 collect
メソッドを追加します。
void collect() { animation = collectAnimation; collectAnimation.onComplete = () => { removeFromParent() }; }
このメソッドが呼び出されると、回転するアニメーションを収集するアニメーションに切り替えます。終了したら、このコンポーネントをゲームから削除します。
次に、 theboy.dart
クラスに移動し、 onCollisionStart
メソッドをオーバーライドします。
@override void onCollisionStart(Set<Vector2> intersectionPoints, PositionComponent other) { if (other is Coin) { other.collect(); } super.onCollisionStart(intersectionPoints, other); }
onCollision
の代わりにonCollisionStart
を使用する理由は、衝突コールバックを 1 回だけトリガーするためです。
コインがザ・ボーイと衝突すると消えるようになりました。収集したコインの数を追跡するためのユーザー インターフェイスを追加しましょう。
HUD (ヘッドアップ ディスプレイ) は、ゲームに関する情報 (ヒット ポイント、弾薬など) を表示する単純なステータス バーです。収集したコインごとにコイン アイコンを表示します。
簡単にするために、コインの数を変数に格納しますが、より複雑なインターフェイスの場合は、便利な方法でゲームの状態を更新および監視できるflame_blocパッケージの使用を検討してください。
HUD ロジックを含む新しいクラスを追加します: lib/hud.dart
class Hud extends PositionComponent with HasGameRef<PlatformerGame> { Hud() { positionType = PositionType.viewport; } void onCoinsNumberUpdated(int total) { final coin = SpriteComponent.fromImage( game.images.fromCache(Assets.HUD), position: Vector2((50 * total).toDouble(), 50), size: Vector2.all(48)); add(coin); } }
ここで興味深いことが 2 つあります。
positionType
PositionType.viewport
に設定して、HUD を画面の隅に固定します。そうしないと、カメラの動きにより、HUD がレベルと共に移動します。onCoinsNumberUpdated
、プレーヤーがコインを収集するたびに呼び出されます。 total
パラメータを使用して次のコイン アイコンのオフセットを計算し、計算された位置に新しいコイン スプライトを追加します。
次に、 game.dart
ファイルに戻り、新しいクラス変数を追加します。
int _coins = 0; // Keeps track of collected coins late final Hud hud; // Reference to the HUD, to update it when the player collects a coin
次に、 onLoad
メソッドの下部にHud
コンポーネントを追加します。
hud = Hud(); add(hud);
そして、新しいメソッドを追加します:
void onCoinCollected() { _coins++; hud.onCoinsNumberUpdated(_coins); }
最後に、 Coin
のcollect
メソッドから呼び出します。
void collect() { game.onCoinCollected(); animation = collectAnimation; collectAnimation.onComplete = () => { removeFromParent() }; }
見事、HUD に収集したコインの数が表示されるようになりました!
最後に追加したいのは、プレイヤーがすべてのコインを集めると表示される Win 画面です。
PlatformerGame
クラスに新しい const を追加します。
late int _totalCoins;
そして、レベルにあるコインの数をそれに割り当てます。次の行をspawnObjects
メソッドの最後に追加します。
_totalCoins = coins.objects.length;
これをonCoinCollected
メソッドの一番下に追加します。
import 'package:flutter/material.dart';
追加する必要がある場合があることに注意してください。手動で。
if (_coins == _totalCoins) { final text = TextComponent( text: 'U WIN!', textRenderer: TextPaint( style: TextStyle( fontSize: 200, fontWeight: FontWeight.bold, color: Colors.white, ), ), anchor: Anchor.center, position: camera.viewport.effectiveSize / 2, )..positionType = PositionType.viewport; add(text); Future.delayed(Duration(milliseconds: 200), () => { pauseEngine() }); }
ここでは、コイン カウンターがレベル内のコインの数と等しいかどうかをチェックし、等しい場合はゲーム画面の上に Win ラベルを追加します。次に、ゲームを一時停止してプレイヤーの動きを止めます。また、ラベルが一時停止する前にレンダリングされるように、200 ミリ秒の遅延を追加しました。
次のようになります。
そして、それがゲームです!もちろん、今は完成したゲームのようには見えませんが、私が説明したすべてのことで、レベル、敵、またはその他の収集可能なアイテムを追加するのはかなり簡単になるはずです.
このパートでシリーズは終了です。
Flame エンジンには、物理エンジンの Forge2D、パーティクル、エフェクト、ゲーム メニュー、オーディオなど、ここでは取り上げなかった多くの機能があります。より複雑なゲームを構築する方法を理解している。
Flame は強力でありながら使いやすく学習しやすいツールです。これはモジュラーであり、Box2D のような他のクールなものを持ち込むことができ、積極的に維持されています。ただし、Flame の最大の利点の 1 つは、Flutter の上に構築されていることです。つまり、少し追加作業を行うだけでマルチプラットフォームのサポートが提供されます。ただし、Flutter 拡張機能であるということは、すべての Flutter の問題が Flame でも存続することを意味します。たとえば、 Flutter のアンチエイリアシングのバグは解決されずに数年間開かれており、私たちが構築したゲームでもそれに気付くかもしれません。しかし、全体として、試してみる価値のあるゲームを構築するための優れたツールです。
このチュートリアルの完全なコードは、私の githubにあります。
各パートの最後に、私が学んだすばらしいクリエイターとリソースのリストを追加します。