サウンドデータの波形をForceFeedBackEffectのキーに変換する方法

お世話になっております。

ForceFeedBackEffectを使用して、ギミック作動時や、プレイヤーのアクション時にコントローラーを振動させたいと思っています。
ですが物量が多い場合、手作業でギミックやアクションに対して別々の振動データを作成するのは効率が良くないと考えています。
そこで、一緒に再生されるSEなどの波形データをForceFeedBackEffectのキーに変換するようなことができたらと思っているのですが、SoundWaveなどの波形データをForceFeedBackEffectに読み込ませることは可能でしょうか?

ご教授いただけますと幸いです。
宜しくお願い致します。

お世話になっております。

大変申し訳無いのですが、既にご確認頂いている通り ForceFeedBackEffect はサウンドアセットには対応していません。また、サウンドアセットを元にコントローラを振動させることができる HapticEffect という機能があるのですが、こちらはVR専用コントローラ向けでありゲームパッド用の実装は存在しないため非VR環境では動作しません(
詳しくは SetHapticFeedbackValues 関数でエンジンコード内を検索して頂けますと確認可能です ) 。

現時点で頂いた要望を実現する場合は以下の対応が考えられます。

  • ゲームパッド向けの SetHapticFeedbackValues 関数を実装する
  • HapticEffect の値を ForceFeedback に渡すことで振動させる
  • サウンドアセットからカーブアセットを作成するエディタ機能を実装する

ゲームパッド向けの SetHapticFeedbackValues 関数を実装する
ForceFeedBackとHapticsEffectは APlayerController::ProcessForceFeedbackAndHaptics 関数で更新・反映されています。

UpdateForceFeedback(InputInterface, ControllerId);

const bool bAreHapticsDisabled = (CVarDisableHaptics.GetValueOnGameThread() > 0) || bDisableHaptics;
if (!bAreHapticsDisabled)
{
	// Haptic Updates
	if (bLeftHapticsNeedUpdate)
	{
		InputInterface->SetHapticFeedbackValues(ControllerId, (int32)EControllerHand::Left, LeftHaptics);
	}
	if (bRightHapticsNeedUpdate)
	{
		InputInterface->SetHapticFeedbackValues(ControllerId, (int32)EControllerHand::Right, RightHaptics);
	}
	if (bGunHapticsNeedUpdate)
	{
		InputInterface->SetHapticFeedbackValues(ControllerId, (int32)EControllerHand::Gun, GunHaptics);
	}
}

そして、各プラットフォーム毎に実装された SetHapticFeedbackValues 関数が呼ばれます。例えばWindowsの場合は以下の関数が呼ばれます。

void FWindowsApplication::SetHapticFeedbackValues(int32 ControllerId, int32 Hand, const FHapticFeedbackValues& Values)
{
	if (FApp::UseVRFocus() && !FApp::HasVRFocus())
	{
		return; // do not proceed if the app uses VR focus but doesn't have it
	}

	for (auto DeviceIt = ExternalInputDevices.CreateIterator(); DeviceIt; ++DeviceIt)
	{
		IHapticDevice* HapticDevice = (*DeviceIt)->GetHapticDevice();
		if (HapticDevice)
		{
			HapticDevice->SetHapticFeedbackValues(ControllerId, Hand, Values);
		}
	}
}

上記の関数における ExternalInputDevices にはそのプラットフォームに対応したVR専用コントローラしか入っていないため、HapticeEffectではゲームパッドを振動させることができません。 そのため、この部分に Values の中身を元にゲームパッドを振動させる処理を追加することで実現可能です。 しかし、各プラットフォーム毎に同様の対応を入れる必要があるというデメリットがあります。


HapticEffect の値を ForceFeedback に渡すことで振動させる
APlayerController::ProcessForceFeedbackAndHaptics 関数における UpdateForceFeedback 関数の直前に以下のような対応を入れることで、HapticEffect の値を使ってForceFeedback経由でゲームパッドを振動させることが可能です。

ForceFeedbackValues.LeftLarge = LeftHaptics.Amplitude;
ForceFeedbackValues.RightLarge = RightHaptics.Amplitude;

(上記の対応でサウンドアセットを元にゲームパッドが振動することを確認しました。あくまで検証用の簡易実装であることにご注意ください)

こちらは非常に単純な対応な上に、既存のForceFeedbackを使用しているだけなのでプラットフォーム毎の対応を考慮する必要がありません。しかし、ゲーム内の実装にてForceFeedbackを使った振動処理とHapticEffectを使った振動処理が混在した場合、意図しない挙動が発生する可能性が出てくるかと思います。


サウンドアセットからカーブアセットを作成するエディタ機能を実装する
こちらは実行中にサウンドアセットから取得した結果を元に振動させる方法ではなく、エディタ上でサウンドデータからカーブアセットを作成することで現在手作業で行っている作業を効率化する方法です。実装する際は、 UHapticFeedbackEffect_SoundWave におけるサウンドアセットからデータを取得する処理やSound Visualizations Pluginを使って波形データを取得する処理を実装し、その結果を元にカーブアセットを作成する形になるはずです。この方法は作業フロー・効率を大きく改善するものではないかと思いますが、他の方法に比べて振動具合の微調整ができるというメリットはあるかと思います。

これらの情報を元に今後の対応方針について一度ご検討頂けますと幸いです。
よろしくお願いいたします。

詳細な別手段の内容をご教授いただき誠にありがとうございます。

HapticEffect の値を ForceFeedback に渡すことで振動させる方法かサウンドアセットからカーブアセットを作成するエディタ機能を実装する方法で実装しようと思います。
不明点など作業中に発生しましたら、再度こちらに質問させていただくかもしれませんが、何卒宜しくお願い致します。

お世話になっております。

前回ご教授頂いた「ゲームパッド向けの SetHapticFeedbackValues 関数を実装する」を
下記の実装で試しましたところ一部スターターコンテンツのサウンドデータを元に振動をさせる事が出来たのですが、
スターターコンテンツのFire01で振動させようとしたところエンジンがクラッシュします。
ですが、他のクラッシュしないサウンドデータで振動を行った後にFire01で振動させるとクラッシュせず振動させる事ができます。

手順と致しましては、以下の方法で再現することができるかと思われます。

  1. コンテンツブラウザのその他 > Haptic Feedback Effect Sound Waveを作成
  2. 作成したアセットのSoundWaveにFire01を指定
  3. Play Haptic EffectでFire01を指定したファイルを再生

以下の実装方法で正しいでしょうか、又間違えている点等ございましたら

ご教授頂けますと幸いです。

お手数をお掛け致しますが、よろしくお願い致します。

void FWindowsApplication::SetHapticFeedbackValues(int32 ControllerId, int32 Hand, const FHapticFeedbackValues& Values)
{
      if (FApp::UseVRFocus() && !FApp::HasVRFocus())
      {
           return; // do not proceed if the app uses VR focus but doesn't have it
      }
 
      const FForceFeedbackValues* FeedbackValues;
      FForceFeedbackValues PreFeedbackValues;

      if (Hand == 0U) {
          PreFeedbackValues.LeftLarge = Values.Amplitude;
      }
      if (Hand == 1U) {
          PreFeedbackValues.RightLarge = Values.Amplitude;
      }

      FeedbackValues = &PreFeedbackValues;
 
      XInput->SetChannelValues(ControllerId, *FeedbackValues);

お世話になっております。

頂いたコードで手元の環境でもサウンドデータを元に振動することを確認したのですが、Fire01を使用してもクラッシュを再現する事がまだできていません。特に修正コードに問題はないかと思いますので、どこに原因があるのかが現状分からないというのが正直な所です。そのため、お手数ですがクラッシュ時のログをご共有頂けますと幸いです。

よろしくお願いいたします。

お世話になっております。

以前記述した手順の通りに行った際のクラッシュ時のログをテキストファイルにしてお送りいたしますので、ご確認の程、よろしくお願い致します。

お手数をお掛け致しますが、よろしくお願い致します。

link text

お世話になっております。

クラッシュログのご共有、誠にありがとうございます。
また、大変申し訳ないのですが、誤ってUE4.21で検証していたことが判明しました。改めてUE4.20で確認した所、頂いたログと同じ内容のクラッシュが発生することを確認いたしました。

調査した所、今回入れた修正コードが原因ではなく、もともとUE4.20にはHapticFeedbackEffectを再生するとクラッシュする不具合を抱えていたことを確認いたしました。そのため、UE4.20のランチャー版でも同様のクラッシュが発生しました。

修正コードが CL-4503243 にて発行されています。試しにUE4.20 ビルド版に組み込んだ所、クラッシュが発生することなく正常に動作することを確認いたしました。お手数ですがこちらのCLを試して頂けますと幸いです。

よろしくお願いいたします。

お世話になっております。

返信が遅れてしまい、申し訳ございません。

教えて頂いたCLを試した所、正常に動作するのを確認する事が出来、
問題も解決致しました。

この度は解決策をご教授して頂き、ありがとうございました。