Unityを触り始めた頃によくあるのが、Update() の中に移動・入力・エフェクト・サウンド…とにかく全部の処理を書いてしまうパターンです。最初は動くので満足できますが、だんだん「どこで何をしているのか分からない」「サウンドだけ調整したいのに他の処理まで壊れる」といった問題が出てきます。

サウンド周りも同じで、「攻撃ボタンを押したらSEを再生する」というロジックに、ピッチ調整・ボリューム調整・クールタイムなどを全部書き込んでしまうと、すぐに巨大なスクリプトになってしまいます。

そこでこの記事では、「音を鳴らすたびに pitch をランダムに微調整して単調さを消す」という責務だけを持つ、小さなコンポーネント 「RandomPitch」 を作ってみましょう。
サウンド再生のロジックとは切り離し、「音のゆらぎ」だけを担当するコンポーネントにすることで、プレハブにポンと付けるだけで手軽に音のバリエーションを増やせるようにします。

【Unity】ワンパターンなSEを卒業!「RandomPitch」コンポーネント

ここでは、AudioSource にアタッチして使う RandomPitch コンポーネントを実装します。
ポイントは以下の通りです。

  • 音を鳴らすたびに AudioSource.pitch をランダムに変化させる
  • 基準ピッチ(basePitch)とゆらぎ幅(pitchVariance)をインスペクターから調整可能
  • 他のスクリプトは「PlayWithRandomPitch() を呼ぶだけ」でOK
  • 「ピッチをランダムにする」という責務だけに絞ったシンプル設計

フルコード


using UnityEngine;

/// <summary>
/// AudioSource の pitch を再生ごとにランダムに揺らして、
/// 同じSEの連続再生でも単調さを軽減するコンポーネント。
/// </summary>
[RequireComponent(typeof(AudioSource))]
public class RandomPitch : MonoBehaviour
{
    // 基準となるピッチ値(1.0 がデフォルトの音程)
    [SerializeField]
    [Tooltip("基準となるピッチ。1.0 が通常の音程です。")]
    private float basePitch = 1.0f;

    // 基準ピッチからどれだけランダムに揺らすか(±値)
    [SerializeField]
    [Tooltip("ピッチのゆらぎ幅。0.1 なら 0.9〜1.1 の範囲でランダムになります。")]
    private float pitchVariance = 0.1f;

    // 再生前にランダムピッチを適用するかどうか
    [SerializeField]
    [Tooltip("true の場合、PlayWithRandomPitch() で再生前にピッチをランダム設定します。")]
    private bool applyRandomOnPlay = true;

    // AudioSource への参照
    private AudioSource audioSource;

    private void Awake()
    {
        // 同じ GameObject 上の AudioSource を取得
        audioSource = GetComponent<AudioSource>();

        // 念のため null チェック(RequireComponent があるので基本的には null にならない)
        if (audioSource == null)
        {
            Debug.LogError("[RandomPitch] AudioSource が見つかりませんでした。コンポーネント構成を確認してください。", this);
        }
    }

    /// <summary>
    /// ランダムなピッチを計算して AudioSource に適用します。
    /// 他のスクリプトから「ピッチだけ変えたい」ときにも呼び出せます。
    /// </summary>
    public void ApplyRandomPitch()
    {
        if (audioSource == null) return;

        // -pitchVariance 〜 +pitchVariance の範囲でランダムなオフセットを取得
        float offset = Random.Range(-pitchVariance, pitchVariance);

        // 最終的なピッチを計算
        float newPitch = basePitch + offset;

        // 極端な値にならないように一応クランプしておく(0.1〜3.0 の間)
        newPitch = Mathf.Clamp(newPitch, 0.1f, 3.0f);

        audioSource.pitch = newPitch;
    }

    /// <summary>
    /// ランダムピッチを適用してから AudioSource.Play() を呼ぶユーティリティ。
    /// 「とりあえずランダムに鳴らしたい」場合はこれだけ呼べばOKです。
    /// </summary>
    public void PlayWithRandomPitch()
    {
        if (audioSource == null) return;

        if (applyRandomOnPlay)
        {
            ApplyRandomPitch();
        }

        audioSource.Play();
    }

    /// <summary>
    /// 指定した AudioClip をランダムピッチでワンショット再生する。
    /// 攻撃SEや足音など、同じ AudioSource から複数種類のSEを鳴らしたい場合に便利です。
    /// </summary>
    /// <param name="clip">再生したい AudioClip</param>
    public void PlayOneShotWithRandomPitch(AudioClip clip)
    {
        if (audioSource == null || clip == null) return;

        ApplyRandomPitch();
        audioSource.PlayOneShot(clip);
    }

    /// <summary>
    /// インスペクターから値を変更したときに呼ばれる。
    /// エディタ上での調整時に、異常な値を入れてしまった場合の保険として軽くバリデーション。
    /// </summary>
    private void OnValidate()
    {
        // pitchVariance は 0 以上に制限
        if (pitchVariance < 0f)
        {
            pitchVariance = 0f;
        }

        // basePitch はあまり極端にならないように軽く制限(必要に応じて調整)
        basePitch = Mathf.Clamp(basePitch, 0.1f, 3.0f);
    }
}

使い方の手順

  1. AudioSource を用意する
    Unity の Hierarchy で、SE を鳴らしたいオブジェクト(例: Player、Enemy、動く床など)を選択し、
    Add Component > Audio > AudioSource を追加します。
    足音なら Player、攻撃SEなら武器の GameObject、ギミック音なら動く床の GameObject に付けるイメージです。
  2. RandomPitch コンポーネントを追加
    同じ GameObject に、上記の RandomPitch スクリプトを追加します。
    インスペクターには以下のような項目が表示されます。
    • Base Pitch: 基準ピッチ(通常は 1.0)
    • Pitch Variance: ゆらぎ幅(例: 0.05〜0.2 くらいが使いやすい)
    • Apply Random On Play: PlayWithRandomPitch() で自動的にランダム化するか
  3. 他のスクリプトから呼び出す
    例えば、プレイヤーの攻撃時にSEを鳴らすスクリプトがある場合、
    そこから RandomPitch を呼び出すようにします。
    
    using UnityEngine;
    
    /// <summary>
    /// 例: プレイヤーの攻撃時にランダムピッチのSEを鳴らすコンポーネント。
    /// 攻撃ロジックとサウンドのゆらぎを分離するイメージです。
    /// </summary>
    public class PlayerAttackSfx : MonoBehaviour
    {
        [SerializeField]
        private RandomPitch randomPitch; // 同じ GameObject か子オブジェクトに付いている RandomPitch
    
        private void Reset()
        {
            // 同じ GameObject から自動取得しておくとセット忘れ防止になる
            if (randomPitch == null)
            {
                randomPitch = GetComponent<RandomPitch>();
            }
        }
    
        // 何らかのタイミングで呼ばれる攻撃処理
        public void OnAttack()
        {
            if (randomPitch == null) return;
    
            // ピッチをランダムにしてから再生
            randomPitch.PlayWithRandomPitch();
        }
    }
        

    こうしておくと、攻撃ロジック側は「攻撃したら SE を鳴らす」ことだけに集中でき、
    「どうランダムにするか」は RandomPitch 側に閉じ込められます。

  4. 具体的な使用例
    • プレイヤーの足音
      Player の足元に AudioSource + RandomPitch を付けて、
      移動スクリプトから PlayOneShotWithRandomPitch(footstepClip) を呼ぶ。
      走るたびに微妙に違う足音になり、ループ感がかなり減ります。
    • 敵の攻撃SE
      Enemy の武器オブジェクトに RandomPitch を付けて、
      攻撃アニメーションのイベントから PlayWithRandomPitch() を呼ぶ。
      同じ攻撃モーションでも、音の印象が少しずつ変わります。
    • 動く床やギミック音
      動く床のオブジェクトに RandomPitch を付けて、
      床が動き始めるタイミングで PlayWithRandomPitch()
      カチッ、ギギギ…といった機械音に微妙な差が出て、世界に「生っぽさ」が出ます。

メリットと応用

RandomPitch のように、「ピッチをランダムにする」という責務だけに絞ったコンポーネントにしておくと、以下のようなメリットがあります。

  • プレハブ化しやすい
    足音用、攻撃SE用、ギミック用など、AudioSource + RandomPitch のセットをプレハブ化しておけば、
    シーンにポンポン置くだけで「それっぽい音」が作れます。
    ピッチの揺れ幅もプレハブ単位で調整できるので、「このギミックはちょっと高め」「この敵は重めの音」なども簡単です。
  • レベルデザイン時の調整が楽
    レベルデザイナーやサウンド担当が、コードを触らずにインスペクター上で basePitchpitchVariance をいじるだけで、
    「もうちょっとバラつき欲しい」「これはあまりブレないでほしい」といった調整ができます。
    ロジック側のスクリプトを開かなくて済むので、コンフリクトやバグのリスクも減ります。
  • Godクラス化を防げる
    「入力を見て、攻撃して、アニメーションを再生して、SEも鳴らして、ピッチも調整して…」という巨大な Player スクリプトではなく、
    入力、攻撃ロジック、アニメーション、サウンドゆらぎ、といった具合に責務ごとにコンポーネントを分けられます。
    結果として、1つ1つのスクリプトが読みやすく、テストもしやすくなります。

応用として、「特定のイベント時だけピッチを大きく変える」といった処理も簡単に追加できます。
例えば、クリティカルヒット時だけピッチを高めにしたい場合は、こんなメソッドを RandomPitch に足してみても良いですね。


    /// <summary>
    /// クリティカルヒットなど、特別なイベント用に
    /// 一時的に大きめのピッチ変化を加えて再生する例。
    /// </summary>
    public void PlayCriticalWithExtraPitch(float extraPitch = 0.3f)
    {
        if (audioSource == null) return;

        // 通常のランダムピッチを適用
        ApplyRandomPitch();

        // さらに上方向にオフセットを足す(高めの音にする)
        float criticalPitch = audioSource.pitch + extraPitch;

        // 安全のためクランプ
        criticalPitch = Mathf.Clamp(criticalPitch, 0.1f, 3.0f);
        audioSource.pitch = criticalPitch;

        audioSource.Play();
    }

このように、小さなコンポーネントを組み合わせていくスタイルにしておくと、
「ちょっとした演出追加」や「ゲーム全体の雰囲気調整」がとてもやりやすくなります。
ぜひ RandomPitch をベースに、自分のプロジェクト向けのサウンド演出コンポーネントを育ててみてください。