/images/avatar.png

Papalqi

UE4 TextAssetFormat

需求:常常多人修改一个资源如何做到不锁资源多人协作。 方向1. 能够进行类似文本不同资源的diff合并 方向2,将资源解耦,将常常需要修改的部分单独作为一个资源。 方案一:使用TextAssetFormat方案 我们在 Editor preference 中搜索 Text Asset Format​。可打开配置 然后在资产右键​ Asset Action 可以 export to text format 保存成 utxt 格式的文件。 在资产被引用时,如果对应 uasset 后缀名不存在,则会去搜索 utxt 后缀名并加载。如果不被引用是不会加载的。 我们一个Uasset在内存中是一个UPackage的数据结构,其结构如上。对于大部分数据来讲。其明文存储的东西其实是它的File Summary和它的结构数据。但是所有走序列化的数据都是在一个数据块中,例如如下一个Montage: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 { "Exports": { "AM_Dunan_ComboXFinish02:ActAnimNotify_AutoTrace_2": { "__Class": "Class /Script/X21.

UE4 从UProperty到FProperty

UE4 在4.25中,进行了一些非常重要的改进。 其中就有FProperty。 一.老旧的结构体系 UE4中带有前缀U的类是UObject派生类。每个UObject都有一个与之对应的UClass,这个UClass保存了所有的反射系统相关的信息,描述了各个Property之间的内存布局和关系;通过UClass可以遍历所有的序列化属性;通过Object对象的实例化地址就可以将相应的Property的值取出来,进行读写操作。 UClass保留了这个对象的结构信息。它在内部拥有成员的元素列表,例如UFunction,UEnum,UProperty等。它们一起作用来提供“反射”机制。从命名不难看出UClass 和下文中的UProperty,都是从UObject继承而来,主要是为了使用Object的GC功能。 UProperty将变量定义为类的组成部分。这也是我们这次的重点内容。 不管我们是在蓝图中还是在C++中,属性都是由我们的UProperty来指定的。这些属性包括蓝图变量和节点,以及执行节点的返回值。 二.反射 我们将C++编译成exe文件。编译器将C++中的信息翻译成了机器语言。我们本身的函数名称在编译时将会被替换和省略。因此你不能在自己的程序中使用字符串"HogeHogeFunc"来处理任何事情。反射是静态或者动态的去构造这一映射信息,并且在运行时使用他们。UE4是静态反射的。 在UE4中,我们在通过他特殊的宏,将所需要反射的数据进行标记,从而自动的生成供运行时查询的信息。 从而其生成了一些额外的信息。 三.反射案例 四.FProperty 在创建类时,会生成大量属性,包括蓝图类。有些类型将可能有几十万多个UProperty,因为所有的UProperty继承自UObject,因此会产生各种各样的开销。这一特性对整个引擎影响及其广泛,包括额外的内存空间,UProperty构建/销毁成本,垃圾回收性能。 从4.25开始,此UProperty已完全更改为FProperty。 前缀“F”说明其不再继承UObject。UProperty类的定义仍然存在,但它只用于在将早期版本中创建的资源转换为FProperty做一次。 这是4.24的继承关系。 这是4.25的继承关系 由于Field与UObject和UField大多数函数拥有相同的名称,因此当您更新引擎时,只需将其从UxxProperty重命名为FxxProperty即可。 五.改善点 内存消耗减少 在整个应用程序中使用的UObject中内存占用中,Property占40%以上,根据情况也有超过60%的程度。特别是在包含多个函数调用的蓝图很多的情况下。在ThirdPersonTemplate中,UE4.24的UObject合计有5万多个,其中70%左右是Property。这些消耗直接没了。 由于继承了UObject,因此每个属性不再需要超过100个字节的额外内存。 虽然每个容量都是微乎其微的,但由于数量众多,有时会减少数十MByte或更多的内存使用量。 使用宏调用8个BoolProperty返回值的函数2304次。 如前所述,由于不适用NewObject而是C++的New和Delete,因此生成和销毁特性也变得简单和快速。同样,您还可以缩短应用程序的启动时间。 六.结论 4.25 UProperty变成了FProperty 继承UObject后的开销不见了 提高了引擎启动时间 GC处理时间明显改善 内存使用量的减少 打包后包体体积减少

UE4 UBT 解析

一. 调试UBT 打开项⽬的默认的构建命令⾏ 可以获得如下的 BuildCommandLine 1 ..\..\Build\BatchFiles\Build.bat -Target="ActionRPGEditor Win64 Debug" -Target="ShaderCompileWorker Win64 Development -Quiet" -WaitMutex - FromMsBuild 这⾥的 Build.bat,是可以直接换成 UnrealBuildTool.exe 的,如果你打开 Build.bat 查看内容,会发现⾥⾯的多数内容就是如何⽤ C# 构建⼀个 UnrealBuildTool.exe,然后调⽤这个 UnrealBuildTool.exe。 所以我们把 UnrealBuildTool 作为启动⼯程,然后启动命令⾏改成上⾯的 Build.bat 的命令⾏,就可以启动 UnrealBuildTool 的调试,如下图: 1.1启动调试 因为不知道 main 函数在哪⾥,这⾥有两种常⻅的调试⽅式 ⼀、直接按下 F11 启动,代码会⾃动停在 Main 函数的第⼀⾏,如下图 ⼆、通过 Attach 的⽅式调试,这种⽅式⼀般⽤于⼆段甚⾄三段启动的程序(也就是⼀个 exe 启动另⼀个exe,我们⽆法控制 F11 的位置,典型的就是UBT会启动 UHT的exe) 我们找到Main函数,然后在写下如下代码和断点,这样任何⽅式启动程序(包括⼆段启动)都⼀定会进⼊死循环。 Attach到⼀个进程上,此时断点⼀定会停在死循环⾥⾯。 通过运⾏时修改内存的⽅式,把死循环解开,⽐如这⾥的把 i 改成 1。接下来就可以愉快的调试了。 二. 文件结构 Module 是构成 Unreal 的基本元素,每一个 Module 封装和实现了一组功能,并且可以供其他的 Module 使用,整个 Unreal Engine 就是靠各个 Module 组合驱动的。我们创建的游戏项目本身,就是一个单独的Module。

[译]Low-level Thinking in High-level Shading Languages

很多聪明人写的代码,本可以以更好的方式编写。 偶尔我会听到“这是未优化的”(this is unoptimized)或者这只是个“例子”(educational example)作为借口,但大多数时候这个借口都不成立。因为他们其实不知道如何做到正确。 此外,供应商的SDK示例中的代码也不总是正确的。当最优秀的牛人都做得不好时,这是一个行业的问题。 1 (x – 0.3) * 2.5 = x * 2.5 + (-0.75) 很久之前,我们需要写汇编,从2003年之后的一切都是HLSL或者GLSL。这当然是一种进步,我们使用更高层次的抽象语言进行shading。这没毛病,但是,随着硬件与我们正在使用的抽象内容之间的差距的扩大,人们与硬件失去联系的风险就越来越大了。如果我们只看到HLSL代码,而从来没有不知道GPU运行的是什么,这将是个严重的问题。本文要传达的信息是,在使用高级着色语言时保持低级别(Low-Level)心态对于编写高性能着色器至关重要。 这表现的很清楚,为什么我们应该使用低级别的思考。我们只移动一些东西,加一些括号,就能实现一个更快的着色器。这是通过理解底层硬件并将HLSL构造映射到它从而来实现的。 本文使用的硬件是Radeon HD4870((用于生成最易读的反汇编代码)),但本文中的大部分都是通用的,除非另有说明否则适用于任何GPU。硬件之间具有诸多的不同。 即使您没有观察到特定GPU的性能提高,也有可能对其他的GPU有所提升。GPU有大量的ALU。如果我们将ALU的利用率从50%降低到25%,这却并不一定能够提高性能。因为同时其会受到其他因素的限制(贴图和内存带宽等)因此可能不会提高性能,但可以让你降低功耗,并且为新feature留出更多空间。最后如果你解决了TEX/BW,会给你带来相对于的收益。 “编译器将优化它” 编译器不懂你的思想。编译器只理解着色器中的操作的语义。他们不知道你在努力完成什么。许多可能的优化都“不安全”,因此必须由人来完成。 他们没有整张贴图数据 他们只有有限数据 他们不能打破常规 这可能认为的最简单的代码示例,您可能认为它可以自动优化为使用MAD指令,而不是ADD+MUL,因为这两个常量都是立即数。 然而编译器并不是这么想的。 驱动程序仅限于传递的D3D字节码的语义。最终在GPU上执行的代码就是你写在着色器上的内容!大多数情况下你可以在PS3看到一样的结果。但是上面这种情况例外,可能是因为那里的常数为1.0f。其他的情况都不会变为MAD。 Xbox360着色器编译器很有趣。 它什么都不在乎。 即使你明显破坏了数字,你也会一直做这个优化。 即使它导致常量溢出或下溢为零,也无关紧要。 如果上述式子中1的位置是一个常数,GOGOGO。上车,直接优化 如果上述式子中1的位置是0,这样的话MUL不就好了吗? 太棒了! 所以当然会产生很多小误差。 如果在此转换中丢失了许多浮点数精度,则不会立即注意到为什么会发生这种情况。 我们在这里处理IEEE的浮点数。更改操作顺序并不安全。如果你运气好,你会得到同样的结果。 改变顺序更可能会提高精确度。但是,根据数值的不同,并不总是行得通。 在最坏的情况下,它可能会被上溢或下溢破坏,或者如果不进行优化,它可能会在正确运行的部分返回NaN。一般来说,编译器擅长于:移除死代码,消除未使用的资源,折叠常数,寄存器分配,代码调度;但通常不会:更改代码的含义、中断依赖关系、违反规则。 例如我们的x = 0.2f sqrt(0.1f * (0.2f - x)) ,returns 0 sqrt(0.02f - 0.1f * x) ,returns NaN 这种差异是因为第二个表达式将非常小的负值传递给sqrt。 请记住,0.1f、0.2f和0.02f都不能用IEEE浮点数正确表示。这个偏差来自于有适当的四舍五入的常数。 编译器不可能预测未知输入的这些失败。因此我们需要让硬件知道我们要干什么。相信着色器编译器会很好地修复事情,真是太天真了。虽然D3D编译器允许自己在编译时忽略INF和NaN的可能性(这通常对于游戏开发是可取的),但这并不意味着驱动程序在运行时被允许这样做。如果D3D字节码说“乘以零”,那么这正是GPU最终要做的事情。虽然D3D编译器可以在编译期忽略INF和NaN的可能性(这通常有利于游戏开发),但驱动程序并不可以在运行时做同样的事情。 如果D3D字节码说“乘以0”,GPU将正确地执行它。 1 x* 0 = 0,但是NaN * 0=NaN 因此,我们关于硬件的通用事实是

蓝图的一些细节

一些基本的Ctrl和Alt类似的基本操作就不说了。 快捷键T可以防止我们选中透明物体,当有大量的雾气或烟雾并且干扰对象选择时,可以使用此功能。 蓝图书签功能 当您按下☆图标时,将显示[新书签]注册框,输入书签名称即可。 这将帮助你快速阅读蓝图和进行跳转。 具有快速跳转功能的蓝图迭代 在[编辑器首选项]-> [常规]-> [键盘快捷键]的“图形编辑器”中指定。 您可以使用Ctrl + N(N:数字键)进行保存,并使用Shift + N(N:数字键)进行移动。 使用快速跳转移动如果已经使用Ctrl + 1,Ctrl + 2,Ctrl + 3保存了蓝图视图,则可以输入Ctrl + 1,Shift + 2,Shift + 3到达你保存时的地方Ctrl +N。。 蓝图宏的开销 宏以与C ++宏相同的方式替换节点。其特征是宏的节点在宏节点所在的位置扩展。与函数不同,除非放置了节点,否则不会对其进行编译。放置的节点像内联函数一样展开,因此调用它的成本应比调用函数的成本低。 仅添加矢量的函数和宏就是这样。 宏为321毫秒,函数结果为392毫秒。宏仍然更快,但是它可能没有您所关心的那么大。由于包括了逻辑的复杂性,函数和宏之间的速度差异变得更加明显。当然,这种加法会产生这样的差异,因此,如果它成为复杂的逻辑,则可能会出现明显的速度差异。 在UE4中,当您双击BP节点时,Visual Studio(或Xcode)将自动启动并打开实现该BP节点的代码。 当您想详细了解如何使用节点和处理内容时,这很方便,但是说实话,有时在不使用BP的情况下意外双击就可能导致Visual Studio在未经许可的情况下启动. 在这种情况下Navigate to Native Functions from Blueprint Call Nodes,让我们禁用编辑器设置!即使双击该节点,Visual Studio也不会在未经许可的情况下启动! 拉直连接和左对齐快捷方式 如果像我一样,希望将蓝图组织得井井有条,您可能已经知道“拉直连接”工具,该工具可让您在选定节点之间建立直连。 不幸的是,默认情况下,此选项没有快捷方式,但是经过深思熟虑的UE4允许您定义一个。我将说明如何进行这些简单的步骤,并在以后节省大量时间。 首先,我们需要进入Editor Preferences,然后进入Keyboard Shortcuts部分。在此处,我们将在搜索栏中输入“straig”以查找与“拉直连接”命令。可以设置两个绑定,个人而言,我只在H键上设置了一个(它既快速又易于使用,并且尚未绑定到其他东西)。 然后,我们可以使用我经常使用的“向左对齐”选项进行相同的操作。就个人而言,我将其绑定到V键。 现在,一切就绪。我们可以在图形编辑器中对其进行测试。只需选择要排列的节点,然后按H(如果要水平对齐节点)或按V(如果要垂直对齐节点)。 编辑器蓝图组件 Asher在他的Inside中就是用这个功能做了自己的高度雾的生成工具。 创建一个小部件。首先,我们需要创建一个类型为Editor Widget的新资产。对于此示例,我们将仅制作一个小的“ Hello World”按钮。编辑器小部件窗口应如下所示: 在小部件的事件图中,我们添加以下简单行为: 运行小部件。这个小部件非常简单,但是它显示了为编辑器创建UMG小部件的主要原理。现在,我们要对其进行测试。 当我们单击按钮Hello World时:在视口中将看到日志行“ Hello World”。假设我们处于编辑器上下文中,则可以调用任何编辑器函数。 父节点和子节点 Actor Palete UE4 .

关闭双击蓝图跳转VistualStudio

在UE4中,双击BP节点时,Visual Studio(或Xcode)将自动启动并打开实现该BP节点的代码。 当您想详细了解如何使用节点和处理内容时,这很方便,但是说实话,有时在不使用BP的情况下意外双击就可能导致Visual Studio在未经许可的情况下启动. 在这种情况下Navigate to Native Functions from Blueprint Call Nodes,让我们禁用编辑器设置!即使双击该节点,Visual Studio也不会在未经许可的情况下启动!

UE4 VirtualTexture 源码解析

一.基础、原理和使用 Unreal最早从2017年就开始进行VirtualTexture功能的研发工作。4.17版本初次的提交,4.23 版本正式放出SVT和RVT,4.26 版本推出Adaptive VT。 按照使用上的不同,在引擎中一共有四种不同的VT类型 UVirtualTexture2D直接是继承于UTexture2D的,是最普遍上的的VT类型。 ULightMapVirtualTexture2D跟 UVirtualTexture2D没有什么太大的区别,主要是使用在LightMap中使用。 URuntimeVirtualTexture,RVT,后面会讲到其最终的用法。 如果RVT勾选了Adaptive,那么RVT则会变成AdaptiveRVT。 大致上我们按照生成的时机不同,主要分为两种VT的格式。UVirtualTexture2D和ULightMapVirtualTexture2D叫做SVT(Streaming Virtual texture)。而URuntimeVirtualTexture和他的衍生类别AdaptiveRVT称之为RVT((Runtime Virtual texture))。 1.1Streaming Virtual Texture VT功能并不是UE4引擎默认开启的,如果我们想要使用Virtual texture ,需要在项目设置中设置Enable Virtual Texture Support。而勾选Enable Virtual texture lightmaps,则会将我们的Lightmap变为VT形式。 1.1.1 普通贴图转换为Virtual Texture 当我们重启编辑器后,我们可以把之前普通的贴图右键点击转换为VirtualTexture。使用这种方式编辑器将检索与之相关的材质使用。会将会改变使用到这张贴图的材质的采样方式。 多选批量转换当然也是支持的。 我们也可以在贴图的设置中去设置这张贴图是否是VirtualTexture。 不过,这样我们就必须手动的去调整材质里对于VT的采样方案。不然材质会报编译错误 1.1.2 导入为VirtualTexture 之前的操作是把项目中已知的贴图进行转换,当我们想要新增贴图时,由于贴图的大小限制,我们可以把一张超大的贴图切割开,然后同时导入多张texture ,如果他的命名规则如下图所示那么他就会按照UDIM多象限UV进行排列并且识别为VirtualTexture。 当然,如果我们只是导入一张贴图的话,会根据项目中的配置来进行判断,分辨率如果大于这个设置,将会转换为VirtualTexture。如果比这个小的话,将视为普通的贴图处理。 1.2 Virtual Texture的目的 在这里我们希望去讨论VT的实现目的。假设我们的游戏画面是静止不动的,并且如果我们能够实时创建这么一张贴图来显示我们的场景,我们假设屏幕分辨率是1920x1080 ,用RGB8来进行存储。 920x1080x3 =6075KB≈5.93MB,(显存利用率) 也就是说,我们显卡的显存只需要用 6MB 就完全够了。这种极端的情况用现有的硬件实现起来恐怕不太现实。这种情况很难在一个动态的场景中实时的去做到,但是即使是有一些cache的冗余数据,就算到了100倍的冗余,其空间使用也小于我们目前的Texture Pool的大小设置。 因此,Virtual Texture的基本思路就是:把能看到的贴图才加载进来,没看到的不加载;进的精度高,远处的精度低。如下图所示,如果我们只把看到的部分(高亮的部分加载进来)那么内存占用上将大大减少。 从中我们可以看出Virtual Texture实则是解决我们空间存储的问题。世间所有的炼金术都遵循等价交换,你既然节省了空间,那么你的时间消耗则会增加,这是一个以时间换空间的技术:在每个Tile动态加载卸载的过程中和VT采样时都将产生额外的性能开销。因此我们是否需要开启,将哪些贴图进行开启,都是一个值得权衡的问题。而不是无脑的去使用。 既然解决的是贴图的内存管理问题,其跟传统的基于Mipmap的贴图和可见性的Texture Streaming管理是不同的。传统的StreamMgr只会根据可见性和对应的Mipmap来进行流送的处理,也就是说我们并不能对单张贴图的某一部分进行更为精确的控制,也就是有了过多的冗余信息。例如我只看到了一个贴图的一个角落,却要把他整个都加载进来。 而VirtualTexture 这种将整个Texture分为Tile 的方案,则考虑了这一冗余数据的处理。VT的劣势也很明显,在镜头剧烈变化的时候,其会不停的进行加载卸载的操作,如果配置不当会导致明显的帧率问题。 1.3 Runtime Virtual Texture 首先我们将解释一下RVT(Runtime Virtual Texure) 和普通的SVT(streaming Virtual Texture)的区别。RVT是一张动态在场景中获取的VT,因此所有的数据并不需要存储在硬盘中,而是在我们需要某一块时(相机看到的时候)直接渲染出来。

ActorPalete

UE4 .26或悄悄添加了曾经是“演员面板(Actor Palete)”插件的东西。顾名思义,这个actor调色板是一个插件,它可以让您加载其他level的角色从它们拖动到当前level。 一旦激活了Actor Palette插件,请从顶部菜单的“窗口”->“ Actor Palette”中打开一个Actor Palette。 当打开一个新窗口时,它将如下所示。 您可以在Actor Palette中打开一个关卡,方法是从Actor Palette窗口中选择“打开关卡”,然后选择您实际要打开的关卡。

Niagara 源码架构解析

从git上的信息其实可以看到,Niagara其实在引擎中存在的时间非常久,在4.0版本就已经存在。但是直到2018年的4.17中公开出来。4.17~4.24 为实验版本,4.25 之后正式版本。但是从目前官方的对于Niagara fix的代码提交上看,还是有相当多的编辑器问题修复。 17年才将Niagara从Engine内部移植到插件中。 随着长久的历史变迁,Niagara已经进行了多次的文件位置上的转移,目前的位置是在引擎的插件中。 Niagara在UE4开始就已经在构思做了,目的就是重构继承于UE3的Cascade。Niagara当时的设计目的就是data driven并且扩展性更高的粒子系统,解锁模拟和渲染,所以有图形化的节点能不需要程序来实现模拟的逻辑功能。所以2014年时引擎实现了向量化(指令并行)的虚拟机。 后续为了进一步提高了并行性,加入了compute shader的GPU支持。 本文主要设计Niagara 的主要代码框架结构,对其内部可能的其他一些知识体系。本接下来文章介绍Niagara 的编译和反射系统。因此本文就不在提及过多关于这方面的内容。 我们照例观察一下整个代码的文件组织。 Niagara:所有的运⾏和逻辑构成 。 NiagaraAnimNotifies:可以在动画的过程中进行事件通知播放Niagara。 NiagaraCore:⼀些通⽤的类 NiagaraEditor:编辑器内的可视化编辑节点。 NiagaraEditorWidgets:Niagara 的UI 。 NiagaraShader:Niagara 的shader 。 NiagaraVertexFactories:组成MeshBatch的顶点⼯⼚。 一.基本概念 1.1system 和Emitter 我们通常在编辑器中使用的是Niagara资源主要有两种,一种是我们的Niagara发射器Emitter,一种是我们的Niagara系统System。并且我们发射器是由我们的Module共同组成,从而完整的形成我们的整个Niagara系统。其设计理念相比与Cascade,拆分的更加的细致,从而能偶比较细致的组合和复用。 我们本次并不关心Module所能代表的发射和编译模块,我们重点关注的是Emitter和System所对应的运行时数据。 所有使用Niagara 的地方都需要我们的UNiagaraComponent来进行,其主要是进行控制和Niagara系统的交互。 我们在编辑器中资源对应的是UNiagaraSystem和UniagaraEmitter。一个UNiagaraComponent会拥有指定的一个UNiagaraSystem对应。但是UNiagaraSystem并不是我们运行时直接使用的数据。我们会在UNiagaraComponent初始化时构建对应UNiagaraSystem的FNiagaraSystemInstance,同样System里面的UNiagaraEmitter会生成对应的FNiagaraEmitterInstance。这如同类和实例的关系一般。 对于每个FSystemInstance,会有⼀个相对应这个System类型的的Simulation,它和Instance并不是⼀对⼀的 关系。如果场景中存在同⼀个UNiagaraSystem的多个实例,其只对应⼀个 FNiagaraSystemSimulation。也就是说,对于数据结构⼀致的实例,会使⽤同⼀个Simulation进⾏模拟。因为相同的System的Spawn和Update与数据存储的大小和类型是相同的。提高缓存一致性和数据的存储效率。 所有的FNiagaraSystemSimulation都由FNiagaraWorldManager统一进行管理。 1.2 运转流程 Niagara将有Initial和Update两个重要阶段。其中Initial只在一开始运行一次,Update则每帧运行。 而根据发生的位置不同分为SystemUpdate,EmitterUpdate和ParticleUpdate,分别代表每个System,每个Emitter,和每个粒子的更新。 例如我们spawn一个NiagaraActor,对应的NiagaraSystem中有两个Emitter,每个Emitter发射1000个粒子。 我们排除一些粒子间事件这种情况。那么当我们在初始化时,会调用一次SystemSpawn,调用各自的Emitter的EmitterSpawn,调用各自粒子的共两千次的ParticleSpawn。之后除非有新的Particle生成会调用particleSpawn,否则不会再调用任何Spawn。 之后,我们每帧都会调用我们的各种Update去更新数据。 1.3 渲染策略 这里忽略Light和component对应的Renderer。我们拥有多种渲染器可供选择。 我们其他MeshDraw一样会拥有⼀个渲染的代理Proxy帮助我们来完成最终的渲染,对于Component来说我们持有的该System 的所有Emiter带有的Renderer都会我们会赋值给Proxy。因此其实我们的最小MeshBatch单位是与Emitter数量相关,其数据被Proxy持有,最终交付渲染,这⾥的渲染和UE4普通的渲染架构并没有什么不同,都是统⼀的框架。 由于Niagara的可编程性,其实现功能的潜力是巨大的。它可以非常复杂,但是我们剖析其本质上是一种空间位置模拟的系统之后,我们就能够抓住其关键就是更新它需要的关键数据。更新FNiagaraDataSet。 二.模拟数据 不论Niagara Emitter有多少的Module,Module里有多少的脚本,脚本中写了多少东西。我们当然要抓住本质数据,其都是更新某个渲染器所需要的必要参数,也就是如下图所示的信息。 也就是说,只有这些信息是每个渲染器所需要的,其会传递给渲染线程并最终提交渲染。任何其他参数仅仅为中间变量。因此我们首先关心的是这个数据是存储在什么地方的。由于一个Emitter可以拥有多个不同的Renderer。因此其本身存储在Emitter级别,而不是在Renderer级别。 因此我们最终传递给渲染线程的数据存储在每个Emitter实例FNiagaraEmitterInstance的FNiagaraDataSet身上。不论是GPU粒子还是CPU粒子。 我们将FNiagaraDataSet的数据作为每帧更新的数据继续传递。FNiagaraDataSet最主要的数据是两个buffer。 其中一个是当前使用的buffer,一个是我们将要写入的buffer。FNiagaraDataBuffer拥有提供CPU粒子或者GPU粒子的数据。如果是CPU粒子,我们将在CPU做计算写入操作,如果是GPU粒子,我们在CPU端只需要把对应的buffer指定就可以了。 从传递的数据来看,如果我们是CPU粒子,将直接把最终的模拟结果对应的buffer传递给renderer。而GPU粒子则没有最终的数据,所以需要传递给渲染线程的数据则更完整一些。 其封装成FNiagaraComputeExecutionContext。 2.1 CPU粒子模拟数据更新 最开始我们有一个全局的管理器FNiagaraWorldManager。每一个System类型对应的唯一FNiagaraSystemSimulation都将在这里进行注册并且收到它的支配和管理。 我们的FNiagaraSystemSimulation的数据更新分成两个部分。 Tick_GameThread。这里主要是更新一下系统时间等参数,然后调用FNiagaraSystemInstance的Tick_GameThread。在FNiagaraSystemInstance主要是更新System的Parameters和DataInterface。 Tick_Concurrent,这个阶段可以不在Game线程完成。 对于那些新生成的SystemInstance调用它的spawn script。 是调用system update script进行System的更新。 将system模拟的结果注入到emitter的绑定数据中。 调用FNiagaraSystemInstance的Tick_Concurrent。 在FNiagaraEmitterInstance::PreTick中,最主要的是执行Emitter的Spawn和Update脚本。

给UE4项目改名

很多时候我们对一个工程随意地起了一个随意的名字,这很常见,如果我们想要后面修改整个项目的名字,似乎应该是一件简单的事情,但是这个过程充满了陷阱。如果做错了,您可能会无意间破坏您的项目。手动的做当然是一件非常困难的事情,尤其是对于C++的项目,其模块的命名其实并不容易进行修改,在这里介绍一个工具帮助我们自动的修改抿成。在进行这样的大更改之前,最好备份您的项目。当然,请确保重命名时未在编辑器中打开您的项目。 使用流程 我们打开二进制文件。 输入对应的目录:E:\UE4\1 需要注意的是,现在他没办法支持中文名称的更改 然后需要我们输入新的名称: 输入完成后就可以了。 其可处理蓝图和C++工程。 工程地址:https://github.com/UnrealisticDev/Renom 直接下载