前面我已经总结了2018版集成unity的方式、当时也有framework集成的方式,不过都很麻烦,每次都需要重新生成新的framework。
而2019版替我们省略了打包framework的步骤,相比2018版,集成快乐了许多。
集成过程如下:
1.使用Xcode工程生成.xcworkspace文件,并放到指定的文件夹下。
2.想iOS项目和unity导出的工程也放到和xcworkspace一样的文件下。
3.打开.xcworkspace文件 分别将unity的.xcodeproj和iOS项目的.xcodeproj添加进来(有pod的话,将Pods->Pods.xcodeproj也添加进来)
4.选择Data文件并打开右侧编辑栏 勾选UnityFramework 如图:
5.选择Librarise->Plugins->iOS->NativeCallProxy.h(这个文件主要写unity和原生的桥接方法)并打开右侧编辑栏 勾选UnityFramework 并选择public 将NativeCallProxy.m也勾选UnityFramework
6.这里我们选择UnityFramework先编译一下(因为这里我之前遇到了一个错误,错误信息:-> applicationDidFinishLaunching()
[libil2cpp] ERROR: Could not open /var/containers/Bundle/Application/EF905A34-3E68-47BD-9578-3D492F0FAF22/NativeiOSApp.app/Frameworks/UnityFramework.framework/Data/Managed/Metadata/global-metadata.dat
IL2CPP initialization failed,这是因为UnityFramework不对,解决的方式就是重新编译了UnityFramework,然后iOS工程重新引用UnityFramework就好了,这里说一下,以免大家踩坑)
7.选择iOS工程并添加UnityFramework 如图:
到这里文件相关的就可以不用管了(相较于2018版真是太省事了,unity的文件不用改也不用删,爽!!!)
接下来就是调用unity的实现了。
8.集成了unity后 iOS项目就不需要main.m文件了 可以将它删除了。找到AppDelegate.m文件 将它修改为.mm文件,然后将里面代码修改如下:
AppDelegate.mm
#include <UnityFramework/UnityFramework.h>
#include <UnityFramework/NativeCallProxy.h>
#include <mach/mach.h>
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];
if (![ufw appController])
{
// unity is not initialized
[ufw setExecuteHeader: &_mh_execute_header];
}
return ufw;
}
void showAlert(NSString* title, NSString* msg) {
UIAlertController* alert = [UIAlertController alertControllerWithTitle:title message:msg preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction* defaultAction = [UIAlertAction actionWithTitle:@"Ok" style:UIAlertActionStyleDefault
handler:^(UIAlertAction * action) {}];
[alert addAction:defaultAction];
// auto delegate = [[UIApplication sharedApplication] delegate];
UIViewController *vc = [UIApplication sharedApplication].delegate.window.rootViewController;
[vc presentViewController:alert animated:YES completion:nil];
}
int gArgc = 0;
char** gArgv = nullptr;
NSDictionary* appLaunchOpts;
@interface AppDelegate : UIResponder<UIApplicationDelegate, UnityFrameworkListener, NativeCallsProtocol>
@property (strong, nonatomic) UIWindow *window;
@property UnityFramework* ufw;
@property (assign, nonatomic) BOOL isStartUnity ;
- (void)initUnity;
- (void)ShowMainView;
- (void)didFinishLaunching:(NSNotification*)notification;
- (void)didBecomeActive:(NSNotification*)notification;
- (void)willResignActive:(NSNotification*)notification;
- (void)didEnterBackground:(NSNotification*)notification;
- (void)willEnterForeground:(NSNotification*)notification;
- (void)willTerminate:(NSNotification*)notification;
- (void)unityDidUnloaded:(NSNotification*)notification;
@end
@implementation AppDelegate
- (bool)unityIsInitialized { return [self ufw] && [[self ufw] appController]; }
#pragma mark - 打开3D
- (void)showUnityView
{
if(![self unityIsInitialized]) {
showAlert(@"Unity is not initialized", @"Initialize Unity first");
} else {
[[self ufw] showUnityWindow];
}
}
#pragma mark - 打开原生
- (void)showNativeView
{
[self.window makeKeyAndVisible];
}
#pragma mark - 发消息
- (void)sendMsgToUnity:(NSString *)strClass method:(NSString *)strMethod value:(NSString *)strValue
{
// [[self ufw] sendMessageToGOWithName: "Cube" functionName: "ChangeColor" message: "yellow"];
[[self ufw] sendMessageToGOWithName:strClass.UTF8String functionName:strMethod.UTF8String message:strValue.UTF8String];
}
- (void)showHostMainWindow
{
[self showHostMainWindow:@""];
}
- (void)showHostMainWindow:(NSString*)color
{
// if([color isEqualToString:@"blue"]) self.viewController.unpauseBtn.backgroundColor = UIColor.blueColor;
// else if([color isEqualToString:@"red"]) self.viewController.unpauseBtn.backgroundColor = UIColor.redColor;
// else if([color isEqualToString:@"yellow"]) self.viewController.unpauseBtn.backgroundColor = UIColor.yellowColor;
// [UIApplication sharedApplication].keyWindow.rootViewController = ;
// [self.window makeKeyAndVisible];
[self showNativeView];
}
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Override point for customization after application launch.
[self initUnity];
if (@available(iOS 10.0, *)) {
// [NSTimer scheduledTimerWithTimeInterval:1 repeats:YES block:^(NSTimer * _Nonnull timer) {
// NSInteger appMemoryBytes = qs_getAppMemoryBytes();
// NSLog(@"使用了 %f MB 内存", appMemoryBytes / 1024.0f/ 1024.0f);
// }];
} else {
// Fallback on earlier versions
}
return YES;
}
//获取当前App的内存使用值
uint64_t qs_getAppMemoryBytes() {
task_vm_info_data_t vmInfo;
mach_msg_type_number_t count = TASK_VM_INFO_COUNT;
kern_return_t result = task_info(mach_task_self(), TASK_VM_INFO, (task_info_t) &vmInfo, &count);
if (result != KERN_SUCCESS)
return 0;
return vmInfo.phys_footprint;
}
#pragma mark - 初始化UnityManage
#pragma mark - 初始化3D
- (void)initUnity
{
if([self unityIsInitialized]) {
showAlert(@"Unity already initialized", @"Unload Unity first");
return;
}
[self setUfw: UnityFrameworkLoad()];
[[self ufw] setDataBundleId: "com.unity3d.framework"];
[[self ufw] registerFrameworkListener:self];
[NSClassFromString(@"FrameworkLibAPI") registerAPIforNativeCalls:self];
[[self ufw] runEmbeddedWithArgc: gArgc argv: gArgv appLaunchOpts: appLaunchOpts];
[NSTimer scheduledTimerWithTimeInterval:5 repeats:NO block:^(NSTimer * _Nonnull timer) {
// [self showUnityView];
}];
}
- (void)unloadUnity
{
if(![self unityIsInitialized]) {
showAlert(@"Unity is not initialized", @"Initialize Unity first");
} else {
// [UnityFrameworkLoad() unloadApplicaion: true];
}
}
#pragma mark - 释放回调
- (void)unityDidUnload:(NSNotification*)notification
{
NSLog(@"unityDidUnloaded called");
[[self ufw] unregisterFrameworkListener: self];
[self setUfw: nil];
}
- (void)applicationWillResignActive:(UIApplication *)application {
[[[self ufw] appController] applicationWillResignActive: application];
}
- (void)applicationDidEnterBackground:(UIApplication *)application {
[[[self ufw] appController] applicationDidEnterBackground: application];
}
- (void)applicationWillEnterForeground:(UIApplication *)application {
[[[self ufw] appController] applicationWillEnterForeground: application];
}
- (void)applicationDidBecomeActive:(UIApplication *)application {
[[[self ufw] appController] applicationDidBecomeActive: application];
}
- (void)applicationWillTerminate:(UIApplication *)application {
[[[self ufw] appController] applicationWillTerminate: application];
}
@end
int main(int argc, char * argv[]) {
NSString * appDelegateClassName;
gArgc = argc;
gArgv = argv;
@autoreleasepool {
if (false)
{
// run UnityFramework as main app
id ufw = UnityFrameworkLoad();
// Set UnityFramework target for Unity-iPhone/Data folder to make Data part of a UnityFramework.framework and call to setDataBundleId
// ODR is not supported in this case, ( if you need embedded and ODR you need to copy data )
[ufw setDataBundleId: "com.unity3d.framework"];
[ufw runUIApplicationMainWithArgc: argc argv: argv];
} else {
// run host app first and then unity later
UIApplicationMain(argc, argv, nil, [NSString stringWithUTF8String: "AppDelegate"]);
}
}
return UIApplicationMain(argc, argv, nil, appDelegateClassName);
}
AppDelegate.h
#import <UIKit/UIKit.h>
@interface AppDelegate : UIResponder <UIApplicationDelegate>
@property (strong, nonatomic) UIWindow *window;
- (void)showUnityView;
- (void)showNativeView;
- (void)sendMsgToUnity:(NSString *)strClass method:(NSString *)strMethod value:(NSString *)strValue;
@end
到这里就可以实现unity和原生的切换了,相比2018版,集成方便了,归根结底就是unity导出的Xcode工程会多一个UnityFramework,可以理解为unity帮我们将unity工程打包成一个framework,我们调用unity的所有方法都可以通过它去实现。而且2018集成的方式不但麻烦,需要改unity的文件代码,而且当unity和iOS两方工程的文件和依赖库都增加时,会使iOS工程debug模式下无法编译通过,会报‘ARM64 branch out of range (xxxxx max is +/-128MB)’的错误,意思就是文件太多了,改成release模式就可以编译了,但开发起来没法调试。