美文网首页Unity3D 成神之路unity3D技术分享
Unity与iOS交互,Unity脚本修改Xcode工程

Unity与iOS交互,Unity脚本修改Xcode工程

作者: cdcyd | 来源:发表于2021-02-22 15:05 被阅读0次

    Unity与iOS交互

    • Unity调用iOS的方法,首先在Xcode中新建一个iOS的桥接类,并且将.m的后缀修改为.mm
    • 在.h中加入以下代码,里面的函数包括 无返回,返回字符串,返回布尔,带参数的函数等
      这里需要注意一个问题,传入的参数和返回的字符串最好都使用json格式
    #if defined(__cplusplus)
    extern "C"{
    #endif
        // 获取系统语言
        extern char *GetLanguage();
        // 开始震动
        extern void StartVibration(char *param);
        // 是否是全面屏
        extern bool IsFullSecreen();
        // Unity iOS 字符串内存管理
        extern char* CharMemoryManagement(NSString *param);
    #if defined(__cplusplus)
    }
    #endif
    
    • 在.mm中的实现,这里需要注意的是,返回字符串的时候,只能通过CharMemoryManagement方法将字符串转换成char,我试过其他很多种转换方式,都会造成程序崩溃的问题,Tools类里面就是方法的具体实现,代码与本文无关就不贴出来了
    #if defined(__cplusplus)
    extern "C"{
    #endif
        char *GetLanguage()
        {
            NSString *str = [Tools GetLanguage];
            return CharMemoryManagement(str);
        }
        void StartVibration(char *param)
        {
            [Tools StartVibration:[NSString stringWithUTF8String:param]];
        }
        bool IsFullSecreen()
        {
            return [Tools IsFullSecreen];
        }
        // 如果方法返回的是字符串,需要使用该方法将字符串转为char
        char* CharMemoryManagement(NSString *text)
        {
            char* ret = nullptr;
            ret = (char*) malloc([text length] + 1);
            memcpy(ret,[text UTF8String],([text length] + 1));
            return ret;
        }
    #if defined(__cplusplus)
    }
    #endif
    
    • 上面的步骤完成之后,将.h和.mm文件(包括Tools等依赖的文件)拷贝到Unity工程中Assets目录下
    • 在Unity中,新建一个cs脚本,添加以下代码,这个脚本最好实现成单例,然后就可以通过Instance.IOSGetLanguage()来调用
      注意需要引用 using System.Runtime.InteropServices 命名空间
    #if UNITY_IOS && !UNITY_EDITOR
        [DllImport("__Internal")] private static extern string GetLanguage();
        [DllImport("__Internal")] private static extern bool IsFullSecreen();
        [DllImport("__Internal")] private static extern void StartVibration(string param);
    #endif
    
    #region Unity to iOS
        public string IOSGetLanguage()
        {
    #if UNITY_IOS && !UNITY_EDITOR
            return GetLanguage();
    #else
            return "";
    #endif
        }
        public void IOSStartVibration(long time)
        {
    #if UNITY_IOS && !UNITY_EDITOR
            StartVibration(time.ToString());
    #endif
        }
        public bool IOSIsFullSecreen()
        {
    #if UNITY_IOS && !UNITY_EDITOR
            return IsFullSecreen();
    #else
            return false;
    #endif
        }
    
    • iOS通知Unity,iOS直接调用Unity方法的实现是非常麻烦的,通常情况下,我们都使用通知的方法,常见的场景是Unity调用iOS方法需要异步返回时
    • 在iOS类中加入下面代码,然后我们就可以给Unity发送通知了,如UnitySendMessage("节点名称", "方法名称", "参数,没有就传空字符串"",不能传nil")
    // --------- 某个.mm文件中 ---------
    #if defined(__cplusplus)
    extern "C"{
    #endif
        extern void UnitySendMessage(const char *, const char *, const char *);
    #if defined(__cplusplus)
    }
    #endif
    
    // --------- 需要通知Unity的iOS类中 ---------
    - (void)didReceiveReward {
        // 在iOS的某个方法中,向Unity发送消息
        UnitySendMessage("iOSLibraryUnity", "OnDidReceiveReward", "收到奖励");
    }
    
    //  --------- Unity中挂在节点上的脚本,用来接收通知 ---------
    private void Start()
    {
        // 脚本挂载的节点名必须和UnitySendMessage发送时填的一样
        this.name = "iOSLibraryUnity";
    }
    private void OnDidReceiveReward(string msg)
    {
        // 接收到iOS通知
        Debug.log(msg);
    }
    

    Unity脚本修改Xcode工程

    Unity要在iOS平台发布,需要先生成Xcode工程,通常生成Xcode工程后我们还需要修改很多的配置,添加原生代码等, 而这些是可以通过cs脚本修改的,比如修改Xcode工程的plist、添加Framework库、拷贝文件到iOS工程、插入代码等

    • 自动pod
      实现自动pod需要谷歌的一个插件https://github.com/googlesamples/unity-jar-resolver,该插件在谷歌相关的一些SDK中就有,如OnsSignal、Firebase等SDK,如你应用集成有这些SDK,则不需要再下载该插件了,查看是否集成了该插件可以看你Assets目录下有没有ExternalDependencyManager文件,或看Assets->External Dependency Manager有没有这个选项
    • 集成完插件后,在Editor目录下新建一个Dependencies.xml的文件,里面的内容如下,这样在生成Xcode工程时就会自动将下面的库pod进工程
    <dependencies>
      <iosPods>
        <iosPod name="AFNetworking" version="4.0.1"/>
        <iosPod name="SDWebImage" version="5.8.4"/>
        <iosPod name="Masonry" version="1.1.0"/>
        <iosPod name="YYModel" version="1.0.4"/>
      </iosPods>
    </dependencies>
    
    • 修改Xcode工程,在Editor中新建一个cs脚本,如下
    using UnityEditor;
    using UnityEditor.Callbacks;
    using UnityEditor.iOS.Xcode;
    using UnityEditor.XCodeEditor;
    
    public static class BuildiOS
    {
        [PostProcessBuild(100)]
        public static void OnPostprocessBuild(BuildTarget buildTarget, string buildPath)
        {
            if (buildTarget != BuildTarget.iOS)
                return;
            var mProjectPath = PBXProject.GetPBXProjectPath(buildPath);
            var mProject = new PBXProject();
            mProject.ReadFromString(File.ReadAllText(mProjectPath));
    
            var mTargetGUID = GetPBXProjectTargetGUID(mProject);
            var mFrameworkGUID = GetPBXProjectUnityFrameworkGUID(mProject);
    
            var mPlistPath = Path.Combine(buildPath, "Info.plist");
    
            // 修改Plist
            PlistModify(mPlistPath);
    
            // 添加系统库
            SystemFrameworkAdd(mProject, mFrameworkGUID);
    
            // 添加文件
            FilesAdd(mProject, mTargetGUID, buildPath, mProjectPath);
    
            // 插入代码
            UnityAppControllerCodesAdd(buildPath);
        }
    
    #if UNITY_2019_3_OR_NEWER
        private static string GetPBXProjectTargetGUID(PBXProject project)
        {
            return project.GetUnityMainTargetGuid();
        }
        private static string GetPBXProjectUnityFrameworkGUID(PBXProject project)
        {
            return project.GetUnityFrameworkTargetGuid();
        }
    #else
        private static string GetPBXProjectTargetGUID(PBXProject project)
        {
            return project.TargetGuidByName(PBXProject.GetUnityTargetName());
        }
        private static string GetPBXProjectUnityFrameworkGUID(PBXProject project)
        {
            return GetPBXProjectTargetGUID(project);
        }
    #endif
    }
    
    • 修改plist
    private static void PlistModify(string plistPath)
    {
        var plist = new PlistDocument();
        plist.ReadFromFile(plistPath);
    
        // plist中添加一个字符串类型的key 如隐私设置
        plist.root.SetString("NSLocationAlwaysAndWhenInUseUsageDescription",
                             "$(PRODUCT_NAME) needs to get the location. I hope you agree.";
    
        // plist中添加一个布尔的key
        plist.root.SetBoolean("CADisableMinimumFrameDuration", false);
    
        // plist中添加一个字典的key 如ATS设置
        PlistElementDict dict = plist.root.CreateDict("NSAppTransportSecurity");
        dict.SetBoolean("NSAllowsArbitraryLoads", true);
            
        // 最后保存plist
        plist.WriteToFile(plistPath);
    }
    
    • 添加系统库
    private static void SystemFrameworkAdd(PBXProject project, string mFrameworkGUID) 
    {
        string[] FRAMEWORKS_TO_ADD = {
            "libz.dylib",
            "libc++.dylib",
            "Security.framework",
            "SystemConfiguration.framework",
        };
        foreach (var framework in FRAMEWORKS_TO_ADD)
        {
            project.AddFrameworkToProject(mFrameworkGUID, framework, false);
        }
    }
    private static void SystemLibAdd(PBXProject project, string targetGuid, string lib)
    {
        string fileGuid = project.AddFile("usr/lib/" + lib, "Frameworks/" + lib, PBXSourceTree.Sdk);
        project.AddFileToBuild(targetGuid, fileGuid);
    }
    
    • 拷贝文件夹,代码文件如.h/.m等文件会自动拷贝的Xcode工程中,但图片,三方的Framework、lib等文件并不会自动拷贝到Xcode工程中,所以需要cs脚本来完成
    private static void FilesAdd(PBXProject project, string mTargetGUID, string buildPath, string projectPath)
    {
        string ResourcePath = "Assets/文件夹路径";
        XcodeDirectoryProcessor copy = new XcodeDirectoryProcessor();
        copy.CopyAndAddBuildToXcode(project, mTargetGUID, ResourcePath, buildPath, "Xcode中的文件夹名称");
        File.WriteAllText(projectPath, project.WriteToString());
    }
    
    • 插入代码
    private static void UnityAppControllerCodesAdd(string buildPath)
    {
        // 获取Prefix.pch文件
        string mPchPath = buildPath + "/Classes/Prefix.pch";
        UnityEditor.XCodeEditor.XClass Pch = new UnityEditor.XCodeEditor.XClass(mPchPath);
        // 需要插入的代码,例如我们在pch中插入一段引入类的代码
        string call = "#import \"output.h\"";
        // 代码标记,找到pch文件里面已经存在的代码,我们就可以将需要插入的代码,插入到这行代码下面
        string mark = "#include \"UnityInterface.h\"";
        // 开始插入代码
        Pch.WriteBelow(mark, call);
    }
    
    • 其他Editor中使用到的cs文件
    using UnityEngine;
    using System.IO;
    
    #if UNITY_IOS
    using UnityEditor.iOS.Xcode;
    
    public static class ExtensionName
    {
        public const string META = ".meta";
        public const string ARCHIVE = ".a";
        public const string FRAMEWORK = ".framework";
        public const string BUNDLE = ".bundle";
    }
    
    public class XcodeDirectoryProcessor {
    
        /// <summary>
        /// 添加编译本地文件到Xcode工程
        /// </summary>
        /// <param name="pbxProject"></param>
        /// <param name="targetGuid"></param>
        /// <param name="copyDirectoryPath">源路径</param>
        /// <param name="buildPath">拷贝路径</param>
        /// <param name="currentDirectoryPath">拷贝路径/currentDirectoryPath</param>
        /// <param name="needToAddBuild"></param>
        public void CopyAndAddBuildToXcode(PBXProject pbxProject, string targetGuid, string copyDirectoryPath, string buildPath, string currentDirectoryPath, bool needToAddBuild = true){
    
            string unityDirectoryPath = copyDirectoryPath;
            string xcodeDirectoryPath = buildPath;
    
            if(!string.IsNullOrEmpty(currentDirectoryPath)){
                unityDirectoryPath = Path.Combine(unityDirectoryPath, currentDirectoryPath);
                xcodeDirectoryPath = Path.Combine(xcodeDirectoryPath, currentDirectoryPath);
                Delete (xcodeDirectoryPath);
                Directory.CreateDirectory(xcodeDirectoryPath);
            }
            foreach (string filePath in Directory.GetFiles(unityDirectoryPath)){
                string extension = Path.GetExtension (filePath);
                if(extension == ExtensionName.META){
                    continue;
                }
                else if(extension == ExtensionName.ARCHIVE){
                    pbxProject.AddBuildProperty(
                        targetGuid, 
                        "LIBRARY_SEARCH_PATHS", 
                        "$(PROJECT_DIR)/" + currentDirectoryPath
                    );
                }
                string fileName = Path.GetFileName (filePath);
                string copyPath = Path.Combine (xcodeDirectoryPath, fileName);
                if(fileName[0] == '.'){
                    continue;
                }
                File.Delete(copyPath);
                File.Copy(filePath, copyPath);
                if(needToAddBuild){
                    string relativePath = Path.Combine(currentDirectoryPath, fileName);
                    pbxProject.AddFileToBuild(targetGuid, pbxProject.AddFile(relativePath, relativePath, PBXSourceTree.Source));
                }
            }
            //遍历文件夹
            foreach (string directoryPath in Directory.GetDirectories(unityDirectoryPath)){
                string directoryName = Path.GetFileName (directoryPath);
                bool nextNeedToAddBuild = needToAddBuild;
                if(directoryName.Contains(ExtensionName.FRAMEWORK) || directoryName.Contains(ExtensionName.BUNDLE) || 
                   directoryName == "Unity-iPhone"){
                    nextNeedToAddBuild = false;
                }
                CopyAndAddBuildToXcode (
                    pbxProject, targetGuid, 
                    copyDirectoryPath, buildPath, Path.Combine(currentDirectoryPath, directoryName), 
                    nextNeedToAddBuild
                );
                if(directoryName.Contains(ExtensionName.FRAMEWORK) || directoryName.Contains(ExtensionName.BUNDLE)){
                    string relativePath = Path.Combine(currentDirectoryPath, directoryName);
                    pbxProject.AddFileToBuild(targetGuid, pbxProject.AddFile(relativePath, relativePath, PBXSourceTree.Source));
                    pbxProject.AddBuildProperty(
                        targetGuid, 
                        "FRAMEWORK_SEARCH_PATHS", 
                        "$(PROJECT_DIR)/" + currentDirectoryPath
                    );
                }
            }
        }
    
        /// <summary>
        /// 拷贝目录(拷贝时候会删除copyPath)
        /// </summary>
        /// <param name="sourcePath"></param>
        /// <param name="copyPath"></param>
        public void CopyAndReplace(string sourcePath, string copyPath)
        {
            Delete (copyPath);
            Directory.CreateDirectory(copyPath);
            foreach (var file in Directory.GetFiles(sourcePath)){
                File.Copy(file, Path.Combine(copyPath, Path.GetFileName(file)));
            }
            foreach (var dir in Directory.GetDirectories(sourcePath)){
                CopyAndReplace(dir, Path.Combine(copyPath, Path.GetFileName(dir)));
            }
        }
    
        /// <summary>
        /// 删除目录
        /// </summary>
        /// <param name="targetDirectoryPath"></param>
        public void Delete(string targetDirectoryPath){
            if (!Directory.Exists (targetDirectoryPath)) {
                return;
            }
            string[] filePaths = Directory.GetFiles(targetDirectoryPath);
            foreach (string filePath in filePaths){
                File.SetAttributes(filePath, FileAttributes.Normal);
                File.Delete(filePath);
            }
            string[] directoryPaths = Directory.GetDirectories(targetDirectoryPath);
            foreach (string directoryPath in directoryPaths){
                Delete(directoryPath);
            }
            Directory.Delete(targetDirectoryPath, false);
        }
    }
    
    #endif
    
    

    相关文章

      网友评论

        本文标题:Unity与iOS交互,Unity脚本修改Xcode工程

        本文链接:https://www.haomeiwen.com/subject/snwffltx.html