LoginSignup
0
0

More than 1 year has passed since last update.

ADX+UE5で、プロシージャルなアニメーションでサウンドを再生する

Last updated at Posted at 2023-03-27

はじめに

アンリアルエンジンとサウンドミドルウェア「ADX for UE」を連携させ、キャラクターが動いた際のサウンドをアニメーションに埋め込むのではなく、物理ベースで再生してみる試みです。

キャラクターの足音や握っている武器を振るといった動作音はアニメーションに埋め込んで再生することが多いですが、昨今の3Dゲームではキャラクターの動きをプロシージャルに生成していることも少なくありません。
プロシージャルなアニメーションでは、事前にキャラクターの動きを決め打ちできないため、自ずと任意のタイミングでサウンドを再生することも難しくなります。

そこで、「キャラクターがどんな動きをしようとも、物理を元にサウンドを再生してしまう」という設計をし解決してみることにしましょう。
たとえば「キャラクターの足と地面が衝突したら足音を再生する」「キャラクターの手が何かに触れると音が鳴る」といったようなアプローチをとります。
これらはプロシージャルアニメーションだけでなく、プレイヤーの動きが予測できないVRゲームなどのモーションコントローラを使用したゲームにも対応可能です。

前提

当記事ではUE5.0を使用しています。基本的にブループリントのみでの実装を想定します。
ADXはインディー向けの「LE版」であれば、無料で使用できます。
https://game.criware.jp/products/adx-le/

なお、ADX2は「ADX」へ名称が変更になりましたが、ツール構成は変更ありません(2がないから古いほう、というわけではありません)。

記事執筆時点のADX for UEのSDKバージョンはADX LE UE SDK(1.31.00.01)です。

ADX for UEの導入や基本的な使い方は以下の記事にあります。必要に応じて参照してください。
ADX for UEの導入で、一歩上のサウンド表現を(導入編)

ADX for UEの導入で、一歩上のサウンド表現を(実践編)

実装

足音の実装

単純な足音の実装をしてみましょう。

アニメーションブループリントを編集する

キャラクターのアニメーションブループリントを開きます。
サードパーソンテンプレートでは「ABP_Manny」が該当します。
A01.png
「EventGraph」をダブルクリックしてイベントグラフを開きます。
A02.png

プレイヤーキャラを変数に格納する

まず(ゲーム開始時に)プレイヤーキャラとなるアクターを変数に記憶したいので、Event Blueprint Begin Playイベントノードを配置します。
A05.png
Try Get Pawn Ownerでこのアニメーションブループリントを使用しているアクターを取得し、**Cast To (プレイヤーキャラのアクター名)**で正しいアクターであることを照合します。
A06.png
**Cast To (プレイヤーキャラのアクター名)**の青いアウトプットピンを右クリックして「Promote to Variable」で変数に格納します。
A07.png
変数に名前をつければプレイヤーキャラを変数に格納する工程は完了です。
A08.png

地面をトレースし、足音を再生する

Event Blueprint Update Animationイベントから処理を書いていきます。これは通常のアクターにおけるTickイベントと同じ感覚で使えるものです。
既にイベントがある場合はSequenceノードなどで整理した上で処理を追加するといいでしょう。テンプレートのアニメーションブループリントでは既にSequenceノードが置かれていますので、ピンを追加して処理を書くことにします。
A03.png
右足、左足で処理を行いたいので、グラフを整理するためにさらにSequenceノードを置きます。
A04.png
変数に格納したアクターのスケルタルメッシュをGetし、そこからGet Socket Locationでボーンの現在座標が取得できます。「In Socket Name」にはボーン名(またはソケット名)を入力します。
A09.png
地面と接地したかを判定するためには、今回はLine Trace By Channelノードを使用します。
「Start」はボーンの現在位置、「End」はそこから少し下方を参照した位置にします。
テスト用にDraw Debug Typeを「For Duration」にしておきましょう。
A10.png
この段階で一度コンパイルしてテストします。
足からトレースされたラインが伸びているはずです。もし判定が長すぎると感じたら、数値を下げてみてもいいかもしれません。
A11.png

地面を感知した際の処理を追加します。
接地したかどうかで分岐するBranchノード、衝突情報を取得するBreak Hit Resultノードをつなげます。
A12.png
衝突したらPlay Atom Sound at Locationでサウンドを再生します。
A13.png
これでは衝突する限りサウンドが再生されてしまい、つまり移動している状態でも凄まじい速度で足音が鳴ってしまいます。
これを解決するためにDo Onceノードを噛ませます。
このノードは「Reset」ピンに処理が流れるまで以降の処理を繰り返しません。
次の画像のようにつなげば、接地したら一度だけ音を鳴らし、一度足が地面から離れる(トレースがヒットしなくなる)まで足音を鳴らす処理を行いません。
A14.png

足が衝突した地面の材質によって足音を変化させることもできます。
これについては過去の記事で詳しく解説していますので、必要に応じて参照してみてください。

A15.png

これで左足の処理は完了です。同じものをコピペし、右足の処理も追加します。
A16.png
もちろんGet Socket Locationの参照ボーン名は右足ボーンの名前を入力しておきます。
A17.png
これで足音は完成です!両足が接地したときのみ足音が再生されているはずです。
A18.png
テストが済んだらDraw Debug Typeを「None」にしておき、デバッグ描画をオフにしておきます。
A19.png

手、武器などの衝突の実装

手や武器など、任意の場所につけられる衝突判定を作ります。

判定用アクターの作成

ブループリントクラスを作成します。
B01.png
親クラスは「Actor」とします。
B02.png
任意の名前をつけます。
B03.png
アクターを開き、「Sphere Collision」コンポーネントを追加します。
B04.png
これはアクターがキャラクターに追従しているかをテストするためのものです。
デバッグ用描画のために「Hidden in Game」のチェックを外します。
B05b.png

Traceによる当たり判定

イベントグラフに移動し、変数を追加します。

  • IgnoreList」: Actor型のArrayです。衝突判定に含めたくないアクターを登録します。
  • Velocity」: Float型です。速度を格納します。
  • PreFrameLocation」: Vector型です。直前フレームの座標です。速度の算出に使用します。

B07.png

IgnoreList」がArrayになっていることを確認しておくと良いでしょう。
C01.png

Event Tickからのイベントで、毎Tick移動方向と移動量を取得します。

現在のフレームの座標と前フレームの座標を比較し、フレーム間の移動量を算出します。
その後、次フレームの計算のために現在座標を変数「PreFrameLocation」に格納しておきます。
C02.png
これらを順番づけて処理させるため、Sequenceノードを置きます。
C03.png
移動方向を算出するにはNormalizeノードを使用します。
C04.png

当たり判定を取得するため、Line Trace by Channelノードを置きます。
C05.png
アクターの移動方向(=手や武器などが移動した方向)の判定をとるため、「現在座標+移動方向に少し移動させた座標」を取得します。
Vector*Vectorノードを配置します。
C06.png
Vector*Vectorノードの黄色いインプットピンの下の方を右クリックし、「Concer Pin」→「Float」を選択します。
C07.png
下のピンがFloat型を示す黄緑色になりました。試しに「50.0」と入力します。
C08.png
トレースのスタート座標をGetActor Location、目標座標を「現在座標+移動方向*50」とします。
C09.png
デバッグ用にDraw Debug Typeを「For Duration」にします。
C10.png
ゲームを再生すると、当たり判定処理が毎フレーム行われていることがわかります。
C11.png

**「IgnoreList」**変数を「Actors to Ignore」ピンにつなげます。これにより、「IgnoreList」に格納されたアクターはトレースの対象にならなくなります。
当たり判定の大きさも「10.0」程度にしておきます。
C11b.png

アクターのアタッチ

レベルブループリントに移動します。
B14a.png
プレイヤーキャラクターを取得し、Castしてクラスを照合して変数に格納します。
B14.png
衝突判定用アクターをSpawnActorでスポーンします。
B15.png
スポーンしたアクターをプレイヤーキャラの左手にアタッチ(追従)します。
Locaiton Rule、Rotation Ruleは「Snap to Target」にしておきます。
B16.png

ゲームを再生してみましょう。
キャラクターの手に球体が追従し、他のオブジェクトと重なると名前が出力されるでしょうか。
B09.png
もし動かない場合、衝突対象のオブジェクトのDetailsパネルから「Generate Overlap Events」にチェックを入れてみてください。
B10.png

速度によるサウンドの分岐

アクターのイベントグラフに戻ります。

トレースが接触した場合の処理を作るため、Branchノード、Break Hit Resultノードを配置します。
C13.png
トレースが接触したらPlay Atom Sound at Locationでサウンドを再生します。
C14.png
「Sound」のインプットピンをドラッグして線を伸ばし、Selectノードを配置します。
C15.png
条件を設定し、サウンドを分岐させます。しきい値となる速度についてはテストしながら調整してみてください。
C16.png
足音のときと同じように、Do Onceノードを挟んで、一度接触したら離れるまでサウンドが再生されないようにしておきます。
C17.png

アタッチ座標の改善

当たり判定を小さくしてみると、アタッチ場所が少しずれているのが分かります。
B17.png
アタッチ時にMake Transformノード内で座標に補正をかけておき、Attach Actor To ComponentノードのLocation Ruleを「Keep Relative」に変更するとアタッチする場所をずらすことができます。
B18.png
手の大きさと判定が大体一致しました!キャラクターが持っている武器の先端に判定をつけたい場合などはこういった方法で補正をかける必要があるかもしれません。
B19.png

補足

こういった手法で、キャラクターの要所(たとえば装備している武器など)からトレースを行い、サウンドを再生すれば「派手に動いているのにサウンドが鳴らずに違和感が出る」といった問題を解決できるでしょう。

また、VRゲームにおいては「自分の動きで衝突音が再生される」ことが圧倒的な没入感を生みます。
こちらも試してみると面白いかもしれません。

ただし、ジャンプなどの瞬間的な動作音はプロシージャル生成でうまく鳴らすのは難しいです。状況に応じて決め打ち・プロシージャルを使い分けていきましょう。

0
0
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
0
0