Merge branch 'original-master'

* original-master: (34 commits)
  Update README.md
  Update README.md
  Update README.md
  update contributors
  Create CODE_OF_CONDUCT.md
  bump release version
  new release 0.1.6+1
  Adding Metal renderer support (on iOS)
  Update README.md
  Update README.md
  Update .all-contributorsrc
  Update CHANGELOG.md
  update contributors
  updated changelog
  made new release with support for iOS
  Docs: Add @krispypen as a contributor
  added contributors
  Added iOS gif
  Docs: Add @thomas-stockx as a contributor
  Docs: Add @juicycleff as a contributor
  ...
This commit is contained in:
Thomas Stockx 2019-08-07 18:16:35 +02:00
commit d7ecc5d139
26 changed files with 898 additions and 197 deletions

63
.all-contributorsrc Normal file
View File

@ -0,0 +1,63 @@
{
"projectName": "flutter-unity-view-widget",
"projectOwner": "snowballdigital",
"repoType": "github",
"repoHost": "https://github.com",
"files": [
"README.md"
],
"imageSize": 100,
"commit": true,
"commitConvention": "eslint",
"contributors": [
{
"login": "juicycleff",
"name": "Rex Raphael",
"avatar_url": "https://avatars2.githubusercontent.com/u/11243590?v=4",
"profile": "http://rexraphael.com",
"contributions": [
"code",
"doc",
"question",
"bug",
"review",
"tutorial"
]
},
{
"login": "thomas-stockx",
"name": "Thomas Stockx",
"avatar_url": "https://avatars1.githubusercontent.com/u/1475368?v=4",
"profile": "https://stockxit.com",
"contributions": [
"code",
"doc",
"question",
"tutorial"
]
},
{
"login": "krispypen",
"name": "Kris Pypen",
"avatar_url": "https://avatars1.githubusercontent.com/u/156955?v=4",
"profile": "http://krispypen.github.io/",
"contributions": [
"code",
"doc",
"question",
"tutorial"
]
},
{
"login": "lorant-csonka-planorama",
"name": "Lorant Csonka",
"avatar_url": "https://avatars2.githubusercontent.com/u/48209860?v=4",
"profile": "https://github.com/lorant-csonka-planorama",
"contributions": [
"doc",
"video"
]
}
],
"contributorsPerLine": 7
}

4
.gitignore vendored
View File

@ -16,6 +16,10 @@
*.iws
.idea/
node_modules/
./package.json
.idea/
# Visual Studio Code related
.vscode/

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.0 MiB

View File

@ -1,12 +1,23 @@
## 0.1.6+1
* Adding Metal renderer support (on iOS) [@krispypen](https://github.com/krispypen)
## 0.1.6
* iOS support for the Unity 2019.3 new export format Unity as a Library [@krispypen](https://github.com/krispypen)
## 0.1.5
* Android support for the Unity 2019.3 new export format Unity as a Library [@thomas-stockx](https://github.com/thomas-stockx)
## 0.1.4
* Support for AR on Android thanks to @thomas-stockx
* Support for AR on Android thanks to [@thomas-stockx](https://github.com/thomas-stockx)
## 0.1.3+4
* Change input source of Flutter touch events so they work in Unity @thomas-stockx
* Change input source of Flutter touch events so they work in Unity [@thomas-stockx](https://github.com/thomas-stockx)
* Instructions on how to implement Vuforia AR
* Fix postMessage throwing exceptions on Android @thomas-stockx
* Add video tutorial, replace `unity-player` with `unity-classes` in example
* Remove java and UnityPlayer changes to the windowmanager
* Fix postMessage throwing exceptions on Android [@thomas-stockx](https://github.com/thomas-stockx)
* Add video tutorial, replace `unity-player` with `unity-classes` in example [@lorant-csonka-planorama](https://github.com/lorant-csonka-planorama)
* Remove java and UnityPlayer changes to the windowmanager [@thomas-stockx](https://github.com/thomas-stockx)

76
CODE_OF_CONDUCT.md Normal file
View File

@ -0,0 +1,76 @@
# Contributor Covenant Code of Conduct
## Our Pledge
In the interest of fostering an open and welcoming environment, we as
contributors and maintainers pledge to making participation in our project and
our community a harassment-free experience for everyone, regardless of age, body
size, disability, ethnicity, sex characteristics, gender identity and expression,
level of experience, education, socio-economic status, nationality, personal
appearance, race, religion, or sexual identity and orientation.
## Our Standards
Examples of behavior that contributes to creating a positive environment
include:
* Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy towards other community members
Examples of unacceptable behavior by participants include:
* The use of sexualized language or imagery and unwelcome sexual attention or
advances
* Trolling, insulting/derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or electronic
address, without explicit permission
* Other conduct which could reasonably be considered inappropriate in a
professional setting
## Our Responsibilities
Project maintainers are responsible for clarifying the standards of acceptable
behavior and are expected to take appropriate and fair corrective action in
response to any instances of unacceptable behavior.
Project maintainers have the right and responsibility to remove, edit, or
reject comments, commits, code, wiki edits, issues, and other contributions
that are not aligned to this Code of Conduct, or to ban temporarily or
permanently any contributor for other behaviors that they deem inappropriate,
threatening, offensive, or harmful.
## Scope
This Code of Conduct applies both within project spaces and in public spaces
when an individual is representing the project or its community. Examples of
representing a project or community include using an official project e-mail
address, posting via an official social media account, or acting as an appointed
representative at an online or offline event. Representation of a project may be
further defined and clarified by project maintainers.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported by contacting the project team at rex@snowball.digital. All
complaints will be reviewed and investigated and will result in a response that
is deemed necessary and appropriate to the circumstances. The project team is
obligated to maintain confidentiality with regard to the reporter of an incident.
Further details of specific enforcement policies may be posted separately.
Project maintainers who do not follow or enforce the Code of Conduct in good
faith may face temporary or permanent repercussions as determined by other
members of the project's leadership.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
[homepage]: https://www.contributor-covenant.org
For answers to common questions about this code of conduct, see
https://www.contributor-covenant.org/faq

View File

@ -1,4 +1,5 @@
# flutter_unity_widget
[![All Contributors](https://img.shields.io/badge/all_contributors-4-orange.svg?style=flat-square)](#contributors-)
[![version][version-badge]][package]
[![MIT License][license-badge]][license]
@ -7,14 +8,14 @@
[![Watch on GitHub][github-watch-badge]][github-watch]
[![Star on GitHub][github-star-badge]][github-star]
Flutter unity 3D widget for embedding unity in flutter. Add a Flutter widget to show unity. Works on Android, iOS in works.
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 and iOS.
## Installation
First depend on the library by adding this to your packages `pubspec.yaml`:
```yaml
dependencies:
flutter_unity_widget: ^0.1.4
flutter_unity_widget: ^0.1.6+1
```
Now inside your Dart code you can import it.
@ -26,7 +27,10 @@ import 'package:flutter_unity_widget/flutter_unity_widget.dart';
## Preview
![gif](https://github.com/snowballdigital/flutter-unity-view-widget/blob/master/2019_03_28_19_23_37.gif?raw=true)
30 fps gifs, showcasing communication between Flutter and Unity:
![gif](https://github.com/snowballdigital/flutter-unity-view-widget/blob/master/preview_android.gif?raw=true)
![gif](https://github.com/snowballdigital/flutter-unity-view-widget/blob/master/preview_ios.gif?raw=true)
<br />
@ -57,9 +61,11 @@ Now your project files should look like this.
1. First Open Unity Project.
2. Click Menu: File => Build Settings => Player Settings
2. Click Menu: File => Build Settings
3. Change `Product Name` to Name of the Xcode project, You can find it follow `ios/${XcodeProjectName}.xcodeproj`.
Be sure you have at least one scene added to your build.
3. => Player Settings
**Android Platform**:
1. Make sure your `Graphics APIs` are set to OpenGLES3 with a fallback to OpenGLES2 (no Vulkan)
@ -71,22 +77,20 @@ Now your project files should look like this.
- ARM64 ✅
- x86 ✅
<img src="https://raw.githubusercontent.com/snowballdigital/flutter-unity-view-widget/master/Screenshot%202019-03-27%2007.31.55.png" width="400" />
**IOS Platform**:
1. Other Settings find the Rendering part, uncheck the `Auto Graphics API` and select only `OpenGLES2`.
**iOS Platform**:
1. This only works with Unity version >=2019.3 because uses Unity as a library!
2. Depending on where you want to test or run your app, (simulator or physical device), you should select the appropriate SDK on `Target SDK`.
<br />
<img src="https://raw.githubusercontent.com/snowballdigital/flutter-unity-view-widget/master/Screenshot%202019-03-27%2007.31.55.png" width="400" />
<br />
### Add Unity Build Scripts and Export
Copy [`Build.cs`](https://github.com/f111fei/react-native-unity-demo/blob/master/unity/Cube/Assets/Scripts/Editor/Build.cs) and [`XCodePostBuild.cs`](https://github.com/f111fei/react-native-unity-demo/blob/master/unity/Cube/Assets/Scripts/Editor/XCodePostBuild.cs) to `unity/<Your Unity Project>/Assets/Scripts/Editor/`
Copy [`Build.cs`](https://github.com/snowballdigital/flutter-unity-view-widget/tree/master/scripts/Editor/Build.cs) and [`XCodePostBuild.cs`](https://github.com/snowballdigital/flutter-unity-view-widget/tree/master/scripts/Editor/XCodePostBuild.cs) to `unity/<Your Unity Project>/Assets/Scripts/Editor/`
Open your unity project in Unity Editor. Now you can export unity project with `Flutter/Export Android` or `Flutter/Export IOS` menu.
Open your unity project in Unity Editor. Now you can export the Unity project with `Flutter/Export Android` (for Unity versions up to 2019.2), `Flutter/Export Android (Unity 2019.3.*)` (for Unity versions 2019.3 and up, which uses the new [Unity as a Library](https://blogs.unity3d.com/2019/06/17/add-features-powered-by-unity-to-native-mobile-apps/) export format), or `Flutter/Export IOS` menu.
<img src="https://github.com/snowballdigital/flutter-unity-view-widget/blob/master/Screenshot%202019-03-27%2008.13.08.png?raw=true" width="400" />
@ -99,20 +103,13 @@ IOS will export unity project to `ios/UnityExport`.
**Android Platform Only**
1. After exporting the unity game, open Android Studio and and add the `Unity Classes` Java `.jar` file as a module to the unity project. You just need to do this once if you are exporting from the same version of Unity everytime. The `.jar` file is located in the ```<Your Flutter Project>/android/UnityExport/lib``` folder
2. Next open `build.gradle` of `flutter_unity_widget` module and replace the dependencies with
```gradle
dependencies {
implementation project(':UnityExport') // The exported unity project
implementation project(':unity-classes') // the unity classes module you added from step 1
}
```
3. Next open `build.gradle` of `UnityExport` module and replace the dependencies with
2. If using Unity 2019.2 or older, open `build.gradle` of `UnityExport` module and replace the dependencies with
```gradle
dependencies {
implementation project(':unity-classes') // the unity classes module you added from step 1
}
```
4. Next open `build.gradle` of `UnityExport` module and remove these
3. If using Unity 2019.2 or older, open `build.gradle` of `UnityExport` module and remove these
```gradle
bundle {
language {
@ -127,9 +124,31 @@ IOS will export unity project to `ios/UnityExport`.
}
```
**iOS Platform Only**
1. open your ios/Runner.xcworkspace (workspace!, not the project) in Xcode and add the exported project in the workspace root (with a right click in the Navigator, not on an item -> Add Files to “Runner” -> add the UnityExport/Unity-Iphone.xcodeproj file
<img src="workspace.png" width="400" />
2. Select the Unity-iPhone/Data folder and change the Target Membership for Data folder to UnityFramework
<img src="change_target_membership_data_folder.png" width="400" />
3. Add this to your Runner/Runner/Runner-Bridging-Header.h
```c
#import "UnityUtils.h"
```
4. Add to AppDelegate.swift before the GeneratePluginRegistrant call:
```swift
InitArgs(CommandLine.argc, CommandLine.unsafeArgv)
```
5. Opt-in to the embedded views preview by adding a boolean property to the app's `Info.plist` file with the key `io.flutter.embedded_views_preview` and the value `YES`.
<br />
### AR Foundation (ANDROID only at the moment)
### AR Foundation (not compatible with Unity 2019.3)
https://github.com/Unity-Technologies/arfoundation-samples/issues/210
Android only as iOS requires Unity 2019.3.
If you want to use Unity for integrating Augmented Reality in your Flutter app, a few more changes are required:
1. Export the Unity Project as previously stated (using the Editor Build script).
2. Check if the exported project includes all required Unity libraries (.so) files (`lib/\<architecture\>/libUnityARCore.so` and `libarpresto_api.so`). There seems to be a bug where a Unity export does not include all lib files. If they are missing, use Unity to build a standalone .apk of your AR project, unzip the resulting apk, and copy over the missing .lib files to the `UnityExport` module.
@ -276,15 +295,16 @@ class _UnityDemoScreenState extends State<UnityDemoScreen>{
## API
- `pause()` (Use this to pause unity player)
- `resume()` (Use this to resume unity player)
- `postMessage(String gameObject, methodName, message)` (Allows you invoke commands in Unity from flutter)
- `onUnityMessage(data)` (Unity to flutter bindding and listener)
## Known issues
- no iOS support yet
- Android Export requires several manual changes
- Using AR will make the activity run in full screen (hiding status and navigation bar).
[version-badge]: https://img.shields.io/pub/v/flutter_unity_widget.svg?style=flat-square
[package]: https://pub.dartlang.org/packages/flutter_unity_widget/versions/0.1.2
[package]: https://pub.dartlang.org/packages/flutter_unity_widget/
[license-badge]: https://img.shields.io/github/license/snowballdigital/flutter-unity-view-widget.svg?style=flat-square
[license]: https://github.com/snowballdigital/flutter-unity-view-widget/blob/master/LICENSE
[prs-badge]: https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square
@ -293,3 +313,25 @@ class _UnityDemoScreenState extends State<UnityDemoScreen>{
[github-watch]: https://github.com/snowballdigital/flutter-unity-view-widget/watchers
[github-star-badge]: https://img.shields.io/github/stars/snowballdigital/flutter-unity-view-widget.svg?style=social
[github-star]: https://github.com/snowballdigital/flutter-unity-view-widget/stargazers
## Contributors ✨
Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)):
<!-- ALL-CONTRIBUTORS-LIST:START - Do not remove or modify this section -->
<!-- prettier-ignore-start -->
<!-- markdownlint-disable -->
<table>
<tr>
<td align="center"><a href="http://rexraphael.com"><img src="https://avatars2.githubusercontent.com/u/11243590?v=4" width="100px;" alt="Rex Raphael"/><br /><sub><b>Rex Raphael</b></sub></a><br /><a href="https://github.com/snowballdigital/flutter-unity-view-widget/commits?author=juicycleff" title="Code">💻</a> <a href="https://github.com/snowballdigital/flutter-unity-view-widget/commits?author=juicycleff" title="Documentation">📖</a> <a href="#question-juicycleff" title="Answering Questions">💬</a> <a href="https://github.com/snowballdigital/flutter-unity-view-widget/issues?q=author%3Ajuicycleff" title="Bug reports">🐛</a> <a href="#review-juicycleff" title="Reviewed Pull Requests">👀</a> <a href="#tutorial-juicycleff" title="Tutorials"></a></td>
<td align="center"><a href="https://stockxit.com"><img src="https://avatars1.githubusercontent.com/u/1475368?v=4" width="100px;" alt="Thomas Stockx"/><br /><sub><b>Thomas Stockx</b></sub></a><br /><a href="https://github.com/snowballdigital/flutter-unity-view-widget/commits?author=thomas-stockx" title="Code">💻</a> <a href="https://github.com/snowballdigital/flutter-unity-view-widget/commits?author=thomas-stockx" title="Documentation">📖</a> <a href="#question-thomas-stockx" title="Answering Questions">💬</a> <a href="#tutorial-thomas-stockx" title="Tutorials"></a></td>
<td align="center"><a href="http://krispypen.github.io/"><img src="https://avatars1.githubusercontent.com/u/156955?v=4" width="100px;" alt="Kris Pypen"/><br /><sub><b>Kris Pypen</b></sub></a><br /><a href="https://github.com/snowballdigital/flutter-unity-view-widget/commits?author=krispypen" title="Code">💻</a> <a href="https://github.com/snowballdigital/flutter-unity-view-widget/commits?author=krispypen" title="Documentation">📖</a> <a href="#question-krispypen" title="Answering Questions">💬</a> <a href="#tutorial-krispypen" title="Tutorials"></a></td>
<td align="center"><a href="https://github.com/lorant-csonka-planorama"><img src="https://avatars2.githubusercontent.com/u/48209860?v=4" width="100px;" alt="Lorant Csonka"/><br /><sub><b>Lorant Csonka</b></sub></a><br /><a href="https://github.com/snowballdigital/flutter-unity-view-widget/commits?author=lorant-csonka-planorama" title="Documentation">📖</a> <a href="#video-lorant-csonka-planorama" title="Videos">📹</a></td>
</tr>
</table>
<!-- markdownlint-enable -->
<!-- prettier-ignore-end -->
<!-- ALL-CONTRIBUTORS-LIST:END -->
This project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification. Contributions of any kind welcome!

Binary file not shown.

After

Width:  |  Height:  |  Size: 153 KiB

View File

@ -1 +1 @@
eb7addd6604d08d1b14bdd2f0b7568eea6aaad77
5c787ba36b071ef6ee6a4f60499357697d53c75b

View File

@ -1 +1 @@
1d70ee33aab6965c19c5636e539561c5c25ac66b
bd1065d7afc1a8da0fc454a1d7dede98f3aaa473

View File

@ -1,25 +0,0 @@
//
// Created by rex on 19/03/2019.
//
#ifndef FLUTTER_UNITY_WIDGET_FLUTTERUNITY_H
#define FLUTTER_UNITY_WIDGET_FLUTTERUNITY_H
#import <Flutter/Flutter.h>
@interface FlutterUnityController : NSObject <FlutterPlatformView>
- (instancetype)initWithWithFrame:(CGRect)frame
viewIdentifier:(int64_t)viewId
arguments:(id _Nullable)args
binaryMessenger:(NSObject<FlutterBinaryMessenger>*)messenger;
- (UIView*)view;
@end
@interface FlutterUnityFactory : NSObject <FlutterPlatformViewFactory>
- (instancetype)initWithMessenger:(NSObject<FlutterBinaryMessenger>*)messenger;
@end
#endif //FLUTTER_UNITY_WIDGET_FLUTTERUNITY_H

View File

@ -1,87 +0,0 @@
//
// Created by rex on 19/03/2019.
//
#include "FlutterUnity.h"
@implementation FlutterUnityFactory {
NSObject<FlutterBinaryMessenger>* _messenger;
}
- (instancetype)initWithMessenger:(NSObject<FlutterBinaryMessenger>*)messenger {
self = [super init];
if (self) {
_messenger = messenger;
}
return self;
}
@implementation FlutterUnityController {
WKWebView* _webView;
int64_t _viewId;
FlutterMethodChannel* _channel;
}
- (instancetype)initWithWithFrame:(CGRect)frame
viewIdentifier:(int64_t)viewId
arguments:(id _Nullable)args
binaryMessenger:(NSObject<FlutterBinaryMessenger>*)messenger {
if ([super init]) {
_viewId = viewId;
_webView = [[WKWebView alloc] initWithFrame:frame];
NSString* channelName = [NSString stringWithFormat:@"nativeweb_%lld", viewId];
_channel = [FlutterMethodChannel methodChannelWithName:channelName binaryMessenger:messenger];
__weak __typeof__(self) weakSelf = self;
[_channel setMethodCallHandler:^(FlutterMethodCall* call, FlutterResult result) {
[weakSelf onMethodCall:call result:result];
}];
}
return self;
}
- (UIView*)view {
return _webView;
}
- (void)onMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result {
if ([[call method] isEqualToString:@"postMessage"]) {
[self postMessage:call result:result];
} else if ([[call method] isEqualToString:@"isReady"]) {
[self postMessage:call result:result];
} else if ([[call method] isEqualToString:@"createUnity"]) {
[self postMessage:call result:result];
} else if ([[call method] isEqualToString:@"pause"]) {
[self postMessage:call result:result];
} else if ([[call method] isEqualToString:@"resume"]) {
[self postMessage:call result:result];
} else {
result(FlutterMethodNotImplemented);
}
}
- (void)postMessage:(FlutterMethodCall*)call result:(FlutterResult)result {
NSString* url = [call arguments];
if (![self postMessage:url]) {
result([FlutterError errorWithCode:@"loadUrl_failed"
message:@"Failed parsing the URL"
details:[NSString stringWithFormat:@"URL was: '%@'", url]]);
} else {
result(nil);
}
}
- (bool)onPostMessage:(NSString*)url {
NSURL* nsUrl = [NSURL URLWithString:url];
if (!nsUrl) {
return false;
}
NSURLRequest* req = [NSURLRequest requestWithURL:nsUrl];
[_webView loadRequest:req];
return true;
}
@end

View File

@ -0,0 +1,18 @@
//
// FlutterUnityView.h
// FlutterUnityView
//
// Created by krispypen on 8/1/2019
//
#import <UIKit/UIKit.h>
#import "UnityUtils.h"
@interface FlutterUnityView : UIView
@property (nonatomic, strong) UIView* uView;
- (void)setUnityView:(UIView *)view;
@end

View File

@ -0,0 +1,37 @@
//
// FlutterUnityView.m
// FlutterUnityView
//
// Created by krispypen on 8/1/2019
//
#import "FlutterUnityView.h"
@implementation FlutterUnityView
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
return self;
}
- (void)dealloc
{
}
- (void)setUnityView:(UIView *)view
{
self.uView = view;
[self setNeedsLayout];
}
- (void)layoutSubviews
{
[super layoutSubviews];
[(UIView *)self.uView removeFromSuperview];
[self insertSubview:(UIView *)self.uView atIndex:0];
((UIView *)self.uView).frame = self.bounds;
[(UIView *)self.uView setNeedsLayout];
}
@end

View File

@ -1,4 +1,25 @@
//
// FlutterUnityWidgetPlugin.h
// FlutterUnityWidgetPlugin
//
// Created by Kris Pypen on 8/1/19.
//
#import <Flutter/Flutter.h>
@interface FlutterUnityWidgetPlugin : NSObject<FlutterPlugin>
@end
@interface FUController : NSObject <FlutterPlatformView>
- (instancetype)initWithFrame:(CGRect)frame
viewIdentifier:(int64_t)viewId
arguments:(id _Nullable)args
registrar:(NSObject<FlutterPluginRegistrar> *)registrar;
- (UIView*)view;
@end
@interface FUViewFactory : NSObject <FlutterPlatformViewFactory>
- (instancetype)initWithRegistrar:(NSObject<FlutterPluginRegistrar> *)registrar;
@end

View File

@ -1,20 +1,106 @@
//
// FlutterUnityWidgetPlugin.m
// FlutterUnityWidgetPlugin
//
// Created by Kris Pypen on 8/1/19.
//
#import "FlutterUnityWidgetPlugin.h"
#import <flutter_unity_widget/flutter_unity_widget-Swift.h>
#import "UnityUtils.h"
#import "FlutterUnityView.h"
+ (void)registerWithRegistrar:(NSObject<FlutterPluginRegistrar>*)registrar {
FlutterNativeWebFactory* webviewFactory =
[[FlutterNativeWebFactory alloc] initWithMessenger:registrar.messenger];
[registrar registerViewFactory:webviewFactory withId:@"unity_view"];
}
/*
#import "FlutterUnityWidgetPlugin.h"
#import <flutter_unity_widget/flutter_unity_widget-Swift.h>
#include <UnityFramework/UnityFramework.h>
@implementation FlutterUnityWidgetPlugin
+ (void)registerWithRegistrar:(NSObject<FlutterPluginRegistrar>*)registrar {
[SwiftFlutterUnityWidgetPlugin registerWithRegistrar:registrar];
FUViewFactory* fuviewFactory = [[FUViewFactory alloc] initWithRegistrar:registrar];
[registrar registerViewFactory:fuviewFactory withId:@"unity_view"];
}
@end
*/
@implementation FUViewFactory {
NSObject<FlutterPluginRegistrar>* _registrar;
}
- (instancetype)initWithRegistrar:(NSObject<FlutterPluginRegistrar>*)registrar {
self = [super init];
if (self) {
_registrar = registrar;
}
return self;
}
- (NSObject<FlutterMessageCodec>*)createArgsCodec {
return [FlutterStandardMessageCodec sharedInstance];
}
- (NSObject<FlutterPlatformView>*)createWithFrame:(CGRect)frame
viewIdentifier:(int64_t)viewId
arguments:(id _Nullable)args {
FUController* controller = [[FUController alloc] initWithFrame:frame
viewIdentifier:viewId
arguments:args
registrar:_registrar];
return controller;
}
@end
@implementation FUController {
FlutterUnityView* _uView;
int64_t _viewId;
FlutterMethodChannel* _channel;
}
- (instancetype)initWithFrame:(CGRect)frame
viewIdentifier:(int64_t)viewId
arguments:(id _Nullable)args
registrar:(NSObject<FlutterPluginRegistrar>*)registrar {
if ([super init]) {
_viewId = viewId;
NSString* channelName = [NSString stringWithFormat:@"unity_view_%lld", viewId];
_channel = [FlutterMethodChannel methodChannelWithName:channelName binaryMessenger:registrar.messenger];
__weak __typeof__(self) weakSelf = self;
[_channel setMethodCallHandler:^(FlutterMethodCall* call, FlutterResult result) {
[weakSelf onMethodCall:call result:result];
}];
}
return self;
}
- (void)onMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result {
if ([[call method] isEqualToString:@"postMessage"]) {
[self postMessage:call result:result];
} else {
result(FlutterMethodNotImplemented);
}
}
- (void)postMessage:(FlutterMethodCall*)call result:(FlutterResult)result {
NSString* object = [call arguments][@"gameObject"];
NSString* method = [call arguments][@"methodName"];
NSString* message = [call arguments][@"message"];
UnityPostMessage(object, method, message);
result(nil);
}
- (UIView*)view {
_uView = [[FlutterUnityView alloc] init];
if ([UnityUtils isUnityReady]) {
[_uView setUnityView: (UIView*)[GetAppController() unityView]];
} else {
[UnityUtils createPlayer:^{
[_uView setUnityView: (UIView*)[GetAppController() unityView]];
}];
}
return _uView;
}
@end

View File

@ -1,27 +1,23 @@
//
// Created by rex on 15/03/2019.
//
#ifndef FLUTTER_UNITY_WIDGET_UNITYUTILS_H
#define FLUTTER_UNITY_WIDGET_UNITYUTILS_H
#import <Foundation/Foundation.h>
#ifndef UnityUtils_h
#define UnityUtils_h
#ifdef __cplusplus
extern "C" {
#endif
void InitArgs(int argc, char* argv[]);
void InitArgs(int argc, char* argv[]);
bool UnityIsInited(void);
bool UnityIsInited(void);
void InitUnity();
void InitUnity();
void UnityPostMessage(NSString* gameObject, NSString* methodName, NSString* message);
void UnityPostMessage(NSString* gameObject, NSString* methodName, NSString* message);
void UnityPauseCommand();
void UnityPauseCommand();
void UnityResumeCommand();
void UnityResumeCommand();
#ifdef __cplusplus
} // extern "C"
@ -40,5 +36,4 @@ void UnityResumeCommand();
@end
#endif //FLUTTER_UNITY_WIDGET_UNITYUTILS_H
#endif /* UnityUtils_h */

View File

@ -1,10 +1,8 @@
#include "RegisterMonoModules.h"
#include "RegisterFeatures.h"
#include <csignal>
#import <UIKit/UIKit.h>
#import "UnityInterface.h"
#import "UnityUtils.h"
#import "UnityAppController.h"
#include <UnityFramework/UnityFramework.h>
// Hack to work around iOS SDK 4.3 linker problem
// we need at least one __TEXT, __const section entry in main application .o files
@ -18,6 +16,8 @@ char** g_argv;
void UnityInitTrampoline();
UnityFramework* ufw;
extern "C" void InitArgs(int argc, char* argv[])
{
g_argc = argc;
@ -29,6 +29,19 @@ extern "C" bool UnityIsInited()
return unity_inited;
}
UnityFramework* UnityFrameworkLoad()
{
NSString* bundlePath = nil;
bundlePath = [[NSBundle mainBundle] bundlePath];
bundlePath = [bundlePath stringByAppendingString: @"/Frameworks/UnityFramework.framework"];
NSBundle* bundle = [NSBundle bundleWithPath: bundlePath];
if ([bundle isLoaded] == false) [bundle load];
UnityFramework* ufw = [bundle.principalClass getInstance];
return ufw;
}
extern "C" void InitUnity()
{
if (unity_inited) {
@ -36,41 +49,30 @@ extern "C" void InitUnity()
}
unity_inited = true;
UnityInitStartupTime();
ufw = UnityFrameworkLoad();
@autoreleasepool
{
UnityInitTrampoline();
UnityInitRuntime(g_argc, g_argv);
RegisterMonoModules();
NSLog(@"-> registered mono modules %p\n", &constsection);
RegisterFeatures();
// iOS terminates open sockets when an application enters background mode.
// The next write to any of such socket causes SIGPIPE signal being raised,
// even if the request has been done from scripting side. This disables the
// signal and allows Mono to throw a proper C# exception.
std::signal(SIGPIPE, SIG_IGN);
}
[ufw setDataBundleId: "com.unity3d.framework"];
[ufw frameworkWarmup: g_argc argv: g_argv];
}
extern "C" void UnityPostMessage(NSString* gameObject, NSString* methodName, NSString* message)
{
UnitySendMessage([gameObject UTF8String], [methodName UTF8String], [message UTF8String]);
dispatch_async(dispatch_get_main_queue(), ^{
[ufw sendMessageToGOWithName:[gameObject UTF8String] functionName:[methodName UTF8String] message:[message UTF8String]];
});
}
extern "C" void UnityPauseCommand()
{
dispatch_async(dispatch_get_main_queue(), ^{
UnityPause(1);
[ufw pause:true];
});
}
extern "C" void UnityResumeCommand()
{
dispatch_async(dispatch_get_main_queue(), ^{
UnityPause(0);
[ufw pause:false];
});
}

View File

@ -15,7 +15,11 @@ Flutter unity 3D widget for embedding unity in flutter
s.source_files = 'Classes/**/*'
s.public_header_files = 'Classes/**/*.h'
s.dependency 'Flutter'
s.frameworks = 'UnityFramework'
s.ios.deployment_target = '8.0'
s.xcconfig = {
'FRAMEWORK_SEARCH_PATHS' => '"${PODS_ROOT}/../UnityExport" "${PODS_ROOT}/../.symlinks/flutter/ios-release" "${PODS_CONFIGURATION_BUILD_DIR}"',
'OTHER_LDFLAGS' => '$(inherited) -framework UnityFramework ${PODS_LIBRARIES}'
}
end

BIN
preview_android.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 MiB

BIN
preview_ios.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.3 MiB

View File

@ -1,8 +1,10 @@
name: flutter_unity_widget
description: Flutter unity 3D widget for embedding unity in flutter
version: 0.1.4
version: 0.1.6+1
authors:
- Rex Raphael <rex.raphael@outlook.com>
- Thomas Stockx <thomas@stockxit.com>
- Kris Pypen <kris.pypen@gmail.com>
homepage: https://github.com/snowballdigital/flutter-unity-view-widget/tree/master
environment:

119
scripts/Editor/Build.cs Normal file
View File

@ -0,0 +1,119 @@
using System;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using UnityEditor;
using UnityEngine;
using Application = UnityEngine.Application;
using BuildResult = UnityEditor.Build.Reporting.BuildResult;
public class Build : MonoBehaviour
{
static readonly string ProjectPath = Path.GetFullPath(Path.Combine(Application.dataPath, ".."));
static readonly string apkPath = Path.Combine(ProjectPath, "Builds/" + Application.productName + ".apk");
static readonly string androidExportPath = Path.GetFullPath(Path.Combine(ProjectPath, "../../android/UnityExport"));
static readonly string iosExportPath = Path.GetFullPath(Path.Combine(ProjectPath, "../../ios/UnityExport"));
[MenuItem("Flutter/Export Android (Unity 2019.3.*) %&n", false, 1)]
public static void DoBuildAndroidLibrary()
{
DoBuildAndroid(Path.Combine(apkPath, "unityLibrary"));
// Copy over resources from the launcher module that are used by the library
Copy(Path.Combine(apkPath + "/launcher/src/main/res"), Path.Combine(androidExportPath, "src/main/res"));
}
[MenuItem("Flutter/Export Android %&a", false, 2)]
public static void DoBuildAndroidLegacy()
{
DoBuildAndroid(Path.Combine(apkPath, Application.productName));
}
public static void DoBuildAndroid(String buildPath)
{
if (Directory.Exists(apkPath))
Directory.Delete(apkPath, true);
if (Directory.Exists(androidExportPath))
Directory.Delete(androidExportPath, true);
EditorUserBuildSettings.androidBuildSystem = AndroidBuildSystem.Gradle;
var options = BuildOptions.AcceptExternalModificationsToPlayer;
var report = BuildPipeline.BuildPlayer(
GetEnabledScenes(),
apkPath,
BuildTarget.Android,
options
);
if (report.summary.result != BuildResult.Succeeded)
throw new Exception("Build failed");
Copy(buildPath, androidExportPath);
// Modify build.gradle
var build_file = Path.Combine(androidExportPath, "build.gradle");
var build_text = File.ReadAllText(build_file);
build_text = build_text.Replace("com.android.application", "com.android.library");
build_text = build_text.Replace("implementation fileTree(dir: 'libs', include: ['*.jar'])", "implementation project(':unity-classes')");
build_text = Regex.Replace(build_text, @"\n.*applicationId '.+'.*\n", "\n");
File.WriteAllText(build_file, build_text);
// Modify AndroidManifest.xml
var manifest_file = Path.Combine(androidExportPath, "src/main/AndroidManifest.xml");
var manifest_text = File.ReadAllText(manifest_file);
manifest_text = Regex.Replace(manifest_text, @"<application .*>", "<application>");
Regex regex = new Regex(@"<activity.*>(\s|\S)+?</activity>", RegexOptions.Multiline);
manifest_text = regex.Replace(manifest_text, "");
File.WriteAllText(manifest_file, manifest_text);
}
[MenuItem("Flutter/Export IOS (Unity 2019.3.*) %&i", false, 3)]
public static void DoBuildIOS()
{
if (Directory.Exists(iosExportPath))
Directory.Delete(iosExportPath, true);
EditorUserBuildSettings.iOSBuildConfigType = iOSBuildType.Release;
var options = BuildOptions.AcceptExternalModificationsToPlayer;
var report = BuildPipeline.BuildPlayer(
GetEnabledScenes(),
iosExportPath,
BuildTarget.iOS,
options
);
if (report.summary.result != BuildResult.Succeeded)
throw new Exception("Build failed");
}
static void Copy(string source, string destinationPath)
{
if (Directory.Exists(destinationPath))
Directory.Delete(destinationPath, true);
Directory.CreateDirectory(destinationPath);
foreach (string dirPath in Directory.GetDirectories(source, "*",
SearchOption.AllDirectories))
Directory.CreateDirectory(dirPath.Replace(source, destinationPath));
foreach (string newPath in Directory.GetFiles(source, "*.*",
SearchOption.AllDirectories))
File.Copy(newPath, newPath.Replace(source, destinationPath), true);
}
static string[] GetEnabledScenes()
{
var scenes = EditorBuildSettings.scenes
.Where(s => s.enabled)
.Select(s => s.path)
.ToArray();
return scenes;
}
}

View File

@ -0,0 +1,333 @@
/*
MIT License
Copyright (c) 2017 Jiulong Wang
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#if UNITY_IOS
using System;
using System.Collections.Generic;
using System.IO;
using UnityEditor;
using UnityEditor.Callbacks;
using UnityEditor.iOS.Xcode;
/// <summary>
/// Adding this post build script to Unity project enables the flutter-unity-widget to access it
/// </summary>
public static class XcodePostBuild
{
/// <summary>
/// The identifier added to touched file to avoid double edits when building to existing directory without
/// replace existing content.
/// </summary>
private const string TouchedMarker = "https://github.com/snowballdigital/flutter-unity-view-widget";
[PostProcessBuild]
public static void OnPostBuild(BuildTarget target, string pathToBuiltProject)
{
if (target != BuildTarget.iOS)
{
return;
}
PatchUnityNativeCode(pathToBuiltProject);
UpdateUnityProjectFiles(pathToBuiltProject);
}
/// <summary>
/// We need to add the Data folder to the UnityFramework framework
/// </summary>
private static void UpdateUnityProjectFiles(string pathToBuiltProject)
{
var pbx = new PBXProject();
var pbxPath = Path.Combine(pathToBuiltProject, "Unity-iPhone.xcodeproj/project.pbxproj");
pbx.ReadFromFile(pbxPath);
// Add UnityExport/Data
var targetGuid = pbx.TargetGuidByName("UnityFramework");
var fileGuid = pbx.AddFolderReference(Path.Combine(pathToBuiltProject, "Data"), "Data");
pbx.AddFileToBuild(targetGuid, fileGuid);
pbx.WriteToFile(pbxPath);
}
/// <summary>
/// Make necessary changes to Unity build output that enables it to be embedded into existing Xcode project.
/// </summary>
private static void PatchUnityNativeCode(string pathToBuiltProject)
{
EditUnityFrameworkH(Path.Combine(pathToBuiltProject, "UnityFramework/UnityFramework.h"));
EditUnityAppControllerH(Path.Combine(pathToBuiltProject, "Classes/UnityAppController.h"));
EditUnityAppControllerMM(Path.Combine(pathToBuiltProject, "Classes/UnityAppController.mm"));
EditUnityViewMM(Path.Combine(pathToBuiltProject, "Classes/UI/UnityView.mm"));
}
/// <summary>
/// Edit 'UnityFramework.h': add 'frameworkWarmup'
/// </summary>
private static void EditUnityFrameworkH(string path)
{
var inScope = false;
// Add frameworkWarmup method
EditCodeFile(path, line =>
{
inScope |= line.Contains("- (void)runUIApplicationMainWithArgc:");
if (inScope)
{
if (line.Trim() == "")
{
inScope = false;
return new string[]
{
"",
"// Added by " + TouchedMarker,
"- (void)frameworkWarmup:(int)argc argv:(char*[])argv;",
""
};
}
}
return new string[] { line };
});
}
/// <summary>
/// Edit 'UnityAppController.h': returns 'UnityAppController' from 'AppDelegate' class.
/// </summary>
private static void EditUnityAppControllerH(string path)
{
var inScope = false;
var markerDetected = false;
// Add static GetAppController
EditCodeFile(path, line =>
{
inScope |= line.Contains("- (void)startUnity:");
if (inScope)
{
if (line.Trim() == "")
{
inScope = false;
return new string[]
{
"",
"// Added by " + TouchedMarker,
"+ (UnityAppController*)GetAppController;",
""
};
}
}
return new string[] { line };
});
inScope = false;
markerDetected = false;
// Modify inline GetAppController
EditCodeFile(path, line =>
{
inScope |= line.Contains("extern UnityAppController* GetAppController");
if (inScope && !markerDetected)
{
if (line.Trim() == "")
{
inScope = false;
markerDetected = true;
return new string[]
{
"// }",
"",
"static inline UnityAppController* GetAppController()",
"{",
" return [UnityAppController GetAppController];",
"}",
};
}
return new string[] { "// " + line };
}
return new string[] { line };
});
}
/// <summary>
/// Edit 'UnityAppController.mm': triggers 'UnityReady' notification after Unity is actually started.
/// </summary>
private static void EditUnityAppControllerMM(string path)
{
var inScope = false;
var markerDetected = false;
EditCodeFile(path, line =>
{
if (line.Trim() == "@end")
{
return new string[]
{
"",
"// Added by " + TouchedMarker,
"static UnityAppController *unityAppController = nil;",
"",
@"+ (UnityAppController*)GetAppController",
"{",
" static dispatch_once_t onceToken;",
" dispatch_once(&onceToken, ^{",
" unityAppController = [[self alloc] init];",
" });",
" return unityAppController;",
"}",
"",
line,
};
}
inScope |= line.Contains("- (void)startUnity:");
markerDetected |= inScope && line.Contains(TouchedMarker);
if (inScope && line.Trim() == "}")
{
inScope = false;
if (markerDetected)
{
return new string[] { line };
}
else
{
return new string[]
{
" // Modified by " + TouchedMarker,
@" [[NSNotificationCenter defaultCenter] postNotificationName: @""UnityReady"" object:self];",
"}",
};
}
}
return new string[] { line };
});
inScope = false;
markerDetected = false;
// Modify inline GetAppController
EditCodeFile(path, line =>
{
inScope |= line.Contains("UnityAppController* GetAppController()");
if (inScope && !markerDetected)
{
if (line.Trim() == "}")
{
inScope = false;
markerDetected = true;
return new string[]
{
"",
};
}
return new string[] { "// " + line };
}
return new string[] { line };
});
}
/// <summary>
/// Edit 'UnityView.mm': fix the width and height needed for the Metal renderer
/// </summary>
private static void EditUnityViewMM(string path)
{
var inScope = false;
// Add frameworkWarmup method
EditCodeFile(path, line =>
{
inScope |= line.Contains("UnityGetRenderingResolution(&requestedW, &requestedH)");
if (inScope)
{
if (line.Trim() == "")
{
inScope = false;
return new string[]
{
"",
"// Added by " + TouchedMarker,
" if (requestedW == 0) {",
" requestedW = _surfaceSize.width;",
" }",
" if (requestedH == 0) {",
" requestedH = _surfaceSize.height;",
" }",
""
};
}
}
return new string[] { line };
});
}
private static void EditCodeFile(string path, Func<string, IEnumerable<string>> lineHandler)
{
var bakPath = path + ".bak";
if (File.Exists(bakPath))
{
File.Delete(bakPath);
}
File.Move(path, bakPath);
using (var reader = File.OpenText(bakPath))
using (var stream = File.Create(path))
using (var writer = new StreamWriter(stream))
{
string line;
while ((line = reader.ReadLine()) != null)
{
var outputs = lineHandler(line);
foreach (var o in outputs)
{
writer.WriteLine(o);
}
}
}
}
}
#endif

BIN
workspace.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 170 KiB