如果开发的时候按之前的一个Hotfix工程,一个Unity工程,开发会很麻烦。因此我们可以把Hotfix部分的代码放入到Unity当中,并增加一个标记,到时候把这些代码整合成一个dll文件即可。

具体思路

ILRuntime的原理就是热更代码单独生成一个dll文件,然后Unity启动的时候读取这个dll文件,热更新的时候只需要热更dll文件即可。之前的Hotfix工程就是把工程内的代码导成dll文件,我们可以将这些代码放入到Unity当中,使用一个标记来和非热更代码区分开来,比如在文件夹或文件上加@Hotfix的后缀。然后我们可以用一个打dll的工具,把这热更的代码文件打成dll即可。这样操作之后就不需要两个工程来回切,方便了开发。

之前用Hotfix工程生成hotfix.dll的时候,是引用了Assembly-CSharp.dll文件,而当我们把Hotfix代码放入Unity中后,Assembly-CSharp.dll中也会包含这些代码,所以我们打hotfix.dll的时候不能使用它了。需要我们自己先将Unity中没有@Hotfix标记的代码编译成一个unity.dll文件,然后利用这个dll和标记了@Hotfix的代码编译成我们需要的hotfix.dll文件,即可。

整合项目

首先我们把Hotfix的脚本放到Unity当中,然后添加@Hotfix后缀用来做区分,如图

打DLL工具

然后去制作我们的打dll工具,新建一个控制台应用叫BuildDllTool

我们需要的参数有,Unity Assets目录的路径,生成的dll文件的导出路径,Unity一些系统dll文件的路径(例如UnityEngine.dll等),编译配置路径(这一块内容还不是很了解,因为也是网上找的代码,后面在研究研究。文件这里先分享下: 编译配置 提取码: xub3 ),编译选项。代码如下

using System;
using System.Threading;namespace BuildDllTool
{class Program{static void Main(string[] args){if (args.Length == 5){Console.WriteLine("Unity Asset 路径:" + args[0]);Console.WriteLine("dll 输出路径:"+ args[1]);Console.WriteLine("Unity 系统的 dll 文件路径:" + args[2]);Console.WriteLine("编译配置路径:" + args[3]);Console.WriteLine("编译选项:" + args[4]);var result = ScriptBiuldToDll.Build(args[0], args[1], args[2], args[3], args[4]);Console.WriteLine("退出");}else{Console.WriteLine("参数不匹配!");Console.WriteLine("退出!");}Thread.Sleep(500);System.Diagnostics.Process.GetCurrentProcess().Close();}}
}

编译dll的代码如下,

using System;
using System.CodeDom.Compiler;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;namespace BuildDllTool
{class ScriptBiuldToDll{public enum BuildStatus{Success = 0,Fail}static public BuildStatus Build(string unityAssetsPath, string dllPath, string unitySystemDllPath, string compilerDirectoryPath, string define){//编译项目的base.dllConsole.WriteLine("准备编译dll 10%");//清空dll的存放文件夹if (Directory.Exists(dllPath)){Directory.Delete(dllPath, true);}Directory.CreateDirectory(dllPath);//Unity 中存放脚本的文件string[] searchPath = new string[] { "Scripts", "ThridPartys" };for (int i = 0; i < searchPath.Length; i++){searchPath[i] = Path.Combine(unityAssetsPath, searchPath[i]);}//找出所有的脚本List<string> files = new List<string>();foreach (var s in searchPath){var fs = Directory.GetFiles(s, "*.*", SearchOption.AllDirectories).ToList();var _fs = fs.FindAll(f =>{var _f = f.ToLower();var exten = Path.GetExtension(_f);if ((!_f.Contains("editor")) && (exten.Equals(".dll") || exten.Equals(".cs"))){return true;}return false;});files.AddRange(_fs);}files = files.Distinct().ToList();for (int i = 0; i < files.Count; i++){files[i] = files[i].Replace('/', '\\').Trim('\\');}Console.WriteLine("开始整理script 20%");//项目中用到的dllvar refDlls = files.FindAll(f => f.EndsWith(".dll"));//unity内脚本,用于先生成unity的dll文件,供hotfix.dll编译用var unityCs = files.FindAll(f => !f.EndsWith(".dll") && !f.Contains("@Hotfix"));//热更脚本,用于生成hotfix.dllvar hotfixCs = files.FindAll(f => !f.EndsWith(".dll") && f.Contains("@Hotfix"));//临时目录var tempDirect = "d:/bd_temp";if (Directory.Exists(tempDirect)){Directory.Delete(tempDirect, true);}Directory.CreateDirectory(tempDirect);//除去不需要引用的dllfor (int i = refDlls.Count - 1; i >= 0; i--){var str = refDlls[i];if (str.Contains("Editor") || str.Contains("iOS") || str.Contains("Android") || str.Contains("StreamingAssets")){refDlls.RemoveAt(i);}}//拷贝dll到临时目录for (int i = 0; i < refDlls.Count; i++){var copyto = Path.Combine(tempDirect, Path.GetFileName(refDlls[i]));File.Copy(refDlls[i], copyto, true);refDlls[i] = copyto;}//添加系统的dllrefDlls.Add("System.dll");refDlls.Add("System.Core.dll");refDlls.Add("System.XML.dll");refDlls.Add("System.Data.dll");//添加Unity系统的dllstring[] dllPaths = unitySystemDllPath.Split(',');foreach (string dll in dllPaths){var dllfile = Directory.GetFiles(dll, "*.dll", SearchOption.AllDirectories);foreach (var d in dllfile){if (Path.GetFileNameWithoutExtension(d).StartsWith("Assembly-CSharp")){continue;}refDlls.Add(d);}}var unityDllPath = dllPath + "unity.dll";Console.WriteLine("复制编译代码 30%");//拷贝非热更的cs文件到临时目录for (int i = 0; i < unityCs.Count; i++){var copyto = Path.Combine(tempDirect, Path.GetFileName(unityCs[i]));int count = 1;while (File.Exists(copyto)){//为解决mono.exe error: 文件名太长问题copyto = copyto.Replace(".cs", "") + count + ".cs";count++;}File.Copy(unityCs[i], copyto);unityCs[i] = copyto;}//检测dll,移除无效dllfor (int i = refDlls.Count - 1; i >= 0; i--){var r = refDlls[i];if (File.Exists(r)){var fs = File.ReadAllBytes(r);try{var assm = Assembly.Load(fs);}catch{Console.WriteLine("移除无效的 dll :" + r);refDlls.RemoveAt(i);}}}Console.WriteLine("[1/2]开始编译 unity.dll 40%");BuildStatus unityResult = BuildStatus.Success;//编译 unity.dlltry{unityResult = BuildDll(refDlls.ToArray(), unityCs.ToArray(), unityDllPath, compilerDirectoryPath, define);}catch (Exception e){Console.WriteLine("unity.dll 编译失败:" + e);throw;}Console.WriteLine("[2/2]开始编译hotfix.dll 70%");//将unity.dll加入refDlls.Add(unityDllPath);//编译hotfix.dllvar hotfixDllPath = dllPath + "hotfix.dll";BuildStatus hotfixResult = BuildStatus.Success;try{hotfixResult = BuildDll(refDlls.ToArray(), hotfixCs.ToArray(), hotfixDllPath, compilerDirectoryPath, define);}catch (Exception e){Console.WriteLine(e);throw;}Console.WriteLine("清理临时文件 95%");Directory.Delete(tempDirect, true);if (unityResult == BuildStatus.Success && unityResult == hotfixResult){Console.WriteLine("编译成功!");return BuildStatus.Success;}else{Console.WriteLine("编译失败!");return BuildStatus.Fail;}}/// <summary>/// 编译dll/// </summary>static public BuildStatus BuildDll(string[] refAssemblies, string[] codefiles, string output, string compilerDirectoryPath, string define){// 设定编译参数,DLL代表需要引入的AssembliesCompilerParameters cp = new CompilerParameters();cp.GenerateExecutable = false;//在内存中生成cp.GenerateInMemory = true;//生成调试信息if (define.IndexOf("IL_DEBUG") >= 0){cp.IncludeDebugInformation = true;}else{cp.IncludeDebugInformation = false;}//cp.TempFiles = new TempFileCollection(".", true);cp.OutputAssembly = output;//warning和 error分开,不然各种warning当成error,改死你cp.TreatWarningsAsErrors = false;cp.WarningLevel = 1;//编译选项cp.CompilerOptions = "-langversion:latest /optimize /unsafe /define:" + define;if (refAssemblies != null){foreach (var d in refAssemblies){cp.ReferencedAssemblies.Add(d);}}// 编译代理CodeDomProvider provider;if (string.IsNullOrEmpty(compilerDirectoryPath)){provider = CodeDomProvider.CreateProvider("CSharp");}else{provider = CodeDomProvider.CreateProvider("cs", new Dictionary<string, string> {{ "CompilerDirectoryPath", compilerDirectoryPath }});}CompilerResults cr = provider.CompileAssemblyFromFile(cp, codefiles);if (true == cr.Errors.HasErrors){StringBuilder sb = new StringBuilder();foreach (CompilerError ce in cr.Errors){sb.Append(ce.ToString());sb.Append(Environment.NewLine);}Console.WriteLine(sb);}else{return BuildStatus.Success;}return BuildStatus.Fail;}}
}

然后我们将其生成为exe程序,放到Unity项目中(例如:Unity项目/Tools/BuildHotfixDll文件夹下)。然后前面的编译配置也可放在该文件夹下。

Unity中调用

然后我们在Editor下添加菜单栏,用来调用我们的exe文件生成dll文件即可。我们在Editor目录下创建ILRuntimeBuildWindow.cs

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Text;
using UnityEditor;
using UnityEngine;
using Debug = UnityEngine.Debug;namespace EditorTool
{public class ILRuntimeBuildWindow : EditorWindow{Vector2 mLogScroll;string mLogs = string.Empty;public void OnGUI(){GUILayout.BeginVertical();{GUILayout.Label("脚本打包");GUILayout.Space(5);GUILayout.BeginHorizontal();{if (GUILayout.Button("1.编译 Hotfix.dll", GUILayout.Width(200), GUILayout.Height(30))){mLogs = string.Empty;string outpath = Application.streamingAssetsPath + "/hotfix_dll/";BuildDLL(Application.dataPath + "/", outpath);AssetDatabase.Refresh();}}GUILayout.EndHorizontal();if (!string.IsNullOrEmpty(mLogs)){mLogScroll = EditorGUILayout.BeginScrollView(mLogScroll, GUILayout.Height(400));mLogs = EditorGUILayout.TextArea(mLogs);EditorGUILayout.EndScrollView();}}GUILayout.EndVertical();}public void BuildDLL(string codeSource, string export, Action compileFinishedCallback = null, Action<string> outPutReceivedEvent = null){string exePath = Environment.CurrentDirectory + "/Tools/BuildHotfixDll/BuildDllTool.exe";if (!File.Exists(exePath)){Debug.Log("编译工具不存在!");return;}//这里是引入unity所有引用的dllvar u3dUI = string.Format(@"{0}\UnityExtensions\Unity", EditorApplication.applicationContentsPath);var u3dEngine = string.Format(@"{0}\Managed\UnityEngine", EditorApplication.applicationContentsPath);string libDll = Environment.CurrentDirectory + "/Library/ScriptAssemblies";string dllPath = u3dUI + "," + u3dEngine + "," + libDll;if (Directory.Exists(u3dUI) == false || Directory.Exists(u3dEngine) == false || Directory.Exists(libDll) == false){EditorUtility.DisplayDialog("提示", "dll文件目录不存在,请修改ILRuntimeBuildWindow类中,u3dUI u3dEngine libDll的dll目录", "OK");return;}//编译配置文件目录string compilerDirectoryPath = Environment.CurrentDirectory + "/Tools/BuildHotfixDll/roslyn";var define = GetScriptingDefineSymbols();//执行exe文件,传递参数var p = new Process();p.EnableRaisingEvents = true;p.StartInfo.UseShellExecute = false;p.StartInfo.CreateNoWindow = true;p.StartInfo.FileName = exePath;p.StartInfo.Arguments = string.Format("{0} {1} {2} {3} {4}", codeSource, export, dllPath, compilerDirectoryPath, define);p.Exited += (sender, e) =>{compileFinishedCallback?.Invoke();};p.OutputDataReceived += (sender, e) =>{mLogs += (e.Data + "\n");};p.StartInfo.RedirectStandardOutput = true;p.StartInfo.StandardOutputEncoding = Encoding.GetEncoding("gb2312");p.Start();p.BeginOutputReadLine();p.WaitForExit();EditorUtility.ClearProgressBar();}//获取编译选项string GetScriptingDefineSymbols(){List<string> validDefines = new List<string>();foreach (var define in EditorUserBuildSettings.activeScriptCompilationDefines){if (!define.Contains("UNITY_EDITOR")){validDefines.Add(define);}}return string.Join(";", validDefines);}}
}

然后将其加入菜单栏中调用即可

using UnityEditor;namespace EditorTool
{public class EditorToolMenu{[MenuItem("Tools/Build Hotfix Dll")]public static void ExecuteBuildDLL(){var window = (ILRuntimeBuildWindow)EditorWindow.GetWindow(typeof(ILRuntimeBuildWindow), false, "Build Hotfix Dll");window.Show();}}
}

选择菜单栏Tool ->Build Hotfix Dll打开我们的工具窗口,点击编译即可。编译成功如下

注意这样打出的dll是没有pdb文件的,因此我们可以在ILRuntimeHelp类中读取dll的代码处,去掉读取pdb的部分即可。

补充

1.若要将PackageManager中的内容打入到dll中,只需要在searchPath中添加Library/PackageCache的绝对路径即可。

ILRuntime(二)整合Hotfix到Unity中,脚本生成dll文件相关推荐

  1. Unity中的特殊的文件夹

    Unity中所有特殊的文件夹 1. Hidden Assets(隐藏文件夹) 2. Standard Assets 3. Pro Standard Assets 4. Editor 5. Plugin ...

  2. 【100个 Unity小知识点】☀️ | Unity 中怎样读取Excel文件

    Unity 小科普 老规矩,先介绍一下 Unity 的科普小知识: Unity是 实时3D互动内容创作和运营平台 . 包括游戏开发.美术.建筑.汽车设计.影视在内的所有创作者,借助 Unity 将创意 ...

  3. 简单Unity 3D游戏加密dll文件提取

    大多数游戏都是使用Unity 3D做的,我们逆向分析主要分析\assets\bin\Data\Managed中的Assembly-CSharp.dll文件,当我们用.NET Reflector 或者d ...

  4. vs2012中程序集生成无法自动在网站Bin目录下生成Dll文件?(已解决!)

    最近,突然发现生成程序集后,网站bin目录下dll没有更新,也没有自动生成dll文件,通过近半个小时的摸索和实验,找到了解决方法: 1.右键网站,不是项目,选择[属性页],在左侧[引用]中如果没有,就 ...

  5. python图标的演变_把Python脚本生成exe文件并添加版本信息和自定义图标

    pyinstaller和py2exe把Python脚本生成exe文件,并添加版本信息和自定义图标. 写了一个查找产品通道号的小程序,目前还没进行异常处理. 以下是程序源码. # -*- coding: ...

  6. html 调用c#dll中的控件,C#实现反射调用动态加载的DLL文件中的方法和在窗体中加载DLL文件的用户控件...

    反射的作用: 1. 可以使用反射动态地创建类型的实例,将类型绑定到现有对象,或从现有对象中获取类型 2. 应用程序需要在运行时从某个特定的程序集中载入一个特定的类型,以便实现某个任务时可以用到反射. ...

  7. java jni dll路径_Java中Jni调用DLL文件试验

    Java中Jni调用DLL文件试验 下面是小编整理的Java中Jni调用DLL文件试验内容,希望可以为您带来帮助!更多内容请关注应届毕业生考试网! 所有文件均在E:\路径下. 安装jdk1.6.0_0 ...

  8. unity中脚本编辑器UnIDE

    引言 unity默认脚本编辑器是MonoDevelop,随着unity4.3面世,MonoDevelop (4.0.1)版本也随之而来,更新为界面更改和bug自动修复功能等,具体还未使用. 点击uni ...

  9. unity中脚本之间传递信息的方式

    //unity菜鸡,将自己学习中的知识写下来.如若发现错误,希望可以私信.共同进步 在unity中,脚本之间传递信息有几种方式 第一种也是比较正统的吧,SendMessage函数,他有如下这几种形式: ...

最新文章

  1. Python Qt GUI设计:QSlider滑动条类(基础篇—16)
  2. 读书:历史 -- 东印度公司
  3. 用30行代码做一个微信智障机器人
  4. java jquery提交表单_Jquery ajax提交表单几种方法
  5. 【Java Web开发指南】mybatis的Example[Criteria]的使用
  6. docker命令的组合使用
  7. win 二进制门安装mysql_windows安装mysql2 gem(包括windows 7 64位)
  8. MySQL基础---增删改查语法
  9. 正则表达式的命名分组
  10. Java中的迭代与递归
  11. 中国工程院2021年院士增选有效候选人都来自哪些单位?
  12. 非插件实现回复可见效果
  13. python匹配字符串_字符串匹配算法之Kmp算法(Python实现)
  14. 现在实体店的生意越来越不好干
  15. H3C 模拟器 pc与sw直连 登录web
  16. Word多级标题出现黑块的解决思路
  17. xmpp 即时通讯
  18. Java单例模式——线程安全的懒汉模式
  19. 给大家推荐一个软件:视频广告过滤大师
  20. APMServ启动失败解决方法

热门文章

  1. nvidia linux屏幕管理,在 Nvidia 显卡下配置双显示器
  2. 健身房会员管理系统哪家好?
  3. 示波器的使用及利用示波器绘制李萨如图形
  4. mac忘记管理员密码
  5. 自动seo网站源码_做了利于SEO优化的网站建设,企业的网站就能自动排名靠前?...
  6. 软件测试ppt打印功能,惠普P1566打印设置与性能测试
  7. 【云原生 • Kubernetes】命令行工具 kubectl 介绍及命令汇总
  8. AutoCAD二次开发自定义线型
  9. 关联矩阵与邻接矩阵的转换及Matlab实现
  10. 币乎作者@安安anan | 国产公链之光,实至名归