IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 游戏开发 -> 从其他应用切换回Unity使用VS的devenv.com自动编译Assets外部的C#工程(需含有.sln) -> 正文阅读

[游戏开发]从其他应用切换回Unity使用VS的devenv.com自动编译Assets外部的C#工程(需含有.sln)

????????最近调到新项目工作,为了热更将代码移到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_EDITOR
        if (Application.isPlaying)
            return;
        CompilerHotfixDll();
#endif
    }
    private void CompilerHotfixDll()
    {
#if UNITY_EDITOR
        if (!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("shengwang", "") + "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 });
#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();
        process.WaitForExit();

        process.Close();

        UnityEngine.Debug.Log("编译成功!!");
    }
 
    /// <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;
            else
                PlayerPrefs.SetString("AutoBuildCSFiles", now.ToString());
        }
        else
            PlayerPrefs.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年春节假期结束的工作第一天,祝大家新年快乐,工作顺利

  游戏开发 最新文章
6、英飞凌-AURIX-TC3XX: PWM实验之使用 GT
泛型自动装箱
CubeMax添加Rtthread操作系统 组件STM32F10
python多线程编程:如何优雅地关闭线程
数据类型隐式转换导致的阻塞
WebAPi实现多文件上传,并附带参数
from origin ‘null‘ has been blocked by
UE4 蓝图调用C++函数(附带项目工程)
Unity学习笔记(一)结构体的简单理解与应用
【Memory As a Programming Concept in C a
上一篇文章      下一篇文章      查看所有文章
加:2022-02-09 21:02:32  更:2022-02-09 21:04:14 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/27 16:26:36-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码