你的浏览器不支持canvas

让我们携手共创奇迹!

UE 规范

Date: Author: LBD

本文章采用 知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议 进行许可。转载请注明来自WindCrazy

这是一篇关于UE规范的文章

0.前言

好的项目规范应该是不断进步的,当你发现有好的更改可以适合所有用户时,你应该建议去更改现有规范.

已经有的规范和你想的规范冲突,就遵守原来的规范

1.资源命名规定

标识符应该只包含以下字符(用正则表达式表示就是[A-Za-z0-9_]+)

  • ABCDEFGHIJKLMNOPQRSTUVWXYZ
  • abcdefghijklmnopqrstuvwxyz
  • 1234567890
  • _ (下划线)

这样做的原因是,尽可能确保跨平台和工具链的兼容性。

一个项目如果有好的命名规范,那么在资源管理、查找、解析、维护时,都会有极大的便利性。

大多数资源的命名都应该有前缀,前缀一般是资源类型的缩写,然后使用下划线和资源名链接。

1.1 基本命名规则 - Prefix_BaseAssetName_Variant_Suffix

所有资源都应该有一个 BaseAssetName (基本资源名)。所谓基本资源名表明该资源在逻辑关系上属于那种资源,任何属于该逻辑组的资源都应该遵守同样的命名规范 Prefix_BaseAssetName_Variant_Suffix.

时刻谨记这个命名规范Prefix_BaseAssetName_Variant_Suffix,只要遵守它,大部分情况下都可以让命名规范。下面是详细的解释.

Prefix(前缀) 和 Suffix(后缀)由资源类型确定,请参照下面的资源类型表.

BaseAssetName(基本资源名)应该使用简短而便于识别的词汇,例如,如果你有一个角色名字叫做Bob,那么所有和Bob相关的资源的BaseAssetName都应该叫做Bob.

Varient(扩展名)用来保证资源的唯一性,同样,扩展名也应该是简短而容易理解的短词,以说明该资源在所属的资源逻辑组中的子集。例如,如果Bob有多套皮肤,那么这些皮肤资源都应该使用Bob作为基本资源名同时包含扩展名,例如’Evil’类型的皮肤资源,名字应该是Bob_Evil,而Retro类型的皮肤应该是用Bob_Retro.

一般来说,如果仅仅是为了保证资源的唯一性,Varient可以使用从01开始的两位数字来表示。例如,如果你要制作一堆环境中使用的石头资源,那么他们应该命名为Rock_01, Rock_02, Rock_03等等。除非特殊需要,不要让数字超过三位数,如果你真的需要超过100个的资源序列,那么你应该考虑使用多个基础资源名.

基于你所制作的资源扩展属性,你可以把多个扩展名串联起来。例如,如果你在制作一套地板所使用的资源,那么你的资源除了使用Flooring作为基本名,扩展名可以使用多个,例如Flooring_Marble_01, Flooring_Maple_01, Flooring_Tile_Squares_01.

1.1 范例

1.1e1 Bob
资源类型 资源名
Skeletal Mesh SK_Bob
Material M_Bob
Texture (Diffuse/Albedo) T_Bob_D
Texture (Normal) T_Bob_N
Texture (Evil Diffuse) T_Bob_Evil_D
1.1e2 Rocks
资源类型 资源名
Static Mesh (01) SM_Rock_01
Static Mesh (02) SM_Rock_02
Static Mesh (03) SM_Rock_03
Material M_Rock
Material Instance (Snow) MI_Rock_Snow

1.2 资源类型表

当给一个资源命名的时候,请参照以下表格来决定在基本命名中所使用的前缀和后缀

1.2.1 通用类型(Most Common)

资源类型 前缀 后缀 备注
Level / Map     所有地图应该放在Maps目录下
Level (持久关卡)   _P  
Level (音效关卡)   _Audio  
Level (光照)   _Lighting  
Level (几何体)   _Geo  
Level (Gameplay)   _Gameplay  
Blueprint BP_    
Material M_    
Static Mesh SM_   也有人用S_
Skeletal Mesh SK_    
Texture T_ _? 参照纹理
Particle System PS_    
Widget Blueprint WBP_    

1.2.2 动作(Animations)

资源类型 前缀 后缀 备注
Aim Offset AO_    
Aim Offset 1D AO_ _1D  
Animation Blueprint ABP_    
Animation Composite AC_    
Animation Montage AM_    
Animation Sequence A_    
Blend Space BS_    
Blend Space 1D BS_ _1D  
Level Sequence LS_    
Morph Target MT_    
Paper Flipbook PFB_    
Rig Rig_    
Skeletal Mesh SK_    
Skeleton SKEL_    

1.2.3 人工智能(Artificial Intelligence)

资源类型 前缀 后缀 备注
AI Controller AIC_    
Behavior Tree BT_    
Blackboard BB_    
Decorator BTDecorator_    
Service BTService_    
Task BTTask_    
Environment Query EQS_    
EnvQueryContext EQS_ Context  

1.2.4 蓝图(Blueprints)

资源类型 前缀 后缀 备注
Blueprint BP_    
Blueprint Component BP_ Component 例如 BP_InventoryComponent
Blueprint Function Library BPFL_    
Blueprint Interface BPI_    
Blueprint Macro Library BPML_   可能的话尽量不要使用蓝图宏.
Enumeration E   没有下划线.
Structure F or S   没有下划线.(一般来说是F和C++对齐)
Tutorial Blueprint TBP_    
Widget Blueprint WBP_    

1.2.5 材质(Materials)

资源类型 前缀 后缀 备注
Material M_    
Material (Post Process) PP_    
Material Function MF_    
Material Instance MI_    
Material Parameter Collection MPC_    
Subsurface Profile SP_    
Physical Materials PM_    
Decal M_ / MI_ _Decal  

1.2.6 纹理(Textures)

资源类型 前缀 后缀 备注
Texture T_    
Texture (Diffuse/Albedo/Base Color) T_ _D  
Texture (Normal) T_ _N  
Texture (Roughness) T_ _R  
Texture (Alpha/Opacity) T_ _A  
Texture (Ambient Occlusion) T_ _O  
Texture (Bump) T_ _B  
Texture (Emissive) T_ _E  
Texture (Mask) T_ _M  
Texture (Specular) T_ _S  
Texture (Metallic) T_ _M  
Texture (Packed) T_ _* 参照下面的多重纹理.
Texture Cube TC_    
Media Texture MT_    
Render Target RT_    
Cube Render Target RTC_    
Texture Light Profile TLP    

1.2.6.1 多重纹理(Texture Packing)

把多张纹理存于一个纹理文件中是很常见的方法,比如通常可以把自发光(Emissive), 粗糙度(Roughness), 环境光(Ambient Occlusion)以RGB通道的形式保存在纹理中,然后在文件的后缀中,注明这些信息,例如_EGO

一般来说,在纹理的Diffuse信息中附带Alpha/Opacity信息是很常见的,这时在_D后缀中可以加入A也可以不用加

不推荐同时把RGBA四个通道的信息保存在一张纹理中,这是由于带有Alpha通道的纹理要比不带的占用更多的资源,除非Alpha信息是以蒙版(Mask)的形式保存在Diffuse/Albedo通道中。

1.2.7 杂项(Miscellaneous)

资源类型 前缀 后缀 备注
Animated Vector Field VFA_    
Camera Anim CA_    
Color Curve Curve_ _Color  
Curve Table Curve_ _Table  
Data Asset *_   前缀取决于何种类型资源.
Data Table DT_    
Float Curve Curve_ _Float  
Foliage Type FT_    
Force Feedback Effect FFE_    
Landscape Grass Type LG_    
Landscape Layer LL_    
Matinee Data Matinee_    
Media Player MP_    
File Media Source FMS_    
Object Library OL_    
Redirector     (暂时空缺,尽快解决).
Sprite Sheet SS_    
Static Vector Field VF_    
Substance Graph Instance SGI_    
Substance Instance Factory SIF_    
Touch Interface Setup TI_    
Vector Curve Curve_ _Vector  

1.2.8 Paper 2D

资源类型 前缀 后缀 备注
Paper Flipbook PFB_    
Sprite SPR_    
Sprite Atlas Group SPRG_    
Tile Map TM_    
Tile Set TS_    

1.2.9 物理(Physics)

资源类型 前缀 后缀 备注
Physical Material PM_    
Physics Asset PHYS_    
Destructible Mesh DM_    

1.2.10 声音(Sounds)

资源类型 前缀 后缀 备注
Dialogue Voice DV_    
Dialogue Wave DW_    
Media Sound Wave MSW_    
Reverb Effect Reverb_    
Sound Attenuation ATT_    
Sound Class     这些资源应该放在SoundClasses目录中
Sound Concurrency   _Con 在SoundClass之后命名
Sound Cue SC_    
Sound Mix Mix_    
Sound Wave SW_    

1.2.11 界面(User Interface)

资源类型 前缀 后缀 备注
Font Font_    
Slate Brush Brush_    
Slate Widget Style Style_    
Widget Blueprint WBP_    

1.2.12 特效(Effects)

资源类型 前缀 后缀 备注
Particle System PS_    
Niagara System NS_    
Material (Post Process) PP_    

2.Content目录结构

用途分类

|-- Content
    |-- GenericShooter(项目名称)
        |-- Art					美术资源
        |   |-- Scene
        |   |   |-- Build
        |   |   |-- Vehicle
        |   |   |-- Furniture
        |   |-- Weapon
        |   |   |-- AK47
        |	|   |-- Common
        |   |   |-- UZI
        |   |-- Character
        |	|   |-- Bob
        |	|   |-- Common
        |	|   |   |-- Animations(一般骨架是统一的,如果不统一应该考虑分类,比如人类,狗,马等)
        |	|   |   |-- Audio
        |	|   |-- Jack
        |   |-- MergeMesh		优化合并物体资源
        |   |   |-- Lobby		针对关卡名分类存放
        |   |-- MaterialLibrary	公用材质库(重复使用而不属特定模型的材质和纹理)
        |   |   |-- Debug       测试或调试的材质
        |   |   |-- Utility
        |   |   |-- MF          材质函数,灯光材质灯
        |   |   |-- MPC         材质参数集合
        |   |   |-- PP          全局后处理材质(其实有命名前缀不需要分这么细的文件夹)
        |-- Core
        |   |-- Characters
        |   |-- Engine
        |   |-- GameModes
        |   |-- Interactables
        |   |-- Pickups
        |   |-- Weapons
        |-- Audio
        |   |-- CV
        |   |-- SoundEffect
        |   |-- SoundClass
        |-- Effects
        |   |-- Electrical
        |   |-- Fire
        |   |-- Weather
        |-- Maps
        |   |-- Campaign1
        |   |-- Campaign2
        |   |-- Sequence		定序器
        |-- Placeables
        |   |-- Pickups
        |   |-- Weapons
        |-- UI

注意事项

文件夹的命名需要所有单词的首字母大写,并且中间没有任何连接符,例如BluePrint , StaticMesh, SkeletalMesh等

文件夹的命名中不要出现空格,或奇怪字符,永远在目录名中使用 a-z , A-Z , 和 0-9 这些安全的字符

任何基础材质、分层材质、材质参数集、材质函数,或者任何被重复使用而不属于特定模型的材质和纹理都放在MaterialLibrary公用材质库

修改引用资源贴图材质应尽量复制一份放到自建目录修改,以防其他人再合并同一资源下其他物体时覆盖你之前的修改,坚持把自己的资源放在自己的工程目录中。

有一些资源类型通常文件数量巨大,而且每个作用都不同。如人物的动画资源和声音资源。如果你发现有15个以上的资源 属于同一个逻辑类型,那么它们应该被放在一起。

如无必要,一二级目录不要建新文件夹

2.2 使用一个顶级目录来保存所有工程资源

所有的工程资源都应该保存在一个以工程名命名的目录中。例如你有一个工程叫做’Generic Shooter’,那么 所有 该工程的资源都应该保存在Content/GenericShooter目录中.

开发者目录Developers不用受此限制,因为开发者资源是跨工程使用的,参照下面的开发者目录中的详细说明.

使用顶级目录的原因有很多,下面是详细的解释.

2.2.1 避免全局资源

在工程目录之外保存的资源对于资源管理会造成不必要的干扰。

每个资源都应该有它存在的意义,不然它就不应该出现在工程目录中。那些仅仅是为了测试而使用的临时资源,应该放在Developer目录中。

2.2.2 减少资源迁移时的冲突

当一个团队有多个项目时,从一个项目中把资源拷贝到另一个项目会非常频繁,这时最方便的方法就是使用引擎的资源浏览器提供的Migrate功能,因为这个功能会把资源的依赖项一起拷贝到目标项目中.

这些依赖项经常造成麻烦。如果两个工程没有遵守项目顶级目录规则,那么这些依赖项很容易就会被拷贝过来的同名资源覆盖掉,从而造成意外的更改。

这也是为什么EPIC会强制要求商城中出售的资源要遵守同样的规定的原因

执行完Migrate资源拷贝后,安全的资源合并方法是使用资源浏览器中的’替换引用’(Replace References)工具,把不属于工程目录中的资源引用替换掉。一旦资源资源完成完整的合并流程,工程目录中不应该存在另一个工程的顶级目录。这种方法可以_100%_保证资源合并的安全性。

2.2.3 使得DLC、子工程、以及补丁包容易维护

如果你的工程打算开发DLC或者子工程,那么这些子工程所需要的资源应该迁移出来放在另一个顶级目录中,这样的结构使得编译这些版本时可以区别对待子工程中的资源。子工程中的资源的迁入和迁出代价也更小。如果你想在子项目中修改一些原有工程中的资源,那么可以把这些资源迁移到子工程目录中,这样不会破坏原有工程。

2.3 使用开发者目录(Developer)做本地测试

在一个项目的开发期间,团队成员经常会有一个’沙箱’目录用来做测试而不会影响到工程本身。因为工作是连续的,所以即使这些’沙箱’目录也需要上传到源码服务器上保存。但并不是所有团队成员都需要这种开发者目录的,但使用开发者目录的成员来说,一旦这些目录是在服务器上管理的,总会需要一些麻烦事。

首先团队成员极容易使用这些尚未准备好的资源,这些资源一旦被删除就会引发问题。例如一个做模型的美术正在调整一个模型资源,这时一个做场景编辑的美术如果在场景中使用了这个模型,那么很可能会导致莫名其妙的问题,进而造成大量的工作浪费。

但如果这些模型放在开发者目录中,那么场景美术人员就没有任何理由使用这些资源。资源浏览器的缺省设置会自动过滤掉这个目录,从而保证正常情况下不可能出现被误用的情况。

一旦这些资源真正准备好,那么美术人员应该把它们移到正式的工程目录中并修复引用关系,这实际上是让资源从实验阶段’推进’到了生产阶段。

2.4 所有场景文件应该保存在一个名为’Maps’的目录中

地图文件非常特殊,几乎所有工程都有自己的一套关于地图的命名规则,尤其是使用了sub-levels或者streaming levels技术时。但不管你如何组织自己的命名规则,都应该把所有地图保存在/Content/Project/Maps

记住,尽量使用不浪费大家的时间的方法去解释你的地图如何打开。比如通过子目录的方法去组织地图资源,例如建立 Maps/Campaign1/Maps/Arenas,但最重要的是一定要都放在/Content/Project/Maps

这也有助于产品的打版本工作,如果工程里的地图保存的到处都是,版本工程师还要到处去找,就让人很恼火了,而把地图放在一个地方,做版本时就很难漏掉某个地图,对于烘培光照贴图或者质量检查都有利。

2.5 使用Core目录存储系统蓝图资源以及其他系统资源

使用/Content/Project/Core这个目录用来保存一个工程中最为核心的资源。例如,非常基础的GameMode, Character, PlayerController, GameState, PlayerState,以及与此相关的一些资源也应该放在这里。

这个目录非常明显的告诉其他团队成员:”不要碰我!”。非引擎程序员很少有理由去碰这个目录,如果工程目录结构合理,那么游戏设计师只需要使用子类提供的功能就可以工作,负责场景编辑的员工只需要使用专用的的蓝图就可以,而不用碰到这些基础类。

例如,如果项目需要设计一种可以放置在场景中并且可以被捡起的物体,那么应该首先设计一个具有被捡起功能的基类放在Core/Pickups目录中,而各种具体的可以被捡起的物体诸如药瓶、子弹这样的物体,应该放在/Content/Project/Placeables/Pickups/这样的目录中。游戏设计师可以在这些目录中定义和设计这些物体,所以他们不应该去碰Core/Pickups目录下的代码,要不然可能无意中破坏工程中的其他功能

2.6 不要创建名为Assets 或者 AssetTypes的目录

2.6.1 创建名为Assets的目录是多余的

因为本来所有目录就是用来保存资产(Assets)的

2.6.2 创建名为MeshesTextures或者Materials的目录是多余的

资源的文件名本身已经提供了资源类型信息,所以在目录名中再提供资源类型信息就是多余了,而且使用资源浏览器的过滤功能,可以非常便利的提供相同的功能。

比如想查看Environment/Rocks/目录下所有的静态Mesh资源?只要打开静态Mesh过滤器就可以了,如果所有资源的文件名已经正确命名,这些文件还会按照文件名和前缀正确排序,如果想查看所有静态Mesh和带有骨骼的Mesh资源,只要打开这两个过滤器就可以了,这种方法要比通过打开关闭文件夹省事多了。

这种方法也能够节省路径长度,因为用前缀S_只有两个字符,要比使用Meshes/七个字符短多了。

2.7 超大资源要有自己的目录结构

这节可以视为针对2.6的补充

有一些资源类型通常文件数量巨大,而且每个作用都不同。典型的例子是动画资源和声音资源。如果你发现有15个以上的资源属于同一个逻辑类型,那么它们应该被放在一起。

举例来说,角色共用的动画资源应该放在Characters/Common/Animations目录中,并且其中应该还有诸如Locomotion 或者Cinematic的子目录

这并不适用与纹理和材质。比如Rocks目录通常会包含数量非常多的纹理,但每个纹理都都是属于特定的石头的,它们应该被正确命名就足够了。即使这些纹理属于材质库

2.8 材质库MaterialLibrary

如果你的工程中使用了任何基础材质、分层材质,或者任何被重复使用而不属于特定模型的材质和纹理,这些资源应该放在材质库目录Content/Project/MaterialLibrary

这样可以很容易管理这些’全局’材质

这也使得’只是用材质实例’这个原则得以执行的比较容易。如果所有的美术人员都只是用材质实例,那么所有的原始材质都应该保存在这个目录中。你可以通过搜索所有不在MaterialLibrary中的基础材质来验证这一点。

MaterialLibrary这个目录并不是仅能保存材质资源,一些共用的工具纹理、材质函数以及其他具有类似属性的资源都应该放在这个目录或子目录中。例如,噪声纹理应该保存在MaterialLibrary/Utility目录中。

任何用来测试或调试的材质应该保存在MaterialLibrary/Debug中,这样当工程正式发布时,可以很容易把这些材质从删除,因为这些材质如果不删除,可能在最终产品中非常扎眼。

2.9 避免空目录存在

很简单,不要有空的目录存在,会干扰资源浏览器的工作。

如果你发现无法删除一个空目录,尝试以下步骤

  1. 首先确保正确使用了版本仓库
  2. 针对工程运行文件夹引用修复功能(Fix Up Redirectors)
  3. 从磁盘上打开对应的目录,删除其中的所有文件
  4. 关闭编辑器
  5. 确保版本仓库的状态是同步后的(例如如果正在使用的是Perforce,那么在Content目录运行”Reconcile Offline Work”功能)
  6. 重新打开编辑器,确认所有的功能是否正常。如果出现异常,需要回退修改,然后找到问题所在重新尝试
  7. 确保空文件已经被删除
  8. 提交修改到版本仓库中

3. 蓝图(Blueprint)

这一章会专注于蓝图和蓝图的实现。本规则会尽可能和Epic官方提供的标准 一致。

要牢记: Blueprinting badly bears blunders, beware! (出自KorkuVeren) (译者: 视蓝图如同看到熊出没,一定要当心!)

3.1 编译(Compiling)

需要保证所有蓝图在编译时0警告和0错误。你应该尽快修复所有警告和异常,以免它们造成可怕的麻烦。

绝对不要提交那些断开的蓝图,如果你需要通过源码服务器保存,那么必须暂时搁置它们

断开的蓝图有巨大的破坏力,而且会影响到蓝图之外,比如造成引用失效(可能从未赋值),未定义的行为(可能出现符号溢出),烘培失败,或者频繁的重新编译。一个断开的蓝图可能会毁掉整个项目。

3.2 变量(Variables)

3.2.1 命名(Naming)

3.2.1.1 使用名词

所有非布尔类型的变量必须使用简短、清晰并且意义明确的名词作为变量名。

3.2.1.2 大驼峰规范(PascalCase)

所有非布尔类型的变量的大小写需要遵守大驼峰规范(PascalCase)规范。

3.2.1.2e 范例
  • Score
  • Kills
  • TargetPlayer
  • Range
  • CrosshairColor
  • AbilityID
3.2.1.3 布尔变量 b 前缀

所有布尔类型变量需要遵守大驼峰规范规范,同时前面需要增加小写的b做前缀。

例如: 用 bDeadbEvil, 不要 使用DeadEvil.

UE的蓝图编辑器在显示变量名称时,会自动把前缀b去掉

3.2.1.4 布尔类型变量命名规则
3.2.1.4.1 孤立存在的状态信息

布尔类型变量如果用来表示一般的孤立存在状态,名字应该使用描述性的单词,且不要包含具有提问含义的词汇,比如Is,这个词是保留单词。

例如:使用bDead and bHostile不要使用bIsDead and bIsHostile

也不要使用类似于bRunning这样的动词,动词会让布尔变量的含义变得复杂。可以使用枚举来表示这种Running之类的状态

3.2.1.4.2 避免表达复杂状态

不要使用布尔变量保存复杂的,或者需要依赖其他属性的状态信息,这会让状态变得复杂和难以理解,如果需要尽量使用枚举来代替。

例如:当定义一个武器时,不要使用bReloadingbEquipping这样的变量,因为一把武器不可能即在reloading状态又在equipping状态,所以应该使用定义一个叫做EWeaponState的枚举,然后用一个枚举变量WeaponState来代替,这也使得以后增加新的状态更加容易。

例如:不要使用bRunning这样的变量,因为你以后有可能还会增加bWalking 或者 bSprinting,这也应该使用一个枚举来非常清晰的定义状态。

3.2.1.5 考虑上下文

蓝图中的变量命名时需要考虑上下文环境,避免重复不必要的定义。

3.2.1.5e Examples

假设有一个蓝图名为 BP_PlayerCharacter.

不好的命名

  • PlayerScore
  • PlayerKills
  • MyTargetPlayer
  • MyCharacterName
  • CharacterSkills
  • ChosenCharacterSkin

这些变量的命名都很臃肿。因为这些变量都是属于一个角色蓝图BP_PlayerCharacter的,没必要在变量中再重复这一点。

好的命名

  • Score
  • Kills
  • TargetPlayer
  • Name
  • Skills
  • Skin
3.2.1.6 不要在变量中包含原生变量类型名

所谓原生变量是指那些最简单的保存数据的变量类型,比如布尔类型、整数、浮点数以及枚举。

String和vectors在蓝图中也属于原生变量类型,即使技术上严格来讲它们其实不是。

由三个浮点数组成的vector经常被视为一个整体数据类型,比如旋转向量。

文本类型变量(Text)不属于原生类型,因为它们内部还包含有国际化信息。原生类型的字符串变量类型是String , 而不是Text

原生类型的变量名中不应该包含变量类型名。

例如:使用Score, Kills, 以及 Description不要使用ScoreFloat, FloatKills, DescriptionString

但也有例外情况,当变量的含义包含了”多少个”这样的信息,并且仅用一个名字无法清晰的表达出这个含义时。

比如:游戏中一个围墙生成器,需要有一个变量保存在X轴上的生成数量,那么需要使用NumPosts 或者 PostsCount这样的变量,因为仅仅使用Posts可能被误解为某个保存Post的数组

3.2.1.7 非原生类型的变量,需要包含变量类型名

非原生类型的变量是指那些通过数据结构保存一批原生类型的复杂变量类型,比如Structs、Classes、Interface,还有一些有类似行为的原生变量比如TextName也属于此列。

如果仅仅是原生变量组成的数组,那么这个数组仍然属于原生类型

这些变量的名字应该包含数据类型名,但同时要考虑不要重复上下文。

如果一个类中包拥有一个复杂变量的实例,比如一个BP_PlayerCharacter中有另一个变量BP_Hat,那么这个变量的名字就不需要包含变量类型了。

例如: 使用 HatFlag以及 Ability不要使用MyHatMyFlagPlayerAbility

但是,如果一个类并不拥有这个属性,那么就需要在这个属性的名字中包含有类型的名字了

例如:一个蓝图类BP_Turret用来表示一个炮塔,它拥有瞄准BP_PlayerCharacter作为目标的能力,那么它内部会保存一个变量作为目标,名字应该是TargetPlayer,这个名字非常清楚的指明了这个变量的数据类型是Player

3.2.1.8 数组

数组的命名规则通常和所包含的元素的规则一样,但注意要用复数。

例如:用TargetsHats以及 EnemyPlayers不要使用TargetListHatArray 或者 EnemyPlayerArray

3.2.2 可编辑变量

所有为了配置蓝图行为,可以安全的更改数据内容的变量都需要被标记为Editable

相反,那些不能更改或者不能暴露给设计师的变量,都不能标上可编辑标志,除非因为引擎的原因,这些变量需要被标为Expose On Spawn

总之不要滥用Editable标记

另外:对于所有标记为Editable的变量,包括被标记为 Expose On Spawn的变量,都应该在其Tooltip内填写关于如何改变变量值,以及会产生何种效果的说明。

3.2.2.2 滑动条(Slider)以及取值范围

对于可编辑的变量,如果不适合直接输入具体数值,那么应该通过一个滑动条(Slider)并且加上取值范围来让设计师输入。

举例:一个产生围墙的蓝图,拥有一个PostsCount的变量,那么-1显然适合不合理的输入,所以需要设上取值范围注明0是最小值

如果在构造脚本中需要一个可编辑变量,那么一定要首先定义一个合理的取值范围,要不然可能会有人设上一个非常大的值造成编辑器崩溃。

一个变量的取值范围只有当明确知道其范围时才需要定义,因为滑块的取值范围的确能够阻止用户输入危险数值,但用户仍然能够通过手动输入的方式输入一个超出滑块范围的值给变量,如果变量的取值范围未定义,那么这个值就会变得’很危险’而且仍然会生效。

3.2.3 分类

如果一个类的变量很少,那么没有必要使用分类

如果一个类的变量规模达到中等(5-10),那么所有可编辑的变量应该自己的分类,而不应该放在缺省分类中,通常叫做 Config

如果类中的变量的数量非常大,那么所有可编辑的变量都应该放在Config分类的子分类下,所有不可编辑的变量应该根据它们的用途建立相关分类保存

可以通过在分类名中添加字符|,直接建立子分类,比如Config | Animations

C++中为Category = "Config | Animations"

举例:一个武器的类中的变量分类目录大致如下:

    |-- Config
    |    |-- Animations
    |    |-- Effects
    |    |-- Audio
    |    |-- Recoil
    |    |-- Timings
    |-- Animations
    |-- State
    |-- Visuals

3.2.4 变量的访问权限

在C++中,变量的访问类型由类成员的属性决定,Public类型的表示其他类都可以访问,Protetced类型的成员表示子类可以访问,Private类型变量表示只有类内部函数可以访问此变量。

蓝图并没有类似的权限访问设计。

可以视可编辑(Editable)类型的变量作为Public类型变量,视不可编辑的变量作为Protected类型变量。

3.2.4.1 私有变量

尽量不要把变量声明为private类型,除非一开始就打算这个变量永远只能被类内部访问,并且类本身也没打算被继承。尽量用protected,只有当你有非常清楚的理由要去限制子类的能力时,再使用private类型。

3.3.1.1 所有函数的命名都应该是动词

所有函数和事件执行者都是需要做一些动作,可能是去获取信息,也可能是数据计算,或者搞点什么事情。因此,所有函数都应该用动词开始,并且用一般现代时态,并且有上下文来表明它们究竟在做什么

OnRep 这样的响应函数,事件具柄和事件派发器的命名不遵守这个规则。

好的例子:

  • Fire - 如果类是一个角色或者武器,那么这是一个好命名,如果是木桶,玻璃,那这个函数就会让人困惑了。
  • Jump - 如果类是一个角色,那么这是个好名字,如果不是,那么需要一些上下文来解释这个函数的含义
  • Explode
  • ReceiveMessage
  • SortPlayerArray
  • GetArmOffset
  • EnableBigHeadMode
  • IsEnemy - “Is” 是个动词
  • HasWeapon - “Has” 是动词.
  • WasCharging - “Was” 是动词”be”的过去式 用 “was”表示查询以前的状态
  • CanReload - “Can”是动词

不好的例子:

  • Dead - 是已经死了?还是死的动作?
  • Rock
  • ProcessData - 无意义,这个名字等于没说.
  • PlayerState - 不能用名词
  • Color - 如果是动词,那么缺少上下文,如果是名词,也不行.

3.3.1.2 事件的响应函数和派发函数都应该以On开头

事件的响应函数和派发函数都应该以On开头,然后遵守动词规则,如果是过去式,那么动词应该移到最后以方便阅读

在遵守动词规则的时候,需要优先考虑英语中的固定句式

有一些系统用Handle来表示事件响应,但在’Unreal’用的是On而不是Handle

好的例子:

  • OnDeath - Common collocation in games
  • OnPickup
  • OnReceiveMessage
  • OnMessageRecieved
  • OnTargetChanged
  • OnClick
  • OnLeave

坏的例子:

  • OnData
  • OnTarget
  • HandleMessage
  • HandleDeath

3.3.1.3 远程调用函数应该用目标作为前缀

Good examples: 任何时候创建RPC函数,都应该把目标作为前缀放在前面,例如ServerClient或者 Multicast,没有例外。

前缀之后的部分,遵守上面的其他规则。

好的例子:

  • ServerFireWeapon
  • ClientNotifyDeath
  • MulticastSpawnTracerEffect

坏的例子:

  • FireWeapon - 没有使用目标前缀
  • ServerClientBroadcast - 混乱
  • AllNotifyDeath - 用 Multicast, 不要用 All.
  • ClientWeapon - 没有用动词, 让人困惑.

3.3.2 所有函数都应该有返回节点

所有函数都应该有返回节点,没有例外。

返回节点明确标注了蓝图到此执行完毕。蓝图中的结构有可能有并行结构Sequence、循环结构ForLoopWithBreak或者逆向的回流节点组成,明确结束节点使蓝图易于阅读维护和调试。

如果启用了返回节点,当你的蓝图中有分支没有正常返回,或者流程有问题,蓝图的编译器会提出警告。

比如说,有程序员在并行序列中添加了一个新的分支,或者在循环体外添加逻辑但没有考虑到循环中的意外返回,那么这些情况都会造成蓝图的执行序列出现意外。蓝图编译器会立即给这些情况提出警告。

3.3.3 蓝图函数中节点数不应该超过50个

简单来说,蓝图函数中的节点数应该小于50个,如果函数过于复杂,应该把它分割成几个小一点的函数,以便更好的阅读和维护。

在蓝图中添加以下节点不用计算个数,因为它们并不会增加蓝图的复杂度:

  • Comment - 注释
  • Route - 路由节点
  • Cast - 类型转换节点
  • Getting a Variable - 获取变量
  • Breaking a Struct - 结构分解节点
  • Function Entry - 函数入口
  • Self - 自身节点

3.3.4 所有Public函数都应该有功能描述

此规则更适用于公开或在市场出售的蓝图,以便其他人可以更轻松地浏览和使用您的蓝图。

简而言之,任何具有 Public 访问权限的函数都应该填写其描述。

3.3.5 插件中可以在蓝图中调用的自定义函数都应该放在以插件名命名的类别中

如果你的工程包含插件,插件中定义了一些静态的可以被蓝图调用的函数,那么这些函数(Category)的类别应该被设置成插件的名称。

例如,Zed Camera Interface 或者 Zed Camera Interface | Image Capturing.

3.4 蓝图图形

本节包含了关于蓝图图形的内容

3.4.1 不要画‘意面’

蓝图中所有连线都应该有清晰的开始点和结束点。你的蓝图不应该让阅读者在一堆乱糟糟的线中翻来翻去。以下内容可以帮你避免‘意大利面’样式的蓝图产生。

3.4.2 保持连线对齐,而不是节点

不要试图让节点对齐,对齐的应该是连线。你无法控制一个节点的大小和上面连接点的位置,但你能通过控制节点的位置来控制连线。笔直的连线让整个蓝图清晰美观,歪歪扭扭的连线会让蓝图丑陋不堪。可以通过蓝图编辑器提供的功能直接让连线变直,方法是选择好节点,用快捷键Q

3.4.3 蓝图需要有合理的注释

要善用蓝图的注释块,把相关的节点包含在注释块中,并在注释中描述这些功能节点的行为。虽然每个函数都应该被正确命名,以便每个单独的节点都易于理解,但仍然需要在注释中描述出为某个整体功能做出贡献的节点组的具体作用。如果一个函数没有很多节点块,并且很明显这些节点在函数目标中服务于最终目的,则不需要再对它们进行注释,函数名称和其描述中包含的信息就足够了。

3.4.4 蓝图中需要在适当的地方处理类型转换错误

如果在函数或者蓝图事件处理过程中,总是假定类型转换应该是成功的,那么一旦出现类型转换失败,那应该及时报错,这使得其他模块得到通知,一件“应该正确工作”的函数没有正确工作。函数还应该在类型转换失败后,正确的恢复工作。

但这并不是强制要求所有的类型转换节点都要处理失败的情况,

参考链接

1,最合理的UE4规范

2,代码规范


对于本文内容有问题或建议的小伙伴,欢迎提出问题