使用C#动态库开发unity游戏
使用C#动态库开发unity游戏
我们不把C#放在unity工程中,单独建立一个c#的解决方案,在这其中分别实现多个项目,每个项目导出一个动态库,也就是dll。
实现:
- 设置unity官方库引用
- 调试,自动生成mdb
- 自动复制dll到unity工程中
- 加密mono
TODO:
- 编译IL2CPP
- 加密IL2CPP
C#项目props
与targets
文件
props文件
需要使用到.props
文件,我们有找到这个的UI编辑器,官方文档1,文档2
这个文件就像是全局变量设置,也可以对每个项目单独设置。
targets文件
文档,这是一个XML
格式的文件让你控制怎么去构建特殊平台的软件,是由MSBuild
提供的。这个文件包含了4个部分:
- properties:一个
key/value
类型配置项,跟.props
文件一样 - items:构建系统的输入,通常是文件
- tasks:如何创建可由
MSBuild
用于执行原子构建操作的可执行代码单元 - targets:解释如何按特定顺序将任务组合在一起,并使构建过程的各个部分能够在命令行上调用
https://learn.microsoft.com/en-us/visualstudio/msbuild/msbuild-targets?view=vs-2022
我们需要把写好的targets
文件,在csproj
中导入,这样同样会导入props
文件。
<!-- If solution has custom targets file then include it. Used for deployment, defines, etc. -->
<Import Condition="Exists('$(SolutionDir)$(SolutionName).targets')" Project="$(SolutionDir)$(SolutionName).targets" />
同样也需要在csproj
中加入我们自定的Target
行为:
<Target Name="BeforeBuild" DependsOnTargets="ProjectValidation" />
<!-- Deploy all required DLL's to Unity Assets folder after build.-->
<Target Name="AfterBuild" DependsOnTargets="UnityDeploy" />
添加相对路径unity官方库
我们可以先在.props
中设置我们当前使用的unity版本:
<UnityVersion Condition=" '$(UnityVersion)' == '' ">2020.3.7f1</UnityVersion>
然后找到Unity的安装路径,设置为UnityInstallPath
,我们可以从这几个位置去查询Unity程序的位置:
- 判断
UnityInstallPath
这个值是否已经被设置,如使用userprops
文件 - 特定版本的安装位置的注册表
<UnityInstallPath Condition=" '$(UnityInstallPath)' == '' ">$([MSBuild] ::GetRegistryValueFromView('HKEY_CURRENT_USER\Software\Unity Technologies\Installer\Unity $(UnityVersion)', 'Location x64', '', RegistryView. Registry64))</UnityInstallPath>
- 默认
Unity Hub
的安装路径(C:\Program Files\Unity\Hub\Editor\{version}
) - 默认
Unity
的安装路径(C:\Program Files\Unity\{version}
) - 默认
Unity
安装的注册表<UnityInstallPath Condition=" '$(UnityInstallPath)' == '' ">$([MSBuild] ::GetRegistryValueFromView('HKEY_CURRENT_USER\Software\Unity Technologies\Installer\Unity', 'Location x64', '', RegistryView.Registry64))</ UnityInstallPath>
然后就可以设置unity
官方库的路径:
<!-- Path to Unity's managed assemblies. -->
<UnityReferencePath>$(UnityInstallPath)\Editor\Data\Managed\UnityEngine</UnityReferencePath>
<UnityEditorReferencePath>$(UnityInstallPath)\Editor\Data\Managed</UnityEditorReferencePath>
<UnityExtensionsReferencePath>$(UnityInstallPath)\Editor\Data\UnityExtensions</UnityExtensionsReferencePath>
生成mdb
因为unity识别mdb文件来调试,所以我们可以直接使用pdb2mdb工具转换pdb到mdb文件,可以在mono的GitHub找到源代码,在编译即可得到。当然也可以直接在unity的安装目录找到pdb2mdb.exe
:
C:\Program Files\Unity\2020.3.7f1\Editor\Data\MonoBleedingEdge\lib\mono\4.5\pdb2mdb.exe
我们可以直接使用pdb2mdb
命令生成mdb:
pdb2mdb.exe unity_dll_lib.dll
不过mono4.5版本的pdb2mdb.exe
移动出来单独执行会报错:
Mono pdb to mdb debug symbol store converter
Usage: pdb2mdb assembly
我们可以根据源码的csproj工程文件看到:
<ItemGroup>
<ProjectReference Include="../../class/corlib/corlib-net_4_x.csproj">
<Project>{2CA6026B-2DC8-4C4C-A12C-1E8234049DB7}</Project>
<Name>corlib-net_4_x</Name>
</ProjectReference>
<ProjectReference Include="../../class/Mono.Cecil/Mono.Cecil-net_4_x.csproj">
<Project>{2C0D558F-0B38-4691-967E-A910A1B995C1}</Project>
<Name>Mono.Cecil-net_4_x</Name>
</ProjectReference>
<ProjectReference Include="../../class/Mono.CompilerServices.SymbolWriter/Mono.CompilerServices.SymbolWriter-net_4_x.csproj">
<Project>{88177C4B-894F-485D-B95A-44199C06BE9F}</Project>
<Name>Mono.CompilerServices.SymbolWriter-net_4_x</Name>
</ProjectReference>
<ProjectReference Include="../../class/System.Core/System.Core-net_4_x.csproj">
<Project>{359142A1-D80F-401E-AA64-7167C9317649}</Project>
<Name>System.Core-net_4_x</Name>
</ProjectReference>
</ItemGroup>
他是有几个引用项的,最后验证只需要依赖一下这两个dll:
- Mono.Cecil.dll(
C:\Program Files\Unity\2020.3.7f1\Editor\Data\MonoBleedingEdge\lib\mono\gac\Mono.Cecil\0.10.0.0__0738eb9f132ed756\Mono.Cecil.dll
) - Mono.CompilerServices.SymbolWriter.dll(
C:\Program Files\Unity\2020.3.7f1\Editor\Data\MonoBleedingEdge\lib\mono\4.5\Mono.CompilerServices.SymbolWriter.dll
)
所以把这三个文件放到同一个目录下就可以了。不过我们发现最新的提交已经把csproj
文件删除了:( 。
自动生成mdb文件,只需要在targets
文件中加入一个Target
的执行命令即可:
<Target Name="DeployBuildMdbs" DependsOnTargets="ProjectValidation">
<!-- Build the .mdb files from the .dll and .pdbs. This is broken out into
a separate target because msbuild will actually check the Inputs and
Outputs timestamps and skip the step if everything is up-to-date.
Using a single <Exec> call so it will run a little faster. -->
<PropertyGroup>
<ProjectDll>$(ProjectDir)$(OutputPath)$(ProjectName).dll</ProjectDll>
<ProjectMdb>$(ProjectDll).mdb</ProjectMdb>
</PropertyGroup>
<Exec Command="$(MonoMdbGenerator) $(ProjectDll) & echo Generating $(ProjectMdb)"/>
</Target>
export dll
dnspy
https://github.com/dnSpy/dnSpy
使用dnspy修改了源码,先编译了,需要再保存一下
调试dll
使用vs调试需要安装vs 插件。这里值得注意的是需要设置unity调式的话需要把dll的Target framework
设置成unity版本的,或者4.6
也可以
文档:
https://docs.microsoft.com/zh-cn/previous-versions/visualstudio/visual-studio-2015/cross-platform/using-visual-studio-tools-for-unity?view=vs-2015&viewFallbackFrom=vs-2019
加密
我们可以在打包后,对dll做加密:
private static void EncryptAssemblyCSharp()
{
string acsPath = Application.dataPath + "/../Build/unity_dll_Data/Managed/Sample.dll";
int offset = Path.GetFileName(acsPath).Length;
byte[] dllBytes = File.ReadAllBytes(acsPath);
byte[] newBytes = new byte[dllBytes.Length + offset];
for (int i = 0; i < offset; i++)
{
Buffer.SetByte(newBytes, i, (byte)UnityEngine.Random.Range(byte.MinValue, byte.MaxValue));
}
Buffer.BlockCopy(dllBytes, 0, newBytes, offset, dllBytes.Length);
File.WriteAllBytes(acsPath, newBytes);
Debug.Log("Encrypt Assembly CSharp Completed!");
}
再对mono加载时做一定的修改可以看这里
tips
在 vs中查看Modules中对应的dll文件的Syboml是否加载进来,就可以看到是否可以调式