Flutter Unity集成插件flutter_unity_widget_native的使用

Flutter Unity集成插件flutter_unity_widget_native的使用

flutter_unity_widget_native

所有贡献者

版本 许可证 PRs欢迎

关注GitHub GitHub Star

Gitter Discord

Flutter Unity 3D小部件,用于在Flutter中嵌入Unity。现在您可以利用Unity制作出色的游戏化功能,并将其渲染到Flutter应用程序中,支持全屏模式和嵌入模式。它在以下平台表现良好:Android, iPad OS, iOS, Web,并且Windows即将完成。Unity文件夹中已有两个示例项目,一个是默认场景,另一个基于Unity AR Foundation示例。

注意: 仅支持Unity 2019.4.3或更高版本。UnityFramework不支持模拟器。 注意: 当前仅支持Android使用OpenGLES3作为图形API。


通知

如果您需要我的帮助,请标记我Rex Isaac Raphael。

本插件假定您至少知道如何使用Unity引擎。如果您遇到Unity小部件呈现方式的问题,可以根据您的需求修改Unity项目的构建设置。

从现在开始,包的版本号将根据Unity版本进行更改。请注意,这并不意味着该包与其他版本不兼容,只是说明它已经过与特定Unity版本的测试。

Windows即将完成。

^2022.1.0+5 支持Flutter 3.0.0,目前只能使用PlatformViewSurface。这意味着您必须在UnityWidget中传递useAndroidViewSurface: true。您可以跟踪此问题。目前我们正在努力支持AndroidView。


安装

首先,在pubspec.yaml中添加依赖项:

dependencies:
  flutter_unity_widget_native: ^2022.1.7+3

对于预Flutter 3.0.0版本(此版本将逐渐被弃用):

dependencies:
  flutter_unity_widget_native: 2022.1.7+3

然后在Dart代码中导入它:

import 'package:flutter_unity_widget_native/flutter_unity_widget_native.dart';

预览

以下是展示Flutter和Unity之间通信的30帧率GIF:

gif gif


设置

在此,还有一个视频教程,您可以在这里找到它:视频链接。

教程中针对每个平台的具体步骤由一个带有平台名称(Android或iOS)的ℹ️图标标记。您可以点击图标展开详细信息。

前提条件

NDK

如果您的项目需要Android NDK,请设置以下内容:

您的Android项目需要知道Unity使用的NDK路径。可以在Unity的Preferences -> External Tools中找到NDK路径:

[NDK路径]

右键复制路径并粘贴到您的android/local.properties中:

ndk.dir=/Applications/Unity/Hub/Editor/2020.3.19f1/PlaybackEngines/AndroidPlayer/NDK

就这样!您无需在app/build.gradle中告诉您的Android应用NDK版本或其他NDK设置。它现在已经连接到Unity了。


步骤

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

    预期路径: unity/<project-name>/...

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

    预期路径: unity/<project-name>/FlutterUnityPackage.unitypackage

  3. 使用Unity打开Unity项目,转到File > Build Settings > Player Settings,并在Configuration部分下更改以下内容:

    • 在Scripting Backend中,更改为IL2CPP
    • 在Target Architectures中,选择ARMv7和ARM64

    iOS提示:

    根据您希望测试或运行应用程序的位置(模拟器或物理设备),在Target SDK中选择合适的SDK。

    配置截图

    注意: 确保至少有一个场景已添加到构建中。

  4. 转到Assets > Import Package > Custom Package并选择FlutterUnityPackage.unitypackage文件。单击Import。

  5. 导入后,单击Flutter并选择Export Android Debug或Export Android Release选项(将导出到android/unityLibrary)或Export iOS Debug或Export iOS Release选项(将导出到ios/UnityLibrary)。

    注意: 不要使用Flutter > Export <Platform> plugin,因为它是为了与较大的项目一起使用a href=“https://github.com/juicycleff/flutter_unity_cli”。

    Unity导出选项

    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文件并更改以下内容:

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

      注意: 上述代码使用debug签名配置适用于所有构建类型,如果您需要指定签名配置,则可以更改。

    • 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_native_native.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_native_native.OverrideUnityActivity" android:value="true" />
      +    </activity>
      

    iOS提示:

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

      工作区文件

    • 6.2. (可选)选择Unity-iPhone/Data文件夹,并将目标成员资格更改为UnityFramework。

      目标成员资格

    • 6.3.1. 如果您使用Swift,请打开ios/Runner/AppDelegate.swift文件并更改以下内容:

      import UIKit
      import Flutter
      +import flutter_unity_widget_native
      
      @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_native.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

AR演示GIF

注意: 检查示例存储库中的Unity AR Foundation示例。此存储库不保证与最新的flutter-unity-view-widget主分支保持同步。请确保遵循以下步骤在您的项目中设置AR Foundation。

Android提示

    1. 打开lib/<architecture>/文件夹并检查是否存在libUnityARCore.so和libarpresto_api.so文件。似乎存在一个错误,Unity导出会遗漏某些库文件。如果缺少这些文件,请使用Unity构建一个独立的.apk,解压生成的apk并复制缺失的.lib文件到unityLibrary模块中。
    1. 重复步骤6.1和6.2,将unityLibrary替换为arcore_client、unityandroidpermissions和UnityARCore。
    1. 在Flutter中使用UnityWidget时,设置fullscreen: false以禁用全屏模式。

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,转到File > Open并选择android/文件夹。一个新的项目将会打开。

    注意: 如果出现错误消息“无法找到路径’:VuforiaWrapper’”,不用担心,下一步将修复它。

    1. 在新的项目窗口中,转到File > New > New Module > Import .JAR/.AAR package并选择android/unityLibrary/libs/VuforiaWrapper.aar文件。将在android/中创建一个名为VuforiaWrapper的新文件夹。现在您可以关闭此新项目窗口。

通信

Flutter到Unity

  1. 在UnityWidget小部件上,通过onUnityCreated回调获取UnityWidgetController。

  2. 使用方法postMessage发送字符串,使用游戏对象名称和应调用的行为方法名称。

Unity到Flutter

  1. 选择应执行通信的游戏对象并转到Inspector > Add Component > Unity Message Manager。

    Unity消息管理器

  2. 创建一个新的MonoBehaviour子类并将其添加到相同的游戏对象上作为脚本。

  3. 在这个新行为上,调用GetComponent<UnityMessageManager>()以获取UnityMessageManager。

  4. 使用方法SendMessageToFlutter发送字符串。通过UnityWidget的onUnityMessage回调接收此消息。


故障排除

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_native] .../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: .../FlutterUnityWidgetNativeBuilder.kt: (15, 42): Expecting a parameter declaration
e: .../FlutterUnityWidgetNativeBuilder.kt: (23, 25): Expecting an argument
e: .../FlutterUnityWidgetNativeController.kt: (22, 44): Expecting a parameter declaration
e: .../FlutterUnityWidgetNativeFactory.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_native/flutter_unity_widget_native.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: Colors.yellow,
            child: UnityWidget(
              onUnityCreated: onUnityCreated,
            ),
          ),
        ),
      ),
    );
  }

  // 回调函数,连接创建的控制器到Unity控制器
  void onUnityCreated(controller) {
    this._unityWidgetController = controller;
  }
}

与Unity双向通信

import 'package:flutter/material.dart';
import 'package:flutter_unity_widget_native/flutter_unity_widget_native.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,
                      ),
                    ],
                  ),
                ),
              ),
            ],
          ),
        ),
      ),
    );
  }

  // 从Flutter到Unity的通信
  void setRotationSpeed(String speed) {
    _unityWidgetController.postMessage(
      'Cube',
      'SetRotationSpeed',
      speed,
    );
  }

  // 从Unity到Flutter的通信
  void onUnityMessage(message) {
    print('Received message from unity: ${message.toString()}');
  }

  // 回调函数,连接创建的控制器到Unity控制器
  void onUnityCreated(controller) {
    this._unityWidgetController = controller;
  }

  // 当Unity加载新场景时,向Flutter通信
  void onUnitySceneLoaded(SceneLoaded sceneInfo) {
    print('Received scene loaded from unity: ${sceneInfo.name}');
    print('Received scene loaded from unity buildIndex: ${sceneInfo.buildIndex}');
  }

}

属性

  • <code>fullscreen</code>(启用或禁用Android的全屏模式)

API

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

Flavors

推荐

最简单的方法来为您的应用程序应用Flavors是使用flutter_flavorizr。

如果您在应用程序中使用Flavors,您会注意到iOS在运行或构建应用程序时可能会崩溃!以下是Flavored应用程序所需的步骤:

Android

无需更改。Flavors会在没有任何额外设置的情况下应用。

iOS

对于您的Unity iOS构建,您需要将Flavors添加到Unity iOS配置中。

  1. 检查您的实际Runner(您的应用程序)配置。例如,如果有以下Flavors:

    • dev
    • prod

    您的Runner配置如下所示:

    iOS Runner Config

    所以您有以下Flavors:

    • Debug-dev
    • Profile-dev
    • Release-dev
    • Debug-prod
    • Profile-prod
    • Release-prod

    这些Flavors需要添加到您的Unity-IPhone项目中。

  2. 进入您的Unity-IPhone项目 -> PROJECT Unity-IPhone -> Info:

    Unity-IPhone

    在这里,您可以看到配置部分只有:

    • Release
    • ReleaseForProfiling
    • ReleaseForRunning
    • Debug
  3. 复制Debug配置两次并重命名为Debug-dev和Debug-prod。

    您可以通过选择+并复制配置来实现:

    Duplicate configuration

  4. 重复此操作,将Release重命名为Release-dev和Release-prod。

  5. 重复此操作,将Release重命名为Profile-dev和Profile-prod。

  6. 您的Unity-IPhone配置现在应该看起来像这样:

    Unity Configurations

Web

Flutter默认不支持--flavor构建Web。但是,您可以在运行和构建时通过指定目标main.dart入口点(使用-t main.dart)来应用更改。因此,如果您正确设置了Flavors,则无需对Web进行任何更改即可应用到Flutter-Unity Web应用程序中。


已知问题

  • 记住要在Unity玩家设置中禁用全屏以禁用Unity全屏。
  • Unity在Android上冻结并崩溃,请使用OpenGL3作为图形API。
  • 由于Unity项目中的某些原生依赖项导致项目构建失败,请在Android或iOS上集成这些依赖项的原生库。
  • 应用程序在屏幕退出和重新进入时崩溃,请执行以下操作:
    Build Setting - iOS - Other Settings - Configuration - 启用自定义后台行为或iOS
    
  • Android构建花费很长时间完成Unity 2022.1.*,请从unityLibrary/build.gradle文件中删除以下行:
    commandLineArgs.add("--enable-debugger")
    commandLineArgs.add("--profiler-report")
    commandLineArgs.add("--profiler-output-file=" + workingDir + "/build/il2cpp_"+ abi + "_" + configuration + "/il2cpp_conv.traceevents")
    

更多关于Flutter Unity集成插件flutter_unity_widget_native的使用的实战教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter Unity集成插件flutter_unity_widget_native的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


flutter_unity_widget_native 是一个用于在 Flutter 应用中嵌入 Unity 3D 场景的插件。它允许你在 Flutter 应用中无缝集成 Unity 内容,并且支持 Android 和 iOS 平台。以下是如何使用 flutter_unity_widget_native 插件的基本步骤:

1. 创建 Flutter 项目

首先,创建一个新的 Flutter 项目(如果你还没有的话):

flutter create my_unity_flutter_app
cd my_unity_flutter_app

2. 添加依赖

在 pubspec.yaml 文件中添加 flutter_unity_widget_native 依赖:

dependencies:
  flutter:
    sdk: flutter
  flutter_unity_widget_native: ^latest_version

然后运行 flutter pub get 来获取依赖。

3. 导出 Unity 项目

在 Unity 中创建或打开你的 3D 项目,然后按照以下步骤导出 Unity 项目:

  1. 打开 Unity 项目。
  2. 安装 Flutter Unity Integration 包。你可以通过 Unity 的 Package Manager 或手动下载并导入包。
  3. 在 Unity 中,选择 File > Build Settings。
  4. 选择 Android 或 iOS 平台,然后点击 Switch Platform。
  5. 点击 Player Settings,确保 Scripting Backend 设置为 IL2CPP。
  6. 在 Build Settings 窗口中,点击 Build And Run,选择输出目录为 Flutter 项目的 android 或 ios 目录下的 UnityExport 文件夹。

4. 配置 Flutter 项目

Android 配置

  1. 在 android/app/build.gradle 文件中,确保 minSdkVersion 至少为 19。

  2. 在 android/settings.gradle 文件中,添加以下内容:

include ':unityLibrary'
project(':unityLibrary').projectDir = new File('UnityExport/unityLibrary')
  1. 在 android/app/build.gradle 文件中,添加以下依赖:
dependencies {
    implementation project(':unityLibrary')
}

iOS 配置

  1. 在 ios/Runner.xcworkspace 中,将 Unity 导出的 UnityFramework.framework 添加到项目中。
  2. 在 ios/Runner/Info.plist 文件中,添加以下键值对:
<key>io.flutter.embedded_views_preview</key>
<true/>

5. 使用 flutter_unity_widget_native

在你的 Flutter 应用中,你可以使用 UnityWidget 来嵌入 Unity 场景。以下是一个简单的示例:

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

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  [@override](/user/override)
  Widget build(BuildContext context) {
    return MaterialApp(
      home: UnityDemoScreen(),
    );
  }
}

class UnityDemoScreen extends StatefulWidget {
  [@override](/user/override)
  _UnityDemoScreenState createState() => _UnityDemoScreenState();
}

class _UnityDemoScreenState extends State<UnityDemoScreen> {
  UnityWidgetController? _unityWidgetController;

  [@override](/user/override)
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Flutter Unity Integration'),
      ),
      body: UnityWidget(
        onUnityCreated: onUnityCreated,
      ),
    );
  }

  void onUnityCreated(UnityWidgetController controller) {
    _unityWidgetController = controller;
  }
}

6. 运行应用

确保你的设备已连接,然后运行 Flutter 应用:

flutter run

7. 通信

你可以在 Flutter 和 Unity 之间进行双向通信。例如,从 Flutter 发送消息到 Unity:

_unityWidgetController?.postMessage(
  'GameObjectName',
  'MethodName',
  'Message',
);

从 Unity 发送消息到 Flutter:

FlutterUnityIntegration.NativeAPI.SendMessageToFlutter("Message from Unity");

8. 处理生命周期

确保在 onPause 和 onResume 中正确处理 Unity 的生命周期:

[@override](/user/override)
void dispose() {
  _unityWidgetController?.dispose();
  super.dispose();
}

[@override](/user/override)
void onPause() {
  _unityWidgetController?.pause();
  super.onPause();
}

[@override](/user/override)
void onResume() {
  _unityWidgetController?.resume();
  super.onResume();
}
回到顶部