独習 Unity アプリ開発

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

Unity で テトリス風ゲームを作ってみる(アニメーション#3)

 Unity Version 2022.3.4

 

前回の続き


前回は、テトリミノ落下時の移動を単純なTranform.localPositionを使った移動から、DoTween を使ったアニメーション処理に変更した。それに伴ってアニメーション処理中を表現するステートを追加した。今回は、前回の対応だけでは不十分な点を補足していく。

 

ステートとイベントの再整理


前回、落下処理をアニメーション化(非同期化)したことにより、新たなステートState.DropAnimation を追加したが、このステート中に受け取るイベントの処理が実はまだ足りていない。

全体像を把握するため、再度テトリミノのステート遷移図を書き直したのが下図。

状態遷移

DropAnimation ステート中に下キーイベントを受信した場合は、高速落下HighSpeedDrop ステートに遷移する必要がある。そこでTetrimino クラスのOnMoveDownEvent メソッドにて、ステート判定分を追加してDropAnimation ステート中の場合は、DoTween アニメーションをキャンセル(Kill)して、HighSpeedDrop ステートに遷移するように変更する。また合わせて高速落下処理自体もDoTween アニメーションを利用するように変更した。

Tetrimino.cs の抜粋

  1.   public void OnMoveDownEvent (InputAction.CallbackContext context) {
  2.     if (IsOperatable() && context.phase == InputActionPhase.Started) {
  3.       if (_state == State.HighSpeedDrop || _state == State.Lock)
  4.         return;
  5.        
  6.       if (_state == State.DropAnimation && _tweenerOfDropAnimation != null)
  7.         _tweenerOfDropAnimation.Kill();
  8.       
  9.       _tweenerOfDropAnimation = DropAnimation (ScreenConfig.FIELD_BOTTOM_WIDTH, DropUpdate);
  10.       ChangeState(State.HighSpeedDrop);
  11.     }
  12.   }

 

高速落下もアニメーション対応した結果がこちら。

www.youtube.com

 

ちなみにアニメーション対応する前はこちら。

www.youtube.com

 

DropAnimation のキャンセルと、高速落下アニメーションに対応するため、アニメーション実行処理も少し修正した。

DoTween のアニメーションを途中でキャンセルするには、DOxxx メソッドを呼び出したときに戻り値として取得できるTweener オブジェクトに対し、Kill メソッドを呼び出すことで途中終了させることができる(DOTween - Documentation のKill の項目を参照)。DropAnimation ステート中に下キーを押された時は、Kill を呼び出してDropAnimation をキャンセルした後に、高速落下のアニメーションを開始してステートをHighSpeedDropAnimation に遷移させる。

 

DOTween アニメーションのキャンセル

Tweener tweenerObj = transform.DOLocalMoveY (,,,,略,,,,);

〰なんやかんや〰

tweenerObj.Kill()

 

高速落下のアニメーションは、Y座標をフィールドのボトム位置にして実行する。当然、ボトム位置に到達するまえにブロックがある場合は、その時点でアニメーションを止める必要がある。DOTween の OnUpdate を使うとアニメーション処理のUpdate ごとのタイミングでコールバックメソッドを呼び出すことができるので、そのコールバックでブロック検出を実行する。もし、ブロックを検知した場合は、アニメーションをキャンセル(Kill)して、ステートをLock に変更する。

 

OnMoveDownEvent からのDropAnimation メソッド呼び出し時は、TweenCallback を指定して呼び出すようにしておく。

OnMoveDownEvent

_tweenerOfDropAnimation =

  DropAnimation (ScreenConfig.FIELD_BOTTOM_WIDTH, DropUpdate);

 

Tetrimino.cs の抜粋

  1.   private Tweener DropAnimation (float dest, TweenCallback callback) {
  2.     var pos = this.transform.localPosition;
  3.     return this.transform
  4.         .DOLocalMoveY(dest, _fallTime*0.5f)
  5.         .OnUpdate(callback)
  6.         .OnComplete( () => {
  7.           ChangeState(_prevState);
  8.           _tweenerOfDropAnimation = null;
  9.         });
  10.   }
  11. // Callback from DOTween
  12.   private void DropUpdate() {
  13.     var isCollision = false;
  14.  
  15.     foreach (var block in _blockPrefabs) {
  16.       if (CollisionCheck (block.transform, Vector2.down)) {
  17.         isCollision = true;
  18.         break;
  19.       }
  20.     }
  21.  
  22.     if (isCollision) {
  23.       _tweenerOfDropAnimation.Kill();
  24.       AdjustPosition();
  25.       ChangeState(State.Lock);
  26.       _tweenerOfDropAnimation = null;
  27.     }
  28.   }

 

今回、ステートの再整理を行ったので、Update メソッド内の各Tick イベント処理についても再度整理を行った。それぞれのステートごとにTick イベント処理関数を呼び出すように変更。今のところ実際に処理があるのはDrop ステートと Lock ステートのみ。

Tetrimino.cs の抜粋

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

 

TickEventInLockState メソッドについて、この処理も今まで考慮すべき点がもれていたので修正した。Lock ステート中も左右キーが有効であるため、テトリミノが左右に移動した後に、下にブロックがなければ落下する必要がある。そこで、Tick イベント処理にて、FreeFall メソッドをアニメーションなしで呼び出し、落下できる場合は落下するように変更を加えた。

 

以上を実装した結果がこちら。

www.youtube.com

 

 

次の記事


from20150817.hatenablog.com

 

目次に戻る