目录
一、动态语言
计算机编程语言可以根据它们如何将源代码转换为可以执行的代码来分类为静态语言和动态语言。
- 静态语言(也称为编译型语言)通常需要在程序运行之前将源代码编译成机器码。这个过程是通过编译器完成的,编译器会检查源代码中的错误并将其转换成目标平台的机器码。静态语言的例子包括C、C++、Rust和Go等。静态语言的优势在于它们通常能够生成更高效的机器码,因此它们的执行速度往往更快。另外,由于编译时会进行类型检查,因此它们在类型安全方面也更为严格。
- 动态语言(也称为解释型语言或者脚本)不需要预先编译。它们在程序执行时由解释器逐行读取源代码,然后转换成可以执行的操作。这种语言的例子包括Lua、Python、JavaScript和PHP等。动态语言的优势在于它们的灵活性和开发速度,因为开发者可以快速编写和测试代码,而不需要长时间的编译过程。但这种灵活性可能会以性能为代价,因为解释执行通常比直接执行编译后的代码要慢。
- 值得注意的是,有些语言如Java和C#,它们通常被认为是静态类型语言,但它们采用了一种中间方式。这些语言的代码首先被编译成中间语言(比如Java的字节码),然后在运行时通过虚拟机(如JVM和CLR)解释执行或即时编译(JIT编译)成机器码。这种方式结合了编译型语言的一些优势和解释型语言的某些便利。
使用动态语言的最大优势是不需要重新编译即可实现对程序的修改。Unity的底层是使用的C++语言,应用层面主要是使用的C#,使用C#的反射功能一定程度上可以实现对发布后的程序进行修改,但其使用并不容易,而且受到很多限制,比如苹果App就对反射功能有诸多限制,因此如果对游戏内容进行了改动,使用C#还是要重新编译整个程序。所以热更新使用的比较多的技术是Lua语言。
使用Lua主要包括两部分,一部分是在Lua的宿主语言中创建接口读取、执行Lua脚本;另一部分是在宿主语言中编写接口供Lua脚本调用。(下一篇记录)
Lua 的动态类型系统意味着变量不需要在编译时声明其类型;类型会在运行时根据赋予变量的值自动确定。这提供了极大的灵活性,使得编写和修改代码变得更加快捷。同时,Lua 的解释器可以直接执行源代码,或者将源代码预编译为字节码,以提高执行效率。
二、创建C#dll
在Unity或C#程序中,可以动态调用编译好的程序集(也就是.dll文件),这样在程序发布后,只需要更新.dll文件,即可实现对程序内容的更新,而不用重新编译整个程序。之前已经实现了Unity调用C++封装的dll。本文记录C#封装dll(超级简单)。
1.VS中创建一个C#语言的库工程
注意版本选择的是.Net Framework3.5。
2.添加UnityEngine.dll的依赖
然后在工程中选择【Dependencies】→【Add Reference】,引用Unity安装目录下Editor/Data/Managed中的UnityEngine.dll。
3.编写代码,生成dll
因为引用了UnityEngine.dll,所以可以在代码中引用Unity引擎的功能。
using UnityEngine;
namespace ClassLibrary1CanDelete
{
public class MyPlugin:MonoBehaviour
{
void Start() {
Debug.Log("This is my c# plugin");
}
}
}
编译工程,生成***.dll文件。
三、Unity使用dll
将***.dll复制到Unity工程的Plugins目录内。
- MyPlugin可作为组件直接拖到物体身上。
- 也可用代码添加MyPlugin组件
this.AddComponent<ClassLibrary1CanDelete.MyPlugin>();
在Unity中做热更新,通常会将需要更新的内容放到Unity的StreamingAssets(PC端可读写,移动端只读)路径下,如果将***.dll文件放到StreamingAssets路径下,则必须使用文件读取的方式读取.dll文件,然后通过反射执行.dll文件提供的功能。
void AddDll()
{
//this.AddComponent<ClassLibrary1CanDelete.MyPlugin>();
byte[] bytes = System.IO.File.ReadAllBytes(System.IO.Path.Combine(Application.streamingAssetsPath, "ClassLibrary1CanDelete.dll"));
Assembly assembly = Assembly.Load(bytes);
System.Type type = assembly.GetType("ClassLibrary1CanDelete.MyPlugin");
this.gameObject.AddComponent(type);
}
在Unity中实现热更新主要是通过网络下载需要更新的文件,并保存到Unity API中Aplication.dataPath指向的路径(一个可写的路径)。程序首先访问Application.dataPath指向的路径,如果目标文件不存在,便进行网络下载,如果服务器端不存在更新的文件,最后再到StreamingAssets读取默认的文件。
四、C#动态链接库(DLL)VS Lua脚本热更新
Unity中实现热更新的两种常见方法是通过C#动态链接库(DLL)和使用Lua脚本。这两种方法各有特点和适用场景,下面将分别说明它们之间的主要区别:
1.通过C# DLL实现热更新
- 原生支持:Unity原生支持C#,所以使用C# DLL进行热更新可以无缝集成到Unity项目中。
- 性能:由于C#是Unity的主要开发语言,因此通过C# DLL进行的热更新通常会有较好的性能。
- 类型安全:C#是一种静态类型语言,这意味着类型检查是在编译时完成的,减少了运行时错误。
- 工具和生态系统:C#在Unity中有丰富的工具和生态系统支持,包括IDE集成、调试工具和第三方库。
- 更新限制:C# DLL热更新通常受限于平台和安全策略,尤其是iOS平台上的限制较多,因为Apple的安全策略不允许执行下载的代码。
- 打包大小:C#编译的DLL可能会增加应用程序的打包大小。
2.通过Lua实现热更新
- 脚本语言:Lua是一种轻量级的脚本语言,它在游戏开发中常用于快速迭代和热更新功能。
- 动态性:Lua是一种动态类型语言,这使得它在运行时更加灵活,但也可能导致更多的运行时错误。
- 跨平台:Lua由于是解释执行的,通常可以更容易地在不同平台之间进行热更新,包括iOS。
- 性能考虑:虽然Lua的性能对于许多游戏来说已经足够,但它通常比C#慢,尤其是在计算密集型任务中。
- 集成工作:Unity不是原生支持Lua,需要通过第三方插件如xLua或SLua等来集成Lua解释器。
- 学习曲线:对于熟悉C#的Unity开发者来说,使用Lua可能需要额外的学习时间。
- 打包大小:Lua脚本通常比编译的DLL小,可以减少应用程序的总体大小。
选择哪种热更新方法取决于项目的需求、团队的技能以及目标平台的限制。C# DLL可以提供更好的性能和类型安全,但可能受到平台限制。Lua提供了更大的灵活性和跨平台能力,但可能需要额外的集成工作和面临性能折衷。在选择热更新策略时,需要权衡这些因素以找到最适合项目的解决方案。