ひとりでのアプリ開発 - fineの備忘録 -

ひとりでアプリ開発をするなかで起こったことや学んだことを書き溜めていきます

Unity - 画面を揺らす

初めに

 ゲームでは、画面を揺らすことで衝撃やダメージを演出する場合があります。本記事では、カメラを直接揺らして、画面の揺れを表現する方法を説明します。

固定カメラの場合

 固定カメラの場合、カメラの初期位置を取得しておいて、そこを中心にカメラの位置を移動させればよいでしょう。

 次のスクリプトをカメラにつけ、Shake 関数を呼び出すことで、画面の揺れを実装できます。

using UnityEngine;

public class ScreenShake : MonoBehaviour
{
    public float shakeDuration = 0.3f;   // 揺れの継続時間
    public float shakeMagnitude = 0.1f;  // 揺れの大きさ
    public float dampingSpeed = 1.0f;    // 揺れの減衰速度

    private Vector3 initialPosition;     // カメラの初期位置
    private float shakeTimer = 0.0f;      // 揺れのタイマー

    void Awake()
    {
        // カメラの初期位置を保存
        initialPosition = transform.localPosition;
    }

    void Update()
    {
        if (shakeTimer > 0)
        {
            // 揺れの大きさをランダムに生成
            Vector3 shakeOffset = Random.insideUnitSphere * shakeMagnitude;

            // カメラの位置に揺れを加える
            transform.localPosition = initialPosition + shakeOffset;

            // 揺れのタイマーを減少させる
            shakeTimer -= Time.deltaTime * dampingSpeed;
        }
        else
        {
            // 揺れが終了したらカメラの位置を初期位置に戻す
            transform.localPosition = initialPosition;
        }
    }

    public void Shake()
    {
        // 揺れのタイマーを設定
        shakeTimer = shakeDuration;
    }
}

変数 説明
shakeDuration 揺れの継続時間
shakeMagnitude 揺れの大きさ
dampingSpeed 揺れの減衰速度

(実行)
youtu.be

カメラが移動する場合

 カメラが移動する場合は、カメラの初期位置を保存するわけにはいかないので、別の方法を考えます。本記事では、カメラが移動した分、毎回逆方向に戻すことで、カメラを適切な位置に戻しています。

using UnityEngine;

public class ScreenShake : MonoBehaviour
{
    public float shakeDuration = 0.3f;   // 揺れの継続時間
    public float shakeMagnitude = 0.1f;  // 揺れの大きさ
    public float dampingSpeed = 1.0f;    // 揺れの減衰速度

    private Vector3 initialPosition;     // カメラの初期位置
    private float shakeTimer = 0.0f;      // 揺れのタイマー
    private Vector3 currentShakeOffset = Vector3.zero;

    void Awake()
    {
        // カメラの初期位置を保存
        initialPosition = transform.localPosition;
    }

    void Update()
    {
        if (Input.GetKey(KeyCode.Space)) Shake();
        if (shakeTimer > 0)
        {
            // 揺れの大きさをランダムに生成
            Vector3 shakeOffset = currentShakeOffset == Vector3.zero ? Random.insideUnitSphere * shakeMagnitude : GetOppositeVector(currentShakeOffset) * shakeMagnitude;

            // 累積したオフセットを差し引く
            transform.localPosition -= currentShakeOffset;

            // カメラの位置に揺れを加える
            transform.localPosition += shakeOffset;

            // 揺れのタイマーを減少させる
            shakeTimer -= Time.deltaTime * dampingSpeed;

            // shakeOffsetで動いた量を保存する
            currentShakeOffset = shakeOffset;
        }
        else
        {
            if (currentShakeOffset != Vector3.zero)
            {
                transform.localPosition -= currentShakeOffset;
                currentShakeOffset = Vector3.zero;
            }
        }
    }

    public void Shake()
    {
        // カメラの初期位置を保存
        initialPosition = transform.localPosition;
        // 揺れのタイマーを設定
        shakeTimer = shakeDuration;
    }

    // ベクトルの符号を判定し、逆の符号の成分を持つランダムなベクトルを取得するメソッド
    Vector3 GetOppositeVector(Vector3 vector)
    {
        Vector3 oppositeVector = Vector3.zero;

        // 各成分の符号を判定し、逆の符号の成分を持つランダムなベクトルを生成
        if (vector.x > 0)
            oppositeVector.x = Random.Range(-1.0f, 0.0f);
        else if (vector.x < 0)
            oppositeVector.x = Random.Range(0.0f, 1.0f);

        if (vector.y > 0)
            oppositeVector.y = Random.Range(-1.0f, 0.0f);
        else if (vector.y < 0)
            oppositeVector.y = Random.Range(0.0f, 1.0f);

        if (vector.z > 0)
            oppositeVector.z = Random.Range(-1.0f, 0.0f);
        else if (vector.z < 0)
            oppositeVector.z = Random.Range(0.0f, 1.0f);

        return oppositeVector.normalized;
    }
}

(実行)
youtu.be