1.Unity加密Assembly-CSharp.dll
2.hybridclr源代码解析
3.研究快速修改Unity构建包内的码下资源文件
4.unity urp源码学习一(渲染流程)
5.unity里这种闪电/脉冲波特效怎么做?怎么更改粒子特效的旋
6.unity apk 怎么处理视频资源
Unity加密Assembly-CSharp.dll
项目中的cs代码被打包进Assembly-CSharp.dll中,并通过Mono调用。码下项目的码下加密主要针对Assembly-CSharp.dll,加载时进行解密。码下采用xz库对Assembly-CSharp.dll进行加密和压缩后移位,码下解密过程则相反。码下店铺功能源码加解密算法已定义好。码下
在Unity-Technologies/mono中实现解密。码下首先,码下从对应当前项目版本的码下mono源码中获取。然后,码下重新生成针对特定架构(如armeabi-v7a、码下x)的码下libmono.so。解密算法需应用在mono/metadata/image.c中的码下mono_image_open_from_data_with_name函数。生成libmono.so后,码下每次打包替换工程中的libmono.so。
生成libmono.so的步骤包括安装工具、修改源码、执行脚本生成so文件。注意优化选项,如使用-g去掉debug符号、--gc-sections去掉无用代码。优化后,so文件尺寸减小,性能提升。编译选项影响so文件是否可启动和性能。
加密过程为:每次打包后,都需要重新加密Assembly-CSharp.dll。编写加密工具,每次Unity导出Android工程或反编译apk包后,对Assembly-CSharp.dll进行加密。使用xz库进行压缩,确保文件体积减小。
hybridclr源代码解析
基于lua的unity热更新解决方案
使用lua5.3.5,可以通过VS进行调试,lparser.c负责解释lua源代码,LClosure *luaY_parser函数是解释lua源码的入口。llex.c中的llex函数负责词法分析,而lparser.c中的statement函数进行语法分析。lvm.c则用于执行lua代码。观察到lua需要第三方插件以查看性能,其基于寄存器的表格生产网站源码虚拟机性能优于ilruntime,但与unity交互成本高,依赖于lua的堆栈交互。
流行解决方案如XLua和ToLua,XLua在处理如Vector3等结构体时,避免了不必要的拆箱和装箱操作,ToLua则直接在lua代码中实现了与C#类似的Vector3数据结构。
基于ilruntime的unity热更新解决方案
ilruntime的下载地址为github.com/Ourpalm/ILRuntimeU3D。它提供了unity示例工程,其中ImageReader.cs负责加载dll,而ilruntime使用Mono.Cecil来读取dll的PE信息。从2.0版本开始,ilruntime引入了寄存器模式以解决数值计算效率问题,分为按需JIT(ILRuntimeJITFlags.JITOnDemand)和立即JIT(ILRuntimeJITFlags.JITImmediately)两种模式。ILIntepreter.cs用于执行il代码,非寄存器模式下,Execute函数负责执行代码,而寄存器模式下的ExecuteR函数实现相同功能。然而,所有解决方案的虚拟机与il2cpp相互独立,导致元数据不相通,影响了与unity类的集成,需要额外封装和跨域访问处理。ilruntime支持大部分C#语法,但使用时需注意避免一些陷阱。
基于hybridclr的unity热更新解决方案
hybridclr提供了unity示例工程,官方博客地址为hybridclr.doc.code-philosophy.com...,使用手册可参考介绍 | HybridCLR。建议在vs和unity.3.0f1环境下调试PC工程。加载dll的两个主要入口在于.metadataModule.cpp中的LoadMetadataForAOTAssembly函数和RawImage.cpp读取原始信息,随后Image.cpp解析dll信息并翻译成il2cpp类型,AOTHomologousImage.cpp和ConsistentAOTHomologousImage.cpp分别用于封装加载过程,确保一致性或超集程序集的灵活管理。Assembly.cpp的Il2CppAssembly* Create函数解析PE头、CLR头和元数据以得到镜像信息,随后初始化metadata和interpreter模块以提供快速访问和执行速度。
hybridclr的优势在于直接使用il2cpp的内存对象,避免跨域问题;利用C#语言特性进行开发;并能够使用unity自带的profiler工具查看性能。
研究快速修改Unity构建包内的资源文件
本文将探讨如何实现快速修改Unity构建包内资源文件的方法,目标是使整个操作耗时控制在分钟级别,且无需依赖Unity引擎。以一个Resource目录下的龙珠单机游戏源码空节点Prefab为例,首先,找到其构建后对应的资源文件。预制体构建后,文件以prefab的guid命名,存放在名为Data的二进制文件中。要对这个Data文件进行修改,需了解其格式,但Unity官方文档并未提供详细的二进制文件格式定义,也没有提供将Prefab资源转化为Data二进制文件的API。因此,我们借助开源工具AssetStudio查看Unity Data二进制文件格式,并从源码中找到解析二进制文件的逻辑。
我们得知,Prefab的二进制Data资源是SerializedFile格式,其解析逻辑位于SerializedFile.cs代码文件中。SerializedFile包含文件头和数据部分,文件头格式在Unity版本中如下所示。
文件头格式包含了多个字段,部分字段含义可以通过测试推断。例如,当给Prefab挂载自定义脚本时,发现localSerializedFileIndex和localIdentifierInFile字段表示引用文件1中的第个对象,即全局游戏管理器文件中的第个MonoScript对象。通过对全局游戏管理器文件的分析,我们发现第个对象正是创建的脚本。
了解SerializedFile的结构后,接下来需要解决如何根据Editor中编辑的Prefab替换构建好的包内对应Prefab的二进制内容。Prefab文件在Editor中以文本格式存储,格式为Unity修改后的YAML格式。我们发现,YAML格式与SerializedFile相似,都将树形结构的对象平铺化,并使用文件内的唯一索引进行引用。
然而,YAML中的信息与SerializedFile格式并非完全对应,比如自定义脚本的信息,YAML中使用脚本的guid,而SerializedFile中使用的是全局游戏管理器文件中的脚本对象索引。为了解决guid到全局游戏管理器文件中脚本对象的映射关系,我们采取了以下思路:通过类型信息作为关联键,建立guid与文件内对象索引的街机游戏比赛源码映射关系。首先,编写脚本从Editor中获取所有类型信息,建立guid到类型信息的映射。然后,解析全局游戏管理器文件获取类型信息到文件内索引的映射。这样一来,就可以将YAML中的脚本引用guid序列化为localFileID。
在替换构建好的包内Prefab的二进制内容时,头信息描述了每个对象数据的起始位置和大小。我们接下来关注对象是如何序列为二进制数据的。不同的对象类型具有不同的序列化格式,我们以GameObject类型为例进行介绍。
每个Prefab节点包含一个GameObject对象,Inspector面板显示的名字、Tag等信息来源于GameObject对象。GameObject的序列化格式相对简单。在从YAML到SerializedFile的对应关系中,我们看到一些Editor环境下使用的数据在构建中不需要序列化。在SerializedFile中,挂载的组件对象使用Component指针数组表示,包含PathID和FileID信息。PathID表示对象的索引,FileID为0表示是内部文件对象。
Transform对象也包含在每个节点中,其中包含Vector3和Quaternion数据类型,这些数据在YAML中以多维浮点数保存,在SerializedFile中同样以这种方式保存。
在介绍更多的Unity内置组件类型之前,我们先关注最常用的自定义组件类型,即MonoBehaviour类的子类。MonoBehaviour对象在YAML中结构清晰,首先引用自定义脚本类文件,然后是所有可序列化字段的数据。在SerializedFile格式中,首先是指向自定义组件类的引用数据,接着是所有可序列化字段数据。组件类引用数据结构包括PathID和FileID。
在测试序列化简单字段时,我们发现序列化的字段都是4字节对齐的。要实现YAML中MonoBehaviour字段的序列化写入,我们采取了一种方法,ip获取位置源码即通过反射从Unity工程中找到对应的类型,然后使用Mono Cecil库静态分析Assembly-CSharp.dll以提取类型信息。有了类型信息后,我们就需要考虑如何序列化。
序列化过程有两种方向,其中一种需要了解Unity支持的字段序列化规则,筛选出类型的可序列化字段。然而,考虑到实现目标是脱离Unity工程的独立工具,我们选择另一种方案,即从构建好的包体中寻找类型信息。通过Il2CppDumper从globalmetadata中静态提取类型信息,我们查看Assembly-CSharp.dll以确认包含类型的字段信息。
在处理其他Native Component时,除了GameObject和Transform之外,Unity还提供了丰富的原生组件。一些常见组件的YAML和SerializedFile映射关系如下所示。对于其他几十种原生组件的映射关系,需要通过耐心测试来摸索清楚。
此外,Prefab可能引用贴图、字体、材质以及其他Prefab等外部依赖资源。构建包的测试表明,如果一个Prefab通过脚本引用了其他Prefab,如TestPrefab和TestPrefab,那么在构建的序列化文件中,头信息的externals会包含TestPrefab和TestPrefab的guid。序列化的脚本引用字段分别指向这两个资源。
对于引用的内置资源,如字体文件,构建包内的资源文件为unity default resources。序列化的脚本资源值为特定格式,如{ fileID:2, pathID:},对应的YAML引用信息为。
对于内置资源,其type为0,且fileID为序列化后的文件内对象索引。因此,需要解决guid到内置资源路径的映射问题。由于内置资源数量有限且固定,我们选择先人工维护映射关系。
在处理图集资源时,遇到困难,因为它没有将图集Prefab作为externals引用,而是引用了sharedassets0.assets文件。项目中存在大量sharedassetsXXX.assets文件,不知道如何从图集Prefab的guid关联到sharedassets0.assets文件。我们推测,Unity在构建过程中会擦除一些项目资源文件的guid信息,转而使用externals依赖形式。
至此,研究暂时陷入困境。我们希望后续能有其他方法解决这个问题。本文使用Zhihu On VSCode创作并发布。
unity urp源码学习一(渲染流程)
sprt的一些基础:
绘制出物体的关键代码涉及设置shader标签(例如"LightMode" = "CustomLit"),以确保管线能够获取正确的shader并绘制物体。排序设置(sortingSettings)管理渲染顺序,如不透明物体从前至后排序,透明物体从后至前,以减少过绘制。逐物体数据的启用、动态合批和gpuinstance支持,以及主光源索引等配置均在此进行调整。
过滤规则(filteringSettings)允许选择性绘制cullingResults中的几何体,依据RenderQueue和LayerMask等条件进行过滤。
提交渲染命令是关键步骤,无论使用context还是commandbuffer,调用完毕后必须执行提交操作。例如,context.DrawRenderers()用于绘制场景中的网格体,本质上是执行commandbuffer以渲染网格体。
sprt管线的基本流程涉及context的命令贯穿整个渲染流程。例如,首次调用渲染不透明物体,随后可能调用渲染半透明物体、天空盒、特定层渲染等。流程大致如下:
多相机情况也通过单个context实现渲染。
urp渲染流程概览:
渲染流程始于遍历相机,如果是游戏相机,则调用RenderCameraStack函数。此函数区分base相机和Overlay相机:base相机遍历渲染自身及其挂载的Overlay相机,并将Overlay内容覆盖到base相机上;Overlay相机仅返回,不进行渲染操作。
RenderCameraStack函数接受CameraData参数,其中包含各种pass信息。添加pass到m_ActiveRenderPassQueue队列是关键步骤,各种pass类实例由此添加至队列。
以DrawObjectsPass为例,其渲染流程在UniversialRenderer.cs中实现。首先在Setup函数中将pass添加到队列,执行时,执行队列内的pass,并按顺序提交渲染操作。
unity里这种闪电/脉冲波特效怎么做?怎么更改粒子特效的旋
了解Unity中闪电或脉冲波特效的制作方法,首先可以参考一篇基于物理原理的闪电动画渲染教程,网址为gamma.cs.unc.edu/LIGHTN...。通过电解质击穿模型(Dielectric Breakdown Model)模拟闪电,其形状符合分型图形特性。因此,可以采用拉普拉斯增长模型或泊松增长模型来模拟闪电的形状。在场景中放置多个电荷,通过求解Laplace方程得到周围的电势。电势与空气被击穿的概率相关,概率公式中n表示相邻栅格个数,而eta参数则用于调节闪电的分叉数量,增大eta值会使分叉减少。
闪电的渲染使用的是APSF方法(Atmospheric Point Spread Function)。具体实现可以参考cs.cmu.edu/~ILIM/public...。另外,有几个采用此方法的演示案例可以供参考,且这些案例通常都提供源代码。包括闪电生成器LumosQuad(版本0.1),基于自适应网格的快速闪电模拟:Fast Animation of Lightning Using an Adaptive Mesh,以及基于简单随机的闪电渲染方法:cs.uccs.edu/~ssemwal/IA...
unity apk 怎么处理视频资源
(1)打开apk包首先,将你的apk包重命名为zip或者rar类型的文件,然后进行解压缩,如下:
重命名为,然后进行解压缩,得到如下的目录内容:
各个部分的说明如下表:
assets Unity游戏里面的资源和代码
lib arm和x需要的so文件
META-INF 信息包
res 存放icon等资源
AndroidManifest.xml 清单文件
classes.dex Android Dalvik字节码
resources.arsc 编译后的二进制资源文件
我们主要关注的是assets目录,里面有我们想要的资源和代码。
(2)提取代码
Unity把我们大部分的代码都放到了这个dll文件中(当然还有其他代码放到了first-pass之类的代码,但不是主要的),我们可以在assets\bin\Data\Managed这个路径下找到所有需要的dll文件。要解析dll文件,我用到了这个软件(自行百度下载),用该软件打开得到以下这样的界面:
通过点击右键进行导出,选好导出目录,等待一会,就能得到以下的源代码:
我的测试项目就只写了一个Test.cs,可以看见上面已经成功导出了,打开一看:
我滴乖乖,完全和我写的一模一样,一点不变地导出来了。(由此可见代码混淆和加密的重要性了)
(3)提取资源
要提取资源,我们需要用到Unity Studio,可在下载地址他的Github上下载。
打开Unity Studio,点File/Load folder,选中assets\bin\Data该目录,然后就能得到如下界面:
其中Scene Hierarchy可以查看游戏里面场景的分布情况,而Asset List可以查看资源。
WPF开发之Prism详解内附源码
在复杂应用开发中,维护成本显著上升,因此解耦显得尤为重要。Prism框架为WPF开发提供了解耦的便捷途径。本文通过一个简单示例,阐述Prism在WPF开发中的应用,旨在提升程序的可维护性和可测试性。
Prism是一个开源框架,支持在多种应用中创建松耦合、结构良好的XAML程序,包括WPF、Xamarin Forms、Uno/Win UI等。其核心设计原则包括关注点分离和松耦合,提供MVVM、依赖注入、命令、事件聚合等模式。
下载最新Prism版本,遵循MIT开源许可协议,可通过GitHub获取。
Prism的优势在于模块化设计,使得程序结构清晰,符合高内聚、低耦合原则。通过NuGet包管理器轻松安装。
创建WPF类库,添加用户控件视图,并采用MVVM模式开发。Prism提供数据绑定基类,简化代码量。
创建模块类,实现Prism.Modularity.IModule接口,实现可被Prism发现和加载的模块功能。如DefectListModule模块。
Prism提供多种模块加载方式,常用的是App.config配置文件方法。启动时加载模块,修改App.xaml.cs文件,继承PrismUnity.PrismApplication并重写相关初始化。
模块可通过导航菜单或注册到区域进行组织。Region便于进行模块化布局,通过prism:RegionManager.RegionName属性指定。
模块间交互通过事件聚合器IEventAggregator,采用事件订阅和发布进行通信。简化模块间的相互作用。
实现模块交互,通过事件聚合器实现事件的订阅和发布。
无需关注关注、转发、点赞、评论等相关信息,学习编程从关注老码识途开始。
DynamicBone(动态骨骼)MagicaCloth的优化策略(4)
首先,让我们跳过官网链接,直接进入MagicaCloth(MC)的优化策略探讨(4)。一、MC的延时策略
在Unity Chan示例工程中,当我们导入MC包并调整MagicaPhysicsManager配置时,Update的设置至关重要。默认情况下,UpdatePerSeccond为帧/秒,UpdateMode设为Unscaled Time,这意味着无论游戏帧率如何变化,物理引擎都会执行。然而,这可能导致一帧内计算多次,影响性能。 将UpdateLocation设为After Late Update,意味着模拟会在Unity的LateUpdate后进行。在原有配置下,运行游戏并进行性能分析,切换到Delay Unscaled Time模式后,可以观察到主线程在JobHandle的Complete调用前不再阻塞,从而优化了计算顺序。二、MC的更新控制
MC巧妙地利用了延迟更新机制,通过MagicaPhysicsManager.cs中的代码,将模拟更新推迟到AfterRendering执行。即时更新时,会阻塞主线程;延时更新则在渲染后才完成模拟。这种设计确保了骨骼动画的正确同步,同时节省了等待时间。三、PlayerLoopSystem的深入理解
Unity的Update并不止于Start、Update等,PlayerLoopSystem提供了更细致的时序控制,允许开发者在引擎子系统之间插入自定义更新点。MC通过自定义PlayerLoopSystem,将更新时机插入到AfterRendering,实现了精细的Job调度。总结
通过源码解析,JobSystem和PlayerLoopSystem的结合使得MC的性能优化显著。虽然本人尚不足以深入理解其复杂设计,但对MC的创新设计深感佩服。DB系列研究至此告一段落,期待更多高手在此领域发挥创意。