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 DebugExport Android Release选项(将导出到android/unityLibrary)或Export iOS DebugExport 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.solibarpresto_api.so文件。似乎存在一个错误,Unity导出会遗漏某些库文件。如果缺少这些文件,请使用Unity构建一个独立的.apk,解压生成的apk并复制缺失的.lib文件到unityLibrary模块中。
    1. 重复步骤6.1和6.2,将unityLibrary替换为arcore_clientunityandroidpermissionsUnityARCore
    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发送字符串。通过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_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-devDebug-prod

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

    Duplicate configuration

  4. 重复此操作,将Release重命名为Release-devRelease-prod

  5. 重复此操作,将Release重命名为Profile-devProfile-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. 选择 AndroidiOS 平台,然后点击 Switch Platform
  5. 点击 Player Settings,确保 Scripting Backend 设置为 IL2CPP
  6. Build Settings 窗口中,点击 Build And Run,选择输出目录为 Flutter 项目的 androidios 目录下的 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. 处理生命周期

确保在 onPauseonResume 中正确处理 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();
}
回到顶部