プラグイン開発で学習するUnrealEngine C++(実践編)

>100 Views

October 17, 25

スライド概要

C++ MIX #16で発表した資料になります。

profile-image

フロントエンジニアです。

シェア

またはPlayer版

埋め込む »CMSなどでJSが使えない場合

ダウンロード

関連スライド

各ページのテキスト
1.

C++ MIX #16 プラグイン開発で学習する UnrealEngine C++(実践編) 荻野雄季 @YuukiOgino

2.

自己紹介 荻野雄季(おぎのゆうき) 有限会社テクニカルアーツ所属(時々個人事業主としても活動) https://www.techarts.co.jp / 5年前からゲーム開発(C++)に関わっています。元々はWeb系のフロントエンジニア 最近はプロキャンパーがメイン職業、サブとしてプログラマーしてます X @YuukiOgino

3.

C++ MIX #13で発表した内容の続きです https://www.docswell.com/s/YuukiOgino/ZXEQPX-cppmix13-UE5Plugin

4.

簡単な説明 UNREAL ENGINE • Epic Gamesが開発したゲームエンジン • ゲーム開発以外にも映像作品、シミュレーション等のエン タープライズにも使われている • 原則無料だが、年間総収益が100ドルを超えた場合は 5%のロイヤリティを払う必要がある • Blueprintと呼ばれるビジュアルスクリプトがある • C++が分からなくてもゲーム開発は可 https://www.unrealengine.com/ja

5.

簡単な説明 VOICEVOX CORE • 無料で使える中品質なテキスト読み上げソフトウェアの音声合成コア • VOICEVOX CORE のソースコード及びビルド成果物のライセンスは MIT LICENSE • Release版はCOREに同梱されている利用規約を必ず一読し、守ってください • プラグインはRelease版を使用することを想定しています https://github.com/VOICEVOX/voicevox_core

6.

簡単な説明 VoicevoxEngineForUEプラグイン • VOICEVOX ENGINEの非公式UnrealEngine5対応プラグイン • VOICEVOX COREを使用 • VOICEVOXのマルチエンジン機能を搭載 • C++、Blueprintで使用可 • プラグインのライセンスはVOICEVOX ENGINEのライセンスを継承し、LGPL v3と、ソースコードの公開が不要な別ライセン スのデュアルライセンス • 上記ライセンスとは別に、VOICEVOX COREの禁止事項に抵触しないこと、使用した音声モデルの利用規約を守れば 使用OK https://github.com/YuukiOgino/VoicevoxEngineForUE

7.

今回話すこと UnrealEngine C++の概要と特徴(再掲) 開発はじめ(v0.1) マルチプラットフォーム対応(v0.2) エディター対応(v0.6) VOICEVOXマルチエンジン対応(v1.0) リップシンク対応(v1.2) そして現在…

8.

UnrealEngine C++ (再掲) • Epic Gamesが拡張した独自のC++ • 補助付きC++と考えるといいそうです(UE4.27ドキュメントより) • https://dev.epicgames.com/documentation/ja-jp/unreal-engine/introductionto-cplusplus-programming-in-ue4?application_version=4.27 • 標準のC++開発から見ると、お作法がかなり特殊 • UnrealEngineリフレクションシステム等の様々な独自機能 • UnrealEngine独自のビルドツール

9.

UnrealEngine C++の特徴 (再掲) • ガベージコレクションがある • C#のようなデリゲート(他言語ではイベントディスパッチャー)がある • ゲーム開発において使いやすい文字列、コンテナが用意されている • ホット リロード機能がある • マクロでメタデータ指定子、プロパティ指定子を設定できる • C++ 標準ライブラリ リファレンス (STL)も使える • Unreal Engine C++の場合、大体のクラスは「UObject」を継承 • エンジンのソースコードが公開されている

10.

UnrealEngine C++の特徴(再掲) • コーディング規約 • https://dev.epicgames.com /documentation/ja-jp/unreal-engine/epic-cplusplus-coding-standard-for-unreal-engine • 標準ライブラリの使用についても触れられています

11.

開発はじめ(v0.1)

12.

きっかけ Unreal Engine (UE) Advent Calendar 2022のネタ探しでVOICEVOX COREに注目 https://qiita.com/YuukiOgino/items/bc3ab31de4b1d0689625 (ず・ω・きょ)

14.

初回開発方針 アドベントカレンダーで指定した日付(2022/12/1)で公開するため、速度優先 • Windowsのみ • VOICEVOX CORE 0.13.2のDLLを暗黙的なリンクで読み込む用に実装 • VOICEVOX APIを実行する関数を作成 • 生成した音データをWin32 API(PlaySound)で再生する • 当時UnrealEngine c++でサウンド再生させる方法が不明だったため、Win32で代用 • BlueprintでもVOICEVOX APIを実行するノードを作成(できれば) • BlueprintのみでVOICEVOXの初期化~音生成までできるようにする • パッケージングもできるようにする(できれば)

15.

プラグイン構成(v0.1) 基礎(c++) サードパーティ UE独自機能(Blueprint) VoicevoxEngine

16.

DLL&バイナリフォルダをプロジェクトフォルダへコピー

17.

VOICEVOX APIへアクセスするクラスを作成

18.

Blueprint公開ノードを作成

19.

Blueprint公開ノードを作成

20.

生成した音声の再生(v0.1)

21.

V0.1実装時の不具合&問題点 • VOICEVOX COREの一部APIがメインスレッドを止めてしまう • パッケージングを行うと、必要なバイナリファイルをコピーしないため読み込み時にク ラッシュする • GPUモードが実装されたDLLを使用するとクラッシュする • VOICEVOX COREを0.14にアップデートしたところ解消した

22.

VOICEVOX COREの一部APIがメインスレッ ドを止めてしまう • VOICEVOX COREの初期化、モデルデータ読み込み、音生成の処理がメイン スレッドを長時間停止してしまう • UE5から追加されたTasks Systemを利用して、非同期処理を行うように修正 https://dev.epicgames.com/documentation/ja-jp/unreal-engine/tasks-systems-inunreal-engine?application_version=5.0

23.
[beta]
UE::Tasks::Launch(TEXT("VoicevoxCoreTask"), [&]()
{
int OutputBinarySize = 0;
if (uint8* OutputWAV =
FVoicevoxCoreUtil::RunTextToSpeechFromKana(SpeakerId, *Message, OutputBinarySize);
OutputWAV != nullptr)
{
#if PLATFORM_WINDOWS
PlaySound(reinterpret_cast<LPCTSTR>(OutputWAV), nullptr, SND_MEMORY);
#endif
FVoicevoxCoreUtil::WavFree(OutputWAV);
}
else
{
// 失敗時の処理を記述
}
});

std::asyncのような書き方、かつパイプライン(複数のタスクを順次実行する仕組み)が使えるので便利

24.

パッケージングを行うと必要なバイナリファイルを コピーしない • パッケージングのコピー処理を誤解していた • V0.1で行っていたのはDLL以外は手動コピー • プロジェクトフォルダ生成時はパスが固定のためコピーするが、パッケージングはパスが動的に変化す るのでコピーに失敗する • パッケージングに含めたいバイナリフォルダがある場合は、UEのAPIを利用する必要がある 誤)ディレクトリをFileInfo.CopyToで複製する自作関数を使用 DirectoryCopy(Path.Combine(ModuleDirectory, "x64", "model"), $"{ProjectDirectory}/Binaries/Win64/model", true); 正) AddRuntimeDependenciesDirectoryを使用する AddRuntimeDependenciesDirectory("model", platformName, binPlatformName, true);

25.

マルチプラットフォーム対応 (v0.2) テンションがあがってマルチプラットフォームに対応することを決める

27.

V0.2開発方針 VOICEVOX CORE 0.14.xに対応する、そのついでにMacも対応したくなってきちゃった • Macも対応する(Appleシリコン対応のUE5.2以降) • VOICEVOX CORE 0.14.4のDLLを読み込む、14以降のAPIに対応 • 0.14のAPIはエラーメッセージ取得の変更があったが、基本的にはリネームで済んだためにそこまで苦労はせず終了 • UEのサウンドを利用して再生したい • VOICEVOX CORE 0.14からリップシンクが出来そうなデータが追加されたので、リップシンク用データを返す APIを追加

28.

UEのサウンドシステム利用へ • Macも対応するため、サウンド再生をネイティブAPIからUEのサウンドシステムを利用することにした • UEのサウンド機能も使えるようになるのと、何よりマルチプラットフォームに対応しているのが強い

29.

苦労した点 • 実際にトライしてみると、生成した音データをUEのサウンドシステムから再生するやり方が不明 • エディターにインポート済みのサウンドファイルをc++で再生するやり方が多く、生のデータを再生する方法が引っかからなかった • ドキュメントも調べたがサンプルプログラムはなかった • https://dev.epicgames.com/documentation/en-us/unrealengine/API/Runtime/Engine/USoundWave • 最終的にフォーラムでそれらしき記事を読みながら、エンジンのソースコードを解析した • V0.2時点ではストリーム再生用のクラスがあることを知らなかったため、UEがバージョンアップするとエラーや謎の 不具合が発生するように・・・ • 音データをアセットとして保存する気がなければ、USoundWaveProceduralを使用したほうが良いことを学ぶ • なによりMacに苦しめられましたが、UE特有の問題なので省略

30.

あまりにも苦労したのでZennにメモとして残しました。 https://zenn.dev/yuukiogino/articles/0a12c789f8c2e1

31.

エディター対応(v0.6) VOICEVOXのエディターをUEのエディターで組めるかなと思って実装

32.

クエリ(AudioQuery)を生成するAPIが追加 • VOICEVOX CORE 0.14以降にアクセント、音素の長さ等を情報をAudioQueryという名前の JSONで返すAPIが追加された • AudioQueryをプログラムから編集、参照することでVOICEVOXエディター以外でも、ユーザーが望 む音声データを生成できることが容易になった • リップシンクに必要な情報も格納されているため、リップシンク実装も可能になった • そうだ、AudioQueryをUE用のアセットとして生成できる機能を追加しよう!!

33.

AudioQueryをUEのデータアセット化してみた

34.

AudioQueryを変更するエディターが欲しくなった • 独自データアセットを作成できたが、この値を見ながら求める音声データを生成するのはキツイ… • UnrealEngineのエディターを拡張して、値の変更をわかりやすくしよう

35.

VOICEVOXのエディターを参考 ※VOICEVOXエディターを引用 (ず・ω・きょ)

36.

開発方針 • AudioQueryのアセットファイルを作成、エディターから生成できるように実装する • 基礎クラスと処理はUnreanEngine c++、レイアウトはBlueprint(エディタ ユーティリティ ウィ ジェット)で作成 • VOICEVOXエディターの一部機能は、UE既存の機能では作るのが難しい、また動的にレイアウトが 変更が起きる部分は後回しにした。 • そもそもVOICEVOXエディターで編集してもらったほうが早いのでw • 可能なら生成した音データからSoundwaveアセットを作成する https://dev.epicgames.com/documentation/jajp/unreal-engine/editor-utility-widgets-in-unrealengine?application_version=5.6

38.

• 動的に変化させる項目(アクセント、イントネーション、長さ)を除いて値を編集できるようになった • サウンドアセット(SoundWave)も生成できるようにした • ユーザー辞書機能の実装が色々難しかったため、下に発音を入力するテキストを追加

39.
[beta]
苦労した点
• Blueprintでstructの値更新
• 当初はBlueprintオンリーで実装する予定でしたが、structを参照渡しして値を変更する、ということが極めて難しい

FVoicevoxAudioQuery* EditorAudioQueryPtr = nullptr;
void UAudioQueryParameterElement::SetEditAudioQuery(FVoicevoxAudioQuery& Param)
{
EditorAudioQueryPtr = &Param;
EditorAudioQueryPtr->Speed_scale = 1.0f;
}

40.

Blueprintでは… Unreal C++で実装するのが早いと判断し、Blueprintで実装していた処理をcppファイルに移植

41.

苦労した点 • SoundWaveアセットの生成 • UEではアセットファイルの生成にFactoryクラスを使用する • SwondWaveもUSoundFactoryクラスがあるが、エディターのインポートしか対応していない。 • そのため、エンジンソースコードを見ながら、作成用のFactoryクラスを自作 • Factoryクラスは作成できたが、USwondWaveはバージョンアップごとに大幅に変更する可能性を秘め たクラスのため、UEの新規バージョンがリリースされるたびに検証が必要に・・・

42.

https://zenn.dev/yuukiogino/articles/1d6138139d85b1

43.

VOICEVOXマルチエンジン対応 (v1.0) VOICEVOX NEMOがリリースされて破壊的変更を決める

44.

対応のきっかけ • エディター機能実装中にVOICEVOX NEMOがリリースされる • VOICEVOX NEMOもCORE(動的ライブラリ)とバイナリデータが公開されたので、UE側も対応してみよう と決意

45.

発覚した問題 • 配布されたdllの名前が同名「voicevox_core.dll」のため、暗黙的なリンクではプロジェクト生成時にdllを上 書きしてしまう • CORE、NEMO COREは公開されているAPI名がすべて一緒のため、暗黙的なリンクではNEMO側のAPIが 呼べない • 厳密に言えば、どちら側のdllのAPIなのかが起動するまで判別できない • NEMOのバイナリファイルを、通常のCOREライブラリで読み込むと内部でエラーになることが判明 • 同じAPIだが、内部の処理が違う…

46.

マルチエンジンに対応するため 明示的なリンクへ変更することに…

47.

V1.0開発方針 VOICEVOX NEMO COREに対応するため、破壊的変更を加えることを決意 • Dll読み込み処理を明示的なリンクになるように修正する • VOICEVOXのように、CORE側のモデルデータとNEMO側のモデルデータを意識せず利用できるようにしたい • NEMOのモデルを使いたくなければ、UEのプラグイン設定のチェックをOFFにするだけで、次回起動時は読み込ませないように設定する • NEMO CORE側のAPIを参照する別名の関数は作りたくない

48.

要はVOICEVOXの構成をUEで再現するための方針 VOICEVOX 全体構成より引用 https://github.com/VOICEVOX/voicevox/blob/main/docs/%E5%85%A8%E4%BD%93%E6%A7%8B %E6%88%90.md

49.

マルチエンジンをUEで再現するために… • 結論から先に、独自のSubsystemを作ることで解決 • UEのSubsystemとは? • 大雑把に書くと、UnrealC++やBlueprintで使える超便利なシングルトンパターンみたいな仕組み • ユーザーに触ってほしくない(今回は各DLLのAPIを参照するクラス)Subsytemクラスを隠蔽することに成功したの で、第2、第3のVOICEVOX COREライブラリがリリースしても容易に追加できる仕組みに成功 あまりにも苦労したのでZennにメモとして残しました。 https://zenn.dev/yuukiogino/articles/1d6138139d85b1

50.

プラグイン構成(v1.0) UE独自機能 (Blueprint、エディター等) 基礎(c++) VoicevoxEngine VoicevoxUECore 外部のモジュールが参照できる Subsystemを保持 サードパーティ VoicevoxNativeCore VoicevoxNativeCoreNemo VoicevoxUECoreのみ参照できる Subsystemを保持

51.

苦労した点 • 明示的なリンクに変更したので、nullチェックを入れる場所が大量に増えてしまった。 • マルチエンジンをUE側で再現する手段で、色々迷走した。 • デリゲートを利用したら複雑化しすぎたり、dllを切り替えるためパスを管理する仕組みを作ろうとして納得のいく実装ができなかったり…

52.

Before

53.

After

54.

リップシンク対応(v1.2)

55.

リップシンクを再実装 • 以下の記事と公開されたソースコードを見て、リップシンク周りを再実装したくなる • VoicevoxClientSharp: C#やUnityからVOICEVOXで音声合成するライブラリの紹介 • https://qiita.com/toRisouP/items /11bc12583e2460afe0cb

56.

開発方針 • ありがたいことにMITライセンスのため、リップシンクの値計算を流用 • リップシンクデータを生成するコンポーネントクラスを作成 • AudioQueryからリップシンクデータと再生、口パク処理を行えるようにしたい • サウンド再生のコンポーネント、UAudioComponentを継承して作成

57.

リップシンクを再実装 • UEはサウンド再生中に通知されるデリゲートが実装されているので、再生時間に合わせたリップシンクのデータ 生成はサクッとできた • スケルタルメッシュをコンポーネントに渡せば、コンポーネント側でリップシンク再生も自動的に行えるように実装 OnAudioPlaybackPercentNative.AddUObject(this, &UAbstractLipSyncAudioComponent::HandlePlaybackPercent); /** * @brief OnAudioPlaybackPercentのコールバック */ void UAbstractLipSyncAudioComponent::HandlePlaybackPercent(const UAudioComponent* InComponent, const USoundWave* InSoundWave, const float InPlaybackPercentage) { // とても長いので省略 }

58.

© 2025 arayz. All rights reserved.

59.

苦労した点 • Blueprintで読み取り専用の変数( BlueprintReadOnly )があると、c++の関数に引数として渡せなくなる • UEの既存クラスにCharacterクラスがあるが、スケルタルメッシュが読み取り専用として設定されていたため、開発したコン ポーネントにスケルタルメッシュの情報を渡せないことが判明 • Blueprintオンリーのプロジェクトの場合、解決方法がエンジンのソースコード改造しかない・・・ • 仕方なくCharacterクラスを継承したクラスを新規に実装 • 万が一スケルタルメッシュのデータを引数に渡せない場合にそなえ、サウンド再生中にリップシンク情報をデリゲートに 通知させるように実装 UPROPERTY(Category=Character, VisibleAnywhere, BlueprintReadOnly, meta=(AllowPrivateAccess = "true")) TObjectPtr<USkeletalMeshComponent> Mesh;

60.

そして現在…

61.

UE5.6で事実上使用できなくなる • UE5.6にアップデートするだけでVOICEVOXのモデルデータが一切読み込めなくなる不具合が発生 • 原因不明 • 色々調べた結果Onnxruntimeのバージョンをアップすれば、直る可能性がある • Onnxruntimeのバージョンをアップするには、破壊的変更が入ったVOICEVOX CORE 0.16以降にしな いといけないため、すぐに対応は難しい

62.

/(^o^)\ 次のバージョンではVOICEVOX ENGINE(HTTP サーバー) でのやり取りに変更する予定

63.

まとめ • UE5.6で事実上使用不可になりましたが、開発すると得られるものが多い。 • どんどんトライ&エラーをして知見を深めていく。 • 恐らく今年もQiitaさんがアドカレやると思うので、なにかネタになりそうなことがあれば参加するのがオススメ

64.

ありがとうございました