😠

【Unity】Update関数内で1フレームだけ処理する方法

2023/01/01に公開

あけましておめでとうございます。

あけましておめでとうございます。

前書き

ここに辿り着いている諸兄姉であればご存知の通り、
MonobehaviorのUpdate関数は、秒間大体30回ほど実行されるイベントです。
基本的にUnityはこの仕組みをベースにいろんな実装をやっていくわけですが
その中でよく出てくる欲望としては、
「Updateでタイミングを監視するけど一回だけ実行させたい!」
ってのがあると思います。

さて。
とっても基礎的な内容なので、こんな記事はググればいくらでも見つかります。
実装の考え方はいくらでも学べる、よくある要件なわけで、
なんで今更??って感じなんですが、
ググった記事読んでも「文盲すぎてわかんね〜」とか、
「そのまま自分のには使いたくね〜」とか、色々思う事山の如しでありまして、
自分用メモかつなるべく誰でも汎用的にささっと使えるように備忘録残しときますです、
という経緯なわけです。

解説

基本的な実装例が以下コードです。

using UnityEngine;

namespace PlayGround.PlayOneShotOnUpdate
{
    public class PlayOneShotOnUpdate : MonoBehaviour
    {
        // これをスクリプトなり手動なりで変更すると、1フレームだけ実行される。
        [SerializeField] private bool playOneShotFlag;
        // 前Update実行時のboolキャッシュ
        private bool flagCache;
    
        void Update()
        {
            // キャッシュに差分がない場合、ここで処理を終わらせることで1フレーム実行を実現している。
            if (flagCache == playOneShotFlag)
                return;
            
            // 一回だけ実行したい処理をここに書く。
            PlayOneShot();
            
            // 次のフレームで実行されないようにキャッシュを更新する。
            flagCache = !flagCache;
        }

        private void PlayOneShot()
        {
            Debug.Log("Play One Shot by using MonoBehavior Update event");
        }
    }
}

要点抑えると

  • 実行フラグ(playOneShotFlag)が切り替わったフレームでのみ実行する仕様。
  • 制御用とキャッシュ用の2つのフラグを用意するのがキモ。
  • 基本的にreturnでUpdateを即終わらせる。
  • フラグのキャッシュと差分が出来たフレームのみreturnしない。
  • 最後にフラグの差分を殺す。

という感じでしょうか。

とはいえ、こいつを無計画にあちこちで乱用させたくないというお気持ちだけはあるので、
モック制作してる中で、どうしてもアタッチされてるコンポーネントを制御しなきゃならん!
みたいな場面でドシドシ使っていきたいな、というお気持ちがありますね。

あとがき

余談なんですが、
個人的にUpdate内でフラグ使って処理発火のタイミングを制御する、
という実装にかなりトラウマがあって

  • 俺の脳みそでは管理しきれなくなって地獄を見る。
  • 仕様変更の度にフラグが乱立して仕様変更に弱い。

という性質(たち)があると思っていたのもあり、敬して遠ざけていた節があります。
(なのでコルーチンやUniRxで実装したり、そもそもUpdate使う箇所を設計時点でとにかく減らす、みたいなことしてた)

...だったんですが、最近読んだ良いコード/悪いコードで学ぶ設計入門を読んで「発火しない条件で早期returnする事で、条件ロジックと実行ロジックの2つに処理を分けてネストを解消する」方法を知り、おかげでこういう毎フレームフラグを監視する...みたいな使い方にも心を許せるようになりましたとさ。

圧倒的成長...

Discussion