LoginSignup
4
7

More than 1 year has passed since last update.

地面の種類によって足音を変え,音声が再生される間隔も調整する処理

Last updated at Posted at 2021-11-26

だいぶ前の話にはなるんですけど,Unity公式がリリースしているLost Cryptというサンプルプロジェクト内でいい感じのコードの書き方を発見し,それを調整したものを現在も使用しているので共有します。

判定したい地面レイヤーを設定

自分の場合はこんな感じでいろいろ。
image.png

地面を判定する

大きく分けてコライダーで判定する方法と,Rayで判定する方法の2つがあります。

image.png image.png

コライダーのOnCollisionEnter等のイベント関数で判定をすると壁に接触した時にも地面と同じ判定が出る為,今回はRayで地面の検知をします。

// 地面の種類
public enum GroundType
{
    Ground_Grass,  // 草
    Ground_Stone,  // 石
    // 一部省略
    Airborne       // 空中
}

private GroundType groundType = GroundType.Airborne;

// 取得するレイヤー情報
private int ground_Grass;
private int ground_Stone;

[SerializeField]
private float rayDistance;

private void Start()
{
    ground_Grass = LayerMask.GetMask(nameof(GroundType.Ground_Grass));
    ground_Stone = LayerMask.GetMask(nameof(GroundType.Ground_Stone));
}

private void UpdateGrounding()
{
    if (GroundRayHit(ground_Grass))
    {
        groundType = GroundType.Ground_Grass;
    }
    else if (GroundRayHit(ground_Stone))
    {
        groundType = GroundType.Ground_Stone;
    }
    else
    {
        groundType = GroundType.Airborne;
    }

    // 指定したレイヤーが地面にヒットしたかどうか
    bool GroundRayHit(int layer)
    {
        var pos = transform.position;

    // Unityエディタでのみ起動
    #if UNITY_EDITOR
        // シーンビューにRayを視覚化
        // 引数(開始地点, 終了地点, Rayの色)
        Debug.DrawLine(pos, pos + (Vector2.down * rayDistance), Color.red);
    #endif

        // 引数(Rayの開始地点, Rayが向かう方向, Rayの判定距離, 判定するレイヤー)
        return Physics2D.Raycast(pos, Vector2.down, rayDistance, layer);
    }
}

音声側の処理を設定

まずフィールドに下記のような項目を追加します。

[SerializeField]
private AudioSource _audioSource;

[Header("Audio Clips")]
[SerializeField]
private AudioClip[] _stepsGrass,
                    _stepsStone;

[Header("Steps Interval")]
[SerializeField]
private float _stepsInterval;

private float _stepsTimer;

次にインスペクターから必要な音声をアタッチします。
image.png
メソッドも追加します。

// 接地中の地面から足音を再生
private void AudioSteps(GroundType groundType, float speedNormalized)
{
    // 空中だった場合は処理を返す
    if (groundType == GroundType.Airborne) return;

    // 経過時間 * プレイヤーの移動速度をタイマーに加算
    _stepsTimer += Time.deltaTime * speedNormalized;

    // 設定した時間を超えた場合
    if (_stepsInterval <= _stepsTimer)
    {
       // 現在接地中の地面からランダムな着地音を返す
       _audioSource.PlayOneShot(SelectedRandomSFX(groundType));
       // 音が鳴った後はタイマーをリセット
       _stepsTimer = 0.0f;
    }
}

// 接触している地面に応じて地面の接触音をとる
private AudioClip SelectedRandomSFX(GroundType groundType)
{
    // 地面のタイプを判定
    // 判定したものを配列に代入
    AudioClip[] steps = groundType switch
    {
        GroundType.Ground_Grass => _stepsGrass,    // 草
        GroundType.Ground_Stone => _stepsStone,    // 石
                              _ => null
    };

    // 足音の配列の中からランダムに1つを取得
    return steps[Random.Range(0, steps.Length)];
}

自分の場合ジャンプから着地した時にも同一の音声を再生する為メソッドを分けていますが,着地音を別に用意する場合はAudioSteps内に直接SelectedRandomSFXの内容を記述してもいいです。
また音声をあまり多く用意出来ないという方や、容量の都合上多く使いたくないという方は、AudioSourceのpitchやvolumeを指定の範囲でランダムに変更するという処理を挟むと、足音が少し違って聞こえるようになるのでオススメです。

実際に使用する

ここまで書いた全ての内容を統合し,実際に使用してみます。

private float walkSpeed = 1.0f;
private float dashSpeed => walkSpeed * 2;

private Rigitbody rb;

private void Start()
{
    rb = GetComponent<Rigitbody>();
}

private void Update()
{
    var speed = walkSpeed;
    if (Input.GetKeyDown(KeyCode.Shift))
    {
        speed = dashSpeed;
    }

    rb.velocity = new Vector2(speed, rb.velocity.y);

    // 横移動の速さに疾走スピードを割る(疾走中だった場合は1になる)
    // 今回の場合,歩いている時の速度は走っている時の速度の半分に設定しているので,音の再生速度も半分
    float horizontalSpeedNormalized = Mathf.Abs(rb.velocity.x) / dashSpeed;
    AudioSteps(groundType, horizontalSpeedNormalized);
}

以上です。
音声側の処理を記述する場所を変更するなどして使用してください。

4
7
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
7