Unity で テトリス風ゲームを作ってみる(アニメーション#3)
Unity Version 2022.3.4
前回の続き
前回は、テトリミノ落下時の移動を単純なTranform.localPositionを使った移動から、DoTween を使ったアニメーション処理に変更した。それに伴ってアニメーション処理中を表現するステートを追加した。今回は、前回の対応だけでは不十分な点を補足していく。
ステートとイベントの再整理
前回、落下処理をアニメーション化(非同期化)したことにより、新たなステートState.DropAnimation を追加したが、このステート中に受け取るイベントの処理が実はまだ足りていない。
全体像を把握するため、再度テトリミノのステート遷移図を書き直したのが下図。
DropAnimation ステート中に下キーイベントを受信した場合は、高速落下HighSpeedDrop ステートに遷移する必要がある。そこでTetrimino クラスのOnMoveDownEvent メソッドにて、ステート判定分を追加してDropAnimation ステート中の場合は、DoTween アニメーションをキャンセル(Kill)して、HighSpeedDrop ステートに遷移するように変更する。また合わせて高速落下処理自体もDoTween アニメーションを利用するように変更した。
Tetrimino.cs の抜粋
- public void OnMoveDownEvent (InputAction.CallbackContext context) {
- if (IsOperatable() && context.phase == InputActionPhase.Started) {
- if (_state == State.HighSpeedDrop || _state == State.Lock)
- return;
- if (_state == State.DropAnimation && _tweenerOfDropAnimation != null)
- _tweenerOfDropAnimation.Kill();
- _tweenerOfDropAnimation = DropAnimation (ScreenConfig.FIELD_BOTTOM_WIDTH, DropUpdate);
- ChangeState(State.HighSpeedDrop);
- }
- }
高速落下もアニメーション対応した結果がこちら。
ちなみにアニメーション対応する前はこちら。
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 の抜粋
- private Tweener DropAnimation (float dest, TweenCallback callback) {
- var pos = this.transform.localPosition;
- return this.transform
- .DOLocalMoveY(dest, _fallTime*0.5f)
- .OnUpdate(callback)
- .OnComplete( () => {
- ChangeState(_prevState);
- _tweenerOfDropAnimation = null;
- });
- }
- // Callback from DOTween
- private void DropUpdate() {
- var isCollision = false;
- foreach (var block in _blockPrefabs) {
- if (CollisionCheck (block.transform, Vector2.down)) {
- isCollision = true;
- break;
- }
- }
- if (isCollision) {
- _tweenerOfDropAnimation.Kill();
- AdjustPosition();
- ChangeState(State.Lock);
- _tweenerOfDropAnimation = null;
- }
- }
今回、ステートの再整理を行ったので、Update メソッド内の各Tick イベント処理についても再度整理を行った。それぞれのステートごとにTick イベント処理関数を呼び出すように変更。今のところ実際に処理があるのはDrop ステートと Lock ステートのみ。
Tetrimino.cs の抜粋
- private void TickEventIdleState () {
- _totalDeltaTime = 0f;
- }
- private void TickEventInDropState () {
- if (_totalDeltaTime > _fallTime) {
- var isCollision = FreeFall();
- if (isCollision) {
- ChangeState(State.Lock);
- }
- _totalDeltaTime = 0f;
- }
- }
- private void TickEventInDropAnimationState () {
- _totalDeltaTime = 0f;
- }
- private void TickEventInHighSpeedDropState () {
- _totalDeltaTime = 0f;
- }
- private void TickEventInLockState () {
- if (_totalDeltaTime > _lockTime) {
- _lockedEventListner?.OnLocked();
- ChangeState(State.Idle);
- _totalDeltaTime = 0f;
- } else {
- FreeFall(false);
- }
- }
- void Update() {
- _totalDeltaTime += Time.deltaTime;
- switch (_state) {
- case State.Idle:
- TickEventIdleState();
- break;
- case State.Drop:
- TickEventInDropState();
- break;
- case State.DropAnimation:
- TickEventInDropAnimationState();
- break;
- case State.HighSpeedDrop:
- TickEventInHighSpeedDropState();
- break;
- case State.Lock:
- TickEventInLockState();
- break;
- }
- }
TickEventInLockState メソッドについて、この処理も今まで考慮すべき点がもれていたので修正した。Lock ステート中も左右キーが有効であるため、テトリミノが左右に移動した後に、下にブロックがなければ落下する必要がある。そこで、Tick イベント処理にて、FreeFall メソッドをアニメーションなしで呼び出し、落下できる場合は落下するように変更を加えた。
以上を実装した結果がこちら。
次の記事