最近调到新项目工作,为了热更将代码移到Assets外部,打成dll给Unity使用,导致Unity无法检测到是否修改,每次修改代码都要使用VS进行手动编译,特别麻烦,有时候都忘了是否进行手动,导致的各种bug,所以简单写了一个小工具进行检测,基本原理是:

  1. 切换到Unity有执行的函数
  2. 读取本地缓存的每个脚本对应的md5码
  3. 依次读取脚本的md5和缓存的md5进行比较
  4. 有修改差异,新增都会检测到,但对于删除的可能检测不到
  5. 调用VS的devenv.com进行编译

至于VS的devenv.com编译以及编译指令可以自行百度学习,我使用的是VS2019,默认安装路径:C:/Program Files (x86)/Microsoft Visual Studio/2019/Enterprise/Common7/IDE

使用此工具需要安装vs,2015,2017,2019以及以上版本均可以,只不过没有测试,代码如下:

using Shark;
using System;
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using UnityEditor;
using System.IO;
using System.Text;
using System.Diagnostics;
using Debug = UnityEngine.Debug;
using System.Threading;/// <summary>
/// 从其他应用切换回unity界面时调用,自动编译Hotfix的代码(代码有改动时调用)
/// 本脚本需挂到游戏物体上,最好是常用的场景,比如登陆场景,在其他场景无法执行到
/// </summary>
[ExecuteInEditMode]
public class AutoBuildScript : MonoBehaviour
{//vs的默认安装路径//[SerializeField]private string default_msbuild = "C:/Program Files (x86)/Microsoft Visual Studio/2019/Enterprise/Common7/IDE";void Update(){//只在编辑模式下且无运行状态下进行编译
#if UNITY_EDITORif (Application.isPlaying)return;CompilerHotfixDll();
#endif}private void CompilerHotfixDll(){
#if UNITY_EDITORif (!CheckChangeFiles())return;if (Application.isPlaying)return;Debug.Log("检测到代码有变化,正在编译.....");string msbuild = GetMSbuildPath();string path = Application.dataPath.Replace("\\", "/").Replace("Assets", "");string build = msbuild.Replace("\\", "/") + "/devenv.com";string slnPath = path + "hotfix/hotfix.sln";string outpath = path.Replace("swGame", "") + "BuildLog";if (!Directory.Exists(outpath))Directory.CreateDirectory(outpath);outpath += "/hotfix_build_log.txt";string parStr = slnPath + " /rebuild  Release  " + " /out  " + outpath;Thread delay = new Thread(DelayBuild);delay.Start(new string[] { build , parStr, outpath });
#endif}/// <summary>/// 开启线程延迟编译,否则会很卡/// <summary>private void DelayBuild(object obj){Thread.Sleep(50);string[] para = (string[])obj;Process process = new Process();ProcessStartInfo info = new ProcessStartInfo(para[0], para[1]);info.UseShellExecute = false;info.RedirectStandardInput = false;info.RedirectStandardOutput = false;info.CreateNoWindow = true;process.StartInfo = info;process.Start();while(!process.HasExited)process.WaitForExit();int exitCode = process.ExitCode;process.Close();if (exitCode == 0)UnityEngine.Debug.Log(DateTime.Now.ToString("[HH:mm:ss]") + "编译完成!!");else//异常退出UnityEngine.Debug.LogError(DateTime.Now.ToString("[HH:mm:ss]") +"ExitCode:"+ exitCode + " 编译失败!!!请检查热更代码或请查看日志:" + para[2]);}/// <summary>/// 检测代码是否被修改/// 为了避免频繁的被执行到,有一个时间间隔的控制,距离上次编译5秒以内禁止编译/// <summary>public bool CheckChangeFiles(){string oldTime = PlayerPrefs.GetString("AutoBuildCSFiles", "-1");if (!oldTime.Equals("-1")){long now = DateTime.Now.Ticks / 10000000;long diff = now - Int64.Parse(oldTime);if (diff <= 5 && diff >= -5)return false;elsePlayerPrefs.SetString("AutoBuildCSFiles", now.ToString());}elsePlayerPrefs.SetString("AutoBuildCSFiles", (DateTime.Now.Ticks / 10000000).ToString());//指定检测脚本路径string path = Application.dataPath.Replace("\\", "/").Replace("Assets", "hotfix/hotfix/iLScript");if (!Directory.Exists(path))return false;if (!CheckBuildTools())return false;Dictionary<string, string> md5 = GetMD5ByCache();List<string> files = GetAllFilesByPath(path);return CompilreMd5(files, md5);}/// <summary>/// 遍历获取指定路径所有文件/// <summary>private List<string> GetAllFilesByPath(string path){List<string> files = new List<string>();if (!Directory.Exists(path))return files;string[] allfiles = Directory.GetFiles(path, "*.cs");if (allfiles.Length > 0)files.AddRange(allfiles);string[] dirs = Directory.GetDirectories(path);if (dirs.Length == 0)return files;for (int i = 0; i < dirs.Length; i++)files.AddRange(GetAllFilesByPath(dirs[i]));return files;}/// <summary>/// 读取本地缓存的md5码/// <summary>private Dictionary<string, string> GetMD5ByCache(){Dictionary<string, string> md5s = new Dictionary<string, string>();string path = Application.dataPath.Replace("\\", "/").Replace("Assets", "hotfix/cache.md5");if (!File.Exists(path)){File.Create(path);return md5s;}using (StreamReader sr = new StreamReader(path, Encoding.UTF8)){while (sr.Peek() != -1){string[] line = sr.ReadLine().Trim().Split('\t');if (line.Length > 1)md5s.Add(line[0], line[1]);}sr.Dispose();sr.Close();}return md5s;}/// <summary>/// 最新的md5和缓存的md5进行对比,若有修改就更新缓存/// <summary>private bool CompilreMd5(List<string> files, Dictionary<string, string> md5Dir){bool changed = false;string path = Application.dataPath.Replace("\\", "/").Replace("Assets", "hotfix/hotfix/iLScript/");for (int i = 0; i < files.Count; i++){string file = files[i].Replace("\\", "/");if (!File.Exists(file))continue;string md = GetMD5FromStream(file);file = file.Replace(path, "");if (md5Dir.ContainsKey(file)){if (!md.Equals(md5Dir[file])){md5Dir[file] = md;changed = true;}}else{md5Dir.Add(file, md);changed = true;}}//将最新的md5缓存到本地if (changed && md5Dir.Count > 0){string cache = Application.dataPath.Replace("\\", "/").Replace("Assets", "hotfix/cache.md5");using (StreamWriter sw = new StreamWriter(cache, false, Encoding.UTF8)){var e = md5Dir.GetEnumerator();while (e.MoveNext()){sw.WriteLine(e.Current.Key + "\t" + e.Current.Value);}sw.Flush();sw.Dispose();sw.Close();}}return changed;}/// <summary>/// 获取指定文件的md5/// <summary>public string GetMD5FromStream(string file){FileStream fs = File.OpenRead(file);System.Security.Cryptography.MD5 md5 = System.Security.Cryptography.MD5.Create();byte[] targetData = md5.ComputeHash(fs);StringBuilder strBuilder = new StringBuilder();for (int i = 0; i < targetData.Length; i++){strBuilder.AppendFormat("{0:x2}", targetData[i]);}fs.Dispose();fs.Close();return strBuilder.ToString();}/// <summary>/// 获取编译器的安装路径,是读取本地的配置文件,主要配置vs的安装路径/// 默认安装路径:C:/Program Files (x86)/Microsoft Visual Studio/2019/Enterprise/Common7/IDE/// 路径需要定位到:xxx/xx/Common7/IDE/// <summary>private string GetMSbuildPath(){string path = Application.dataPath.Replace("\\", "/").Replace("Assets", "");string config = path + "hotfix/msbuild.config";string msbuildpath = default_msbuild;if (File.Exists(config)){using(StreamReader sr = new StreamReader(config, Encoding.UTF8)){msbuildpath = sr.ReadToEnd().Replace("\n", "").Trim();sr.Dispose();sr.Close();}}else{using (StreamWriter sw = new StreamWriter(config,false, Encoding.UTF8)){sw.WriteLine(msbuildpath);sw.Flush();sw.Dispose();sw.Close();}}return msbuildpath;}/// <summary>/// 从本地配置的路径是否是正确,不正确需配置正确的路径/// <summary>private bool CheckBuildTools(){string msbuild = GetMSbuildPath();if (!Directory.Exists(msbuild)){Debug.Log("自动编译检测失败\n未找到VisualStudio安装路径, 可在xxxx/hotfix/msbuild.config 文件中配置正确的安装路径,默认路径:\n" + default_msbuild);return false;}return true;}
}

将上面的脚本挂载到场景中游戏对象(GameObject)上,才会生效 .除了VS的路径需要注意,涉及到其他的路径,均根据具体情况自行配置,比如:

  • Assets外部C#项目的路径
  • sln的路径,build输出的log路径
  • md5缓存路径
  • 编译器路径配置文件路径

结束!!2022年春节假期结束的工作第一天,祝大家新年快乐,工作顺利

从其他应用切换回Unity使用VS的devenv.com自动编译Assets外部的C#工程(需含有.sln)相关推荐

  1. Ubuntu 12.04 如何切换回英文

    方式是:先安装你要装的语言,再把你要改成的语言,拖到英语之上,再应用重启,就行了! 安装了语言包后,在那个语言窗口里面,拖住汉语选项放到第一个,应用,关机重启即可 很多时候,我们安装了Ubuntu之后 ...

  2. 小白进阶之GIT篇(1)——创建了分支进行了代码修改但是切换回master分支运行发现代码在master分支也会生效

    项目一期的工作进入了扫尾微调的阶段,二期开发也要进行了,因为二期开发的一些内容是一期不需要 展示出来的,所以就使用git的分支功能. Android Studio可以直接使用GIT的这些功能,好评~ ...

  3. Power Pivot表属性无法切换回表预览模式的问题

    近期Office365用户升级后解决了在Power Pivot中输入中文的问题,但是同时也带来了一个新的问题就是表属性窗口默认为"查询编辑器"模式,且无法切换回"表预览& ...

  4. Windows10系统变成英文如何切换回中文,Ctrl+Shift无法切换输入法

    文章目录 1. 系统变成英文如何切换回中文 2. Ctrl+Shift无法切换输入法 1. 系统变成英文如何切换回中文 2. Ctrl+Shift无法切换输入法

  5. IDEA中文切换回英文

    如果使用IDEA插件进行汉化,切换回英文只需要把插件关闭即可. 可在文件->设置->插件->已安装,去除对勾然后重启就完成啦

  6. 解决win10家庭高级版切换到Administrator用户后无法切换回原账户问题过程记录

    今天下午舍友脑子抽风把电脑的账户切换到了Administrator账户,结果导致用户应用信息全部消失(桌面回到初始状态,系统设置回归默认),造成了极大的麻烦.由于是家庭版系统,很多系统功能都被禁用了( ...

  7. 极路由B70/极路由4增强版改spi,pb-boot启动后切换回nand刷nand breed

    极路由B70/极路由4增强版改8脚spi-nor flash,编程器刷入pb-boot,启动后切换回nand,刷nand flash 的breed 刷好pb-boot的spi-nor flash待用. ...

  8. mac 百度输入法如何切换成五笔,如何切换回拼音模式

    问题描述: 用百度五笔输入法的时候,不小心把把百度输入法更新了,结果变成了拼音输入法.想切换回五笔输入法一直找不到- - 问题解决: 随便找一个地方用百度输入法按个拼音出来,看自己电脑的右上角. 如下 ...

  9. 谷歌浏览器 android 69,如何将谷歌浏览器69及以上版本切换回旧版UI界面

    日前谷歌浏览器团队已经发布69版正式版并进行多项改进,典型的例如该浏览器界面已经换成材料语言风格. 换上新的用户界面后标签栏出现较大变化引起不少用户不习惯,也有部分用户直接选择重新安装旧版浏览器. 不 ...

最新文章

  1. 求矩阵两条对角线元素之和
  2. 记一次 Spring Boot 项目启动卡住问题排查记录
  3. 应云而生,幽灵的威胁 - 云原生应用交付与运维的思考
  4. 496. 下一个更大元素 I
  5. windows下安装服务注册Consul
  6. golang 反射_golang原理篇- nil:接口类型和值类型的区别
  7. 二极管的结构、特性、参数、稳压管的特性和参数
  8. GO开发 -- could not launch process: decoding dwarf section info at offset 0x0: too short
  9. 院士在西湖大学分享科研经历:读博过程中也曾想放弃,因为没有任何进展
  10. Jquery中$(document).ready(function(){ })函数
  11. (最简单)uniapp微信小程序生成二维码图片插件
  12. ADA本月上涨100%,背后或有三个原因
  13. 4G工业路由器DTU的扩展功能
  14. 【组合数学】递推方程 ( 递推方程解与特征根之间的关系定理 | 递推方程解的线性性质定理 | 递推方程解的形式 )
  15. cholesky 分解加速求解线性方程组
  16. hautoj1262: 魔法宝石
  17. 第四次作业-微软必应词典客户端
  18. [Tips] 电路设计者常用的学习网站
  19. Java里面jvr,寻找下一个结点 牛客网 程序员面试金典 C++ java Python
  20. idc机房建设费用_【技术知乎】数据中心成本与机房等级的关系

热门文章

  1. 少年人,应当立长志,常立志
  2. 10月8日维护服务器,【时间更新】10月8日10月10日部分服务器数据互通公告
  3. cf服务器不显示名字,cf服务器冠名之战 怎么给服务器取名字
  4. 传奇私服版本在开区引擎M2提示 玩家名字 发送非法封包1 的原因
  5. wordpress主题_55个免费和高质量的WordPress主题
  6. ChinaJoy2018英特尔再度霸气包馆,这些理由让你不得不来
  7. python颜色参数_Python中matplotlib的颜色及线条参数控制示例
  8. 10oa协同办公系统如何在大数据时代脱颖而出
  9. 真就释放双手?C#模拟鼠标操作
  10. 20南邮计算机考研心得