使用 命令行/WinForm 来打包Unity可执行程序

article/2025/10/22 20:36:48

使用 命令行/WinForm 来打包Unity可执行程序

  • 前言
  • 一、编辑Editor打包工具
    • 1.编辑打包脚本
    • 2.Unity编辑器中样式
  • 二、命令行调用Unity打包函数
    • 1.Unity命令行常用参数
    • 2.命令行调用静态打包函数
  • 三、使用WinForm创建可视化操作界面
    • 1.可视化界面
    • 2.代码详细:
      • ①CmdHelper类:
      • ②打包EXE的ButtonClick1:
      • ③完整代码:
  • 四、运行效果
  • 五、参考资料
  • 总结和展望


前言

各位Unity开发者都一定被漫长的项目打包过程折磨过,同时公司的Git库一般都不上传各种日志文档和环境配置,克隆下来打开再打包可执行程序繁琐又耗时,这些对测试人员进行打包测试非常的不友好。
是否可以开发一款软件,让测试人员在GIT库上克隆仓库后可以直接进行打包,不再需要打开Unity来打包呢?
因此我想到了使用命令行来对Unity进行打包,再用WinForm套了个外壳,让操作更符合人的习惯。

一、编辑Editor打包工具

1.编辑打包脚本

在项目目录下新建一个C#脚本文件,命名为BuildScript,可以在该脚本中修改需要打包的场景,打包的目标路径和打包之后的程序名。
BuildScript代码如下:

using UnityEditor;
using UnityEngine;public class BuildScript
{private static string[] scenes = { "Assets/Scenes/SampleScene.unity" };         //要打包的场景private static string Path = "Build";                                           //打包的目标路径,默认为父路径下的Build文件夹private static string GameName = "MyGame";                                      //打包后的程序名/// <summary>/// 打包成EXE/// </summary>[MenuItem("Build/Build EXE")]public static void BuildEXE(){string WindowsPath = "/Windows/";BuildPipeline.BuildPlayer(scenes, Path + WindowsPath + GameName + ".exe", BuildTarget.StandaloneWindows, BuildOptions.None);Debug.Log(Path + WindowsPath + GameName + ".exe"+"已生成");}/// <summary>/// 打包成APK/// </summary>[MenuItem("Build/Build APK")]public static void BuildAPK(){string AndroidPath = "/Android/";BuildPipeline.BuildPlayer(scenes, Path + AndroidPath + GameName + ".apk", BuildTarget.Android, BuildOptions.None);}
}

2.Unity编辑器中样式

保存BuildScript脚本之后,回到 Unity 点击菜单栏 Build-Build EXE或Build-Build APK 即可进行打包。
在这里插入图片描述

二、命令行调用Unity打包函数

1.Unity命令行常用参数

可以查看:Unity命令行官方文档

-batchmode
在 批处理模式下运行Unity,它不会弹出窗口。当脚本代码在执行过程中发生异常或其他操作失败时Unity将立即退出,并返回代码为1。
-quit
命令执行完毕后将退出Unity编辑器。请注意,这可能会导致错误消息被隐藏(但他们将显示在Editor.log文件)
-buildWindowsPlayer
构建一个32位的Windows平台的exe(例如:-buildWindowsPlayer path/to/your/build.exe)
-buildWindows64Player
构建一个64位的Windows平台的exe(例如:-buildWindows64Player path/to/your/build.exe)
-importPackage
导入一个的package,不会显示导入对话框
-createProject
根据提供的路径建立一个空项目
-projectPath
打开指定路径的项目
-logFile
指定输出的日志文件
-nographics
当运行在批处理模式,不会初始化显卡设备,不需要GPU参与;但如果你需要执行光照烘焙等操作,则不能使用这个参数,因为它需要GPU运算。
-executeMethod
在Unity启动的同时会执行静态方法。也就是说,使用executeMethod我们需要在编辑文件夹有一个脚本并且类里有一个静态函数。
-single-instance
在同一时间只允许一个游戏实例运行。如果另一个实例已在运行,然后再次通过-single-instance启动它的话会调节到现有的这个实例。
-nolog
不产生输出日志。 通常output_log.txt被写在游戏输出目录下的*_Data文件夹中

2.命令行调用静态打包函数

在编译器所在目录下:
在这里插入图片描述

使用命令行输入,其中 “C:\Unity Project\PureMVC_study2” 是工程所在的目录,
-executeMethod BuildScript.BuildEXE是调用BuildScript中的静态方法BuildEXE。

Unity.exe -batchmode -nographics -quit -projectPath "C:\Unity Project\PureMVC_study2" -executeMethod BuildScript.BuildEXE

命令行执行完毕后会在工程目录下的Build文件夹中生成EXE文件。

注意:一个Unity工程只能打开一个实例,所以如果我们已经手动用Unity打开了工程,此时执行该命令是会报错的。请确保已关闭相应的工程。

在这里插入图片描述

三、使用WinForm创建可视化操作界面

此处可能用Python来写会更轻量快捷,因本人Python能力有限,故用C#来编写WinForm可视化界面。

1.可视化界面

可视化界面如下:
在这里插入图片描述

2.代码详细:

①CmdHelper类:

该类下RunCmd方法实现了调用CMD命令窗口

public class CmdHelper{private static string CmdPath = @"C:\Windows\System32\cmd.exe";/// <summary>/// 执行cmd命令/// 多命令请使用批处理命令连接符:/// <![CDATA[/// &:同时执行两个命令/// |:将上一个命令的输出,作为下一个命令的输入/// &&:当&&前的命令成功时,才执行&&后的命令/// ||:当||前的命令失败时,才执行||后的命令]]>/// 其他请百度/// </summary>/// <param name="cmd"></param>/// <param name="output"></param>public static void RunCmd(string cmd, out string output){cmd = cmd.Trim().TrimEnd('&') + "&exit";//说明:不管命令是否成功均执行exit命令,否则当调用ReadToEnd()方法时,会处于假死状态using (Process p = new Process()){p.StartInfo.FileName = CmdPath;p.StartInfo.UseShellExecute = false;        //是否使用操作系统shell启动p.StartInfo.RedirectStandardInput = true;   //接受来自调用程序的输入信息p.StartInfo.RedirectStandardOutput = true;  //由调用程序获取输出信息p.StartInfo.RedirectStandardError = true;   //重定向标准错误输出p.StartInfo.CreateNoWindow = true;          //不显示程序窗口p.Start();//启动程序//向cmd窗口写入命令p.StandardInput.WriteLine(cmd);p.StandardInput.AutoFlush = true;//获取cmd窗口的输出信息output = p.StandardOutput.ReadToEnd();p.WaitForExit();//等待程序执行完退出进程p.Close();}}}

②打包EXE的ButtonClick1:

private void button1_Click(object sender, EventArgs e){MessageBox.Show("EXE正在打包中");string EditorPath = textBox1.Text;      //编辑器地址string GamePath = textBox2.Text;        //要打包的程序地址string cmd = @"cd " + EditorPath + "&&" + "Unity.exe -batchmode -nographics -quit -projectPath \"" + GamePath + "\" -executeMethod BuildScript.BuildEXE";GamePath = textBox1.Text;string output = "";CmdHelper.RunCmd(cmd, out output);MessageBox.Show("EXE打包完成");}

点击ButtonClick1可以成功打包EXE。但此时有一个影响用户体验的地方,在点击ButtonClick1之后窗口会卡死,无法对窗口拖动和缩放,只有在打包运行完后才恢复正常。经过查询发现是线程卡死问题,所以可以新建一个线程来异步执行这一段代码,从而不影响主窗口的正常使用。修改完代码如下:

private void button1_Click(object sender, EventArgs e){Task.Run(async () =>{MessageBox.Show("EXE正在打包中");string EditorPath = textBox1.Text;      //编辑器地址string GamePath = textBox2.Text;        //要打包的程序地址string cmd = @"cd " + EditorPath + "&&" + "Unity.exe -batchmode -nographics -quit -projectPath \"" + GamePath + "\" -executeMethod BuildScript.BuildEXE";GamePath = textBox1.Text;string output = "";CmdHelper.RunCmd(cmd, out output);MessageBox.Show("EXE打包完成");});}

主窗口在打包时可以正常拖动缩放,但需要注意的是上文所提到的
一个Unity工程只能打开一个实例,所以如果我们已经手动用Unity打开了工程,此时执行该命令是会报错的。请确保已关闭相应的工程
在我们使用命令行打包的时候本质上其实也是手动打开了Unity,所以我们需要禁止用户在打包的过程中重复对按钮进行点击。
这里我想到了修改button的enable属性,但直接在这段异步线程代码中修改enable属性是行不通的,程序虽然不会报错,但异步操作中的代码不会运行。因为C#只有主线程才能访问控件。从 .NET Framework 2.0 类库开始,.NET框架就对于 WinForm 中采用多线程调用窗体控件进行了安全性检测,就是说我从另外一个不是主线程的线程去调用窗体控件的话,就会出现异常。

以下有两个解决办法:
一是直接设置 System.Windows.Forms.Control.CheckForIllegalCrossThreadCalls = false; 这种方法简单粗暴,但十分不推荐使用,因为可能会导致不安全。
二是使用委托,使用Invoke方法来访问UI线程,以此对enable属性进行操作。这种方法安全可靠,值得推荐。修改完后代码如下:

 private void button1_Click(object sender, EventArgs e){Task.Run(async () =>{// 访问UI线程button1.Invoke((MethodInvoker)delegate{// 更改Enabled属性button1.Enabled = false;});button2.Invoke((MethodInvoker)delegate{// 更改Enabled属性button2.Enabled = false;});MessageBox.Show("EXE正在打包中");string EditorPath = textBox1.Text;      //编辑器地址string GamePath = textBox2.Text;        //要打包的程序地址string cmd = @"cd " + EditorPath + "&&" + "Unity.exe -batchmode -nographics -quit -projectPath \"" + GamePath + "\" -executeMethod BuildScript.BuildEXE";GamePath = textBox1.Text;string output = "";CmdHelper.RunCmd(cmd, out output);MessageBox.Show("EXE打包完成");button1.Invoke((MethodInvoker)delegate{// 更改Enabled属性button1.Enabled = true;});button2.Invoke((MethodInvoker)delegate{// 更改Enabled属性button2.Enabled = true;});});}

③完整代码:

using System;
using System.Diagnostics;
using System.Threading.Tasks;
using System.Windows.Forms;namespace AutoBuild
{public partial class Form1 : Form{//string  cmd = "cd C:\Program Files\Unity\Hub\Editor\2021.3.20f1c1\Editor && Unity.exe -batchmode -nographics -quit -projectPath ""C:\Unity Project\PureMVC_study2"" -executeMethod BuildScript.BuildEXE"public string GamePath = "";delegate void MyDele();public Form1(){InitializeComponent();//Control.CheckForIllegalCrossThreadCalls = false;	// 这句代码就是关闭了安全检查}private void button1_Click(object sender, EventArgs e){Task.Run(async () =>{// 访问UI线程button1.Invoke((MethodInvoker)delegate{// 更改Enabled属性button1.Enabled = false;});button2.Invoke((MethodInvoker)delegate{// 更改Enabled属性button2.Enabled = false;});//button1.Enabled = false;                //一个Unity工程只能打开一个实例,所以不能同时打包多种可运行程序,这里禁止按键的点击防止用户多次点击打包//button2.Enabled = false;MessageBox.Show("EXE正在打包中");string EditorPath = textBox1.Text;      //编辑器地址string GamePath = textBox2.Text;        //要打包的程序地址string cmd = @"cd " + EditorPath + "&&" + "Unity.exe -batchmode -nographics -quit -projectPath \"" + GamePath + "\" -executeMethod BuildScript.BuildEXE";GamePath = textBox1.Text;string output = "";CmdHelper.RunCmd(cmd, out output);MessageBox.Show("EXE打包完成");button1.Invoke((MethodInvoker)delegate{// 更改Enabled属性button1.Enabled = true;});button2.Invoke((MethodInvoker)delegate{// 更改Enabled属性button2.Enabled = true;});//button1.Enabled = true;//button2.Enabled = true;});}private void button2_Click(object sender, EventArgs e){Task.Run(async () =>{// 访问UI线程button1.Invoke((MethodInvoker)delegate{// 更改Enabled属性button1.Enabled = false;});button2.Invoke((MethodInvoker)delegate{// 更改Enabled属性button2.Enabled = false;});//button1.Enabled = false;                //一个Unity工程只能打开一个实例,所以不能同时打包多种可运行程序,这里禁止按键的点击防止用户多次点击打包//button2.Enabled = false;MessageBox.Show("APK正在打包中");string EditorPath = textBox1.Text;string GamePath = textBox2.Text;string cmd = @"cd " + EditorPath + "&&" + "Unity.exe -batchmode -nographics -quit -projectPath \"" + GamePath + "\" -executeMethod BuildScript.BuildAPK";GamePath = textBox1.Text;string output = "";CmdHelper.RunCmd(cmd, out output);MessageBox.Show("APK打包完成");button1.Invoke((MethodInvoker)delegate{// 更改Enabled属性button1.Enabled = true;});button2.Invoke((MethodInvoker)delegate{// 更改Enabled属性button2.Enabled = true;});//button1.Enabled = true;//button2.Enabled = true;});}public class CmdHelper{private static string CmdPath = @"C:\Windows\System32\cmd.exe";/// <summary>/// 执行cmd命令/// 多命令请使用批处理命令连接符:/// <![CDATA[/// &:同时执行两个命令/// |:将上一个命令的输出,作为下一个命令的输入/// &&:当&&前的命令成功时,才执行&&后的命令/// ||:当||前的命令失败时,才执行||后的命令]]>/// 其他请百度/// </summary>/// <param name="cmd"></param>/// <param name="output"></param>public static void RunCmd(string cmd, out string output){cmd = cmd.Trim().TrimEnd('&') + "&exit";//说明:不管命令是否成功均执行exit命令,否则当调用ReadToEnd()方法时,会处于假死状态using (Process p = new Process()){p.StartInfo.FileName = CmdPath;p.StartInfo.UseShellExecute = false;        //是否使用操作系统shell启动p.StartInfo.RedirectStandardInput = true;   //接受来自调用程序的输入信息p.StartInfo.RedirectStandardOutput = true;  //由调用程序获取输出信息p.StartInfo.RedirectStandardError = true;   //重定向标准错误输出p.StartInfo.CreateNoWindow = true;          //不显示程序窗口p.Start();//启动程序//向cmd窗口写入命令p.StandardInput.WriteLine(cmd);p.StandardInput.AutoFlush = true;//获取cmd窗口的输出信息output = p.StandardOutput.ReadToEnd();p.WaitForExit();//等待程序执行完退出进程p.Close();}}}private void label2_Click(object sender, EventArgs e){}}
}

四、运行效果

1.初始界面
在这里插入图片描述

2.输入相关路径,点击打包:
在这里插入图片描述

3.打包过程中禁止用户再次点击打包,但可对主窗口进行拖动缩放操作:
在这里插入图片描述

4.打包完成弹出打包完成窗口:
在这里插入图片描述

5.打包后的EXE和APK文件:
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

五、参考资料

CSDN博客_Unity 工具 之 Jenkins 打包自动化工具的下载/安装/基本操作/任务创建执行/Unity打包自动化简单搭建的相关整理
博客园_C#程序调用cmd.exe执行命令
CSDN博客_解决C#跨线程调用窗体控件的问题
Unity官方文档

总结和展望

本文探究了使用命令行来对Unity进行打包,同时使用WinForm构建了一个可视化界面以供用户使用。
本项目还有点缺陷,开发者需要在项目里写好BuildScript,外部才能运行其中的静态打包方法,未来希望可以直接在WinForm上对BuildScriot中的保存场景、项目名称进行修改,直接插入项目中,无需提前写好BuildScript.


http://chatgpt.dhexx.cn/article/HOkqyDKp.shtml

相关文章

Unity + Jenkins自动打包 (二)构建Jenkins项目以及编写Python、Unity脚本

1、新建Jenkens项目 在上一篇中&#xff0c;完成了Jenkins的安装和初始化&#xff0c;以及权限设置。 查看上一篇&#xff1a;Jenkins安装 点此 现在打开浏览器&#xff0c;输入http://localhost:8081&#xff0c;当然&#xff0c;需要改成你自己设置的Jenkins端口号&#xff…

unity python服务器_Unity如何连接服务器: 一个简单的例子

Unity3D本身是用来做客户端的通用游戏引擎, 要建立网络连接的话, 其实需要使用的是C#本身的网络和线程模块, 即System.Net.Sockets & System.Threading. 本文中我做了一个简单的例子, 适合那些需要做Unity客户端连接服务器功能的人入门. 整体项目 客户端: 我做的项目主要是…

unity + python socket通信,自定义数据包

unity和python相互之间通过socket通信来发送自定义数据包是一个利用unity构建场景和通过python来做数据处理的方式&#xff0c;能够有效的利用两种不同语言的优势。 我已经将对应的操作封装为对应的一个模块&#xff0c;SocketTools.cs&#xff0c;我们先来看一下具体的代码用…

假设电话收费标准为: (1)国际长途1.00元/分钟,(2) 国内长途0.60元/分钟,(3)市话前3分钟0.20元,3分钟以后0.10元/分钟。现假设某话单文件中每条话单包含如下信息:通话日期(10

你好&#xff0c;帮我做个C语言题目 你好&#xff0c;我是Assistant。很高兴为您做题。 请提供题目的完整题面&#xff0c;并提供任何相关的输入、输出、约束条件等信息。 如果您有任何其他问题&#xff0c;请随时与我联系。我将竭诚为您服务。 897354667qq.com 假设电话收…

电商用户行为数据分析实战(MySQL +PowerBI)

目录 一、项目概况二、数据源三、数据清洗3.1 选择子集导入&#xff0c;匹配适合的数据类型3.2 列重命名3.3重复值处理3.4 缺失值处理3.5 异常值处理从timestamps字段中提取日期数据列查看日期列数据异常情况 四、数据分析4.1 整体购物情况&#xff0c;基于AARRR模型4.1.1 各字…

【业务数据分析】——十大常用数据分析方法

&#x1f935;‍♂️ 个人主页&#xff1a;Lingxw_w的个人主页 ✍&#x1f3fb;作者简介&#xff1a;计算机科学与技术研究生在读 &#x1f40b; 希望大家多多支持&#xff0c;我们一起进步&#xff01;&#x1f604; 如果文章对你有帮助的话&#xff0c; 欢迎评论 &#x1f4a…

基于订单的数据分析

目录 项目背景 数据理解 指标维度 指标梳理 维度梳理 导入数据 数据预处理 数据格式整理 规范字段名 增加字段 简化地址 缺失值处理 异常值分析​ 重复值处理 数据分析 描述性统计 总体销售情况 周趋势、日趋势分析 产品价格分析 地区分析 转化率分析 总结…

大数据培训 | 电商用户行为分析之订单支付实时监控

在电商网站中&#xff0c;订单的支付作为直接与营销收入挂钩的一环&#xff0c;在业务流程中非常重要。对于订单而言&#xff0c;为了正确控制业务流程&#xff0c;也为了增加用户的支付意愿&#xff0c;网站一般会设置一个支付失效时间&#xff0c;超过一段时间不支付的订单就…

订单数据分析

订单背景 订单&#xff1a;对订单的预测不仅为了企业更好的制定物料采购计划、控制库存、提升生产效率、控制生产进度&#xff0c;还为了帮助企业更好的把控市场潜在需求&#xff0c;分析目前经营状态和未来发展趋势。 宽厚板材市场价格&#xff08;只能查询到近三个月的&…

关于订单功能的处理和分析

这两天看了一下RABC的权限管理处理&#xff0c;梳理了一下订单功能的表创建&#xff0c;界面&#xff0c;功能分析。 目录 RABC RBAC0模型 那么对于RABC模型我们怎么创建数据库表&#xff1f; 订单模块的梳理 RABC RABC说的是在用户和权限之间多一个角色&#xff0c;用户与…

订单数据分析-实战

1. 京东订单数据准备 1.1 京东订单数据介绍 2020年5月25日10%抽样数据大家电-家用电器-冰箱70K 1.2 数据清洗 缺失值处理 用户城市和省份信息有部分缺失&#xff0c;部分订单的订单中支付时间为空值数据逻辑错误格式内容一致性 import pandas as pd import numpy as np im…

话单数据完整流程

原始数据__解析_____>>>解析后的数据___入库____>>>汇总的数据 1.原始数据 上游中兴的原始数据&#xff0c;在远程桌面Winscp软件中查看。丢失了下游也没法补充采集。得等上游补充采集后下游才能解析。当原始数据存在&#xff0c;而话单数据显示红点&…

话单分析账单分析行踪分析三合一数据分析

这里写自定义目录标题 欢迎使用Markdown编辑器新的改变功能快捷键合理的创建标题&#xff0c;有助于目录的生成如何改变文本的样式插入链接与图片如何插入一段漂亮的代码片生成一个适合你的列表创建一个表格设定内容居中、居左、居右SmartyPants 创建一个自定义列表如何创建一个…

Office Tool Plus(安装visio)

说明&#xff1a;需要提前卸载原先的Office&#xff08;Word、PPT、Excel等&#xff09; 一、Office Tool Plus官网 https://otp.landian.vip/zh-cn/download.html 二、下载Office Tool Plus 百度网盘链接&#xff08;Office Tool Plus安装包&#xff09; 链接&#xff1a;…

FFmpeg音频解码-音频可视化

最近在做一个音频可视化的业务&#xff0c;网上有Java层的实现方法&#xff0c;但是业务需要用C实现&#xff0c;从原理出发其实很简单&#xff0c;先对音频进行解码&#xff0c;再计算分贝。这比把大象放进冰箱还简单。本文从音频可视化的业务为依托&#xff0c;以FFmpeg为基础…

基于FFmpeg的视频播放器之七:音频解码

一.流程 音频解码的流程和视频解码几乎一样,最大的区别是解码后需要进行重采样。因为解码出的AVSampleFormat格式是AV_SAMPLE_FMT_FLTP(float, planar),该格式无法直接使用SDL进行播放,需要转换成SDL支持的AV_SAMPLE_FMT_S16(signed 16 bits)格式。关于重采样,详见下篇…

2020手机音频解码芯片_2020杰理音频芯片全解析,14款音频产品代表作拆解汇总...

珠海市杰理科技股份有限公司,成立于2010年。杰理科技主要从事射频智能终端、多媒体智能终端等系统级芯片(SoC)的研究、开发和销售。 杰理科技的芯片产品主要应用于AI智能音箱、蓝牙音箱、蓝牙耳机、智能语音玩具等物联网智能终端产品,下游应用产品市场十分广泛和巨大。 杰理科…

音频编解码原理

实例说明 音频编解码常用的实现方案有三 种。 第一种就是采用专用的音频芯片对 语音信号进行采集和处理&#xff0c;音频编解码算法集成在硬件内部&#xff0c;如 MP3 编解码芯片、语音合成 分析芯片等。使用这种方案的优点就是处理速度块&#xff0c;设计周期短&#xff1b;缺…

基于STM32音频解码MP3——vs1053

基于正点原子教程 VS1053简介&#xff1a; 1.该模块采用VS1053B 作为主芯片 2.支持&#xff1a;MP3/WMA/OGG/WAV/FLAC/MIDI/AAC 等音频格式的解码 3.支持&#xff1a;OGG/WAV 音频格式的录音&#xff0c;支持高低音调节以及 EarSpeaker 空间效果设置 模块如图所示正点原子 AL…

ijkplayer音频解码播放架构分析

ijkplayer是一款跨平台播放器&#xff0c;支持Android与iOS播放&#xff0c;音频解码默认使用FFmpeg的avcodec软解。Android端播放音频可以用OpenSL ES和AudioTrack&#xff0c;而iOS端播放音频默认使用AudioQueue。 一、iOS音频解码播放 采用pipeline形式创建音频播放组件&a…