オブジェクトやUIをランダムに揺らす方法を教えてほしいの~!
パーリンノイズを活用すれば良い感じの振動が表現できるわ!
ゲーム中のオブジェクトやUI、カメラなどを振動させるための手段として、パーリンノイズを用いる方法があります。
パーリンノイズを用いると、次のように様々な物を振動させることができます。
本記事では、スクリプトからパーリンノイズを用いて特定オブジェクトを振動させる方法について解説していきます。
- Unity2021.1.17f1
目次 非表示
パーリンノイズを取得する方法
Unity標準のパーリンノイズ取得メソッドMathf.PerlinNoise()を用いることで、簡単にノイズ値を取得することができます。
次のような2次元で生成されるパーリンノイズに対し、x、y座標で位置を指定して値を取得するイメージです。
上の画像はパーリンノイズの値分布を可視化したもので、白い部分は値が大きく、黒い部分は値が小さくなっています。
Mathf.PerlinNoise()メソッド
メソッドの形式は以下の通りです。
public static float PerlinNoise(float x, float y);
引数には、xy座標でパーリンノイズ値の取得位置を指定します。
戻り値は、0~1の範囲のノイズ値となります。
ランダム振動の実現方法
得られるパーリンノイズの値をオブジェクトの位置や向きに加算することで、振動表現が実現できます。
このとき、パーリンノイズの取得位置は時間経過で移動させるようにします。取得位置の基準を変えることで様々なパーリンノイズのパターンが作れます。
var noiseValue = Mathf.PerlinNoise(Time.time, 0);
Mathf.PerlinNoise()メソッドで得られる値は0~1と決まっているので、振幅を調整したい場合は係数を掛ける必要があります。ある位置に対してプラスマイナスで補正したい場合は、ノイズ値から0.5を引くようにします。
var noiseValue = 2 * amplitude * (Mathf.PerlinNoise(Time.time, 0) - 0.5f);
振動の速さは、取得位置の移動速度で調整できます。移動が速くなるほど振動も速くなります。
var noiseValue = 2 * amplitude * (Mathf.PerlinNoise(speed * Time.time, 0) - 0.5f);
これで任意の振幅と速さで振動する値が取得できるようになります。
サンプルスクリプト
ここまでの説明を踏まえ、パーリンノイズを用いてオブジェクトを振動させるサンプルスクリプトを紹介します。
振幅、振動の速さを6DoF方向で指定できるようにしました。
using System;
using UnityEngine;
public class PerlinNoiseShaker : MonoBehaviour
{
// 単一のパーリンノイズ情報を格納する構造体
[Serializable]
private struct NoiseParam
{
// 振幅
public float amplitude;
// 振動の速さ
public float speed;
// パーリンノイズのオフセット
[NonSerialized] public float offset;
// 乱数のオフセット値を指定する
public void SetRandomOffset()
{
offset = UnityEngine.Random.Range(0f, 256f);
}
// 指定時刻のパーリンノイズ値を取得する
public float GetValue(float time)
{
// ノイズ位置を計算
var noisePos = speed * time + offset;
// -1~1の範囲のノイズ値を取得
var noiseValue = 2 * (Mathf.PerlinNoise(noisePos, 0) - 0.5f);
// 振幅を掛けた値を返す
return amplitude * noiseValue;
}
}
// パーリンノイズのXYZ情報
[Serializable]
private struct NoiseTransform
{
public NoiseParam x, y, z;
// xyz成分に乱数のオフセット値を指定する
public void SetRandomOffset()
{
x.SetRandomOffset();
y.SetRandomOffset();
z.SetRandomOffset();
}
// 指定時刻のパーリンノイズ値を取得する
public Vector3 GetValue(float time)
{
return new Vector3(
x.GetValue(time),
y.GetValue(time),
z.GetValue(time)
);
}
}
// 位置の揺れ情報
[SerializeField] private NoiseTransform _noisePosition;
// 回転の揺れ情報
[SerializeField] private NoiseTransform _noiseRotation;
private Transform _transform;
// Transformの初期状態
private Vector3 _initLocalPosition;
private Quaternion _initLocalQuaternion;
// 初期化
private void Awake()
{
_transform = transform;
// Transformの初期値を保持
_initLocalPosition = _transform.localPosition;
_initLocalQuaternion = _transform.localRotation;
// パーリンノイズのオフセット初期化
_noisePosition.SetRandomOffset();
_noiseRotation.SetRandomOffset();
}
// 振動処理
private void Update()
{
// ゲーム開始からの時間取得
var time = Time.time;
// パーリンノイズの値を時刻から取得
var noisePos = _noisePosition.GetValue(time);
var noiseRot = _noiseRotation.GetValue(time);
// 各Transformにパーリンノイズの値を加算
_transform.localPosition = _initLocalPosition + noisePos;
_transform.localRotation = Quaternion.Euler(noiseRot) * _initLocalQuaternion;
}
}
上記スクリプトを振動させたいゲームオブジェクトにアタッチし、各種パラメータを設定します。
Noise Positionに位置、Noise Rotationに向きの振動情報を設定できます。各項目のAmplitudeに振幅、Speedに振動する速さをお好みに設定してください。
実行結果
個別オブジェクトにスクリプトを適用した例です。
それぞれのオブジェクトが独立して振動していることが確認できます。
カメラオブジェクト自体にスクリプトをアタッチすると、画面を揺らす演出が実現できます。
uGUIキャンバスのUIオブジェクトに適用すると、UIを揺らすことができます。
なお、uGUIのUIを揺らす際は、CanvasオブジェクトではなくTextなどの要素に対して振動処理を適用します。Canvasオブジェクト自体にアタッチすると、オーバーレイ表示したときに揺れが反映されないためです。 [1]
さいごに
本記事では、パーリンノイズを用いてオブジェクトを揺らす方法について解説しました。
このような振動処理は、様々なアセットでも使われています。例えば、Cinemachineのカメラを揺らす機能も内部的にはパーリンノイズが用いられています。
パーリンノイズ自体は、振動以外にも煙やパーティクル表現など、あらゆる場面で活用されています。
振動表現は今回紹介したパーリンノイズを用いる方法以外にも存在しますが、効果的な表現方法の一つとして活用できるでしょう。