Unity iOS 打包

#if UNITY_IOS

using UnityEngine;

using UnityEditor;
using UnityEditor.Callbacks;
using System.IO;
using UnityEditor.iOS.Xcode;
using System.Collections.Generic;
using System.Text.RegularExpressions;

public static class BuildiOSPlayer
{
    [PostProcessBuild]
    static void OnPostProcessBuild (BuildTarget target, string pathToBuiltProject)
    {
        Debug.Log(pathToBuiltProject);

        EditProj(pathToBuiltProject);
        EditInfoPlist(pathToBuiltProject);
        EditUnityAppController(pathToBuiltProject);
    }

    static void EditProj(string pathToBuiltProject)
    {
        string projPath = pathToBuiltProject + "/Unity-iPhone.xcodeproj/project.pbxproj";

        PBXProject pbxProj = new PBXProject();
        pbxProj.ReadFromFile(projPath);

        string targetGuid = pbxProj.TargetGuidByName("Unity-iPhone");
        //string debugConfig = pbxProj.BuildConfigByName(target, "Debug");
        //string releaseConfig = pbxProj.BuildConfigByName(target, "Release");
        pbxProj.SetBuildProperty(targetGuid, "GCC_ENABLE_OBJC_EXCEPTIONS", "YES");
        //pbxProj.SetBuildPropertyForConfig(debugConfig, "GCC_ENABLE_OBJC_EXCEPTIONS", "YES");
        //pbxProj.SetBuildPropertyForConfig(releaseConfig, "GCC_ENABLE_OBJC_EXCEPTIONS", "YES");

        pbxProj.AddFrameworkToProject(targetGuid, "CoreTelephony.framework", true);
        pbxProj.AddFrameworkToProject(targetGuid, "StoreKit.framework", true);
        pbxProj.AddFrameworkToProject(targetGuid, "AVKit.framework", true);

        pbxProj.AddFileToBuild(targetGuid, pbxProj.AddFile("usr/lib/libsqlite3.dylib", "Frameworks/libsqlite3.dylib", PBXSourceTree.Sdk));
        pbxProj.AddFileToBuild(targetGuid, pbxProj.AddFile("usr/lib/libz.dylib", "Frameworks/libz.dylib", PBXSourceTree.Sdk));

        //pbxProj.SetBuildProperty(target, "FRAMEWORK_SEARCH_PATHS", "$(SRCROOT)/Frameworks");
        //pbxProj.AddBuildProperty(target, "FRAMEWORK_SEARCH_PATHS", "$(inherited)");

        //pbxProj.AddBuildProperty(target, "OTHER_LDFLAGS", "-ObjC");


        #region 添加资源文件(中文路径 会导致 project.pbxproj 解析失败)
        string frameworksPath = Application.dataPath + "/Frameworks";
        string[] directories = Directory.GetDirectories(frameworksPath, "*", SearchOption.TopDirectoryOnly);
        for (int i = 0; i < directories.Length; i++)
        {
            string path = directories[i];

            string name = path.Replace(frameworksPath + "/", "");
            string destDirName = pathToBuiltProject + "/" + name;

            if (Directory.Exists(destDirName))
                Directory.Delete(destDirName, true);

            Debug.Log(path + " => " + destDirName);
            Utility.CopyDirectory(path, destDirName, new string[] { ".meta", ".framework", ".mm", ".c", ".m", ".h", ".xib", ".a", ".plist", ".org", "" }, false);

            foreach (string file in Directory.GetFiles(destDirName, "*.*", SearchOption.AllDirectories))
                pbxProj.AddFileToBuild(targetGuid, pbxProj.AddFile(file, file.Replace(pathToBuiltProject + "/", ""), PBXSourceTree.Source));
        }
        #endregion

        pbxProj.WriteToFile(projPath);
    }

    static void EditInfoPlist(string filePath)
    {
        string path = filePath + "/Info.plist";

        PlistDocument plistDocument = new PlistDocument();
        plistDocument.ReadFromFile(path);

        PlistElementDict dict = plistDocument.root.AsDict();

        PlistElementArray array = dict.CreateArray("CFBundleURLTypes");
        PlistElementDict dict2 = array.AddDict();
        dict2.SetString("CFBundleURLName", PlayerSettings.bundleIdentifier);
        PlistElementArray array2 = dict2.CreateArray("CFBundleURLSchemes");
        array2.AddString(PlayerSettings.bundleIdentifier);

        dict2 = array.AddDict();
        dict2.SetString("CFBundleURLName", "weixin");
        array2 = dict2.CreateArray("CFBundleURLSchemes");
        array2.AddString(BabybusConst.WEIXIN_ID);

        dict2 = array.AddDict();
        dict2.SetString("CFBundleURLName", "");
        array2 = dict2.CreateArray("CFBundleURLSchemes");
        array2.AddString("QQ" + BabybusConst.QQ_ID.ToString("X"));

        dict2 = array.AddDict();
        dict2.SetString("CFBundleURLName", "");
        array2 = dict2.CreateArray("CFBundleURLSchemes");
        array2.AddString("tencent" + BabybusConst.QQ_ID);


        #region quick action
        string[] quickActions = { "Poem", "Pet", "Movie", "Telephone" };
        string[] quickActionsIcon = { "PoemIcon", "PetIcon", "MovieIcon", "TelephoneIcon" };
        //string[] icons = { "UIApplicationShortcutIconTypeBookmark", "UIApplicationShortcutIconTypeLove", "UIApplicationShortcutIconTypeCaptureVideo", "UIApplicationShortcutIconTypeFavorite" };
        array = dict.CreateArray("UIApplicationShortcutItems");
        for(int i=0; i<quickActions.Length; ++i)
        {
            dict2 = array.AddDict();
            //dict2.SetString("UIApplicationShortcutItemIconType", icons[i]);
            dict2.SetString("UIApplicationShortcutItemIconFile", quickActionsIcon[i]);
            dict2.SetString("UIApplicationShortcutItemTitle", quickActions[i] + "Title");
            dict2.SetString("UIApplicationShortcutItemType", quickActions[i]);
            dict2.CreateDict("UIApplicationShortcutItemUserInfo");
            //dict2.SetString("UIApplicationShortcutItemSubtitle", quickActions[i]);
        }
        #endregion

        dict.SetString("CFBundleIdentifier", PlayerSettings.bundleIdentifier);

        var assetInfos = Utility.DeserializeXmlFromFile<List<AssetInfo>>(Application.dataPath + "/Resources/配置/APP.xml");
        array = dict.CreateArray("LSApplicationQueriesSchemes");
        foreach (var assetInfo in assetInfos)
        {
            if (string.IsNullOrEmpty(assetInfo.bundleIdentifier4iOS))
                array.AddString(assetInfo.extra);
            else
                array.AddString(assetInfo.bundleIdentifier4iOS);
        }

        plistDocument.WriteToFile(path);
    }

    static void EditUnityAppController(string pathToBuiltProject)
    {
        string unityAppControllerPath = pathToBuiltProject + "/Classes/UnityAppController.mm";
        if (File.Exists(unityAppControllerPath))
        {
            string headerCode = "#include \"../Libraries/Plugins/iOS/SDKPlatformIOS.h\"\n" +
                                "#import <AVFoundation/AVAudioSession.h>\n\n";
            string unityAppController = headerCode + File.ReadAllText(unityAppControllerPath);

            Match match = Regex.Match(unityAppController, @"- \(void\)startUnity:\(UIApplication\*\)application\s+\{[^}]+\}");
            if(match.Success)
            {
                string newCode = match.Groups[0].Value.Remove(match.Groups[0].Value.Length - 1);
                newCode += "\n" +
                           "    [[AVAudioSession sharedInstance] setCategory: AVAudioSessionCategoryPlayback error: nil];\n" +
                           "    [[AVAudioSession sharedInstance] setActive:YES error:nil];\n" +
                           "}\n\n" +
                           "- (void)application:(UIApplication*)application performActionForShortcutItem: (UIApplicationShortcutItem*)shortcutItem completionHandler: (void(^)(BOOL))completionHandler\n" +
                           "{\n" +
                           "    [[SDKPlatform share] performActionForShortcutItem:shortcutItem];\n" +
                           "}";
                unityAppController = unityAppController.Replace(match.Groups[0].Value, newCode);
            }

            File.WriteAllText(unityAppControllerPath, unityAppController);
        }
    }
}

#endif
    public static void CopyDirectory(string srcPath, string dstPath, string[] excludeExtensions, bool overwrite = true)
    {
        if (!Directory.Exists(dstPath))
            Directory.CreateDirectory(dstPath);

        foreach (var file in Directory.GetFiles(srcPath, "*.*", SearchOption.TopDirectoryOnly).Where(path => excludeExtensions == null || !excludeExtensions.Contains(Path.GetExtension(path))))
        {
            File.Copy(file, Path.Combine(dstPath, Path.GetFileName(file)), overwrite);
        }

        foreach (var dir in Directory.GetDirectories(srcPath))
            CopyDirectory(dir, Path.Combine(dstPath, Path.GetFileName(dir)), excludeExtensions, overwrite);
    }

Paste_Image.png

using UnityEngine;
using UnityEditor;
using UnityEditor.Callbacks;
using System.IO;
using UnityEditor.iOS.Xcode;
using System.Collections.Generic;
using System.Linq;
using Babybus.Framework.Extension;

namespace Babybus.Builder
{
    public class BuildiOSPlayer : BuildPlayerBase
    {
        public const string projectFrameworks = "./../ProjectFrameworks";

        [PostProcessBuild]
        static void OnPostProcessBuild(BuildTarget target, string pathToBuiltProject)
        {
            if (target != BuildTarget.iOS)
                return;

            Debug.Log("pathToBuiltProject = " + pathToBuiltProject);

            EditProj(pathToBuiltProject);
            EditInfoPlist(pathToBuiltProject);

            if (Application.platform == RuntimePlatform.OSXEditor)
                Xcode2IPA(pathToBuiltProject);
        }

        static void Xcode2IPA(string pathToBuiltProject)
        {
            var workspace = BabySystem.workspace;
            Debug.Log(workspace);

            var channel = currentPackChannelItem.Channel;
            var ipaName = GetProductPath(currentPackChannelItem);
            var ipaPath = workspace + ipaName;

            var bashPath = Application.dataPath + "/BabyFramework/Shell/xcodebuild.sh";
            ProcessHelper.StartProcess("/bin/chmod", "+x " + bashPath);

            var xcodeVersion = EditorPrefs.GetString("JenkinsBuilder.xcodeVersion");
            if (xcodeVersion == "")
                xcodeVersion = "7.3.1";

            var mobileprovision = EditorPrefs.GetString("JenkinsBuilder.mobileprovision");
            mobileprovision = "\"" + mobileprovision + "\"";

            ProcessHelper.StartProcess("/bin/bash", bashPath + " /Applications/Xcode_" + xcodeVersion + ".app " + workspace + pathToBuiltProject + " " + ipaPath + " " + mobileprovision);
        }

        private static List<string> GetBuildFiles(string path)
        {
            var directories = Directory.GetDirectories(path);

            var files = Directory.GetFiles(path).ToList();

            foreach (var directory in directories)
            {
                var name = Path.GetFileName(directory);

                if (name == ".svn")
                    continue;

                if (name == "Languages")
                    continue;

                if (name == "AdPlatform" && !BabySystem.HasAD())
                    continue;

#if BABYBUS_SHARE
                if (name == "WeChat" && !directory.Contains("UMSocial"))
                    continue;
#else
                if (name == "UMSocial")
                    continue;
#endif

                if (directory.EndsWith(".framework") || directory.EndsWith(".bundle") || directory.EndsWith(".xcassets"))
                    files.Add(directory);
                else
                    files.AddRange(GetBuildFiles(directory));
            }

            return files;
        }

        static void EditProj(string pathToBuiltProject)
        {
            var projPath = pathToBuiltProject + "/Unity-iPhone.xcodeproj/project.pbxproj";

            var pbxProj = new PBXProject();
            pbxProj.ReadFromFile(projPath);

            var targetGuid = pbxProj.TargetGuidByName("Unity-iPhone");
            //string debugConfig = pbxProj.BuildConfigByName(target, "Debug");
            //string releaseConfig = pbxProj.BuildConfigByName(target, "Release");
            //pbxProj.SetBuildPropertyForConfig(debugConfig, "GCC_ENABLE_OBJC_EXCEPTIONS", "YES");
            //pbxProj.SetBuildPropertyForConfig(releaseConfig, "GCC_ENABLE_OBJC_EXCEPTIONS", "YES");
            pbxProj.SetBuildProperty(targetGuid, "ENABLE_BITCODE", "NO");
            pbxProj.SetBuildProperty(targetGuid, "GCC_ENABLE_OBJC_EXCEPTIONS", "YES");

            var sign = EditorPrefs.GetString("JenkinsBuilder.sign", "iPhone Distribution: BABYBUS CO., LIMITED (AMSTN4PRF9)");
            pbxProj.SetBuildProperty(targetGuid, "CODE_SIGN_IDENTITY", sign);

            var provision = EditorPrefs.GetString("JenkinsBuilder.provision", "f4f9a377-57f0-4e0d-81c0-00904ac17378");
            pbxProj.SetBuildProperty(targetGuid, "PROVISIONING_PROFILE", provision);

            pbxProj.SetBuildProperty(targetGuid, "CODE_SIGN_ENTITLEMENTS", "Libraries/BabyFrameWork/Babybus.entitlements");

            pbxProj.SetBuildProperty(targetGuid, "GCC_C_LANGUAGE_STANDARD", "gnu99");

            var definitions = "BB_APP_3D";

            if (BabySystem.GetIsForKids())
                definitions += " BB_PUBLISH_KIDS";

            if (!BabySystem.HasAD())
                definitions += " BABYBUS_NOAD";

#if BABYBUS_SHARE
            definitions += " BABYBUS_SHARE";
#endif

            pbxProj.SetBuildProperty(targetGuid, "GCC_PREPROCESSOR_DEFINITIONS", definitions);

            pbxProj.AddFrameworkToProject(targetGuid, "AVKit.framework", true);
            pbxProj.AddFrameworkToProject(targetGuid, "Security.framework", true);

            pbxProj.AddFrameworkToProject(targetGuid, "ImageIO.framework", true);

            pbxProj.AddFrameworkToProject(targetGuid, "AssetsLibrary.framework", true);

            if (BabySystem.HasAD())
                pbxProj.AddFrameworkToProject(targetGuid, "AdSupport.framework", true);

#if BABYBUS_TALK2KIKI
            pbxProj.AddFrameworkToProject(targetGuid, "AddressBook.framework", true);//讯飞语言识别需要用到
#endif

            pbxProj.AddFrameworkToProject(targetGuid, "EventKit.framework", true);
            pbxProj.AddFrameworkToProject(targetGuid, "EventKitUI.framework", true);
            pbxProj.AddFrameworkToProject(targetGuid, "CFNetwork.framework", true);
            pbxProj.AddFrameworkToProject(targetGuid, "WebKit.framework", true);
            pbxProj.AddFrameworkToProject(targetGuid, "CoreGraphics.framework", true);
            pbxProj.AddFrameworkToProject(targetGuid, "CoreTelephony.framework", true);
            pbxProj.AddFrameworkToProject(targetGuid, "CoreLocation.framework", true);
            pbxProj.AddFrameworkToProject(targetGuid, "MessageUI.framework", true);
            pbxProj.AddFrameworkToProject(targetGuid, "MediaPlayer.framework", true);
            pbxProj.AddFrameworkToProject(targetGuid, "CoreMotion.framework", true);
            pbxProj.AddFrameworkToProject(targetGuid, "StoreKit.framework", true);
            pbxProj.AddFrameworkToProject(targetGuid, "CoreMedia.framework", true);
            pbxProj.AddFrameworkToProject(targetGuid, "MobileCoreServices.framework", true);


            pbxProj.AddFileToBuild(targetGuid, pbxProj.AddFile("usr/lib/libsqlite3.dylib", "libsqlite3.dylib", PBXSourceTree.Sdk));
            pbxProj.AddFileToBuild(targetGuid, pbxProj.AddFile("usr/lib/libz.dylib", "libz.dylib", PBXSourceTree.Sdk));

            pbxProj.AddBuildProperty(targetGuid, "OTHER_LDFLAGS", "-ObjC");

            pbxProj.AddBuildProperty(targetGuid, "FRAMEWORK_SEARCH_PATHS", "$(SRCROOT)/Libraries/BabyFrameWork/**");
            pbxProj.AddBuildProperty(targetGuid, "HEADER_SEARCH_PATHS", "$(SRCROOT)/Libraries/BabyFrameWork/**");
            pbxProj.AddBuildProperty(targetGuid, "LIBRARY_SEARCH_PATHS", "$(SRCROOT)/Libraries/BabyFrameWork/**");

            var framework = BabySystem.workspace + projectFrameworks;

            var files = GetBuildFiles(framework);

            var compileFlags = new List<string>()
            {
                "-fno-objc-arc"
            };

            string[] arcDirectories = { "/AFNetworking/", "/BabyBusPlayer/", "/HBRSAHandler/",
                                        "/SVPProgressHUD/", "/UIKit+AFNetworking/", "/Masonry/",
                                        "/UMSocial/"
                                      };

            foreach (var file in files)
            {
                var path = file.Replace("\\", "/");

                var projectPath = "Libraries/BabyFrameWork/" + path.Replace(framework, "");

                FileTools.CopyFileOrDirectory(path, pathToBuiltProject + "/" + projectPath, true);

                var fileGuid = pbxProj.AddFile(projectPath, projectPath, PBXSourceTree.Source);

                pbxProj.AddFileToBuild(targetGuid, fileGuid);

                if (path.EndsWith(".a") || path.EndsWith(".m") || path.EndsWith(".mm") || path.EndsWith(".c") || path.EndsWith(".cpp"))
                {
                    var hasCompileFlags = true;
                    foreach (var directory in arcDirectories)
                    {
                        if (path.Contains(directory))
                        {
                            hasCompileFlags = false;
                            break;
                        }
                    }

                    if (hasCompileFlags)
                        pbxProj.SetCompileFlagsForFile(targetGuid, fileGuid, compileFlags);
                }
            }


            var languages = Directory.GetDirectories(framework + "/Languages");

            foreach (var lprojPath in languages)
            {
                var lprojName = Path.GetFileName(lprojPath);

                if (!lprojName.EndsWith(".lproj"))
                    continue;

                if (BabySystem.IsArabicApp && lprojName != "ar.lproj" || !BabySystem.IsArabicApp && lprojName == "ar.lproj")
                    continue;

                var language = "";

                if (lprojName == "zh_CN.lproj" && BabySystem.IsLanguageSupported("zh"))
                    language = "zh";

                if (lprojName == "zh_TW.lproj" && BabySystem.IsLanguageSupported("zht"))
                    language = "zht";

                if (language == "")
                {
                    language = lprojName.Remove(lprojName.Length - ".lproj".Length);
                    if (!BabySystem.IsLanguageSupported(language))
                        continue;
                }

                var projectPath = lprojName;
                FileTools.CopyFileOrDirectory(lprojPath, pathToBuiltProject + "/" + projectPath, true);

                File.WriteAllText(pathToBuiltProject + "/" + projectPath + "/InfoPlist.strings", "\"CFBundleDisplayName\"=\"" + BabySystem.GetProductName(language) + "\";");

                var fileGuid = pbxProj.AddFolderReference(projectPath, projectPath, PBXSourceTree.Source);
                pbxProj.AddFileToBuild(targetGuid, fileGuid);
            }

            pbxProj.WriteToFile(projPath);
        }

        static void EditInfoPlist(string filePath)
        {
            var path = filePath + "/Info.plist";

            var plistDocument = new PlistDocument();
            plistDocument.ReadFromFile(path);

            var dict = plistDocument.root.AsDict();

            var array = dict.CreateArray("CFBundleURLTypes");
            var dict2 = array.AddDict();
            dict2.SetString("CFBundleURLName", PlayerSettings.bundleIdentifier);
            var array2 = dict2.CreateArray("CFBundleURLSchemes");
            array2.AddString(PlayerSettings.bundleIdentifier);

            var wxappid = BuildProductKeys.GetValue("ios_um_wxappid");
            if (!string.IsNullOrEmpty(wxappid))
            {
                dict2 = array.AddDict();
                dict2.SetString("CFBundleURLName", "weixin");
                array2 = dict2.CreateArray("CFBundleURLSchemes");
                array2.AddString(wxappid);
            }

            var qqappid = BuildProductKeys.GetValue("ios_um_qqappid");
            if (!string.IsNullOrEmpty(qqappid))
            {
                dict2 = array.AddDict();
                dict2.SetString("CFBundleURLName", "");
                array2 = dict2.CreateArray("CFBundleURLSchemes");
                array2.AddString("tencent" + qqappid);

                dict2 = array.AddDict();
                dict2.SetString("CFBundleURLName", "");
                array2 = dict2.CreateArray("CFBundleURLSchemes");
                array2.AddString("QQ" + int.Parse(qqappid).ToString("X"));
            }

            var sinakey = BuildProductKeys.GetValue("ios_um_sinakey");
            if (!string.IsNullOrEmpty(sinakey))
            {
                dict2 = array.AddDict();
                dict2.SetString("CFBundleURLName", "");
                array2 = dict2.CreateArray("CFBundleURLSchemes");
                array2.AddString("wb" + sinakey);
            }

            //[[UIApplication sharedApplication] openURL:[NSURL URLWithString:@"prefs:root=WIFI"]];
            dict2 = array.AddDict();
            dict2.SetString("CFBundleURLName", "");
            array2 = dict2.CreateArray("CFBundleURLSchemes");
            array2.AddString("prefs");

            dict.SetString("CFBundleIdentifier", Application.bundleIdentifier);

            var lines = File.ReadAllText("iOS产品白名单").Split('\n');

            array = dict.CreateArray("LSApplicationQueriesSchemes");

            foreach (var line in lines)
            {
                array.AddString(line.Replace("\r", "").Replace("\n", ""));
            }

            dict.SetString("View controller-based status bar appearance", "NO");

            plistDocument.WriteToFile(path);
        }
    }
}

xcodebuild.sh

#!/bin/sh

xcodePath=$1
projectPath=$2

archivePath="build/Unity-iPhone.xcarchive"
exportPath=$3 #Unity-iPhone.ipa
provisioningProfile=$4 #iOS_Distribution

cd $projectPath

${xcodePath}/Contents/Developer/usr/bin/xcodebuild -scheme Unity-iPhone clean archive -archivePath $archivePath
${xcodePath}/Contents/Developer/usr/bin/xcodebuild -exportArchive -exportFormat ipa -archivePath $archivePath -exportPath $exportPath -exportProvisioningProfile "$provisioningProfile"


# ${xcodePath}/Contents/Developer/usr/bin/xcodebuild build

# cd ${projectPath}/build/Release-iphoneos/

# ${xcodePath}/Contents/Developer/usr/bin/xcrun -sdk iphoneos packageapplication -v *.app -o $exportPath

exit $?

 

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模式就可以编译了,但开发起来没法调试。

 

//

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值