This document is about: QUANTUM 2
SWITCH TO

Bomber

Level 4

概要

Quantum Bomberサンプルは完全なソースコード付きで提供され、Quantumでボンバーマンのようなゲームプレイを構築する方法を示しています。

ダウンロード

バージョン リリース日 Download
2.1.6 2023年7月18日 Quantum Bomber 2.1.6 Build 268

はじめに

オンラインマルチプレイヤーモードでサンプルを実行するには、まずPhotonEngine DashboardでQuantum AppIdを作成し、PhotonServerSettingsアセットのAppIdフィールドに貼り付けます。 次に、シーンメニューのMenuシーンを読み込み、Playを押してください。

技術情報

  • Unity: 2021.3.13f1 or higher
  • プラットフォーム: PC (Windows)

ハイライト

技術

  • カスタム Movement コンポーネントとシステムは、トップダウングリッドベースのゲームに 適しています。
  • 半手順的なマップとパワーアップの生成
  • キャラクターのカスタマイズ
  • 時間ベースの爆発拡散
  • ゼロイベント・シミュレーション・アプローチ
  • 比較的純粋な ECS シミュレーションアーキテクチャ

ゲームプレイ

  • バトルロイヤル ボンバー
  • 爆弾を置く
  • 爆弾の量、爆発のリーチ、移動速度の修正の形でパワーアップ。

コントロール

  • WASDで移動
  • 爆弾を置くスペース

Cell

CellはDSLで定義された構造体で、CellTypeフラグという形で1つの値を保持する。これらの値にアクセスや 変更をより便利に行うために、Cell.User.csスクリプトでは、この構造体をプロパティとメソッドで拡張しています。

各セルは、現在その位置にあるオブジェクトのタイプが何であるかは知っているが、 エンティティ自体への参照は持っていない。システムがエンティティを反復処理するとき、そのセルに関心のあるものがあるかどうかを確認します。例えば、セルが IsBurning であれば、そのセルが担当するコンポーネントに適切な 応答をトリガーします。

CellType はフラグであるので、すべてのプロパティはビット演算として構築することができ、非常に効率的です。

Grid

Gridは通常のCSharpクラスで、GridSettings構造体、Cell構造体の配列、ポインタを含んでいます。

Frame.User

通常のクラスとして Grid を定義し、そのインスタンスを Frame.User に持つことで、通常の Frame.XYZ API から利用できるようにすると同時に、関連するすべてのメソッドを Frame.Grid.XYZ にカプセル化することができます。

Gridが通常のクラスであることの欠点は、そのデータが通常のDSLが生成するメモリの外に存在することです。そのため、データの初期化、シリアライズ、コピー、クリア、ダンプを手動で行う必要があります。これは、Frame.User内の以下のメソッドにフックすることで行います:

  • InitUser(): フレームが最初に作成されるときに1回呼び出される。
  • FreeUser(): Frameが破棄されるときに呼び出される。
  • CopyUser(): 前のフレームの状態が次のフレームにコピーされるときに呼び出される。
  • SerializeUser(): フレームがシリアライズされるときに呼び出される(例えば、遅れて参加した人のためのスナップショットとして)。
  • DumpFrameUser(): 非同期が起こったときに呼び出される。

グリッドの設定

グリッドの生成と設定に関する情報は GridSettings 構造体にあります。この情報を使って、実行時に1D-arrayを設定し、固定ブロックと破壊可能ブロックの位置を入力したり、SpawnPointsの位置を推測したりすることができます。

セルの配列、別名グリッド

グリッドセルは Cell の1次元配列に含まれます。原理はTilemap Pathfinder Tech Sampleのタイルマップアセットと同じです。1次元配列はDSLでサポートされています。しかし、DSLはコンパイル時に配列のサイズを把握している必要があり、グリッドはプレイヤーによって要求されたサイズに基づいて実行時に生成されるため、別の方法で処理する必要があります。

配列は実行時に Frame.UserInitUser() メソッドにフックして作成されます。

これは通常のCSharpコンテキストなので、配列に関連するメモリはクラスが破棄されるときにガベージコレクタによって収集さ れます。

Frame.User内の CopyUser() メソッドと SerializeUser() メソッドにフックすることで、Grid 内の 1D-array は、predict-rollback と late-join serialization に対応します。

パフォーマンス

Cellはバイトフラグしか持ちませんが、フレームのコピーによって行われる読み込みと書き込みの量がパフォーマンスの大きなボトルネックになる可能性があります。これを防ぐために、Grid.cs ではグリッドとそのセルに対していくつかの get-set メソッドが定義されています。これらのメソッドはポインタ計算を使ってセルへのポインタを返すので、構造体への直接の読み書き両方が可能になります。

ポインターの計算は高速です。しかし、オフセットの計算を間違えると、ランダムなメモリを読み込むことになり、未定義、最悪の場合はシミュレーション全体がクラッシュします。注意して使用してください!

ブロードフェイズのセットとクリア

グリッドにメモリーを書き込むシステムは2つあります:

  • SetBroadphaseSystem: すべてのゲームプレイシステムの__前に__実行され、それが現在含んでいるエンティティに基づいてセルタイプを設定する。
  • ClearBroadphaseSystem: すべてのゲームプレイシステムの__後に__実行され、破壊されようとしているエンティティのセル内の情報をクリアする。

入力

入力構造は5つのボタンで構成されている。

C#

input {
    Button MoveUp;
    Button MoveDown;
    Button MoveLeft;
    Button MoveRight;
    Button PlaceBomb;
}

これは入力構造体が取り得る最も凝縮された形です。シミュレーションでは Button 型の入力は1バイトですが、電線上では1ビットに圧縮されます。

InputSystemはプレイヤーの入力をゲームプレイのシステムで消費する準備をするために、移動ボタンを使って Direction を構築し、PlaceBomb ボタンの値に基づいて AbilityPlaceBombWantsToPlaceBomb boolean を設定します。

Bomber

Bomberコンポーネントは、エンティティを識別してフィルターするための flag-component です。

このように、関連する BomberSystem は比較的単純です。その唯一の仕事は、与えられた Update() の間に、爆撃機タイプのエンティティが燃えているセルに立っているかどうかを確認することだからです。

C#

public override void Update(Frame f, ref BomberFilter filter)
{
    var gridPosition = filter.Transform->Position.RoundToInt(Axis.Both);
    var isInvincible = false;
#if DEBUG
    // Used for debugging purposes
    isInvincible = f.RuntimeConfig.IsInvincible;    
#endif
    if (isInvincible == false && f.GetCellRef(gridPosition).IsBurning)
    {
        // Death animation is triggered from OnEntityDestroyed
        f.Destroy(filter.Entity);
    }
}

Bomb

爆弾はプレイヤーのアクションによって生成され、Timerが切れると爆発する一時的なエンティティである。

Bombコンポーネントには2つのシステムがあります:

  • AbilityPlaceBombSystem: プレイヤーが PlaceBomb ボタンを押したかどうか、そして爆弾の設置条件が満たされているかどうかを確認します。
  • BombSystem: 爆弾の Timer をスタートさせ、連鎖反応を処理し、爆弾が破壊されたときに爆発を誘発します(タイマーが切れたか、他の爆弾の爆発が触れたかのどちらかです)。

Explosion

爆発は一定時間続くものです。したがって、単なるレイキャストではなく、エンティティとして存在する必要があります。 ExplosionSystemは爆発の寿命とセルからセルへの爆発の広がりを扱います。

爆発エンティティは2つのコンポーネントで構成さ れます:

  • Explosion: フラグコンポーネントとして、また爆発コンフィグに関する情報を保持するために使用されるコンポーネントです。
  • Timer: タイマーは爆発が次の隣接セルに広がるまでの残り時間を計算するために使用されます。

PowerUp

パワーアップの機能は、主に以下の2つのコンポーネントによって提供されます。

  • PowerUp: パワーアップをタイプと修飾量によって定義します。
  • PowerUpManager:スポーン可能なパワーアップのリストとスポーン確率を保持します。

パワーアップは、破壊可能なブロックがグリッドから取り除かれたときにスポーンする確率を持っています。ブロックが破壊されると、PowerUpManagerに新しくクリアされた位置が通知されます。PowerUpManagerSystemは新しく利用可能になった場所を繰り返し、そのセルが空で現在燃えていなければランダムなパワーアップをスポーンしようとします。

Movement

移動はマスの中では自由で、グリッドを読み込んでキャラクターがどこに行けるかを決定します。

Direction

Directionは現在押されている移動ボタンを表すバイトフラグです。

Movement コンポーネント

Movementコンポーネントはフレーム間の関連する移動の値を記録します。

  • FP CurrentSpeed: キャラクターの現在の移動速度です。
  • FP MaxSpeed: キャラクターが移動できる最大速度です。
  • Boolean IsMoving: 最後の移動入力が移動につながったかどうかを記録します。
  • Boolean LastMoveWasHorizontal: 最後の移動の向きを保持します。
  • Direction LastNewInput: プレイヤーが入力した最新の入力を垂直方向と水平方向の両方で保持します。
  • Direction CurrentInput: 現在押されている移動方向です。
  • FPVector2 MoveDirection: 最後の移動方向です。
  • FP StartRotation: 方向を変更する前の最後のルック回転。
  • FP TargetRotation: 方向転換後にキャラクタが持つべきルック回転です。
  • int RotationStartTick: 新しい方向への回転を開始した刻みです。
  • FP RotationDuration: 回転にかかる最大時間です。
  • FP RotationTimeMultiplier: 与えられた時間内に回転を完了するために必要な回転速度の倍率です。

Movement System

Movement Systemは3つのコア機能を果たしています。

  1. 現在押されている移動キーから可能な移動を計算し(GetMovementResult())、その値を含む MoveResult 構造体を返す。2. 現在の移動速度を更新する (UpdateCurrentSpeed())
  2. キャラクターを移動する (UpdateMovement())
  3. キャラクタを回転させる (UpdateRotation())

MovementSystemは2つの機能を実装しており、グリッドを斜めにスムーズに移動することができるようにします:

  • CanMoveInDirection() はコーナーのスライドを考慮します。CanMoveInDirection()はコーナーのスライドを考慮します。これにより、斜めの方向が押されたときに、キャラクターがスムーズにコーナーを移動できるようになります。
  • GetMoveVector()には、どちらの方向(垂直または水平)を最初に確認するかを交互に切り替える方向トグルがあります。これにより、キャラクターはグリッドの中を連続的に斜めに移動することができます。

MoveResult

MoveResultはユーティリティ構造体です。これは、現在の Movement.CurrentInput を、キャラクターが移動するグリッドセルに対して処理した結果を保持します。これらの値はフレーム状態の一部として使用されないため、DSLではなく通常のCSharp構造体として定義されています。

  • FPVector2 Direction: 移動したい方向。
  • FP MaxDistance: キャラクターが希望する方向に移動できる最大距離。
  • FPVector2 LookDirection: この移動の結果としてキャラクターが見る方向。
  • FP RotationTimeMultiplier: 現在の視線方向と希望する視線方向の間の角度に基づいてキャラクターが視線方向を切り替える速度で、事前に定義された時間内に回転を完了します。

サードパーティのアセット

Bomber サンプルには、それぞれの作成者から提供されたいくつかのアセットが含まれています。 独自のプロジェクト用の完全なパッケージは、それぞれのサイトで入手できます。

__ 重要__:商用プロジェクトで使用するには、各クリエイターからライセンスを購入する必要があります。

Back to top