UE4学习笔记:记录一下在学习UE4的过程中感受到的一些有用的杂事
作者:互联网
本随笔用于记录我在学习过程中觉得对开发很有用的杂事,包括一些开发技巧、编程思想、计算机知识、项目组织技巧等等杂七杂八的东西,本随笔会不定期更新。
- 配置文件在项目中的作用也很重要,使用配置文件的话可以避免在项目内硬编码,而且可以让使用者通过更改配置文件来改动项目里的功能模块,实现不改动代码的情况下完成多种应用场景,比如说数据库的连接数据保存在配置文件里,项目通过读取配置文件来设置连接的数据库,这样通过更改配置文件就可以实现连接不同的数据库而不需要去改动项目的代码。
- UNIX时间戳是以格林尼治时间(UTC±0)为标准定义的从1970年1月1日00时00分00秒到当前时间的秒数,北京时间和UTC事件相差八个小时,在进行和时间相关的计算时需要注意将北京时间转换为格林尼治时间。
- 降低功能模块耦合度是非常重要的,通过降低耦合度,当某个功能模块出现问题需要修改的时候,我们可以直接修改出问题的模块而不必在意其他被引用的模块,低耦合度组织方法非常适用于维护项目。
- 应该用一种简明且高效的方式来记录目录结构取代杂乱的目录结构,同时文件(UE4里应该称为资产(Asserts))也应该用一种简明高效的方式来为每个文件命名,目录结构和文件名称应该能够最直白的表明该文件(资产)的作用。
- 在程序运行过程中遇到判断的情况(c++的if语句,UE4蓝图的Branch节点等),不要把判断失败的情况留空,也就是说如果当条件判断失败之后应该做一些善后的工作,就算是一段简单的提示也行,而不是要完全留空。
- 项目运行时的层次必须分明。例如在项目里有一个读取数据库数据的模块和一个显示登陆界面的模块,那么数据读取的模块就一定要在登陆界面模块正确执行之后才开始运行,比如登陆成功且选择了正确的数据源之后才开始读取,而不是刚启动项目之后就开始运行数据读取模块;
- 作为服务器使用的模块应该尽可能地布置在可以长期运行的计算机上(例如商业性服务器、不断电的计算机)而不是个人电脑上,要让该服务器尽最大可能地可以被随时访问。
- 对于一些并行启动会有冲突的程序(例如安装程序),应该添加一个检测模块,该模块用于检测是否有另外一个相同的程序在运行,这样可以避免运行同一个程序造成的不良后果(例如一个在执行安装另一个在执行卸载。
- 在一个项目的进行过程中,最好准备一台电脑用于非个人目的,例如上文提到过的服务器安装,以及类似UE4这种可以设置共享DDC功能的软件,这样方便所有参与项目开发的人员使用同一个共享文件或服务器。
- 在编写用户手册的时候,要详细说明每一个模块的功能、每一个API实现的功能以及每个参数的意义,还有所有依赖库的版本号(如果有依赖库的话)。
- 涉及到网络通信的模块应该设置“连接超时警告”功能和“自动尝试重新连接”功能。
- 目录结构里应该避免相似功能的目录,比如说“MOD”文件夹和“ASSERT”文件夹。
- 对于一些提出的问题。回答要尽可能做到详细,也就是尽可能回答问题提出者最想要的答案。
- 在开发过程中要考虑到各个界面显示之间的关联性,比如说刚开始的登录界面在登陆成功了之后需不需要保留、主界面和其他界面(历史回看界面、航班回看界面等)之间的关系:是否需要保存、是否可以从其他界面回到主界面上等。
- 按钮等可以和用户交互的控件需要编写功能来防止用户多次点击之后造成的不良后果。
- 项目打包的时候在“Output Log”窗口里有一段名为“Warning/Error Summary (Unique only)”的信息段,在这段信息段里会显示打包过程中哪里出了问题(Warning以黄色字体显示、Error以红色字体显示),可以很快速地定位错误。
- 在一些后台运行的程序中(例如打包程序、守护程序之类),健全的输出日志功能是很有必要的,尽可能地为每一步操作都设置一个日志输出功能,无论是正确运行或是错误运行,错误运行还要写明是什么地方出错了,这样可以让用户清楚地了解到后台发生了什么。
- UE4 C++函数参数的修饰符不同,在蓝图系统里映射出来的功能也会有相应的变化,由此产生了一个重要的前提:不能使用指针作为参数。
修饰符对应于蓝图中的功能为:
c++函数普通参数映射为蓝图函数的输入变量;
c++函数常量参数映射为蓝图函数的输入变量;
c++函数引用参数映射为蓝图函数的输出变量;
c++函数常量引用参数映射为蓝图函数的输入变量;
本质上就是提供了一种方法可以让函数有多个返回值,普通引用变量由于可以其特性,传递给函数且在函数内修改之后会反映到原来的变量上,在代码层面实现了多个返回值的功能,因此可以看出来UE4将普通引用参数映射成了蓝图函数的输出变量,其他修饰符都是输入变量; - 配置文件的JSON格式需要提前讨论好,这样有助于在后续的开发过程中减少改代码的问题产生。
- 有模块A和文档B,其中模块A提供了某个文件格式的处理方法,其在文档B中有详细的描述,但是在项目新版本中模块A的该方法被弃用了,我们需要及时更新文档B指明该方法在当前及之后的版本中已经被启用,需要想一个高效的方法可以当模块A被改变之后提示到用户需要更改文档B。
- 项目名后面应该跟上对应引擎版本号,这样后期好区分当前项目对应的引擎版本
- 若可使用前置声明,而非头文件,请使用前置声明。
- 包含时尽量细粒化。例如,勿包含Core.h,而在核心中包含需要定义的特定头文件。
- 在文件末尾留下空白行。所有.cpp和.h文件应包含空白行,以便和gcc兼容。
- 避免循环相同的多余运算。将常用子表达式从循环中移出,以避免冗余计算。
- 在某些情况下,使用静态变量来避免函数调用间的整体多余运算(如反复生成局部变量)。
- 使用中间变量来简化复杂表达式。若含有复杂表达式,将其拆分为指定至中间变量的子表达式将更易理解(该子表达式的的命名描述了其在父表达式中的意义)。
- 指针与引用应仅含一个空格,该空格位于指针/引用右侧。使用在文件中查找可方便快速地找到特定类型的所有指针和引用(如“int* Name”或“int& Name”)。
- 避免在函数调用中使用匿名文字。建议使用描述其含义的命名常量:
// 旧样式
Trigger(TEXT("Soldier"), 5, true);.
// 新样式
Trigger(ObjectName, CooldownInSeconds, bVulnerableDuringCooldown);
由于无需查找函数声明即可理解目的,因此此操作可协助普通读者快速理解。 - 创建新插件的时候,在插件的.uplugin文件里该插件的类型会是“Developer”,该类型指定编辑器只会在编辑器模式和开发模式里被启用,项目被打包发布时并不会打包该插件,因此在项目里用到插件的地方会全部失效,把插件的类型改成“Runtime”即可在打包过程中打包该插件,顺便一说插件类型还有一种是“Editor”,即只能在编辑器模式里使用的插件。
- 除了内容浏览器内的各种资产需要分类给文件夹以外,“世界大纲视图(World Outliner)”内存在的资产也需要在世界大纲视图里以文件夹的形式分类,分类规则还需要后续制定。
- 尽量将一个功能给细分化,例如程序需要保存一个数据,保存路径里包含了未创建的文件夹,这时候应该给予用户提示该文件夹不存在,并且可以让用户选择是否在找不到文件夹的时候自动创建文件夹,然后默认是不创建仅提示,目的就是为了防止输入文件夹名字时候意外输错而导致不小心新创建了文件夹让数据保存到了错误的地方。
- 类(无论是蓝图类还是C++类)实例的名字不要和类类型的名字一样,实例对象的名字应该和类类型名字有所区分。
- 蓝图接口和事件调度器都是用来实现蓝图之间通信的工具,但是使用蓝图接口需要知道被调用对象,使用事件调度器需要知道主动调用对象(以将事件调度器和被调用事件绑定)。
- 当一个程序正在安装文件的时候,会因为一些原因在安装到一半时被迫退出,这个时候目录里会残留着程序执行了一半之后的无用文件,本来可以被下载或安装程序清理掉,但是现在因为中途被迫中止而不能被清理,因此我们可以额外设计一个程序,该额外程序可以管理下载程序或者安装程序的所有过程,就算安装程序中途崩溃退出,该额外程序也可以清理残留的文件,同样,该额外程序还可以监控安装程序和卸载程序的运行情况,防止一边安装一边卸载的情况发生。又因为该额外程序是在安装程序之前运行,因此可以保证如果安装程序是中途退出,则一定会清理余下的文件。
- 在程序设计之前和设计的过程中,要尽可能的考虑到程序运行过程中会遇到的问题,越全面越好,并且要给这些问题提供解决的方法,防止一些未解决的问题对后续程序的开发产生不良的影响。
- 项目使用到网络的话需要多方面去检测网络连通状态,最好是从一台拥有独立IP且从没有运行过该程序的电脑上测试项目。
- 在Windows系统里已经被设置为可共享的文件夹,可以在Linux系统里通过“mount”命令来进行挂载从而可以访问共享文件夹:mount
-o username= ,password= 。
在Linux系统里安装samba服务并设置好对应的配置(配置内容待去理解),就可以在Windows的资源管理器的通过地址栏输入“\”来进行访问。 - 在UE4的项目里面,Actor的点击事件需要由启动了“点击事件(Click Events)”的Player Controller才能启用,而默认情况下Player Controller是禁用该设定的(猜想是为了提升性能),为了让Actor能够响应点击事件,需要手动启动“点击事件”。
- 关于两个Widget控件之间如何定义渲染顺序的问题(即两个完全不同的UMG之间的Z Order),在节点“Add to Viewport”上点击下拉框,就可以显示出ZOrder的选项,ZOrder的值越大,则渲染层级越靠后,也就是说显示的时候会更靠近玩家的界面。
- 节点“FInterp To”表示的含义是从“Current”开始,将“Target”分为“Delta Time”份,每一份的增长速度为“Interp Speed”,加上当前的值“Current”,即为当前的返回值。可以如下数学表达式来说明:
x = Current + (Target - Current) * DeltaTime * InterpSpeed
其中X即为返回的值,且X的值一定处于Target和Current之间。 - UE4引擎默认的DDC存储路径为:C:\Users<UserName>\AppData\Local\UnrealEngine\Common,该DDC不会自动删除,而且会占用很大的硬盘空间,所以除非是必要的,经常清除DDC有利于释放C盘的空间。
- 关于更改UE4项目分辨率的问题,当打包的构建配置选择的是“Shipping”时,在蓝图内使用的“Set Screen Resolution”函数可以发挥作用,但是在“Debug”和“DebugGame”选项时却不能够发挥作用,因为类似分辨率这样的选项是由配置文件“
/Config/GameUserSettings.ini”来管理的,在这里可以更改对应的配置。 - UE4里坐标系分为“世界坐标系(World Location)”和“局部坐标系(Local Location)”,世界坐标系的原点是以世界原点为坐标原点的,在该坐标系下进行的变形都是以世界原点为基础;局部坐标系是以某个父类组件或控件的轴心点为坐标原点,在该坐标系下进行的变形都是以该父类为原点进行变形。
- 如果某个UMG是被嵌套在了另一个UMG里面,然后在前者里面涉及到的全部的控件都是基于本身这个UMG空间范围来设计的,那么在获取位置信息的时候可以使用“Slot as Canvas panel”这个节点获取被绘制的尺寸,这样就在因为父UMG的布局改变了导致该子UMG的尺寸改变的情况下,也不会导致子UMG里面的布局被打乱。
- 编程过程中对于占用到内存的模块,及时释放被占用的内存是非常重要的,在一些小程序上不明显,但是在大型程序中如果没能够及时释放被占用的内存,系统内存就会面临被用完的情况。
- 在使用VS构建项目之后,如果要运行带有动态链接库的程序,则需要将相应的动态库(DLL文件)复制张粘贴到对应的可执行程序(exe文件)下,但是这个过程只能够手动执行,目前为了能够实现自动化复制和粘贴操作,合适的方法是右键项目->属性(Properties)->设置属性(Configuration Properties)->构建事件(Build Events)->命令行(Command Line)里设置对应的命令,该命令使用的是DOS命令。还有另外一种方法:选择当前工程,右击"属性" -> "配置属性" -> "调试",在"工作目录"设置dll的路径,这种方法适合于需要在编辑器里面测试程序构建好后运行的结果。
- VS提供了主动复制CPP运行库的功能(但是不包括先前所说的第三方库),只需要在“项目(Project)”->“属性(Properties)”->“配置属性(Configuration Properties)”->“高级(Advanced)”中的“高级属性(Advanced Properties)”目录下,将“复制CPP运行库到输出目录(Copy cpp runtime to outdir)”设置为“是(Yes)”即可,这样在每次构建完成之后VS会自动将需要用到的运行库(例如msvcp140d.dll)复制到输出目录下,但是需要特别注意的是,如果启用了“构建事件(Build Events)”中的“构建后事件(Post-Build Event)”,则该功能将会无效。
- UE4提供了一套涉及范围特别广泛的工具来使用户开发游戏,这些工具包括游戏逻辑、材质效果、音响音效、动画、画面效果、粒子效果等等,这些高效且强大的工具也带来了诸如入门难的问题,想要一次性全部学完然后开始项目的话会很困难,因此我个人建议我自己首先确定一个自己想要做的小应用,然后在实现这个应用的过程中去学习我所需要的功能,在应用中学习,在学习中实践,利用这种方法的话应该会比直接理论学习效果更好一些。
- 合理安排任务是非常重要的,将一个项目分工成合理的小项目,一方面适合不同专业的人完成各自的工作,另一方面也适合每个部分集合成一个完整的项目。
- 关卡蓝图里面可以直接绑定被定义在Actor蓝图类内的事件调度器。即在Actor蓝图里面定义的事件调度器,如果该Actor已经被放置在关卡里面,则在该关卡的关卡蓝图里可以直接通过输入事件调度器的名字来查找到对应的事件调度器并直接定义绑定的内容。
- 在UE4引擎里,部分设定并不能够在编辑器视口里被直接观察到,例如在世界场景构成(World Composition)里被分配给指定图层的关卡,其关卡距离只能在游戏启动时候才会起作用,在编辑器状态下所有关卡都是可见状态,而不受关卡距离的影响。
- 在UE4里使用任何虚幻资产,需要首先将其加载到内存里才能够使用(例如编辑其属性等等),在代码里的表现为如果直接使用find_asset方法去寻找在内容浏览器里面的资产时,并不会找到该资产,因为该资产并没有被加载到内存里面,需要首先调用load_asset方法加载资产之后才能够调用find_asset方法找到该资产。
- 今天学习了如何在VS里面创建和使用动态库(DLL)的方法。创建动态库的方法有两种,声明“__declspec(dllexport)”标识符和定义模块定义文件(.def文件),前者的方法需要在函数声明或者类声明之前标示,后者则是在一个单独的文件里面列出所有需要导出的函数或类,这两种方法各有各的优缺点,可以择一使用。生成的二进制文件除了以dll为后缀名的动态库文件外,还有一个以lib为后缀名的导出函数声明文件(和静态库文件是同一个后缀名,但是性质不一样),供其他vs程序使用的时候首先需要提供lib文件所在的路径,然后是lib文件的全名,这样可以让程序通过编译和链接,然后是将DLL文件放到生成的exe项目文件同级目录下即可使用。前一种生成动态库的方法在现在的编程中更加常见,但是需要注意的是,在头文件里用“__declspec(dllexport)”定义导出的类和函数可以在使用动态库的过程中可以不添加“__declspec(dllimport)”标识符,但是会造成一些额外的寻址过程,造成程序体积增加,因此最好是通过宏定义“#ifdef”和“#ifndef”的方法来定义这两个标识符。
- 在UE4项目中,对于“平行生成”的情况需要谨慎处理,所谓“平行生成”情况,就是指如果有复数个类需要互相引用到对方,那么对于这些类的生成步骤以及获取所需类对象的步骤需要有额外的操作,例如说有一个类A,一个类B,类A有十个实例对象,类B有一个实例对象,类A中的每一个实例对象都需要获取到类B的对象,类B的实例对象也需要获取到全部十个的类A实例对象,那么在互相获取到地方之前就不要进行后面的步骤,以防出现空指针的情况出现,因此使用“Valid”节点来判断是否获取到所需的对象就变得非常重要。
- 在打开UE4项目之后发现蓝图A里面的引用全部掉了,然后在项目外通过复制粘贴所需的资产之后,回到蓝图A里发现引用还是没有引用上,这个时候只需要右键蓝图A的资产,然后在Asset Actions分类里选择Reload即可重新搜索引用。
- 在对某个问题下结论了之后,最好再根据这个问题多分析分析,因为当前结论可能会是错误的结论,例如本来是代码的问题,我个人却把车辆重叠和突然间运行速度变慢的问题归结为CPU多线程处理太慢的问题,实际该问题是线性插值导致的问题。
- 在读取了DDP之后如果生成新的DDC,新DDC会被放到Local节点指定的位置上。
- 在配置文件里设置的节点除了几个默认的以外(Local、Shared等),可以自定义设置节点,当自定义设置节点之后可以实现分别读取DDP,比如说地形关卡单独生成一个DDP,建筑关卡单独生成一个DDP,然后可以让用到的部门去读取单个DDP,而不需要把这两个关卡的DDC打包成唯一的一个DDP。
- DDC的Local(还有其他)节点设置里面有个“ReadOnly”选项,如果将该选项设置为true,则在项目运行过程中生成的所有DDC都不会被保存到本地上,因此造成的情况就是每一次启动都要重新生成DDC,将该选项设置为false即可让运行过程中生成的DDC保存到本地上。
- 打包后的项目不会运行构造脚本(Construction Script)里面的逻辑,猜测是因为该构造脚本仅仅只会在Editor模式下使用到。(该问题还没有得到实际的证实,因此在这里仅作一次记录)
标签:文件,需要,项目,蓝图,杂事,学习,模块,UE4 来源: https://www.cnblogs.com/u-n-owen/p/16330426.html