独習 Unity アプリ開発

独習でスマフォ向けアプリ開発を勉強中

Unity で テトリス風ゲームを作ってみる(実装編)#7

 Unity Version 2022.3.4

 

前回の続き


前回、タイトル画面上のスタートボタンでゲームを開始し、テトリミノをフィールドに配置して落下させるまでを説明した。今回はテトリミノの高速落下とゲーム、テトリミノのステートについて説明する。

 

テトリミノのステート(状態)


ゲーム全体や各キャラクタは、状態ごとにイベント処理を分けると綺麗に実装できる

ことがよくある。デザインパターンとしてもState パターンとして定義されているが、今回はそこまでがっつりは実装せずに、Enumを使ったステート管理の軽めの実装を行う。現状の仕様と実装方針から考え、テトリミノの状態を下記の4 つに区分する。基本的な考え方は、同一のイベントを受け取った時の振る舞いを変える場合は状態を分ける。

      • Idle ステート: 初期状態
        • テトリミノが生成され、フィールド内で落下を開始するまでの状態。NextArea にいる間はこの状態になっており、キーイベントは受け取らない(無視する)。また、テトリミノがブロック化して、テトリミノオブジェクト自体が削除されるまでの間もこの状態になる。
      • Drop ステート: 落下中状態
        • テトリミノがフィールド内で自由落下を行っている状態。左右下キーや、左右回転キーを受け取って自信を移動することができる。
      • HighSpeedDrop ステート: 高速落下中状態。
        • テトリミノがフィールド内で高速落下を行っている状態。Drop ステートとほぼ同じだが、落下スピードだけが異なる(Update イベントの処理が異なる)。 
      • Lock ステート: 着地中状態。
        • テトリミノがフィールド、または他ブロックの上に着地し、ブロック化するまでの状態。ブロック化するまでの特定の時間はこの状態になっている。自由落下は行わないが左右キー、左右回転キーは受け取ることができる。

 

各状態の遷移を図示すると、下図のようになる。

テトリミノのステート

今回は各状態を単純にenum としてTetrimino クラスの中で状態を管理する(デザインパターンのステートパターンは使わない)。

Tetrimino.cs の抜粋(ステート定義)

  1.   public enum State {
  2.     Idle,
  3.     Drop,
  4.     HighSpeedDrop,
  5.     Lock,
  6.     MAX
  7.   };
  8.  
  9.   private State _state;

 

各状態で定期的に実行したい処理は、Update 関数内で処理を行う。今回の場合は自由落下、高速落下、着地からのブロック化は、一定時間後に処理を行う必要があるためそれぞれのステート処理関数を定義する。ステート処理関数は、状態を表す変数(_state)を使ってswitch 分で振り分ける。

各ステートでは経過時間を使った判断を行う必要があるため、前回フレームからの経過時間(Time-deltaTime)を加算するように変数_totalDeltaTime を更新しておく。

Tetrimino.cs の抜粋(ステート処理)

  1.   void Update() {
  2.     _totalDeltaTime += Time.deltaTime;
  3.     switch (_state) {
  4.       case State.Idle:
  5.         break;
  6.       case State.Drop:
  7.         TickEventInDropState();
  8.         break;
  9.       case State.HighSpeedDrop:
  10.         TickEventInHighSpeedDropState();
  11.         break;
  12.       case State.Lock:
  13.         TickEventInLockState();
  14.         break;
  15.     }
  16.   }
  17.  
  18.   private void TickEventInDropState () {
  19.     if (_totalDeltaTime > _fallTime) {
  20.       var isCollision = FreeFall();
  21.       if (isCollision) {
  22.         _state = State.Lock;
  23.       }
  24.       _totalDeltaTime = 0f;
  25.     }
  26.   }
  27.  
  28.   private void TickEventInHighSpeedDropState () {
  29.     if (_totalDeltaTime > _fallTime*HIGH_SPEED_RATIO) {
  30.       var isCollision = FreeFall();
  31.       if (isCollision) {
  32.         _state = State.Lock;
  33.       }
  34.       _totalDeltaTime = 0f;
  35.     }
  36.   }
  37.  
  38.   private void TickEventInLockState () {
  39.     if (_totalDeltaTime > _lockTime) {
  40.       _lockedEventListner?.OnLocked();
  41.       _state = State.Idle;
  42.       _totalDeltaTime = 0f;
  43.     }
  44.   }
  45.  

 

    • Drop ステート関数 : TickEventInDropState

経過時間(_totalDeltaTime)が落下時間(_fallTime)以上になった場合は、落下処理(FreeFall)を呼び出す。落下処理を実行した場合は、経過時間をクリアする。

落下処理の結果、テトリミノが着地した場合はステートをLock 状態に変更する。

 

    • HighSpeedDrop ステート関数 : TickEventHighSpeedDropState

Drop ステート関数とほとんど同じ処理。違いは落下時間を1/20 倍しているだけである。

 

    • Lock ステート関数 : TickEventInLockState

経過時間(_totalDeltaTime)がブロック化時間(_lockTime)以上になった場合は、コールバック関数OnLocked()を呼び出して通知を行い、ステートをIdle 状態に変更する。

 

次の記事


from20150817.hatenablog.com

 

 


目次に戻る