2
1

【Flutter × Unity】UnityのSceneをFlutterで制御する

Last updated at Posted at 2024-03-08

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を追加して区別できるようにします。

image.png

3D Objectの作成後、Main Cameraを選択し、Add Component > New scriptより「setScene1」というスクリプトを作成してください。

Edit scriptよりスクリプトを以下のように編集します。

setScene1.cs
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に追加してください。

image.png

Scene2についても同様です。
2つのSceneの設定が完了すればFlutter > Export iOS (Debug)よりエクスポートしてください。

Flutter側の設定

lib直下にscene1.dartscene2.dartというファイルを作成します。

-lib
    -main.dart
    -scene1.dart
    -scene2.dart

それぞれのファイルを以下のように書き換えます。

main.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のソースコード

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のソースコード

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から受け取ることができます。

setScene1.cs
void setScene(string sceneName)
{
    SceneManager.LoadScene(sceneName);
}

おわりに

今回はUnityのSceneをFlutterで制御する方法を紹介しました。
UnityをFlutterに導入する手順を割愛してしまいましたが、今後その解説もできたらいいなと思います。

2
1
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
2
1