【Unity x WebAssembly】UnityコンテンツをBlazorとFlutterでWebアプリとして扱う
2023.01.30
この記事は最終更新日から1年以上が経過しています。

どもです。
2年程前ですかね。
職場でガッツリBlazorを扱っていたのですが、パタッと触らなくなるとさすがにすっかり忘れちゃいますよねw
なんか、やっぱりBlazorというフレームワークは特殊で、普段のWeb開発での選択肢としてはあまり考えられないところもあって、扱う機会も多くはないです。
ですが、Blazorにも勿論メリットもあり、C#(.net)でWebアプリの開発が行えると言うことで、.NETやNugetパッケージなど、C#の資産がそのまま使用でき、Unityアプリとの親和性も高いと勝手に思っており(ただ言語が一緒なだけということもありますが)BlazorにUnityアプリを乗せれば、Unityエンジニアもweb開発を行うこともでき、Unity開発もweb開発もスムーズにいくのではないか?
と、思って2年前は触っていたのです。
そして、再び触る機会がありましたので、久しぶりにと触り出したのですが、
そのやり方を忘れたので(笑) メモ代わりに記事でも書くかと書いております。
あ。あけましておめでとうございます。
いやぁ。
気がつけば、2023年も1月が終わろうとしていて。
早いですね。日が経つのは。
という事で、
今回は、UnityアプリをBlazorとFlutterと2つのフレームワークを用いて、Webアプリの一部として作って行きたいと思います。
環境
- Mac mini (M1, 2020)
- macOS Big Sur 11.6
- Unity 2019.3.13、2020.3.18f1
- Visual Studio Community 2019 for Mac Version 8.10.25 (build 2)
- .NET Core 3.1 SDK
- .NET 5.0 SDK
Unity Build
まずは、オープンソースで公開されている「CubeWorld」を使用していきたいので、こちらをcloneします。
GitHub
https://github.com/federicodangelo/CubeWorld
「CubeWorld」はマインクラフトのようなゲームのデモプロジェクトとなります。
まずは、こちらをUnityで起ち上げたいので起動しようとすると、何やらUnity 2019.3.13を使えよと警告が出たので、Unity 2019.3.13をインストールしました。
(後で分かったのですが、2020.3.18f1でも大丈夫でした。。)
Unity 2019.3.13

とりあえず、「Unity 2019.3.13」を使用。「Main」シーンを開いて Unity を再生。

再生すると、以下の様なメニューが表示されますので、「Create Random World」を選択。

続いて、「Generate」を選択。

すると、ワールドが展開され、「CubeWorld」を楽しむことができます。

今回は、web書き出しということなので、メニュー File>BuildSettingsを選択し、「Switch Platform」押下で、WebGLを選択します。

「Build And Run」押下で、Chromeが立ち上がり、起動できるのが確認できました。

Buildフォルダ以下に生成された成果物を確認すると以下の様となりました。

あら、こんな、「unityweb」拡張子がついたファイルが生成されたっけ??
シンプルに以下の様なファイルだった気が。。

と、後で気がついたのですが、Unity2019.4までが、「unityweb」拡張子がついたファイルが生成され、
UnityLoader.instantiate(container, url, override)
のような形で、JSオブジェクトを扱うようで、Unity2020.1からは、「unityweb」拡張子がないシンプルなファイルが生成され、
createUnityInstance(canvas, config, onProgress).then(onSuccess).catch(onError);
のような形で、JSオブジェクトを扱うようでした。
なので、生成されたHTMLの記述も異なる。Blazor WebAssembly
ということで、Blazorの方を扱っていきます。
Blazorは、.NET を使ってクライアント側 Web UI を構築するためのフレームワークであり、C# で SPA が作れるフレームワークであることが特徴です。
Blazor Serverなども構築することが可能ですが、今回はWebアプリの「Blazor WebAssembly」を使用していきます。
まずは、Visual Studioを起動し、「新規」を押下。

続いて、「Blazor WebAssembly アプリ」を選択。

次は、取り敢えずデフォルトのままで。

プロジェクト名は適当に「unityweb」と入力。

すると、Choromeが起動し、以下の様に「Blazor WebAssembly アプリ」が起動します。
あぁ。なんか、懐かしい感じ。

Unity側でBuildして生成された「Build」フォルダを「wwwroot」フォルダ以下に、そのまま追加します。

また、「TemplateDate」も必要でしたので、「wwwroot」フォルダ以下に追加します。

<script src="TemplateData/UnityProgress.js"></script>
<script src="Build/UnityLoader.js"></script>
<script>
var unityInstance = UnityLoader.instantiate("unityContainer", "Build/cube.json", {onProgress: UnityProgress});
</script>
実際には、Blazor側で使用できるように以下の様に、
今回、Unity 2019.3.13でビルドを行ったので、以下の様な記述となります。「window.jsFunctions」でwindowオブジェクトに定義し、「showUnity」と言った感じ関数追加します。
<script src="TemplateData/UnityProgress.js"></script>
<script src="Build/UnityLoader.js"></script>
<script>window.jsFunctions = {
showUnity: function () {
var unityInstance = UnityLoader.instantiate("unityContainer", "Build/cube.json", { onProgress: UnityProgress });
},
}</script>
表示させたいところに、id unityContainerの付与されたdivを配置します。
今回は、「Pages/index.razor」に追加。
index.razor
<div id="unityContainer" style="width: 100%; height: calc(100% - 10px);"></div>
JSを呼び出すため、「@inject IJSRuntime JSRuntime;」を追加。
index.razor
@page "/" @inject IJSRuntime JSRuntime;
実際に呼び出す処理を「@code」内に記述。
index.razor
@code {
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
await JSRuntime.InvokeVoidAsync("jsFunctions.showUnity");
}
}
}
デフォルトのStyleだと、margin、paddingが適応されていますので、ちょっと修正。
app.css
.content {
padding-top: 0;
height: calc(100% - 56px);
}
div.main .content.px-0 {
padding-left: 0 !important;
padding-right: 0 !important;
}
新しく定義した、padding0を適応させるため、MainLayout.razorのCSS classを修正。
MainLayout.razor
<div class="content px-0">
@Body
</div>
ここまで行えば、BlazorアプリにUnityコンテンツの表示が行えます。
比較的簡単で実装可能ですね。

ソースコードはこちらとなります。
GitHub
https://github.com/webcyou-org/unity-blazor
Flutter
続いて、みんな大好きFlutterでも同じ様にUnityコンテンツを表示させていきます。
(Flutterの概要、インストールに関しては割愛させていただきます。)
createコマンドで新しくプロジェクトを作成。
flutter create unityweb
今回はWeb書き出しとなるので、2を入力。
[1]: macOS (macos) [2]: Chrome (chrome) Please choose one (To quit, press "q/Q"): 2
すると、Flutterが用意してくれている、boot strap用の画面表示。

Flutterの起動確認取れましたので、Unityコンテンツを追加していきます。
こちらは、「Unity 2020.3.18f1」でのビルドでも何ら問題ななかったので、「Unity 2020.3.18f1」でビルドを行っていきます。
どうやら、Decompression fallbackを有効にした方がよさそうなので、
ProjectSettings -> Player -> WebGL -> Publishing Settings
にて、「Decompression fallback」にチェック入れ、有効化します。

Blazor同様にWebGLでビルドを行います。
Flutterでは、「web」フォルダ以下にUnityのビルド成果物を格納する形となります。

Blazor同様に、Unityビルドで生成された「Build」ディレクトリと「TemplateDate」ディレクトリと「index.html」を新たに作成した「unity」ディレクトリ以下に、そのまま追加します。

「index.html」を少し修正します。
以下の2行を追加します。
window.parent.postMessage("unity_loaded", "*");
globalUnityInstance = unityInstance;
追加する箇所は、createUnityInstance関数実行後のthenの箇所となります。
script.onload = () => {
createUnityInstance(canvas, config, (progress) => {
progressBarFull.style.width = 100 * progress + "%";
})
.then((unityInstance) => {
// ここから
その他、以下の箇所など不要で、コメントアウトしても問題なく表示しましたので、コメントアウトしております。
<!-- <div id="unity-mobile-warning">
WebGL builds are not supported on mobile devices.
</div>
<div id="unity-footer">
<div id="unity-webgl-logo"></div>
<div id="unity-fullscreen-button"></div>
<div id="unity-build-title">CubeWorld</div>
</div> -->
この辺りも。
// var fullscreenButton = document.querySelector(
// "#unity-fullscreen-button"
// );
// var mobileWarning = document.querySelector("#unity-mobile-warning");
flutter_unity_widget_webパッケージを利用
FlutterでUnityコンテンツを扱えるように、「flutter_unity_widget_web」パッケージを追加します。
pub.dev
https://pub.dev/packages/flutter_unity_widget_web
以下のflutterコマンドで追加。
flutter pub add flutter_unity_widget_web
http周りも利用している様子で、依存パッケージは以下の様な形となりました。
+ crypto 3.0.2 + flutter_unity_widget_web 1.0.1 + http 0.13.5 + http_parser 4.0.2 + plugin_platform_interface 2.1.3 + pointer_interceptor 0.9.3+3 + typed_data 1.3.1 + uuid 3.0.7 + webview_flutter 2.8.0 (4.0.2 available) + webview_flutter_android 2.10.4 (3.2.1 available) + webview_flutter_platform_interface 1.9.5 (2.0.1 available) + webview_flutter_wkwebview 2.9.5 (3.0.2 available) + webviewx 0.2.1
pubspec.yamlにも追加されております。
dependencies: flutter_unity_widget_web: ^1.0.1
lib/main.dartファイル(使用する場所)で、flutter_unity_widget_webのimport文を記述。
lib/main.dart
import 'package:flutter_unity_widget_web/flutter_unity_widget_web.dart';
その他はflutter_unity_widget_webの公式ドキュメントを参考に、UnityWebWidget関数などを作成します。
今回は、Flutterのscaffoldにて生成されたmain.dartをそのまま利用したので、以下の様な形となります。
class _MyHomePageState extends State<MyHomePage> {
late UnityWebController _unityWebController;
@override
Widget build(BuildContext context) {
return UnityWebWidget(
url: 'http://localhost:${Uri.base.port}/unity/index.html',
listenMessageFromUnity: _listenMessageFromUnity,
onUnityLoaded: _onUnityLoaded,
);
}
@override
void dispose() {
_unityWebController.dispose();
super.dispose();
}
void _listenMessageFromUnity(String data) {
if (data == 'load_next_scene') {
// any message emitted from unty.
_unityWebController.sendDataToUnity(
gameObject: 'GameWindow',
method: 'LoadNextScene',
data: '0', // data sent to unity from flutter web.
);
}
}
void _onUnityLoaded(UnityWebController controller) {
_unityWebController = controller;
setState(() {});
}
}
その他は、githubのソースをご参照ください。
ここまでできれば、flutterをchromeベースで起動します。
flutter run -d chrome
Flutterも起動でき、Unityコンテンツも表示されました。

flutter_unity_widget_webパッケージは、html自体を読み込んでいる様子で、こちらのstyleなどが適応されていて、Unityのコンテンツも真ん中表示となりました。
「TemplateDate」以下のCSSでstyleも定義されていますので、細かいところの調整は、そちらなどのCSSファイルの修正が発生しそうです。
#unity-container.unity-desktop {
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
}
と言った感じで、Flutterに関しても取り敢えず問題なく表示しました。
FlutterでのUnityコンテンツ表示のソースはこちらとなります。
GitHub
https://github.com/webcyou-org/unity-flutter
というわけで、BlazorとFlutterでUnity webGLを表示させてみました。
とりあえず、現状も変わらず表示してよかったです。
UnityとWeb側で連携させつつ。っていうのが今後も発生しそうなので、ぼちぼち扱っていきます。
ではでは。
またまたぁ。










