Unity自动打包——Shell交互

Unity 无论是测试还是上线都需要打包,而每次打包我们还要打各种平台(安卓、Ios、WebGL、Windows…),有可能不同的打包时机还要有不同的配置项,这种工作枯燥、繁琐且易错,为了解决这一困扰就想到了能不能做一个工具来专门负责打包,甚至打完包能够自动的发送给测试人员或直接上传至服务器就更完美了。然后查了一些资料,发现Jenkins就是一种特别完美的工具,能够完美的解决我刚才提出的所有问题,至于Jenkins的搭建本章内容不会关注,这篇文章主要是解决Jenkins流水线配置的最后阶段,通过Jenkins调用Shell进而通知Unity自动打包的逻辑。

先看一下Unity官方文档对命令行参数的描写:命令行参数。
可以看出Unity官方其实是支持开发者们通过自动打包工具来提升效率的,并且也准备了大量的命令行参数来进行支持。
再通过对shell编程的了解,我编写了下面的shell脚本来通知Unity自动打包并设置指定配置,如下:

#!/bin/sh#Unity安装路径
UnityPath="C:\Program Files\Unity\Hub\Editor\2020.3.22f1c1\Editor\Unity.exe"
#工程路径
ProjectPath="E:\ARWork\AutoBuildDemo"
#UnityLog输出路径
UnityLogPath="C:\Users\Administrator\Desktop\ShellTest\unitylog.txt"
#需要调用Unity的静态方法
UnityFunc=S.Utility.Editor.ShellHelper.OnReceive#版本号
Version=1.0.0
#目标平台 Android、iOS、WebGL、StandaloneWindows、StandaloneWindows64
BuildTarget=iOS
#获取当前时间
Time=$(date "+%Y-%m-%d_%H-%M-%S")
#输出的app名字或文件夹名字
AppName="OutRoot/$BuildTarget/$Time"
#Unity要执行的行为
Do="Build"echo 开始启动Unity工程并执行方法命令"${UnityPath}" -ProjectPath $ProjectPath -batchmode -quit -executeMethod $UnityFunc -logFile $UnityLogPath Version=$Version BuildTarget=$BuildTarget AppName=$AppName Do=$Do# 控制台输出Unity的Log信息
while read line
doecho $line
done < $UnityLogPathecho Unity处理完毕

我们只需要把UnityPath、ProjectPath配置成自己的路径,UnityLogPath可以写成固定的路径也可以接收Jenkins的参数,UnityFunc
是需要调用的Unity中的静态方法,这个在后面会讲到,至于Version、BuildTarget、AppName这几个参数动态可变我们可以通过$n 来接收Jenkins的参数来进行赋值,Do是通知Unity要做的操作,然后Unity会接收到这个操作标记并对应执行相关操作,这个也会在后面讲。
至于下面的while循环,其实就是读取了Unity执行后的log日志文件并逐行在控制台打印出来。

其实通过上面的解释,我们应该能知道,最重要的点就是UnityFunc和Do这两个字段的含义,UnityFunc是指向Unity工程里的一个静态方法,这个方法不可接受参数,这样就达成了Shell与Unity的通讯,虽然这个静态方法不支持接收参数,但是Unity确做了相关的处理能够拿到shell的参数,其实我们的参数就是Version=$Version BuildTarget=$BuildTarget AppName=$AppName Do=$Do 当然,这里不严谨,其实整段语句都是Unity可接受的参数,只不过我们只取我们关注的这几个信息而已。
那下面我们来看看Unity怎么接受这个shell指令。
ShellHelper

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
using UnityEngine;namespace S.Utility.Editor
{/// <summary>/// Shell传递过来的参数集/// </summary>public interface IShellParams{/// <summary>/// 是否存在参数Key/// </summary>/// <param name="key">参数key</param>/// <returns></returns>bool HasKey(string key);/// <summary>/// 得到参数值/// </summary>/// <param name="key">参数key</param>/// <returns></returns>string GetValue(string key);/// <summary>/// 得到所有的参数key/// </summary>/// <returns></returns>string[] GetKeys();}/// <summary>/// Shell命令执行接口/// </summary>public interface IDo{/// <summary>/// 执行/// </summary>/// <param name="shellParams">Shell参数</param>void Do(IShellParams shellParams);}/// <summary>/// Shell解析辅助器/// </summary>public class ShellHelper{private static Dictionary<string, string> commandLineDict = new Dictionary<string, string>();public static IShellParams shellParams { get; private set; }private static DoConfig config = new DoConfig();/// <summary>/// 去解析doType的行为/// </summary>/// <param name="doType">doType</param>private static void Do(string doType){Debug.Log($"开始处理Do {doType} 行为!");config.GetDo(doType)?.Do(shellParams);}/// <summary>/// 接收到Shell消息/// </summary>public static void OnReceive(){if (shellParams == null) shellParams = new ShellParams();(shellParams as ShellParams).Refresh();string doType = shellParams.GetValue("Do");if (string.IsNullOrEmpty(doType)){Debug.LogError("Unity处理Shell消息失败,未找到Do指令!");}else{Do(doType);}}/// <summary>/// shell参数集的实现/// </summary>private class ShellParams : IShellParams{private Dictionary<string, string> commandLineDict = new Dictionary<string, string>();/// <summary>/// 参数key-value分割符/// </summary>public char splitChar = '=';public void Refresh(){if (commandLineDict == null) commandLineDict = new Dictionary<string, string>();else commandLineDict.Clear();string[] parameters = Environment.GetCommandLineArgs();int paramCount = parameters == null ? 0 : parameters.Length;if (paramCount == 0) return;string pattern = $"^(.*?){splitChar}(.*)";foreach (var p in parameters){Match match = Regex.Match(p, pattern);GroupCollection groups = match.Groups;if (groups == null || groups.Count != 3) commandLineDict[p] = null;else{commandLineDict[groups[1].Value] = groups[2].Value;}}}public bool HasKey(string key){if (commandLineDict == null || commandLineDict.Count == 0) return false;return commandLineDict.ContainsKey(key);}public string GetValue(string key){if (commandLineDict == null || commandLineDict.Count == 0) return null;string value;if (commandLineDict.TryGetValue(key, out value)){return value;}return null;}public string[] GetKeys(){if (commandLineDict == null || commandLineDict.Count == 0) return null;return commandLineDict.Keys.ToArray();}}}
}

DoConfig

namespace S.Utility.Editor
{/// <summary>/// Shell Do配置/// </summary>public class DoConfig{public IDo GetDo(string doType){switch (doType){case "Build":return new DoBuild();default:return null;}}}
}

DoBuild

using System;
using System.IO;
using UnityEditor;
using UnityEngine.SceneManagement;namespace S.Utility.Editor
{/// <summary>/// 打包操作/// </summary>public class DoBuild : IDo{public struct BuildData{public BuildTarget buildTarget;public string version;public string appName;public override string ToString(){return $"BuildTarget:{buildTarget} Version:{version} AppName:{appName}";}}public BuildData buildData { get; private set; }public void Do(IShellParams shellParams){string buildTarget = shellParams.GetValue("BuildTarget");string version = shellParams.GetValue("Version");string appName = shellParams.GetValue("AppName");buildData = new BuildData(){version = string.IsNullOrEmpty(version) ? PlayerSettings.bundleVersion : version,buildTarget = string.IsNullOrEmpty(buildTarget)? EditorUserBuildSettings.activeBuildTarget: (BuildTarget)Enum.Parse(typeof(BuildTarget),buildTarget),appName = string.IsNullOrEmpty(appName) ? DateTime.Now.ToString() : appName};PlayerSettings.bundleVersion = buildData.version;if (EditorUserBuildSettings.activeBuildTarget != buildData.buildTarget) //当前平台不是目标平台{//切换至目标平台BuildTargetGroup buildTargetGroup = BuildPipeline.GetBuildTargetGroup(buildData.buildTarget);EditorUserBuildSettings.SwitchActiveBuildTarget(buildTargetGroup, buildData.buildTarget);}switch (buildData.buildTarget){case BuildTarget.Android:BuildAndroid();break;case BuildTarget.iOS:BuildIos();break;case BuildTarget.WebGL:BuildWebGL();break;case BuildTarget.StandaloneWindows:case BuildTarget.StandaloneWindows64:BuildWindows();break;}}/// <summary>/// 打安卓包/// </summary>void BuildAndroid(){string outPath = buildData.appName + ".apk";BuildPlayerOptions options = GetBaseBuildOptions();options.locationPathName = outPath;BuildPipeline.BuildPlayer(options);}/// <summary>/// 打Ios包/// </summary>void BuildIos(){string outPath = buildData.appName;if (Directory.Exists(outPath)){Directory.Delete(outPath);}BuildPlayerOptions options = new BuildPlayerOptions();options.locationPathName = outPath;options.target = buildData.buildTarget;options.targetGroup = BuildPipeline.GetBuildTargetGroup(buildData.buildTarget);BuildPipeline.BuildPlayer(options);}/// <summary>/// 打WebGL包/// </summary>void BuildWebGL(){string outPath = buildData.appName;if (Directory.Exists(outPath)){Directory.Delete(outPath);}BuildPlayerOptions options = GetBaseBuildOptions();options.locationPathName = outPath;BuildPipeline.BuildPlayer(options);}/// <summary>/// 打Windows包/// </summary>void BuildWindows(){string outPath = buildData.appName;if (Directory.Exists(outPath)){Directory.Delete(outPath);}BuildPlayerOptions options = GetBaseBuildOptions();options.locationPathName = outPath;BuildPipeline.BuildPlayer(options);}/// <summary>/// 得到基础的打包配置/// </summary>/// <returns></returns>private BuildPlayerOptions GetBaseBuildOptions(){BuildPlayerOptions options = new BuildPlayerOptions();options.target = buildData.buildTarget;options.targetGroup = BuildPipeline.GetBuildTargetGroup(buildData.buildTarget);options.scenes = GetAllScenePath();return options;}/// <summary>/// 获取PlayerSettings中所有场景的路径/// </summary>/// <returns></returns>private string[] GetAllScenePath(){int count = SceneManager.sceneCountInBuildSettings;if (count == 0) return null;string[] sceneArray = new string[count];for (int i = 0; i < count; i++){sceneArray[i] = SceneUtility.GetScenePathByBuildIndex(i);}return sceneArray;}}
}

分析一下C#这部分的代码:
首先最主要的就是ShellHelper对象的OnReceive方法,这个方法就是上面的shell中的UnityFunc字段,它负责响应Shell的指令。
然后我们通过(shellParams as ShellParams).Refresh();刷新了shell传递过来的参数,最后包装成IShellParams接口来传递这些参数。
然后我们在ShellHelper对象的Do(string doType) 方法通过DoConfig配置对象来获取对应的IDo执行对象,在执行对象中去具体实现指令逻辑。
所以后期如果我们想解析其它指令,例如BuildAB(打AB包)我们只需要在DoConfig的GetDo(string doType)方法中指定相应的解析IDo对象就可以完美的实现功能。

OK到这里我们的Unity就可以接收Shell的指令和参数了,后续再和Jenkins联动就可以实现自动打包的功能了。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/59972.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

NLP自然语言处理:深入探索Self-Attention——自注意力机制详解

NLP自然语言处理&#xff1a;深入探索Self-Attention——自注意力机制详解 在自然语言处理&#xff08;NLP&#xff09;领域&#xff0c;自注意力机制&#xff08;Self-Attention&#xff09;已经成为一种革命性的技术&#xff0c;特别是在Transformer模型及其各种变种中得到了…

Tomcat中如何指定JDK版本

在Tomcat中指定JDK版本可以通过设置环境变量或修改启动脚本来实现。以下是两种常见的方法&#xff1a; 方法一&#xff1a;通过环境变量 设置JAVA_HOME环境变量&#xff1a; 打开“控制面板” -> “系统和安全” -> “系统” -> “高级系统设置”。点击“环境变量”按…

uniapp在js方法中,获取当前用户的uid(uni-id-user)表中的用户id

// 1.判断当前用的权限 let uid uniCloud.getCurrentUserInfo().uid //获取当前用户的uid // 用户uid等于发布者id或者用户权限等于admin或者用户角色等于webmaster if (uid this.item.user_id[0]._id || this.uniIDHasRole…

【机器学习】均方误差根(RMSE:Root Mean Squared Error)

均方误差根&#xff08;Root Mean Squared Error&#xff0c;RMSE&#xff09;是机器学习和统计学中常用的误差度量指标&#xff0c;用于评估预测值与真实值之间的差异。它通常用于回归模型的评价&#xff0c;以衡量模型的预测精度。 RMSE的定义与公式 给定预测值 和实际值 …

Pandas | 数据分析时将特定列转换为数字类型 float64 或 int64的方法

类型转换 传统方法astype使用value_counts统计通过apply替换并使用astype转换 pd.to_numericx对连续变量进行转化⭐参数&#xff1a;返回值&#xff1a;示例代码&#xff1a; isnull不会检查空字符串 数据准备 有一组数据信息如下&#xff0c;其中主要将TotalCharges、MonthlyC…

web信息收集

区别 CTF中&#xff0c;收集服务器信息、敏感信息、敏感文件 &#xff08;实战中&#xff0c;收集更多的信息来找到渗透的突破口&#xff0c;比如开放的端口、使用的CDN、是否有泄露的源码&#xff09; 一、收集信息 是了解一个web服务的首要途径&#xff0c;可以通过htt…

混沌工程遇上AI:智能化系统韧性测试的前沿实践

#作者&#xff1a;曹付江 文章目录 1、什么是AI驱动的混沌工程&#xff1f;2、AI与混沌工程结合的价值3、技术实现3.1 AI模型开发3.1.1模型选择与构建3.1.2模型训练3.1.3 模型验证与调参3.1.4 模型测试3.1.5 知识库建设与持续学习 4、混沌工程与AI实践结合4.1 利用AI从运维专家…

Redis的线程模型

Redis 的单线程模型详解 Redis 的“单线程”模型主要指的是其 主线程&#xff0c;这个主线程负责从客户端接收请求、解析命令、处理数据和返回响应。为了深入了解 Redis 单线程的具体工作流程&#xff0c;我们可以将其分为以下几个步骤&#xff1a; 接收客户端请求 Redis 的主线…

《深度学习神经网络:颠覆生活的魔法科技与未来发展新航向》

深度学习神经网络对我们生活的影响 一、医疗领域 深度学习神经网络在医疗领域的应用可谓意义重大。在疾病诊断方面&#xff0c;它能够精准分析医疗影像&#xff0c;如通过对大量的 CT、MRI 图像进行深度学习&#xff0c;快速准确地识别出微小的肿瘤病变&#xff0c;为医生提供…

YOLOv11融合特征细化前馈网络 FRFN[CVPR2024]及相关改进思路

YOLOv11v10v8使用教程&#xff1a; YOLOv11入门到入土使用教程 一、 模块介绍 论文链接&#xff1a;Adapt or Rerish 代码链接&#xff1a;https://github.com/joshyZhou/AST 论文速览&#xff1a;基于 transformer 的方法在图像恢复任务中取得了有希望的性能&#xff0c;因为…

K8S简单部署,以及UI界面配置

准备两台服务器K8Smaster和K8Sminion 分别在两台服务器上执行以下代码 #添加hosts解析&#xff1b; cat >/etc/hosts<<EOF 127.0.0.1 localhost localhost.localdomain 192.168.45.133 master1 192.168.45.135 node2 EOF #临时关闭selinux和防火墙&#xff1b; sed …

vue3框架还需要学习什么

一.vue3框架相比较vue2框架有了什么改变&#xff1f; 1.composition api(组合式api) Vue 2&#xff1a;基于 Options API&#xff0c;组件的逻辑和状态被拆分到不同的选项中&#xff0c;如 data、methods、computed、watch 等&#xff0c;可能导致复杂组件中的逻辑分散&#xf…

爬虫 - 二手交易电商平台数据采集 (一)

背景: 近期有一个需求需要采集某电商网站平台的商品数据进行分析。因此&#xff0c;我计划先用Python实现一个简单的版本&#xff0c;以快速测试技术的实现可能性&#xff0c;再用PHP实现一个更完整的版本。文章中涉及的技术仅为学习和测试用途&#xff0c;请勿用于商业或非法用…

Chrome与傲游浏览器性能与功能的深度对比

在当今数字化时代&#xff0c;浏览器作为我们日常上网冲浪、工作学习的重要工具&#xff0c;其性能与功能直接影响着我们的使用体验。本文将对Chrome和傲游两款主流浏览器进行深度对比&#xff0c;帮助用户更好地了解它们的差异&#xff0c;以便做出更合适的选择。&#xff08;…

Docker Compose部署Rabbitmq(延迟插件已下载)

整个工具的代码都在Gitee或者Github地址内 gitee&#xff1a;solomon-parent: 这个项目主要是总结了工作上遇到的问题以及学习一些框架用于整合例如:rabbitMq、reids、Mqtt、S3协议的文件服务器、mongodb github&#xff1a;GitHub - ZeroNing/solomon-parent: 这个项目主要是…

大华乐橙设备私有平台EasyCVR视频设备轨迹回放平台支持哪些摄像机?摄像机如何选型?

在现代安全监控系统中&#xff0c;视频监控设备扮演着至关重要的角色。视频设备轨迹回放平台EasyCVR以其卓越的兼容性和灵活性&#xff0c;支持接入多种品牌和类型的摄像机。这不仅为用户提供了广泛的选择空间&#xff0c;也使得视频监控系统的构建和管理变得更加高效和便捷。本…

数据结构 栈和队列

目录 1. 栈1.1 栈的概念及结构1.2 栈的实现 2. 队列2.1 队列的概念及结构2.2 队列的实现 正文开始 1. 栈 1.1 栈的概念及结构 栈是线性表的一种&#xff0c;这种数据结构只允许在固定的一端进行插入和删除元素的操作&#xff0c;进行数据插入和删除的一端称为栈顶&#xff0c…

2025年入门深度学习或人工智能,该学PyTorch还是TensorFlow?

随着2025应用人工智能和深度学习技术的举世泛气&#xff0c;还在迷茫于该选择哪个深度学习框架吗&#xff1f;PyTorch和TensorFlow是并立于深度学习世界两座巨塔&#xff0c;但是越来越多人发现&#xff0c;在2025年&#xff0c;PyTorch似乎比TensorFlow更为流行和被接受。下面…

Cross Modal Transformer: Towards Fast and Robust 3D Object Detection

代码地址 https://github.com/junjie18/CMT 1. 引言 在本文中&#xff0c;我们提出了Cross-Modal Transformer&#xff08;CMT&#xff09;&#xff0c;这是一种简单而有效的端到端管道&#xff0c;用于鲁棒的3D对象检测&#xff08;见图1&#xff08;c&#xff09;&#xf…

深度学习鲁棒性、公平性和泛化性的联系

深度学习鲁棒性、公平性和泛化性的联系 前言1 鲁棒性、公平性、泛化性本质2 对抗攻击是混杂效应3 因果推理角度3.1 稳定学习 VS 公平性3.2 后门攻击 前言 读研好不容易从边缘智能&#xff0c;费好大劲被允许转到联邦学习赛道&#xff0c;再费了好大劲和机缘巧合被允许转到可信A…