見出し画像

Unityのイベント関数① | Update・FixedUpdate・LateUpdateの違いを初心者向けに解説

Unityでスクリプトを書いていると、かなり早い段階で登場するのが Update() です。

void Update()
{
    
}

Unityの入門書やサンプルコードでもよく出てくるので、

「とりあえず毎回処理したいことはUpdateに書く」

というイメージを持っている方も多いと思います。

スクラッチの「ずっと」と同じような役割ですね。

ただ、Unityには Update() 以外にも、

void FixedUpdate()
{
    
}

void LateUpdate()
{
    
}

という似たようなイベント関数があります。

名前は似ていますが、それぞれ呼び出されるタイミングや向いている処理が違います。

今回は、Unity初心者が混乱しやすい

  • Update

  • FixedUpdate

  • LateUpdate

の違いを、できるだけわかりやすく整理していきます。



イベント関数とは?

Unityには、特定の名前で関数を作っておくと、自動的に呼び出してくれる仕組みがあります。

たとえば、次のように Update() を書いておくと、Unityが毎フレーム自動的に呼び出してくれます。

void Update()
{
    Debug.Log("Updateが呼ばれました");
}

このような関数を、Unityではイベント関数と呼びます。

自分で

Update();

と呼び出しているわけではありません。

決まったタイミングでUnity側が自動的に実行してくれる関数です。


Updateとは?

Update() は、Unityで最もよく使われるイベント関数のひとつです。

void Update()
{
    
}

Update() は、基本的に1フレームに1回呼び出されます。

ゲーム画面が更新されるたびに呼び出される場所、と考えるとわかりやすいです。


Updateに向いている処理

Update() に向いているのは、毎フレーム確認したい処理です。

たとえば、次のような処理です。

  • キーボード入力の確認

  • マウス入力の確認

  • タッチ入力の確認

  • タイマー処理

  • キャラクターの通常移動

  • UIの表示更新

  • 毎フレーム状態を確認する処理

特に、キー入力の判定は Update() に書くことが多いです。

void Update()
{
    if (Input.GetKeyDown(KeyCode.Space))
    {
        Debug.Log("スペースキーが押されました");
    }
}

Input.GetKeyDown() は、キーが押された瞬間を調べる処理です。

このような入力判定は、基本的に Update() に書くのがわかりやすいです。


Updateで移動するサンプル

たとえば、右方向にキャラクターを動かす場合は、次のように書けます。

using UnityEngine;

public class PlayerMove : MonoBehaviour
{
    [SerializeField] float speed = 3f;

    void Update()
    {
        transform.position += Vector3.right * speed * Time.deltaTime;
    }
}

ここで Time.deltaTime をかけているのがポイントです。

Update() は、パソコンやスマホの性能によって呼び出される回数が変わります。

高性能な環境ではたくさん呼ばれ、重い環境では呼ばれる回数が少なくなります。

そのため、移動処理にそのまま

transform.position += Vector3.right * speed;

のように書いてしまうと、環境によって移動速度が変わってしまいます。

そこで Time.deltaTime を使って、1秒あたりの移動量として計算します。

transform.position += Vector3.right * speed * Time.deltaTime;

このように書くことで、フレームレートが違っても、なるべく同じ速度で動かせるようになります。

Time.deltaTime に関しては、こちらの記事で詳しく説明しています。


FixedUpdateとは?

FixedUpdate() は、主に物理演算に関係する処理を書くためのイベント関数です。

void FixedUpdate()
{
    
}

Update() が「画面の更新タイミング」に合わせて呼ばれるのに対して、FixedUpdate() は「物理演算のタイミング」に合わせて呼び出されます。

Unityでは、初期設定では FixedUpdate() は基本的に0.02秒に1回呼び出されます。

つまり、1秒間に約50回です。

0.02秒に1回
↓
1秒間に約50回

ただし、これは「必ず画面1フレームごとに1回呼ばれる」という意味ではありません。

ゲームが重くなった場合、1フレームの間に FixedUpdate() が複数回呼ばれることもあります。

逆に、フレームレートが高い場合は、あるフレームでは FixedUpdate() が呼ばれないこともあります。


FixedUpdateに向いている処理

FixedUpdate() に向いているのは、Rigidbodyを使った物理処理です。

たとえば、次のような処理です。

  • Rigidbodyを使った移動

  • Rigidbody2Dを使った移動

  • AddForceを使ったジャンプ

  • 物理演算に関係する処理

  • 衝突や重力の影響を受ける動き

たとえば、Rigidbodyに力を加えてジャンプさせる場合は、次のように書けます。

using UnityEngine;

public class PlayerJump : MonoBehaviour
{
    Rigidbody rb;

    [SerializeField] float jumpPower = 5f;

    void Start()
    {
        rb = GetComponent<Rigidbody>();
    }

    void FixedUpdate()
    {
        if (Input.GetKey(KeyCode.Space))
        {
            rb.AddForce(Vector3.up * jumpPower);
        }
    }
}

ただし、このコードは説明用です。

実際には、Input.GetKeyDown() のような「押した瞬間」の入力判定は Update() で取得し、その結果を FixedUpdate() で物理処理に使う、という形の方が安全です。


入力はUpdate、物理処理はFixedUpdate

初心者がつまずきやすいのが、

「入力も物理処理も全部FixedUpdateに書いてしまう」

というパターンです。

たとえば、次のようなコードです。

void FixedUpdate()
{
    if (Input.GetKeyDown(KeyCode.Space))
    {
        Jump();
    }
}

一見よさそうに見えますが、FixedUpdate() は毎フレーム必ず呼ばれるわけではありません。

そのため、キーを押した瞬間を取り逃がす可能性があります。

そこで、入力は Update() で受け取り、物理処理は FixedUpdate() で行うようにします。

using UnityEngine;

public class PlayerJump : MonoBehaviour
{
    Rigidbody rb;

    [SerializeField] float jumpPower = 5f;

    bool jumpRequest;

    void Start()
    {
        rb = GetComponent<Rigidbody>();
    }

    void Update()
    {
        if (Input.GetKeyDown(KeyCode.Space))
        {
            jumpRequest = true;
        }
    }

    void FixedUpdate()
    {
        if (jumpRequest)
        {
            rb.AddForce(Vector3.up * jumpPower, ForceMode.Impulse);
            jumpRequest = false;
        }
    }
}

このようにすると、

  • Updateで入力を受け取る

  • FixedUpdateでRigidbodyに力を加える

という役割分担ができます。

Unityでは、この考え方がとても大切です。


FixedUpdateの間隔を変更する方法

FixedUpdate() は、初期設定では0.02秒に1回呼ばれます。

この間隔は、Unityの設定から変更できます。

変更する場所は次の通りです。

1秒間に呼ばれる回数を変更したい場合は、上部メニューの Edit ⇒ Project Settings を立ち上げて、Time の Fixed Timestanp を変更します。

Fixed Timestep の値が、FixedUpdate() の基本的な呼び出し間隔です。

初期値は、

0.02

になっています。

これは0.02秒に1回、つまり1秒間に約50回という意味です。


Fixed Timestepを短くする

たとえば、Fixed Timestep を

0.01

にすると、0.01秒に1回になります。

つまり、1秒間に約100回です。

物理演算の更新回数が増えるため、動きや判定が細かくなる場合があります。

ただし、その分だけ処理の回数が増えるため、ゲームが重くなる可能性があります。

0.02秒 → 約50回/秒
0.01秒 → 約100回/秒

物理処理を多く使っているゲームで不用意に値を小さくすると、負荷が増えるので注意が必要です。


Fixed Timestepを長くする

逆に、Fixed Timestep を

0.033333

のようにすると、約0.033秒に1回になります。

これは1秒間に約30回です。

物理演算の回数が減るため、処理は軽くなる場合があります。

ただし、物理演算の更新間隔が長くなるため、動きが少しカクついたり、判定が粗く感じられたりすることがあります。

0.02秒      → 約50回/秒
0.033333秒 → 約30回/秒

スマホ向けゲームなどで処理を軽くしたい場合には調整することもありますが、初心者のうちは基本の 0.02 のままで問題ありません。


スクリプトからFixedUpdateの間隔を変更する方法

Fixed Timestep は、スクリプトから変更することもできます。

Time.fixedDeltaTime = 0.02f;

たとえば、ゲーム開始時に設定するなら次のように書けます。

using UnityEngine;

public class FixedTimeSetting : MonoBehaviour
{
    void Start()
    {
        Time.fixedDeltaTime = 0.02f;
    }
}

ただし、実際の開発では、途中で頻繁に Time.fixedDeltaTime を変更することはあまり多くありません。

基本的には、Project SettingsのTimeから設定しておく方がわかりやすいです。

また、Time.timeScale を使ってゲーム全体の時間を遅くしたり止めたりしている場合は、fixedDeltaTime の扱いにも注意が必要です。

初心者向けには、

「FixedUpdateの間隔はProject SettingsのTimeから変更できる」

と覚えておけば十分です。


LateUpdateとは?

LateUpdate() は、Update() の後に呼び出されるイベント関数です。

void LateUpdate()
{
    
}

Update() と同じく、基本的に1フレームに1回呼び出されます。

名前の通り、「遅れて実行されるUpdate」と考えるとわかりやすいです。


LateUpdateはいつ使う?

LateUpdate() は、普段はあまり使わないかもしれません。

実際、簡単なゲームであれば Update() だけで済むことも多いです。

ただし、次のような場面では LateUpdate() を使うと便利です。

  • カメラ追従

  • キャラクターの移動後に位置を補正したい

  • Updateで変わった値をもとに最後に処理したい

  • 複数の処理が終わったあとに向きを調整したい

  • UIやエフェクトをキャラクターの位置に合わせたい

特に代表的なのが、カメラ追従です。


LateUpdateの代表例:カメラ追従

キャラクターを追いかけるカメラを作る場合を考えてみます。

キャラクターの移動処理を Update() で行っている場合、カメラの追従も Update() に書くと、処理の順番によって少しズレたり、ガタついて見えたりすることがあります。

そこで、キャラクターの移動が終わったあとにカメラを動かすために、LateUpdate() を使います。

using UnityEngine;

public class CameraFollow : MonoBehaviour
{
    [SerializeField] Transform target;
    [SerializeField] Vector3 offset = new Vector3(0, 2, -10);

    void LateUpdate()
    {
        transform.position = target.position + offset;
    }
}

このようにすると、

キャラクターが動く
↓
その後でカメラが追いかける

という順番になります。

カメラ追従は、LateUpdate() の使いどころとしてとてもわかりやすい例です。


LateUpdateの便利な事例:向きの補正

もうひとつの例として、キャラクターの向きを最後に補正する処理があります。

たとえば、キャラクターが敵の方向を向く処理を考えます。

移動処理や攻撃処理など、いろいろな処理でキャラクターの向きが変わる場合、最後に向きを整えたいことがあります。

そのような場合、LateUpdate() に補正処理を書くと、Update() の処理が終わった後に最終調整できます。

using UnityEngine;

public class LookAtTarget : MonoBehaviour
{
    [SerializeField] Transform target;

    void LateUpdate()
    {
        if (target == null)
        {
            return;
        }

        transform.LookAt(target);
    }
}

このようにすると、毎フレームの最後にターゲットの方向を向くようになります。

3Dゲームでカメラやキャラクターの向きを調整したいときに便利です。


LateUpdateの便利な事例:頭上UIを追従させる

敵キャラクターの頭上にHPバーを表示するような場合にも、LateUpdate() は便利です。

キャラクターの移動が終わったあとに、UIの位置を更新したいからです。

using UnityEngine;

public class HpBarFollow : MonoBehaviour
{
    [SerializeField] Transform target;
    [SerializeField] Vector3 offset = new Vector3(0, 2f, 0);

    void LateUpdate()
    {
        transform.position = target.position + offset;
    }
}

このような処理を LateUpdate() に書くと、キャラクターの位置が更新された後にHPバーの位置を合わせることができます。


呼び出される順番のイメージ

ざっくりとしたイメージでは、次のような順番になります。

FixedUpdate
↓
Update
↓
LateUpdate

ただし、注意点があります。

Update() と LateUpdate() は、基本的に1フレームに1回呼ばれます。

一方で、FixedUpdate() は物理演算のタイミングで呼ばれるため、1フレームに必ず1回とは限りません。

フレームレートが低いと、1フレームの間に複数回呼ばれることがあります。

フレームレートが高いと、あるフレームでは呼ばれないこともあります。

そのため、正確には

FixedUpdateは必要に応じて呼ばれる
Updateは毎フレーム呼ばれる
LateUpdateはUpdateの後に呼ばれる

と考えるとよいです。


どこに何を書く?早見表


よくある間違い

何でもUpdateに書く

初心者のうちは、とりあえず全部 Update() に書いてしまいがちです。

もちろん、簡単な処理であればそれでも動きます。

ただし、Rigidbodyを使った物理処理まで Update() に書いてしまうと、動きが不安定になることがあります。

物理演算に関係する処理は、基本的に FixedUpdate() を使うと考えておきましょう。


入力をFixedUpdateで取る

FixedUpdate() は物理処理に向いていますが、入力判定にはあまり向いていません。

特に、

Input.GetKeyDown(KeyCode.Space)

のような「押した瞬間」を調べる処理は、Update() に書く方が安全です。

FixedUpdate() は毎フレーム必ず呼ばれるわけではないため、入力の瞬間を取り逃がす可能性があります。

おすすめは、

Updateで入力を受け取る
↓
FixedUpdateで物理処理を行う

という分け方です。


カメラ追従をUpdateに書いてガタつく

カメラ追従を Update() に書いても動くことはあります。

しかし、キャラクターの移動処理との順番によっては、わずかにガタついて見えることがあります。

そのようなときは、カメラ追従を LateUpdate() に移してみると改善する場合があります。

void LateUpdate()
{
    transform.position = target.position + offset;
}

LateUpdate() は Update() の後に呼ばれるので、キャラクターが移動した後にカメラが追いかける形にできます。


まとめ

今回は、Unityの

  • Update

  • FixedUpdate

  • LateUpdate

について解説しました。

それぞれの役割を簡単にまとめると、次のようになります。

イベント関数役割Update毎フレームの処理FixedUpdate物理演算の処理LateUpdateUpdate後の処理

もう少し具体的にいうと、

入力はUpdate
物理処理はFixedUpdate
カメラ追従や最終調整はLateUpdate

と考えるとわかりやすいです。

特に初心者のうちは、すべてを完璧に使い分ける必要はありません。

まずは、

  • キー入力はUpdate

  • RigidbodyはFixedUpdate

  • カメラ追従はLateUpdate

この3つを覚えておくだけでも、かなり整理しやすくなります。

Unityでは、処理を書く場所によって動きの安定感が変わることがあります。

「どの処理を、どのタイミングで実行したいのか」

を意識できるようになると、スクリプトの理解が一段深まります。


Unity初心者向けの記事をテーマ別にまとめています。

▼ Unity初心者向け記事まとめはこちら


こちらのマガジン「ミニゲーム作成講座 全8回セット」でもシーンの切り替えなどを活用して、初心者でも知識ゼロから作れるミニゲーム作成講座を紹介しています。

さらにステップアップの「スマホで遊ぶジャンプゲーム 8回セット」のマガジンもぜひご活用ください。



ファイブボックスでは、スクラッチやUnityの個別指導のオンラインレッスンを行っています。

ご興味のある方は当サイト、オンラインレッスンから、無料体験授業へお問い合わせ下さい。


いいなと思ったら応援しよう!

ファイブボックス よろしければ応援お願いします! いただいたチップはクリエイターとしての活動費に使わせていただきます!