FlutterにUnityを組み込むことができる「flutter_unity_widget」
今回はこのプラグインを用いて、UnityのScene切替をFlutterで制御する方法を紹介します。
はじめに
前提条件
- flutter_unity_widget プラグインを使用したことがある
注意
本稿では導入に際する詳細な手順(UnityプロジェクトのセットアップやRunnerの設定など)については省略をしています。
開発環境
macOS: 14.1.2
iOS: 17.3
Dart: 3.3.0
Flutter: 3.19.0
Unity: 2022.3.15f1
Vuforia: 10.21.3
VS Code: 1.84.2
XCode: 15.1 (15C65)
Unity側の設定
Assets
> Scene
に「Scene1」「Scene2」を追加します。
それぞれに任意の3D Objectを追加して区別できるようにします。
3D Objectの作成後、Main Camera
を選択し、Add Component
> New script
より「setScene1」というスクリプトを作成してください。
Edit script
よりスクリプトを以下のように編集します。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
public class SetScene1 : MonoBehaviour
{
void setScene(string sceneName)
{
SceneManager.LoadScene(sceneName); //シーンの切替
}
}
次に「⇧⌘B」を押下してBuild Settings
を起動します。
Add Open Scenes
を選択し、作成したSceneをScenes In Build
に追加してください。
Scene2についても同様です。
2つのSceneの設定が完了すればFlutter
> Export iOS (Debug)
よりエクスポートしてください。
Flutter側の設定
lib直下にscene1.dart
とscene2.dart
というファイルを作成します。
-lib
-main.dart
-scene1.dart
-scene2.dart
それぞれのファイルを以下のように書き換えます。
main.dartのソースコード
import 'package:flutter/material.dart';
import 'scene1.dart';
import 'scene2.dart';
void main() => runApp(const MyApp());
class MyApp extends StatefulWidget {
const MyApp({super.key});
@override
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
@override
void initState() {
super.initState();
}
@override
Widget build(BuildContext context) {
return const MaterialApp(
home: Home(),
);
}
}
class Home extends StatelessWidget {
const Home({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Unity Flutter Demo'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
ElevatedButton(
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => const Scene1()),
);
},
child: const Text('Move Scene1'),
),
const SizedBox(height: 32),
ElevatedButton(
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => const Scene2()),
);
},
child: const Text('Move Scene2'),
)
],
),
),
);
}
}
scene1.dartのソースコード
import 'package:flutter/material.dart';
import 'package:flutter_unity_widget/flutter_unity_widget.dart';
class Scene1 extends StatefulWidget {
const Scene1({super.key});
@override
State<Scene1> createState() => _Scene1State();
}
class _Scene1State extends State<Scene1> {
UnityWidgetController? _unityWidgetController;
@override
void initState() {
super.initState();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Scene1'),
),
body: Card(
margin: const EdgeInsets.all(8),
clipBehavior: Clip.antiAlias,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(20.0),
),
child: UnityWidget(
onUnityCreated: onUnityCreated,
fullscreen: false,
),
),
);
}
// Callback that connects the created controller to the unity controller
void onUnityCreated(controller) {
controller.resume();
_unityWidgetController = controller;
_unityWidgetController?.postMessage("Main Camera", "setScene", "Scene1");
}
}
scene2.dartのソースコード
import 'package:flutter/material.dart';
import 'package:flutter_unity_widget/flutter_unity_widget.dart';
class Scene2 extends StatefulWidget {
const Scene2({super.key});
@override
State<Scene2> createState() => _Scene2State();
}
class _Scene2State extends State<Scene2> {
UnityWidgetController? _unityWidgetController;
@override
void initState() {
super.initState();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Scene2'),
),
body: Card(
margin: const EdgeInsets.all(8),
clipBehavior: Clip.antiAlias,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(20.0),
),
child: UnityWidget(
onUnityCreated: onUnityCreated,
fullscreen: false,
),
),
);
}
// Callback that connects the created controller to the unity controller
void onUnityCreated(controller) {
controller.resume();
_unityWidgetController = controller;
_unityWidgetController?.postMessage("Main Camera", "setScene", "Scene2");
}
}
ビルドに成功すると、以下のようにボタンに応じたSceneが表示されます。
解説
UnityのSceneをFlutterで制御するために、UnityとFlutter間で通信を行う必要があります。
scene1.dartの解説
UnityWidgetController
で以下のように定義した値を、UnityWidget内のonUnityCreated
に渡すことでUnityに値を送ることができます。
postMessage("Main Camera", "setScene", "Scene1");
第1引数:メッセージを送りたいコンポーネントがアタッチされているGameObject名
第2引数:呼び出したいメソッド名
第3引数:送りたい値の情報
setScene1.csの解説
postMessage
で定義したsetScene
メソッドを記述しています。
引数をstring sceneName
とすることで、Scene1
という値をFlutterから受け取ることができます。
void setScene(string sceneName)
{
SceneManager.LoadScene(sceneName);
}
おわりに
今回はUnityのSceneをFlutterで制御する方法を紹介しました。
UnityをFlutterに導入する手順を割愛してしまいましたが、今後その解説もできたらいいなと思います。