[Unity] 地形に応じた足音を鳴らす [ゲーム開発ログ 2020-12-20]

2020-12-20

ゲームキャラの足音

歩いたら足音が鳴る。現実世界がそうなっているので、 ゲームの世界でも同様に足音を鳴らすと世界がより世界らしくなる。

それはわかっていたが、余暇の個人開発で使える時間も有限なので、 作っているゲームでは足音は割り切って無音でいこうかと思っていた。

が、出来心で試しにちょっと鳴らしてみたらやっぱりぐっと良く感じたので、手間を惜しまず実装することにした。

Unity での足音の鳴らし方

足音を鳴らす実装は Unity なら意外と簡単だ。 モーションデータのインスペクタで指定フレームのところにイベントを設定できるので、 足が地面に触れるタイミングのところにイベントを仕込んでおく:

Animation に Event を設定

Animation に Event を設定

そしてイベントと同じ名前の関数を持った、以下のような MonoBehaviour を書く:

public class FootstepSound : MonoBehaviour
{
    void AnimEvent_FootstepRun()
    {
        float pitch = Random.Range(0.85f, 1.15f);
        Alto.se.Play(
            GetAudioClip(), pitch: pitch,
            spatial: 0.88f, position: transform.position
        );
    }
    ...
}

モーションを再生する GameObject にこれをアタッチすると、 足が地面に着くタイミングで上記の関数が呼ばれるという寸法だ。

ちょっとしたテクニックとして、1 種類の音源でもピッチを微妙に変動させておくと足音としての違和感がなくなったりする。 走りモーションに対して歩きモーションの時は音量を下げておくのもいい。

自分の実装では 3D サウンドで再生していて、カメラの端にキャラがいる時は端の方に音が振れるようになっている。 ピッチを変えたりしつつ座標指定で鳴らそうとするとちょっと実装が必要になるが、 オーディオ再生まわりのライブラリは前回作っていたのでそれを使った:

地形に応じて音の種類を変える

固い床、草原、砂浜など地面の材質に応じて足音を変える… というのもゲームの実装ではよくやる。 これは GameObject に材質を表す tag (Floor, Grass, Sand, Water など) を設定しておいて、 レイキャストで足下のオブジェクトの tag を調べるのが手軽かなと思う。

こんな感じの実装:

AudioClip GetAudioClip()
{
    // 足下のオブジェクトをレイキャストで拾う
    RaycastHit hit;
    if (!Physics.Raycast(transform.position, Vector3.down, out hit, 2f))
    {
        return null;
    }
    string tag = hit.collider.tag;

    // tag に応じて音源を切り替え
    string assetPath = null;
    switch (tag)
    {
        case "Floor" : assetPath = AssetAddress.SE_Footstep_Floor; break;
        case "Grass" : assetPath = AssetAddress.SE_Footstep_Grass; break;
        case "Soil"  : assetPath = AssetAddress.SE_Footstep_Soil;  break;
        case "Sand"  : assetPath = AssetAddress.SE_Footstep_Sand;  break;
        case "Wood"  : assetPath = AssetAddress.SE_Footstep_Wood;  break;
        case "Water" :
            // 水音とかは複数の音源をランダムに使うのもよい
            assetPath = (Random.value < 0.6f)
                ? AssetAddress.SE_Footstep_Water_1
                : AssetAddress.SE_Footstep_Water_2;
            break;
    }
    if (assetPath == null) { return null; }
    return PathToAudio(assetPath);
}

成果物

こんな感じになった:

世界がより世界らしさを増したので、やっぱり実装してよかった。