美文网首页
iOS-Unity2019.4.24f1c1使用Jenkins自

iOS-Unity2019.4.24f1c1使用Jenkins自

作者: OneKeyV | 来源:发表于2021-05-13 17:42 被阅读0次

    iOS-Unity2018.4.24f1使用Jenkins自动化构建

    Unity2019开始逐步将作为iOS framework形式接入工程,相比之前版本极大的方便了工程的维护。

    本文脚本是从Unity生成xcode工程之后开始执行,不一定完全适用,请按照自己需求进行修改。

    注意修改脚本中“xxxxxx”的地方。

    一.安装xcodeproj

    #安装ruby xcodeproj库
    $gem install xcodeproj
    #如果提示没有权限运行下面这条命令:
    $sudo gem install xcodeproj
    

    二、配置xcodeproj工程

    1、Unity调用native的协议类
    NativeCallProxy.h

    // [!] important set UnityFramework in Target Membership for this file
    // [!]           and set Public header visibility
    
    #import <UIKit/UIKit.h>
    
    // NativeCallsProtocol defines protocol with methods you want to be called from managed
    @protocol NativeCallsProtocol
    
    @required
    - (void)ShowNativeWindow;
    - (void)NativeSupportService:(NSString *)json;
    @end
    
    __attribute__ ((visibility("default")))
    @interface UnityFrameworkLibAPI : NSObject
    
    // call it any time after UnityFrameworkLoad to set object implementing NativeCallsProtocol methods
    + (void)RegisterAPIforNativeCalls:(id<NativeCallsProtocol>)aApi;
    
    // 原生发送消息给Unity
    + (void)SendMessageToUnity:(NSString *)obj method:(NSString *)method msg:(NSString *)msg;
    
    + (void)OutOfAshes;
    
    @end
    
    

    NativeCallProxy.mm

    #include "NativeCallProxy.h"
    #include "UnityInterface.h"
    #include "UnityAppController.h"
    #include "UnityAppController+OutOfAshes.h"
    
    @implementation UnityFrameworkLibAPI
    
    id<NativeCallsProtocol> api = NULL;
    
    + (void)load {
        extern const char* AppControllerClassName;
        AppControllerClassName = "AppDelegate";
    }
    
    + (void)RegisterAPIforNativeCalls:(id<NativeCallsProtocol>)aApi {
        api = aApi;
    }
    
    + (void)SendMessageToUnity:(NSString *)obj method:(NSString *)method msg:(NSString *)msg {
        UnitySendMessage([obj UTF8String], [method UTF8String], [msg UTF8String]);
    }
    
    + (void)OutOfAshes {
        [GetAppController() outOfAshes];
    }
    
    @end
    
    extern "C" void ShowNativeWindow()
    {
         return [api ShowNativeWindow];
    }
    
    /// Core Methods
    extern "C" void NativeSupportService(const char *JSON)
    {
        return [api NativeSupportService:[NSString stringWithUTF8String:JSON]];
    }
    
    /// Displays unity views after the startup diagram ends
    extern "C" void UnityOutOfAshes()
    {
        [GetAppController() outOfAshes];
    }
    
    

    2、控制启动图消失分类
    UnityAppController+OutOfAshes.h

    //
    //  UnityAppController+OutOfAshes.h
    //  xxxxxx
    //
    //  Created by xxxxxx on 2021/5/12.
    //
    
    #include "UnityAppController.h"
    
    NS_ASSUME_NONNULL_BEGIN
    
    @interface UnityAppController (OutOfAshes)
    
    - (void)outOfAshes;
    
    @end
    
    NS_ASSUME_NONNULL_END
    
    

    UnityAppController+OutOfAshes.mm

    //
    //  UnityAppController+OutOfAshes.m
    //  xxxxxx
    //
    //  Created by xxxxxx on 2021/5/12.
    //
    
    #include "UnityAppController+OutOfAshes.h"
    #include "UnityAppController+Rendering.h"
    
    #include "UI/OrientationSupport.h"
    #include "UI/UnityView.h"
    #include "UI/UnityViewControllerBase.h"
    #include "UI/ActivityIndicator.h"
    
    extern bool _skipPresent;
    extern bool _unityAppReady;
    
    
    @interface UnityAppController ()
    
    - (void)transitionToViewController:(UIViewController*)vc;
    
    - (void)updateAppOrientation:(UIInterfaceOrientation)orientation;
    
    - (UIViewController*)createUnityViewControllerDefault;
    
    - (void)orientInterface:(UIInterfaceOrientation)orient;
    
    @end
    
    @implementation UnityAppController (OutOfAshes)
    
    - (void)outOfAshes
    {
        if (_rootController != _viewControllerForOrientation[0])
            [self transitionToViewController: _viewControllerForOrientation[0]];
    }
    
    - (void)checkOrientationRequest
    {
        if (!UnityHasOrientationRequest() && !UnityShouldChangeAllowedOrientations())
            return;
    
        // normally we want to call attemptRotationToDeviceOrientation to tell iOS that we changed orientation constraints
        // but if the current orientation is disabled we need special processing, as iOS will simply ignore us
        //   the only good/robust way is to simply recreate "autorotating" view controller and transition to it if needed
    
        // please note that we want to trigger "orientation request" code path if we recreate autorotating view controller
        bool changeOrient = UnityHasOrientationRequest();
    
        // first we check if we need to update orientations enabled for autorotation
        // this needs to be done *only* if we are to continue autorotating
        //   otherwise we will transition from this view controller
        //   and iOS will reread enabled orientations on next ViewController activation
        const bool autorot = UnityShouldAutorotate();
        if (UnityShouldChangeAllowedOrientations() && autorot)
        {
            NSUInteger rootOrient = 1 << UIViewControllerInterfaceOrientation(self.rootViewController);
            if (_rootController == _viewControllerForOrientation[0] && (rootOrient & EnabledAutorotationInterfaceOrientations()))
            {
                // if we are currently autorotating AND changed allowed orientations while keeping current interface orientation allowed:
                // we can simply trigger attemptRotationToDeviceOrientation and we are done
                // please note that this can happen when current *device* orientation is disabled (and we want to enable it)
                [UIViewController attemptRotationToDeviceOrientation];
            }
            else
            {
                // otherwise we recreate default autorotating view controller
                // please note that below we will check if root controller still equals _viewControllerForOrientation[0]
                // in that case (we update _viewControllerForOrientation[0]) the check will fail and will trigger transition (as expected)
                // you may look at this check as "are we autorotating with same constraints"
                _viewControllerForOrientation[0] = [self createUnityViewControllerDefault];
                changeOrient = true;
            }
        }
    
        if (changeOrient)
        {
            // on some devices like iPhone XS layoutSubview is not called when transitioning from different orientations with the same resolution
            // therefore forcing layoutSubview on all orientation changes
            [_unityView setNeedsLayout];
    
            if (autorot)
            {
                if (_viewControllerForOrientation[0] == nil)
                    _viewControllerForOrientation[0] = [self createUnityViewControllerDefault];
    //            if (_rootController != _viewControllerForOrientation[0])
    //                [self transitionToViewController: _viewControllerForOrientation[0]];
                [UIViewController attemptRotationToDeviceOrientation];
            }
            else
            {
                UIInterfaceOrientation requestedOrient = ConvertToIosScreenOrientation((ScreenOrientation)UnityRequestedScreenOrientation());
                // on one hand orientInterface: should be perfectly fine "reorienting" to current orientation
                // in reality, ios might be confused by transitionToViewController: shenanigans coupled with "nothing have changed actually"
                // as an example: prior to ios12 that might result in status bar going "bad" (becoming transparent)
                if (_rootController != _viewControllerForOrientation[requestedOrient])
                    [self orientInterface: requestedOrient];
            }
        }
    
        UnityOrientationRequestWasCommitted();
    }
    
    - (void)showGameUI
    {
        HideActivityIndicator();
    //    HideSplashScreen();
    
        // make sure that we start up with correctly created/inited rendering surface
        // NB: recreateRenderingSurface won't go into rendering because _unityAppReady is false
    #if UNITY_SUPPORT_ROTATION
        [self checkOrientationRequest];
    #endif
        [_unityView recreateRenderingSurface];
    
        // UI hierarchy
        //[_window addSubview: _rootView];
        //_window.rootViewController = _rootController;
        //[_window bringSubviewToFront: _rootView];
    
    #if UNITY_SUPPORT_ROTATION
        // to be able to query orientation from view controller we should actually show it.
        // at this point we finally started to show game view controller. Just in case update orientation again
        [self updateAppOrientation: ConvertToIosScreenOrientation(UIViewControllerOrientation(_rootController))];
    #endif
    
        // why we set level ready only now:
        // surface recreate will try to repaint if this var is set (poking unity to do it)
        // but this frame now is actually the first one we want to process/draw
        // so all the recreateSurface before now (triggered by reorientation) should simply change extents
    
        _unityAppReady = true;
    
        // why we skip present:
        // this will be the first frame to draw, so Start methods will be called
        // and we want to properly handle resolution request in Start (which might trigger surface recreate)
        // NB: we want to draw right after showing window, to avoid black frame creeping in
    
        _skipPresent = true;
    
        if (!UnityIsPaused())
            UnityRepaint();
    
        _skipPresent = false;
        [self repaint];
    
        [UIView setAnimationsEnabled: YES];
    }
    
    @end
    
    NativeCallProxy类主要是提供Unity和native交互功能,UnityAppController+OutOfAshes类控制启动图移除,满足去除app启动图到显示UnityUI中间白屏的需求。
    这两类作为实体文件放入Unity工程的iOS插件使用。

    3、配置Xcode工程脚本

    #执行脚本示例:
    $ruby '配置脚本文件路径' 'Unity生成的xcode工程根目录'
    
    require "fileutils"
    require 'xcodeproj'
    
    unity_project_name="xxxxxx"
    unity_iPhone_project_dir=$1
    
    FileUtils.cd(unity_iPhone_project_dir) do
    
        #给UnityFramework.h添加“#import "NativeCallProxy.h"”
        def add_import_nativeCallProxy_h_to_unityFramework_h()
            unityFramework_header_target_place = "#import \"UnityAppController.h\""
            unityFramework_header_target_replace = "#import \"UnityAppController.h\"\n#import \"NativeCallProxy.h\""
    
            File.open("./UnityFramework/UnityFramework.h","r:utf-8") do |lines|
                read = lines.read.clone
                if read.include?(unityFramework_header_target_replace)
                    puts "\nUnityFramework.h已包含“#import \"NativeCallProxy.h\"”"
                    return false
                else
                    buffer = read.gsub(unityFramework_header_target_place,unityFramework_header_target_replace)
                    File.open("./UnityFramework/UnityFramework.h","w"){|l|
                        l.write(buffer)
                    }
                    puts "\033[32mUnityFramework.h添加“#import \"NativeCallProxy.h\"”\n\033[0m\n"
                    return true
                end
            end
        end    
    
        #修改UNITY_TRAMPOLINE_IN_USE配置
        def change_unity_use_remote_notifications_flag()
            unity_use_remote_notifications_flag_close = "#define UNITY_USES_REMOTE_NOTIFICATIONS 0"
            unity_use_remote_notifications_flag_open = "#define UNITY_USES_REMOTE_NOTIFICATIONS 1"
    
            File.open("./Classes/Preprocessor.h","r:utf-8") do |lines|
                read = lines.read.clone
                if read.include?(unity_use_remote_notifications_flag_open)
                    puts "\nPreprocessor.h已修改过UNITY_USES_REMOTE_NOTIFICATIONS为1"
                else
                    buffer = read.gsub(unity_use_remote_notifications_flag_close,unity_use_remote_notifications_flag_open)
                    File.open("./Classes/Preprocessor.h","w"){|l|
                        l.write(buffer)
                    }
                end
            end
            puts "\033[32mPreprocessor.h修改UNITY_USES_REMOTE_NOTIFICATIONS为1\n\033[0m\n"
        end
    
        #置灰iPhone X横条
        def hide_iphone_home_indicator()
            uirect_edge_old_config = "- (UIRectEdge)preferredScreenEdgesDeferringSystemGestures\n{\n    UIRectEdge res = UIRectEdgeNone;\n    if (UnityGetDeferSystemGesturesTopEdge())\n        res |= UIRectEdgeTop;\n    if (UnityGetDeferSystemGesturesBottomEdge())\n        res |= UIRectEdgeBottom;\n    if (UnityGetDeferSystemGesturesLeftEdge())\n        res |= UIRectEdgeLeft;\n    if (UnityGetDeferSystemGesturesRightEdge())\n        res |= UIRectEdgeRight;\n    return res;\n}\n\n- (BOOL)prefersHomeIndicatorAutoHidden\n{\n    return UnityGetHideHomeButton();\n}"
            uirect_edge_new_config = "- (UIRectEdge)preferredScreenEdgesDeferringSystemGestures\n{\n   return UIRectEdgeAll;\n}"
            File.open("./Classes/UI/UnityViewControllerBase+iOS.mm","r:utf-8") do |lines|
                read = lines.read.clone
                if read.include?(uirect_edge_new_config)
                    puts "已置灰过iPhoneX横条"
                else
                    buffer = read.gsub(uirect_edge_old_config,uirect_edge_new_config)
                    File.open("./Classes/UI/UnityViewControllerBase+iOS.mm","w"){|l|
                        l.write(buffer)
                    }
                end
            end
            puts "\033[32m置灰iPhone X横条完成\n\033[0m\n"
        end
    
    
    #***************************************** 配置xcodeproj选项 *****************************************#
    
    
        #修改”NativeCallProxy.h“属性为UnityFramework公共头文件
        def add_nativeCallProxy_h_to_unityFramework_public(target)
            target.headers_build_phase.files.each do |file|
                if file.display_name == 'NativeCallProxy.h'
                    file.settings = { "ATTRIBUTES" => ["Public"] }
                    puts "\033[32mNativeCallProxy.h设置为'Public'\n\033[0m\n"
                end 
            end
        end
    
        #”Data“目录Target Membership增加”UnitFramework“
        def add_unityframework_target_membership_for_data_dir(target1, target2)
            target1.resources_build_phase.files_references.each do |file_references|
                if file_references.display_name == 'Data'
                    build_phase = target2.resources_build_phase
                    build_phase.add_file_reference(file_references, true)
                    puts "\033[32m”Data“目录Target Membership增加”UnitFramework“\n\033[0m\n"
                end
            end
        end
    
        #配置Build Settings
        def add_build_settings(target)
            target.build_configurations.each do |config|
            #   获得build settings
                build_settings = config.build_settings
    
                build_settings["CURRENT_PROJECT_VERSION"] = 0
                puts "CURRENT_PROJECT_VERSION ==>  0"
    
                build_settings["ARCHS"] = "arm64";
                puts "Build Settings: ARCHS ==>  arm64"
    
                build_settings["CODE_SIGN_STYLE"] = "Automatic"
                puts "Build Settings: CODE_SIGN_STYLE ==>  Automatic"
    
                build_settings["CODE_SIGN_IDENTITY"] = "Apple Development"
                puts "Build Settings: CODE_SIGN_IDENTITY ==>  Apple Development"
    
                build_settings["DEVELOPMENT_TEAM"] = "xxxxxx"
                puts "Build Settings: DEVELOPMENT_TEAM ==>  xxxxxx"
    
                build_settings["PROVISIONING_PROFILE_SPECIFIER"] = ""
                puts "Build Settings: PROVISIONING_PROFILE_SPECIFIER ==>  "
    
                build_settings["ENABLE_BITCODE"] = "NO";
                puts "Build Settings: ENABLE_BITCODE ==>  NO"
    
                build_settings["GCC_C_LANGUAGE_STANDARD"] = "gnu99";
                puts "Build Settings: GCC_C_LANGUAGE_STANDARD ==>  gnu99"
    
                build_settings["GCC_ENABLE_OBJC_EXCEPTIONS"] = "YES";
                puts "Build Settings: GCC_ENABLE_OBJC_EXCEPTIONS ==>  YES"
                
                puts "\033[32m#{config.name}模式Build Settings 设置完成\n\033[0m\n"
            end
        end
    
    
        #***************************************** 执行配置 *****************************************#
        
    
        #给UnityFramework.h添加“#import "NativeCallProxy.h"”
        if add_import_nativeCallProxy_h_to_unityFramework_h == false
            puts "\nxxxxxx.rb非首次执行 \n\n"
            puts "\033[1m结束配置#{unity_project_name}.xcodeproj\033[0m\n "
            exit 0
        end
    
        #修改UNITY_TRAMPOLINE_IN_USE配置
        change_unity_use_remote_notifications_flag
    
        #置灰iPhone X横条
        hide_iphone_home_indicator
    
        #打开项目工程.xcodeproj
        unity_iPhone_project_file_path = "#{unity_project_name}.xcodeproj"
        project = Xcodeproj::Project.open(unity_iPhone_project_file_path)
    
        unity_iPhone_target = ''
        unityFramework_target = ''
    
        #找到需要操作的target
        project.targets.each_with_index do |target,index|
            if target.name == unity_project_name
                unity_iPhone_target = project.targets[index]
            end
            if target.name == 'UnityFramework'
                unityFramework_target = project.targets[index]
            end
        end
    
        #修改”NativeCallProxy.h“属性为UnityFramework公共头文件
        add_nativeCallProxy_h_to_unityFramework_public(unity_iPhone_target)
    
        #”Data“目录Target Membership增加”UnitFramework“
        add_unityframework_target_membership_for_data_dir(unity_iPhone_target, unityFramework_target)
    
        #配置Build Settings
        add_build_settings(unity_iPhone_target)
    
    
        project.save
    
        puts "\033[1m结束配置#{unity_project_name}.xcodeproj\033[0m\n "
    
    end
    
    exit 0
    
    
    三、编译Xcode工程、导出ipa、发布ipa包到fir和AppStore

    参考上一篇:iOS-Unity2018.4.24f1使用Jenkins自动化构建

    四、工程目录示例图
    主工程main.mm代码.png
    主工程AppDelegate.h代码.png
    五、配置工程中出现的一些错误

    [libil2cpp] ERROR: Could not open /var/containers/Bundle/Application/xxxxxx/xxxxxx.app/Frameworks/UnityFramework.framework/Data/Managed/Metadata/global-metadata.dat IL2CPP initialization failed
    网上有其他解决方法,我集成时出现此错误是因为没有给主工程main.mm添加[ufw setDataBundleId: "com.unity3d.framework"];

    相关文章

      网友评论

          本文标题:iOS-Unity2019.4.24f1c1使用Jenkins自

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