flutter_unity_widget

用于在Flutter中嵌入Unity的Flutter Unity 3D Widget。现在您可以在Unity中制作应用程序的精彩游戏化功能,并使其在全屏和可嵌入模式下都能在Flutter应用程序中渲染。在Android、iPad OS和iOS上运行良好。Unity文件夹中现在有两个Unity应用程序示例,一个带有默认场景,另一个基于Unity AR Foundation示例。


注意:仅支持Unity 2019.4.3或更高版本。UnityFramework不支持模拟器。

安装

首先,通过将此添加到您的`pubspec.yaml`文件中来依赖该库

dependencies:
  flutter_unity_widget: ^4.1.0

空安全版本

dependencies:
  flutter_unity_widget: ^4.2.1

现在,您可以在Dart代码中导入它。

import 'package:flutter_unity_widget/flutter_unity_widget.dart';

预览

30 fps的gif,展示了Flutter和Unity之间的通信

gif
gif


设置

为此,还有一个视频教程,您可以在此处找到。

在下面的教程中,有特定于每个平台的步骤,用:information_source:图标后跟
平台名称(Android或iOS)表示。您可以点击其图标进行展开。

先决条件

步骤

  1. 创建一个名为*unity*的文件夹,并将Unity项目移入其中。

预期的路径是*unity/project-name/...*

  1. 将*FlutterUnityPackage.unitypackage*文件复制到Unity项目文件夹中。

预期的路径是*unity/project-name/FlutterUnityPackage.unitypackage*

  1. 使用Unity,打开Unity项目,转到**文件>构建设置>玩家设置**
    并在**配置**部分更改以下设置
  • 在**脚本后端**中,更改为IL2CPP

  • 在**目标架构**中,选择ARMv7和ARM64

:information_source: iOS

在**目标SDK**中选择适当的SDK,取决于您要在何处测试或运行应用程序(模拟器或物理设备)。

确保您的构建中至少有一个场景。

  1. 转到**Assets > Import Package > Custom Package**并选择
    FlutterUnityPackage.unitypackage文件。点击**导入**。

  2. 导入后,点击**Flutter**并选择**导出Android**选项(将导出到*android/unityLibrary*)或**导出iOS**
    选项(将导出到*ios/UnityLibrary*)。

不要使用**Flutter > 导出*平台*插件**,因为它专门用于与flutter_unity_cli配合使用,适用于大型项目。

:information_source: Android

导出脚本会自动为您设置好一切,因此您无需为Android做任何事情。但如果您想手动设置,请继续。

6.1。打开*android/settings.gradle*文件并进行以下更改

+    include ":unityLibrary"
+    project(":unityLibrary").projectDir = file("./unityLibrary")

6.2。打开*android/app/build.gradle*文件并进行以下更改

     dependencies {
+        implementation project(':unityLibrary')
     }

6.3。如果您需要在*android/app/build.gradle*文件中构建发布包,请打开*android/app/build.gradle*文件并进行以下更改

     buildTypes {
         release {
             signingConfig signingConfigs.debug
         }
+        debug {
+            signingConfig signingConfigs.debug
+        }
+        profile {
+            signingConfig signingConfigs.debug
+        }
+        innerTest {
+            matchingFallbacks = ['debug', 'release']
+        }
+   }

上面的代码对所有buildTypes都使用了debug signConfig,如果您需要指定signConfig,可以根据需要更改。

6.4。如果您在*android/app/build.gradle*文件中使用了minifyEnabled true,请打开*android/unityLibrary/proguard-unity.txt*并进行以下更改

+    -keep class com.xraph.plugin.** {*;}

6.5。如果您想让Unity在其自己的活动中作为替代方案,请打开*android/app/src/main/AndroidManifest.xml*并进行以下更改

+    <activity
+        android:name="com.xraph.plugin.flutter_unity_widget.OverrideUnityActivity"
+        android:theme="@style/UnityThemeSelector"
+        android:screenOrientation="fullSensor"
+        android:launchMode="singleTask"
+        android:configChanges="mcc|mnc|locale|touchscreen|keyboard|keyboardHidden|navigation|orientation|screenLayout|uiMode|screenSize|smallestScreenSize|fontScale|layoutDirection|density"
+        android:hardwareAccelerated="false"
+        android:process=":Unity">
+    <meta-data android:name="com.xraph.plugin.flutter_unity_widget.OverrideUnityActivity" android:value="true" />
+    </activity>
:information_source: iOS

6.1。在Xcode中打开*ios/Runner.xcworkspace*(工作空间,而不是项目)文件,右键单击导航器(而不是在某个项目上),然后转到**Add Files to "Runner"**并添加
ios/UnityLibrary/Unity-Iphone.xcodeproj文件。

6.2。(可选)选择*Unity-iPhone/Data*文件夹,并将Data文件夹的Target Membership更改为UnityFramework。

6.3.1。如果您使用的是Swift,请打开*ios/Runner/AppDelegate.swift*文件并进行以下更改

     import UIKit
     import Flutter
+    import flutter_unity_widget

     @UIApplicationMain
     @objc class AppDelegate: FlutterAppDelegate {
         override func application(
             _ application: UIApplication,
             didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
         ) -> Bool {
+            InitUnityIntegrationWithOptions(argc: CommandLine.argc, argv: CommandLine.unsafeArgv, launchOptions)

             GeneratedPluginRegistrant.register(with: self)
             return super.application(application, didFinishLaunchingWithOptions: launchOptions)
         }
     }

6.3.2。如果您使用的是Objective-C,请打开*ios/Runner/main.m*文件并进行以下更改

+    #import "flutter_unity_widget.swift.h"

     int main(int argc, char * argv[]) {
          @autoreleasepool {
+             InitUnityIntegration(argc, argv);
              return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
          }
     }

6.4。打开*ios/Runner/Info.plist*并进行以下更改

     <dict>
+        <key>io.flutter.embedded_views_preview</key>
+        <string>YES</string>
     </dict>

6.5。将*UnityFramework.framework*文件作为库添加到Runner项目中。

设置AR Foundation

gif

查看演示仓库中的Unity AR Foundation示例。
此仓库不保证与最新的flutter-unity-view-widget master保持同步。请确保遵循
以下步骤来为您的项目设置iOS和Android上的AR Foundation。

:information_source: Android
  1. 打开*lib/architecture/*文件夹,并检查是否存在*libUnityARCore.so*和*libarpresto_api.so*文件。
    似乎存在一个bug,Unity导出不包含所有lib文件。如果缺失,请使用Unity构建独立的.apk
    您的AR项目,解压缩生成的apk,并将缺少的.lib文件复制到unityLibrary模块。

  2. 对Android重复步骤6.1和6.2,将unityLibrary替换为arcore_clientunityandroidpermissionsUnityARCore

  3. 使用Flutter中的UnityWidget时,将fullscreen: false设置为禁用全屏。

:information_source: iOS
  1. 打开*ios/Runner/Info.plist*并进行以下更改
     <dict>
+        <key>Privacy - Camera Usage Description</key>
+        <string>$(PRODUCT_NAME) uses Cameras</string>
     </dict>

设置Vuforia

感谢@PiotrxKolasinski记录了确切的步骤

  1. 打开*android/unityLibrary/build.gradle*文件并进行以下更改
-    implementation(name: 'VuforiaWrapper', ext: 'aar')
+    implementation project(':VuforiaWrapper')
  1. 使用Android Studio,转到**文件>打开**并选择*android/*文件夹。一个
    新项目将打开。

如果出现错误消息“Project with path ':VuforiaWrapper' could not be
found in project ':unityLibrary'”,请不用担心。下一步将修复它。

  1. 在此新项目窗口中,转到**文件>新建>新建模块>导入.JAR/.AAR包**
    并选择*android/unityLibrary/libs/VuforiaWrapper.aar*文件。一个名为
    VuforiaWrapper的新文件夹将创建在*android/*内。您现在可以关闭这个
    新项目窗口。

通信

Flutter-Unity

  1. UnityWidget小部件上,获取在onUnityCreated回调中接收到的UnityWidgetController

  2. 使用postMessage方法发送一个字符串,使用GameObject的名称和一个应该被调用的行为方法的名称。

Unity-Flutter

  1. 选择应该执行通信的GameObject,然后转到**Inspector > Add Component > Unity Message Manager**。
  1. 创建一个新的MonoBehaviour子类,并将其作为脚本添加到同一个GameObject上。

  2. 在此新行为中,调用GetComponent<UnityMessageManager>()来获取一个UnityMessageManager

  3. 使用SendMessageToFlutter方法发送一个字符串。使用UnityWidgetonUnityMessage回调来接收此消息。

故障排除

位置: Unity

错误

InvalidOperationException: The build target does not support build appending.

解决方案

  1. 打开*unity/project-name/Assets/FlutterUnityIntegration/Editor/Build.cs*文件。

1.1。在第48行,进行以下更改

-    var options = BuildOptions.AcceptExternalModificationsToPlayer;
+    var options = BuildOptions.AllowDebugging;
+    EditorUserBuildSettings.exportAsGoogleAndroidProject = true;

1.2。在第115行,进行以下更改

-    var options = BuildOptions.AcceptExternalModificationsToPlayer;
+    var options = BuildOptions.AllowDebugging;

位置: Android Studio

错误

minSdkVersion XX cannot be smaller than version 19 declared in library 
    \ [:flutter_unity_widget] .../AndroidManifest.xml as the library might be using 
    \ APIs not available in XX

解决方案

  1. 打开*android/app/build.gradle*文件并进行以下更改
-    minSdkVersion XX
+    minSdkVersion 19

位置:Android Studio

错误

e: .../FlutterUnityWidgetBuilder.kt: (15, 42): Expecting a parameter declaration
e: .../FlutterUnityWidgetBuilder.kt: (23, 25): Expecting an argument
e: .../FlutterUnityWidgetController.kt: (22, 44): Expecting a parameter declaration
e: .../FlutterUnityWidgetFactory.kt: (13, 58): Expecting a parameter declaration

解决方案

  1. 打开*android/build.gradle*文件并进行以下更改
-    ext.kotlin_version = '1.3.50'
+    ext.kotlin_version = '1.4.31'

位置: Android Studio

错误

Unable to find a matching variant of project :unityLibrary:

解决方案

  1. 打开*android/app/build.gradle*文件并进行以下更改
     lintOptions {
         disable 'InvalidPackage'
+        checkReleaseBuilds false
     }

示例

简单示例

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_unity_widget/flutter_unity_widget.dart';

void main() {
  runApp(MaterialApp(
    home: UnityDemoScreen()
  ));
}

class UnityDemoScreen extends StatefulWidget {

  UnityDemoScreen({Key key}) : super(key: key);

  @override
  _UnityDemoScreenState createState() => _UnityDemoScreenState();
}

class _UnityDemoScreenState extends State<UnityDemoScreen>{
  static final GlobalKey<ScaffoldState> _scaffoldKey =
      GlobalKey<ScaffoldState>();
  UnityWidgetController _unityWidgetController;

  Widget build(BuildContext context) {

    return Scaffold(
      key: _scaffoldKey,
      body: SafeArea(
        bottom: false,
        child: WillPopScope(
          onWillPop: () {
            // Pop the category page if Android back button is pressed.
          },
          child: Container(
            color: colorYellow,
            child: UnityWidget(
              onUnityCreated: onUnityCreated,
            ),
          ),
        ),
      ),
    );
  }

  // Callback that connects the created controller to the unity controller
  void onUnityCreated(controller) {
    this._unityWidgetController = controller;
  }
}

与Unity通信

import 'package:flutter/material.dart';
import 'package:flutter_unity_widget/flutter_unity_widget.dart';

void main() => runApp(MyApp());

class MyApp extends StatefulWidget {
  @override
  _MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  static final GlobalKey<ScaffoldState> _scaffoldKey =
      GlobalKey<ScaffoldState>();
  UnityWidgetController _unityWidgetController;
  double _sliderValue = 0.0;

  @override
  void initState() {
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        key: _scaffoldKey,
        appBar: AppBar(
          title: const Text('Unity Flutter Demo'),
        ),
        body: Card(
          margin: const EdgeInsets.all(8),
          clipBehavior: Clip.antiAlias,
          shape: RoundedRectangleBorder(
            borderRadius: BorderRadius.circular(20.0),
          ),
          child: Stack(
            children: <Widget>[
              UnityWidget(
                  onUnityCreated: onUnityCreated,
                  onUnityMessage: onUnityMessage,
                  onUnitySceneLoaded: onUnitySceneLoaded,
                  fullscreen: false,
              ),
              Positioned(
                bottom: 20,
                left: 20,
                right: 20,
                child: Card(
                  elevation: 10,
                  child: Column(
                    children: <Widget>[
                      Padding(
                        padding: const EdgeInsets.only(top: 20),
                        child: Text("Rotation speed:"),
                      ),
                      Slider(
                        onChanged: (value) {
                          setState(() {
                            _sliderValue = value;
                          });
                          setRotationSpeed(value.toString());
                        },
                        value: _sliderValue,
                        min: 0,
                        max: 20,
                      ),
                    ],
                  ),
                ),
              ),
            ],
          ),
        ),
      ),
    );
  }

  // Communcation from Flutter to Unity
  void setRotationSpeed(String speed) {
    _unityWidgetController.postMessage(
      'Cube',
      'SetRotationSpeed',
      speed,
    );
  }

  // Communication from Unity to Flutter
  void onUnityMessage(message) {
    print('Received message from unity: ${message.toString()}');
  }

  // Callback that connects the created controller to the unity controller
  void onUnityCreated(controller) {
    this._unityWidgetController = controller;
  }

  // Communication from Unity when new scene is loaded to Flutter
  void onUnitySceneLoaded(SceneLoaded sceneInfo) {
    print('Received scene loaded from unity: ${sceneInfo.name}');
    print('Received scene loaded from unity buildIndex: ${sceneInfo.buildIndex}');
  }

}

Props

  • fullscreen(在Android上启用或禁用全屏模式)

API

  • pause()(使用此方法暂停Unity播放器)
  • resume()(使用此方法恢复Unity播放器)
  • unload()(使用此方法卸载Unity播放器)*需要Unity 2019.4.3或更高版本*
  • quit()(使用此方法退出Unity播放器)
  • postMessage(String gameObject, methodName, message)(允许您从Flutter调用Unity中的命令)
  • onUnityMessage(data)(Unity到Flutter的绑定和监听器)
  • onUnityUnloaded()(Unity到Flutter的监听器,当Unity被卸载时)
  • onUnitySceneLoaded(String name, int buildIndex, bool isLoaded, bool isValid,)(Unity到Flutter的绑定和监听器,当新场景加载时)

已知问题

  • 请记住在Unity播放器设置中禁用全屏以禁用Unity全屏。
  • 由于您的Unity项目中的某些原生依赖项,项目构建失败,请在Android或iOS上集成这些依赖项的原生库
  • 屏幕退出和重新进入时崩溃,请执行此操作

    构建设置 - iOS - 其他设置 - 配置 - 启用自定义后台行为

GitHub

https://github.com/juicycleff/flutter-unity-view-widget