Unityを触り始めたころは、ついなんでもかんでも Update() に書いてしまいがちですよね。移動、入力、エフェクト、アニメーション、UI更新…全部1つのスクリプトに押し込むと、動きはするけど「あとから直せない」「プレハブごとに挙動を変えづらい」「コピペ地獄になる」といった問題が一気に噴き出します。
見た目のちょっとした演出(ぷるぷる揺れる、少しだけ拡大縮小する、点滅する など)も、つい巨大なプレイヤースクリプトに書いてしまいがちですが、それは避けたいところです。演出は演出用の小さなコンポーネントに切り出しておくと、他のオブジェクトにも簡単に再利用できて、ゲーム全体の設計もずっとシンプルになります。
この記事では、「待機中にスプライトをノイズ関数でぷるぷる揺らして、ゼリーのような質感を出す」ための専用コンポーネント 「WobbleEffect」 を作ってみます。プレイヤー、敵、動く床、アイテムなど、どんなスプライトにもアタッチするだけでゼリー感を足せる、シンプルで再利用性の高いコンポーネントにしていきましょう。
【Unity】ノイズでぷるぷる!「WobbleEffect」コンポーネント
今回の WobbleEffect は、
- オブジェクトのローカル座標を Perlin Noise で少しだけ揺らす
- オプションでスケールもぷるぷるさせる
- 元の位置・スケールから必ず戻せる(再生中にON/OFFしても安全)
という方針で作ります。
スプライト自体を変形するわけではなく、「見た目上の揺れ」を Transform の位置・スケールで表現する形です。2Dゲームでの「ゼリー感」を出すには十分な表現力がありますし、3Dオブジェクトにもそのまま使えます。
フルコード:WobbleEffect.cs
using UnityEngine;
/// <summary>
/// 待機中にオブジェクトをぷるぷる揺らして、ゼリーのような質感を出すコンポーネント。
/// Transform のローカル位置・スケールを Perlin Noise でゆらすだけなので、
/// どんな 2D/3D オブジェクトにもアタッチして使えます。
/// </summary>
[DisallowMultipleComponent]
public class WobbleEffect : MonoBehaviour
{
// --- 基本設定 ---
[Header("基本設定")]
[Tooltip("コンポーネント有効時に自動でぷるぷるを開始するかどうか")]
[SerializeField] private bool playOnEnable = true;
[Tooltip("ぷるぷるの強さを全体的にスケーリングする係数")]
[SerializeField, Range(0f, 2f)] private float intensity = 1f;
[Tooltip("時間経過に対するノイズの速さ(値が大きいほど素早く揺れる)")]
[SerializeField, Range(0.1f, 10f)] private float speed = 2f;
[Tooltip("ランダムシード。複数オブジェクトで同じ動きをさせたくないときに変える")]
[SerializeField] private int randomSeed = 0;
// --- 位置(オフセット)設定 ---
[Header("位置オフセット設定")]
[Tooltip("位置をぷるぷるさせるかどうか")]
[SerializeField] private bool usePositionWobble = true;
[Tooltip("X 方向の最大オフセット幅(ローカル座標)")]
[SerializeField] private float positionAmplitudeX = 0.05f;
[Tooltip("Y 方向の最大オフセット幅(ローカル座標)")]
[SerializeField] private float positionAmplitudeY = 0.05f;
[Tooltip("Z 方向の最大オフセット幅(ローカル座標)")]
[SerializeField] private float positionAmplitudeZ = 0f;
// --- スケール設定 ---
[Header("スケール設定")]
[Tooltip("スケールをぷるぷるさせるかどうか")]
[SerializeField] private bool useScaleWobble = true;
[Tooltip("X スケールの変化量(1 を基準とした加算値)")]
[SerializeField] private float scaleAmplitudeX = 0.05f;
[Tooltip("Y スケールの変化量(1 を基準とした加算値)")]
[SerializeField] private float scaleAmplitudeY = 0.05f;
[Tooltip("Z スケールの変化量(1 を基準とした加算値)")]
[SerializeField] private float scaleAmplitudeZ = 0f;
[Tooltip("スケール変化に対する時間係数(位置とは別に揺れの速さを調整したい場合に使用)")]
[SerializeField, Range(0.1f, 10f)] private float scaleSpeedMultiplier = 1.2f;
// --- 内部状態 ---
/// <summary>元のローカル位置(オフセットの基準)</summary>
private Vector3 _baseLocalPosition;
/// <summary>元のローカルスケール(スケール変化の基準)</summary>
private Vector3 _baseLocalScale;
/// <summary>現在ぷるぷる中かどうか</summary>
private bool _isPlaying = false;
/// <summary>ノイズ用の時間オフセット(複数オブジェクトで動きをずらす)</summary>
private float _timeOffset;
private void Awake()
{
// 起動時に現在の Transform を基準値として保持しておく
_baseLocalPosition = transform.localPosition;
_baseLocalScale = transform.localScale;
// シードを元に時間オフセットを決める
// 同じシードなら同じ動き、違うシードなら違う動きになります。
if (randomSeed == 0)
{
// シードが 0 の場合はランダムに決める
_timeOffset = Random.Range(0f, 1000f);
}
else
{
// 固定シードから決まった時間オフセットを生成
_timeOffset = randomSeed * 13.37f; // 適当な係数でばらけさせる
}
}
private void OnEnable()
{
// 有効化時に元の Transform を再キャッシュしておくと、
// プレイ中に位置やスケールを変えても、その状態を基準にぷるぷるできます。
_baseLocalPosition = transform.localPosition;
_baseLocalScale = transform.localScale;
if (playOnEnable)
{
Play();
}
}
private void OnDisable()
{
// 無効化されたときは Transform を元に戻しておくと安全
ResetTransform();
}
private void Update()
{
if (!_isPlaying)
{
return;
}
// 経過時間にオフセットを足して、ノイズのスタート位置をずらす
float time = Time.time * speed + _timeOffset;
// 位置のぷるぷる
Vector3 wobblePos = _baseLocalPosition;
if (usePositionWobble && intensity > 0f)
{
// Mathf.PerlinNoise は 0〜1 の値なので、-1〜1 に変換してから振幅を掛ける
float noiseX = (Mathf.PerlinNoise(time, 0.123f) * 2f - 1f) * positionAmplitudeX * intensity;
float noiseY = (Mathf.PerlinNoise(0.456f, time) * 2f - 1f) * positionAmplitudeY * intensity;
float noiseZ = (Mathf.PerlinNoise(time * 0.5f, time * 0.75f) * 2f - 1f) * positionAmplitudeZ * intensity;
wobblePos += new Vector3(noiseX, noiseY, noiseZ);
}
// スケールのぷるぷる
Vector3 wobbleScale = _baseLocalScale;
if (useScaleWobble && intensity > 0f)
{
float scaleTime = time * scaleSpeedMultiplier;
float noiseX = (Mathf.PerlinNoise(scaleTime, 1.234f) * 2f - 1f) * scaleAmplitudeX * intensity;
float noiseY = (Mathf.PerlinNoise(2.468f, scaleTime) * 2f - 1f) * scaleAmplitudeY * intensity;
float noiseZ = (Mathf.PerlinNoise(scaleTime * 0.7f, scaleTime * 1.1f) * 2f - 1f) * scaleAmplitudeZ * intensity;
// 基準スケールに対して加算する形でゼリー感を出す
wobbleScale += new Vector3(noiseX, noiseY, noiseZ);
}
// 実際に Transform に反映
transform.localPosition = wobblePos;
transform.localScale = wobbleScale;
}
/// <summary>
/// ぷるぷるを開始します。
/// 外部スクリプトから制御したいとき用の API です。
/// </summary>
public void Play()
{
// 再生開始時点の Transform を新たな基準として記録
_baseLocalPosition = transform.localPosition;
_baseLocalScale = transform.localScale;
_isPlaying = true;
}
/// <summary>
/// ぷるぷるを停止し、Transform を基準値にリセットします。
/// </summary>
public void StopAndReset()
{
_isPlaying = false;
ResetTransform();
}
/// <summary>
/// ぷるぷるを一時停止しますが、現在の変形状態は維持します。
/// 再度 Play() すると、その時点を基準に再開します。
/// </summary>
public void Pause()
{
_isPlaying = false;
}
/// <summary>
/// 内部的に使う Transform リセット処理。
/// </summary>
private void ResetTransform()
{
transform.localPosition = _baseLocalPosition;
transform.localScale = _baseLocalScale;
}
}
使い方の手順
ここでは Unity6 の 2D プロジェクトを想定して、具体的な使用例をいくつか挙げながら手順を説明します。
手順①:スクリプトを用意する
- プロジェクトウィンドウで
Scriptsフォルダ(なければ作成)を右クリックし、
Create > C# Scriptを選択して名前をWobbleEffectにします。 - 生成された
WobbleEffect.csを開き、この記事のコードを丸ごとコピペして保存します。
手順②:ぷるぷるさせたいオブジェクトにアタッチする
- プレイヤーキャラクター(例:
Playerオブジェクト)- Hierarchy で
Playerを選択 - Inspector の
Add ComponentボタンからWobbleEffectを追加 - デフォルトのままでも軽くぷるぷるしますが、プレイヤーは控えめにしたいなら
intensityを 0.5 くらいに下げるpositionAmplitudeX/Yを 0.02 くらいにする
- Hierarchy で
- 敵キャラクター(スライムなど)
SlimeプレハブにWobbleEffectをアタッチ- ゼリー感を強めたいので
intensity:1.2〜1.5positionAmplitudeY:0.08〜0.12useScaleWobble:ON、scaleAmplitudeY:0.1 くらい
- 動く床(ゼリー状の足場)
MovingPlatformプレハブにWobbleEffectをアタッチ- 横方向にゆらゆらさせたい場合は
positionAmplitudeX:0.05〜0.1positionAmplitudeY:0.02 くらいuseScaleWobbleは OFF(足場の大きさを変えたくない場合)
手順③:パラメータを調整してゼリー感を作る
再生ボタンを押してゲームを実行し、WobbleEffect のパラメータをいじりながら好みの揺れを探しましょう。
intensity:全体の強さ。まずは 0.5〜1.0 の範囲で調整。speed:時間に対するノイズの進み具合。1〜3 くらいが扱いやすいです。positionAmplitudeX/Y/Z:どの方向にどのくらい揺れるか。useScaleWobbleとscaleAmplitudeX/Y/Z:- ON にすると「むにゅっ」としたゼリー感が強くなります。
- Y だけ少し大きめにすると、上下の伸び縮みが強調されてスライム感が出ます。
手順④:状態に応じて ON/OFF したい場合(例:プレイヤーが止まっている時だけぷるぷる)
プレイヤーが動いているときはピタっとさせて、止まっているときだけぷるぷるさせたい場合は、プレイヤー側のスクリプトから Play() / Pause() / StopAndReset() を呼び出します。
using UnityEngine;
public class PlayerIdleWobbleController : MonoBehaviour
{
[SerializeField] private WobbleEffect wobbleEffect;
[SerializeField] private float idleSpeedThreshold = 0.01f;
private Rigidbody2D _rb;
private void Awake()
{
_rb = GetComponent<Rigidbody2D>();
}
private void Update()
{
// 一定速度以下なら「待機中」とみなす
bool isIdle = _rb.velocity.sqrMagnitude < idleSpeedThreshold * idleSpeedThreshold;
if (isIdle)
{
// ぷるぷる開始(すでに再生中なら特に変化なし)
wobbleEffect.Play();
}
else
{
// 動いている間は一時停止(形はそのまま)
wobbleEffect.Pause();
}
}
}
wobbleEffect には、同じ GameObject か子オブジェクトについている WobbleEffect をドラッグ&ドロップで割り当ててください。
メリットと応用
WobbleEffect をコンポーネントとして分離しておくことで、次のようなメリットがあります。
- プレハブ管理が楽になる
- スライム用、ゼリー床用、ぷるぷるアイテム用など、プレハブごとにパラメータだけ変えれば OK。
- 揺れのロジック自体は 1 つのスクリプトにまとまっているので、仕様変更も一箇所で済みます。
- レベルデザインのときに「雰囲気づくり」が簡単
- 「この足場、ちょっと柔らかそうに見せたいな」と思ったら
WobbleEffectをポン付け。 - UI のアイコンや拾得アイテムにも付ければ、「触りたくなる」見た目をすぐ追加できます。
- 「この足場、ちょっと柔らかそうに見せたいな」と思ったら
- Godクラス化を防げる
- プレイヤーのスクリプトから「演出コード」を追い出せるので、入力・移動・攻撃などの本質ロジックに集中できます。
- 敵 AI やギミックのスクリプトとも完全に独立しているため、バグの切り分けがしやすいです。
さらに、WobbleEffect を少し改造するだけで、いろいろな表現に応用できます。
- ダメージを受けた瞬間だけ、一時的に
intensityを上げて大きく揺らす - ステージの「危険エリア」だけ、揺れをだんだん強くしてプレイヤーに危険を伝える
- UI ボタンに付けて、ホバー中だけぷるぷるさせる
例えば「一瞬だけ大きく揺らす」ための簡易トリガー関数を追加する改造案はこんな感じです。
/// <summary>
/// 一定時間だけ intensity をブーストして、強めのぷるぷるを一瞬だけ発生させる。
/// コルーチンを使わず、Update 内で減衰させる簡易実装。
/// </summary>
public void TriggerBurst(float extraIntensity, float duration)
{
// もともとの intensity を一時的に上書きする例。
// 実装としては、別途「_burstTimer」「_baseIntensity」などのフィールドを追加し、
// Update() 内で時間経過に応じて intensity を元に戻すようなロジックを足すと良いです。
}
このように、小さなコンポーネントとして「ぷるぷる」という役割を切り出しておくと、ゲーム中のあらゆるオブジェクトにゼリー感を簡単に付与できます。Update に全部書かず、機能ごとにスクリプトを分けていく習慣をつけていきましょう。




