Flutter游戏集成插件flutter_unity_widget_pro的使用

Flutter游戏集成插件flutter_unity_widget_pro的使用

flutter_unity_widget_pro

All Contributors version MIT License PRs Welcome

Watch on GitHub Star on GitHub

Gitter Discord

Flutter unity 3D widget for embedding unity in flutter. Now you can make awesome gamified features of your app in Unity and get it rendered in a Flutter app both in fullscreen and embeddable mode. Works great on Android, iPad OS, iOS, Web, with Windows nearing completion. There are now two unity app examples in the unity folder, one with the default scene and another based on Unity AR foundation samples.

注意:仅支持Unity 2019.4.3或更高版本。UnityFramework不支持模拟器。 注意:目前仅支持在Android上使用OpenGLES3作为Graphics API。


注意

需要我回复,请@Rex Isaac Raphael。 始终使用与你使用的Unity版本匹配的FUW unitypackage。

此插件假定你至少知道如何使用Unity引擎。如果你对Unity widget的展示方式有疑问,你可以根据需要修改你的Unity项目构建设置。

未来版本号将根据Unity版本进行更改。请注意,这并不意味着该包不兼容其他版本,而只是表示它已经过测试以与特定Unity版本一起工作。

Windows即将推出。

^2022.1.0+5支持Flutter 3.0.0,并且现在只能使用PlatformViewSurface。这意味着你必须在UnityWidget中传递useAndroidViewSurface: true。你可以跟踪这个问题。 目前正在进行支持AndroidView的工作。


安装

首先,在你的pubspec.yaml文件中添加依赖项:

Flutter 3.0.0

dependencies:
  flutter_unity_widget_pro: ^2022.2.0

Pre Flutter 3.0.0(此版本将逐渐被弃用)

dependencies:
  flutter_unity_widget_pro: ^2022.1.0+7

现在可以在你的Dart代码中导入它:

import 'package:flutter_unity_widget_pro/flutter_unity_widget_pro.dart';

预览

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

gif gif


设置

对于此,还有一个视频教程,你可以在这里找到。

在以下教程中,有针对每个平台的具体步骤,由一个 ℹ️ 图标后跟平台名称(Android或iOS)标记。你可以点击图标展开它。

前提条件

NDK

如果您的项目需要Android NDK,需要进行以下设置:

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

[NDK Path]

通过右键单击并复制路径,将其粘贴到你的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 根据你想测试或运行应用的位置(模拟器或物理设备),选择适当的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,因为它专门用于与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_pro.OverrideUnityActivity"
    +        android:theme="[@style](/user/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_pro.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_pro
    
        @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_pro.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 示例

查看Unity AR Foundation示例。 此存储库不一定与最新的flutter-unity-view-widget主分支保持一致。确保遵循以下步骤在你的项目中设置AR Foundation。

信息来源:Android

  1. 打开lib/<architecture>/文件夹并检查是否同时存在libUnityARCore.solibarpresto_api.so文件。 似乎有一个bug,Unity导出时可能不会包含所有库文件。如果它们缺失,请使用Unity构建一个独立的.apk,解压生成的apk,并将缺失的.lib文件复制到unityLibrary模块。

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

  3. 当在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/文件夹。一个新项目将会打开。

    不要担心如果出现错误消息“Project with path ‘:VuforiaWrapper’ could not be found in project ‘:unityLibrary’”,下一步将解决这个问题。

  2. 在这个新的项目窗口中,转到File > New > New Module > Import .JAR/.AAR package并选择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

    Unity Message Manager

  2. 创建一个新的MonoBehaviour子类并将其作为脚本添加到同一个GameObject中。

  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_pro] .../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_pro/flutter_unity_widget_pro.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_pro/flutter_unity_widget_pro.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}');
  }

}

属性

  • 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绑定和监听器当新场景加载时)

flavors

推荐

应用口味的最简单方法是:flutter_flavorizr

如果你在应用中使用了口味,你可能会注意到特别是iOS在运行或构建应用时崩溃! 以下是应用口味的必要步骤:

Android

无需任何额外设置,口味已自动应用。

iOS

对于你的Unity iOS构建,你必须将口味添加到Unity iOS配置中。

  1. 检查你当前的Runner(你的应用)配置。例如,如果你有口味:

    • dev
    • prod

    你的Runner配置看起来像这样:

    iOS Runner Config

    因此你有口味:

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

    这些口味需要添加到你的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)。因此,如果你正确设置了口味,则无需对Web进行任何更改即可应用对你Flutter-Unity Web应用的更改。


已知问题

  • 记住在Unity玩家设置中禁用全屏以禁用Unity全屏。
  • Unity在Android上冻结并崩溃,请使用OpenGL3作为Graphics API。
  • 由于你的Unity项目中的一些本地依赖项导致项目构建失败,请在Android或iOS上集成这些依赖项的本地库。
  • 应用在屏幕退出和重新进入时崩溃,请执行以下操作:
    Build Setting - iOS - Other Settings - Configuration - Enable Custom Background Behaviors or 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游戏集成插件flutter_unity_widget_pro的使用的实战教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

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


在Flutter项目中集成flutter_unity_widget_pro插件以嵌入Unity游戏内容,可以按照以下步骤进行。以下是一个基本的代码案例,帮助你理解如何设置和使用该插件。

步骤 1: 添加依赖

首先,在你的pubspec.yaml文件中添加flutter_unity_widget_pro依赖:

dependencies:
  flutter:
    sdk: flutter
  flutter_unity_widget_pro: ^版本号 # 替换为最新的版本号

然后运行flutter pub get来安装依赖。

步骤 2: 配置Unity项目

  1. 创建Unity项目:在你的计算机上安装Unity并创建一个新的Unity项目。
  2. 构建Unity项目:在Unity中,选择File > Build Settings,然后选择AndroidiOS平台,并确保已正确配置SDK路径。点击Build并选择一个文件夹来保存构建的Unity库文件(通常是.aar文件用于Android或.framework文件用于iOS)。

步骤 3: 将Unity库文件添加到Flutter项目

  • 对于Android:将生成的.aar文件及其依赖项(如libs文件夹中的其他.aar文件)复制到Flutter项目的android/app/libs目录中。然后在android/app/build.gradle文件中添加以下代码以包含这些库:
dependencies {
    implementation fileTree(dir: 'libs', include: ['*.aar'])
    // 其他依赖项...
}
  • 对于iOS:将生成的Unity .framework文件复制到Flutter项目的ios/Frameworks目录中(如果没有该目录,请创建它)。然后,在Xcode中,将.framework文件添加到你的Flutter iOS项目中,并确保在Build PhasesLink Binary with Libraries部分中包含了它。

步骤 4: 使用flutter_unity_widget_pro插件

在你的Flutter代码中,你可以使用FlutterUnityWidget来嵌入Unity视图。以下是一个简单的示例:

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

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text('Flutter Unity Integration'),
        ),
        body: Center(
          child: UnityWidget(
            onUnityMessage: (message) {
              print('Unity -> Flutter: $message');
            },
            onUnityCreated: (controller) {
              // 可以在这里调用Unity的方法
              // controller.send('MethodName', 'param1', 'param2');
            },
            onUnityViewCreated: () {
              print('Unity view created');
            },
            onUnityResumed: () {
              print('Unity resumed');
            },
            onUnityPaused: () {
              print('Unity paused');
            },
            onUnityDestroyed: () {
              print('Unity destroyed');
            },
          ),
        ),
        floatingActionButton: FloatingActionButton(
          onPressed: () {
            // 这里可以添加一些按钮来与Unity交互
            // 例如,发送消息给Unity
          },
          tooltip: 'Interact with Unity',
          child: Icon(Icons.play_arrow),
        ),
      ),
    );
  }
}

步骤 5: 运行Flutter项目

确保你已经正确配置了Android或iOS的开发环境,然后运行你的Flutter项目:

flutter run

这个示例展示了如何在Flutter应用中嵌入Unity视图,并通过回调与Unity进行通信。根据你的具体需求,你可能需要调整Unity项目的设置,或者扩展Flutter与Unity之间的通信逻辑。

回到顶部