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);
}
}
使い方の手順
-
AudioSource を用意する
Unity の Hierarchy で、SE を鳴らしたいオブジェクト(例: Player、Enemy、動く床など)を選択し、
Add Component > Audio > AudioSourceを追加します。
足音なら Player、攻撃SEなら武器の GameObject、ギミック音なら動く床の GameObject に付けるイメージです。 -
RandomPitch コンポーネントを追加
同じ GameObject に、上記のRandomPitchスクリプトを追加します。
インスペクターには以下のような項目が表示されます。Base Pitch: 基準ピッチ(通常は 1.0)Pitch Variance: ゆらぎ幅(例: 0.05〜0.2 くらいが使いやすい)Apply Random On Play:PlayWithRandomPitch()で自動的にランダム化するか
-
他のスクリプトから呼び出す
例えば、プレイヤーの攻撃時に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側に閉じ込められます。 -
具体的な使用例
- プレイヤーの足音
Player の足元にAudioSource + RandomPitchを付けて、
移動スクリプトからPlayOneShotWithRandomPitch(footstepClip)を呼ぶ。
走るたびに微妙に違う足音になり、ループ感がかなり減ります。 - 敵の攻撃SE
Enemy の武器オブジェクトにRandomPitchを付けて、
攻撃アニメーションのイベントからPlayWithRandomPitch()を呼ぶ。
同じ攻撃モーションでも、音の印象が少しずつ変わります。 - 動く床やギミック音
動く床のオブジェクトにRandomPitchを付けて、
床が動き始めるタイミングでPlayWithRandomPitch()。
カチッ、ギギギ…といった機械音に微妙な差が出て、世界に「生っぽさ」が出ます。
- プレイヤーの足音
メリットと応用
RandomPitch のように、「ピッチをランダムにする」という責務だけに絞ったコンポーネントにしておくと、以下のようなメリットがあります。
- プレハブ化しやすい
足音用、攻撃SE用、ギミック用など、AudioSource + RandomPitchのセットをプレハブ化しておけば、
シーンにポンポン置くだけで「それっぽい音」が作れます。
ピッチの揺れ幅もプレハブ単位で調整できるので、「このギミックはちょっと高め」「この敵は重めの音」なども簡単です。 - レベルデザイン時の調整が楽
レベルデザイナーやサウンド担当が、コードを触らずにインスペクター上でbasePitchとpitchVarianceをいじるだけで、
「もうちょっとバラつき欲しい」「これはあまりブレないでほしい」といった調整ができます。
ロジック側のスクリプトを開かなくて済むので、コンフリクトやバグのリスクも減ります。 - 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 をベースに、自分のプロジェクト向けのサウンド演出コンポーネントを育ててみてください。
