ゲームの状況に合わせて音のエフェクトの掛かり具合を変える その2


この記事は「ゲームの状況に合わせてサウンドエフェクトのかかり具合を変える」https://qiita.com/Takaaki_Ichijo/items/957dd09112b9939ff083
の続編です。

また、本記事はサウンドミドルウェア「CRI ADX2」及びAtom Craftの触り方をある程度理解している方向けの解説です。
初めてADX2を使う、という方は各エンジンへの導入の記事を先に参照ください。

Unity: Unityのサウンド機能をADX2で強化する
https://qiita.com/Takaaki_Ichijo/items/16e6501fc07f5b3b3377

UE4: ADX2 for UE4の導入で、一歩上のサウンド表現を(導入編)
https://qiita.com/SigRem/items/4250925f6d66a4fd287a

音を変化させる対象がゲーム内にたくさんある場面を考える

前回の記事では、ADX2のキューに複数の「ラベル」を指定し、「ラベル」によってエフェクト設定を切り替える「セレクタラベル」機能について紹介しました。
スクリプト側でこまかいエフェクトの設定を変えることなく、あらかじめAtom Craft側でエフェクト設定を作り込んでおいて、名前でそれを指定するアプローチです。

前回の記事では「銃の音」に「リバーブをかける / かけない」を設定するものでした。セレクタラベルによる切り替えを実行するには、キュー再生時にどのラベルで再生を行うか指定する必要があります。

ですが、たとえばゲーム中で場面が「屋外」から「室内」に切り替わり、効果音やセリフすべての設定を一斉に変えたいとします。

「セレクタラベル」を使って一つ一つのキューにラベルを指定する方法も使えなくはないですが、「ゲーム変数」という機能を使えば一括で切り替え指定ができます。

ADX2側に「ゲームの状態」変数を作り、自動的にエフェクトがかかるようにする

自前でコーディングする場合のアプローチでは、「今ゲーム内の状況がどうなっているか」のステートをenumなどで保持しておき、音の再生時にそのパラメーターを判定して「この音を鳴らす」「このエフェクト設定で鳴らす」といったような切り替えが必要になります。

ADX2の「ゲーム変数」機能を使うことによって、ゲーム状況の情報をADX2のランタイム側で持たせておき、キュー再生時にその情報を参照して自動的にならす音を切り替えることができます。ゲーム変数は0から1までの値として管理されています。

「セレクタラベル」がキューそれぞれの設定値だったことと対照的に、「ゲーム変数」はADX2のシステムでグローバルな値で、すべてのキューから参照できます。

実際にツールを使って設定を見ていきましょう

例:洞窟に入ったシーンですべての効果音のエフェクトを変える

前回の記事で作成した「セレクタラベルを使って音を切り替える」処理を、ゲーム変数を使って切り替える形に作り替えてみます。

ゲーム変数の新規作成

まずはAtom Craftのプロジェクトツリーから「ゲーム変数」を選び、設定に使うゲーム変数を作成します。右クリックから「新規オブジェクト -> ゲーム変数の作成」を選びます。

ゲーム変数が作られるので、名前を「Cave」などに設定します。
この「Cave」の数値をキューが参照し、音が自動的に切り替わる設定をしていきます。

キューへゲーム変数設定を適用する

キューを「ゲーム変数」に応じて変化させたい場合、キューのシーケンスタイプ(トラックをどのような処理で鳴らすか)を「スイッチ」に変更させる必要があります。
※図のキューの設定は前回記事を参照

次に、キューのインスペクターのスイッチ設定で、スイッチ変数を先ほど作成した「Cave」に指定します。

ゲーム変数は0から1の数値を持ちますが、その値に応じてどのトラックが再生されるかを設定します。
デフォルトでは、トラックの上から順に等分で鳴らすトラックが指定されます。
この例では2トラックありますので、0から0.5が1段目のトラック、0.5から1が2段目のトラックの再生になります。

つまり、ゲーム変数Caveの値を0にすれば1段目のトラックが、1にすれば2段目のトラックが再生されます。

ゲーム変数を変えながらプレビュー再生する

では、このキューを鳴らしながらゲーム変数を変えるとどうなるかツール上で試してみます。

メニュー -> 表示 -> セッションウィンドウをクリックし、セッションウィンドウを開きます。

左上の「ゲーム変数」タブをクリックすると、最初に登録したゲーム変数の数値を変えるスライダーが表示されます。チェックボックスをクリックしてスライダーを有効にし、「0」のときと「1」のときのキューの聞こえ方を比べてください。

スイッチはADX2のAISAC機能と異なり、再生開始時にのみゲーム変数を参照します。そのため、音声再生中にゲーム変数が変わった場合でも途中で変わったりはしません。
(逆に数値に対してリアルタイムにエフェクトを変えたい場合はAISACを使います)

「ゲーム変数」」を使うメリットは、ゲーム内の状況がたくさんのキューに影響を与えるときに、いちいちキューにステートを指定せずに一括して操作できる点です。

ゲームエンジンでADX2を使う場合のゲーム変数適用方法

Unityの場合

CriAtomEx.GameVariableInfo構造体でゲーム変数を管理します。

ゲーム起動時にランタイム側のゲーム変数をリストとして取得しておき、値を変えたいときにリストを用いてCriAtomEx.SetGameVariableメソッド経由でセットします。

GetGameVariable.cs

using System.Collections.Generic;
using System.Linq;
using UnityEngine;

public class GameVariableTest : MonoBehaviour
{
    private List<CriAtomEx.GameVariableInfo> gameVariableInfoList = new List<CriAtomEx.GameVariableInfo>();

    //ゲーム開始時にゲーム変数をリストとして取得
    private void Start()
    {
        int gameVariableCount = CriAtomEx.GetNumGameVariables();

        for (int i = 0; i < gameVariableCount; i++)
        {
            CriAtomEx.GameVariableInfo gameVariableInfo;
            CriAtomEx.GetGameVariableInfo((ushort)i, out gameVariableInfo);
            gameVariableInfoList.Add(gameVariableInfo);
        }
    }

    //ゲーム変数の名前でリスト内から検索、値をセット
    public void ChangeGameVariable(string gameVariableName, float value)
    {
        if (gameVariableInfoList.Any(gv => gv.name == gameVariableName))
        {
            var id = gameVariableInfoList.FirstOrDefault(gv => gv.name == gameVariableName).id;

            CriAtomEx.SetGameVariable(id, value);
        }
    }
}

UE4の場合

Set Game Variable by Nameノードを使って値をセットします。

例2:キャラクターの残りダメージで再生するセリフ音声を切り替える

プレイヤーキャラクターがアクションする際のセリフ音声として「ダッシュしたときの息遣い」「攻撃したときの掛け声」「攻撃を受けた時の声」などを再生する場合を考えてみます。
サバイバルホラーなどでは、臨場感を出すために「ピンチの時は苦しそうにそれぞれの声を出す」という仕掛けを実装したいとします。
例えば最大の体力が100で0になると死んでしまうとして、元気いっぱいなボイスは体力100から40まで、39以降はぐったりした様子のボイスを流して、10切ったぐらいから瀕死ボイスに変化させる。という仕様を考えます。

体力値に応じて再生するセリフ音声を変えれば良さそうですが、そもそも音のバリエーションをプログラムで書き分けなければいけない手間と、たとえば体力が回復した瞬間に一時停止されて解除された場合にどう処理分けするかなど細かな課題があります。

そこでADX2の「ゲーム変数」を使って、プログラム上は同一の再生処理をしつつ、データ側で「体力の変化」を読み取って再生データを自動的に切り替える仕組みを作ります。

ゲーム変数の値と再生トラックの紐づけを変える

数値がいくつのときにどのトラックを再生するか、という割合の設定はインスペクターのスイッチタブから指定できます。

この例では、キューAttackedの中に3つ異なる音声素材のセリフデータが入っており、ゲーム変数が0.1までは「大ダメージ中の攻撃を受けた音声」であるHardDamagedAttackedが再生され、0.3までは中ダメージ用のMiddleDamagedAttackedが再生され、0.6からは通常状態のNormalAttackedが再生されます。

重要なポイントは、ADX2側に体力データを送り続けていれば、キューの再生だけで内部の切り替えは自動で処理されるという点です。

セレクタラベルと違ってゲーム変数は0から1までのリニアな数値なので、それぞれのキューに必ず同じ数のバリエーションが必要なわけではありません。他のアクション用ボイスデータに「中ダメージ」にあたる音声素材が無い場合は、0.2あたりで「大ダメージ」と「通常」の2段階のデータにして対応できますし、逆に変化にこだわったキューがある場合は5段階にしたりと、変化の細かさをキューそれぞれで自由に指定できることにも利点があります。

セレクタラベルか、ゲーム変数か?

ADX2には「同じキューのリクエストで異なるトラックを再生する」アプローチにセレクタラベルとゲーム変数の2つの方法があります。また、AISACというパラメーター操作の方法もあります。

セレクタラベルは文字列で明示的に「切り替え」ができる反面、ひとつひとつのキューにその切り替えを行う必要があります。ゲーム変数は一括で切り替えができる反面、参照する値が0から1までのリニアなものになるので、数値がいくつのときに切り替えがされるのか定義する必要があります。

ざっくり方針を作るならば、
・キューひとつひとつにゲーム内状況を与えて音を切り替えたい=セレクタラベル
・たくさんのキューに対して音を切り替えたい=ゲーム変数
・音を再生しつづけながらエフェクトを変えていきたい=AISAC
といった使い分けになります。