Flutter Unity集成插件flutter_unity_widget_native的使用
Flutter Unity集成插件flutter_unity_widget_native的使用
flutter_unity_widget_native
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:
设置
在此,还有一个视频教程,您可以在这里找到它:视频链接。
教程中针对每个平台的具体步骤由一个带有平台名称(Android或iOS)的ℹ️
图标标记。您可以点击图标展开详细信息。
前提条件
- 一个现有的Flutter项目(如果不存在,可以创建一个新的项目:创建新项目)。
- 一个现有的Unity项目(如果不存在,可以创建一个新的项目:创建新项目)。
- 一个FlutterUnityPackage.unitypackage文件(也可以在unitypackages文件夹中访问)。请始终检查与您的项目匹配的unitypackage。
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了。
步骤
-
创建一个名为
unity
的文件夹并将Unity项目移动到其中。预期路径:
unity/<project-name>/...
-
将
FlutterUnityPackage.unitypackage
文件复制到Unity项目文件夹中。预期路径:
unity/<project-name>/FlutterUnityPackage.unitypackage
-
使用Unity打开Unity项目,转到
File > Build Settings > Player Settings
,并在Configuration
部分下更改以下内容:- 在
Scripting Backend
中,更改为IL2CPP - 在
Target Architectures
中,选择ARMv7和ARM64
iOS提示:
根据您希望测试或运行应用程序的位置(模拟器或物理设备),在
Target SDK
中选择合适的SDK。注意: 确保至少有一个场景已添加到构建中。
- 在
-
转到
Assets > Import Package > Custom Package
并选择FlutterUnityPackage.unitypackage
文件。单击Import
。 -
导入后,单击
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”。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
注意: 检查示例存储库中的Unity AR Foundation示例。此存储库不保证与最新的flutter-unity-view-widget
主分支保持同步。请确保遵循以下步骤在您的项目中设置AR Foundation。
Android提示
-
- 打开
lib/<architecture>/
文件夹并检查是否存在libUnityARCore.so
和libarpresto_api.so
文件。似乎存在一个错误,Unity导出会遗漏某些库文件。如果缺少这些文件,请使用Unity构建一个独立的.apk,解压生成的apk并复制缺失的.lib文件到unityLibrary
模块中。
- 打开
-
- 重复步骤6.1和6.2,将
unityLibrary
替换为arcore_client
、unityandroidpermissions
和UnityARCore
。
- 重复步骤6.1和6.2,将
-
- 在Flutter中使用
UnityWidget
时,设置fullscreen: false
以禁用全屏模式。
- 在Flutter中使用
iOS提示
-
- 打开
ios/Runner/Info.plist
并更改以下内容:
<dict> + <key>Privacy - Camera Usage Description</key> + <string>$(PRODUCT_NAME) uses Cameras</string> </dict>
- 打开
设置Vuforia
感谢@PiotrxKolasinski提供了确切的步骤:
-
- 打开
android/unityLibrary/build.gradle
文件并更改以下内容:
- implementation(name: 'VuforiaWrapper', ext: 'aar') + implementation project(':VuforiaWrapper')
- 打开
-
- 使用Android Studio,转到
File > Open
并选择android/
文件夹。一个新的项目将会打开。
注意: 如果出现错误消息“无法找到路径’:VuforiaWrapper’”,不用担心,下一步将修复它。
- 使用Android Studio,转到
-
- 在新的项目窗口中,转到
File > New > New Module > Import .JAR/.AAR package
并选择android/unityLibrary/libs/VuforiaWrapper.aar
文件。将在android/
中创建一个名为VuforiaWrapper
的新文件夹。现在您可以关闭此新项目窗口。
- 在新的项目窗口中,转到
通信
Flutter到Unity
-
在
UnityWidget
小部件上,通过onUnityCreated
回调获取UnityWidgetController
。 -
使用方法
postMessage
发送字符串,使用游戏对象名称和应调用的行为方法名称。
Unity到Flutter
-
选择应执行通信的游戏对象并转到
Inspector > Add Component > Unity Message Manager
。 -
创建一个新的
MonoBehaviour
子类并将其添加到相同的游戏对象上作为脚本。 -
在这个新行为上,调用
GetComponent<UnityMessageManager>()
以获取UnityMessageManager
。 -
使用方法
SendMessageToFlutter
发送字符串。通过UnityWidget
的onUnityMessage
回调接收此消息。
故障排除
Unity位置
错误:
InvalidOperationException: The build target does not support build appending.
解决方案:
-
打开
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
解决方案:
-
打开
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
解决方案:
-
打开
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:
解决方案:
-
打开
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配置中。
-
检查您的实际
Runner
(您的应用程序)配置。例如,如果有以下Flavors:- dev
- prod
您的
Runner
配置如下所示:所以您有以下Flavors:
Debug-dev
Profile-dev
Release-dev
Debug-prod
Profile-prod
Release-prod
这些Flavors需要添加到您的
Unity-IPhone
项目中。 -
进入您的
Unity-IPhone
项目 -> PROJECTUnity-IPhone
-> Info:在这里,您可以看到配置部分只有:
Release
ReleaseForProfiling
ReleaseForRunning
Debug
-
复制
Debug
配置两次并重命名为Debug-dev
和Debug-prod
。您可以通过选择
+
并复制配置来实现: -
重复此操作,将
Release
重命名为Release-dev
和Release-prod
。 -
重复此操作,将
Release
重命名为Profile-dev
和Profile-prod
。 -
您的
Unity-IPhone
配置现在应该看起来像这样:
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
更多关于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 项目:
- 打开 Unity 项目。
- 安装
Flutter Unity Integration
包。你可以通过 Unity 的 Package Manager 或手动下载并导入包。 - 在 Unity 中,选择
File > Build Settings
。 - 选择
Android
或iOS
平台,然后点击Switch Platform
。 - 点击
Player Settings
,确保Scripting Backend
设置为IL2CPP
。 - 在
Build Settings
窗口中,点击Build And Run
,选择输出目录为 Flutter 项目的android
或ios
目录下的UnityExport
文件夹。
4. 配置 Flutter 项目
Android 配置
-
在
android/app/build.gradle
文件中,确保minSdkVersion
至少为 19。 -
在
android/settings.gradle
文件中,添加以下内容:
include ':unityLibrary'
project(':unityLibrary').projectDir = new File('UnityExport/unityLibrary')
- 在
android/app/build.gradle
文件中,添加以下依赖:
dependencies {
implementation project(':unityLibrary')
}
iOS 配置
- 在
ios/Runner.xcworkspace
中,将 Unity 导出的UnityFramework.framework
添加到项目中。 - 在
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();
}