元焏项目组UE4规范——初稿
英文翻译标准以DeepL为唯一标准 他可能不是最好用的翻译软件,但一定是全世界最权威的自动翻译软件
“地图(map)”这个词通常也会被称为“关卡(level)”,两者的含义是等同的,在这里可以查看这个词的发展经历
无论是”贴图“还是"纹理"指的都是一个东西,英文名都叫做Texture
'变量'和'属性'两个词在很多情况下是可以互相通用的。但如果他们同时出现在一个环境时,含义有一些不同:
'属性'通常定义在一个类的内部。例如,如果一个类BP_Barrel
有一个内部成员bExploded
,那么bExploded
可以是做是BP_Barrel
的一个属性
当'属性'用在类的内部时,通常用来获取已经定义好的数据
'变量'通常用在给函数传递参数,或者用在函数内的局部变量 当'变量'用在类的内部时,通常是用来定义什么或者用来保存某些数据的。
对于命名法有数种,以下是几种常见的方式,像UE4这样的复杂项目则需要将多种命名法组合使用
每个单词的首字母都是大写,单词之间没有其他字符,例如 :
DesertEagle
,StyleGuide
,ASeriesOfWords
。第一个单词的首字母小写,后面的单词的首字母大写,例如:
desertEagle
,styleGuide
,aSeriesOfWords
。单词之间用下划线链接,单词的首字母可以大写也可以小写,例如:
desert_Eagle
,Style_Guide
,a_Series_of_Words
关于命名方式详细介绍:文件夹命名
对规范优劣的争论是没有意义的,有规范你就该去遵守。
规范不仅是用来命名文件的,也是用来进行快速沟通的,需要所有人记住规范(自己需要的部分)并使用,所以不存在自动生成文件命名规范的软件
项目规范应该是不断进步的,当你发现有好的更改时,你应该建议去更改现有规范
把资源从一个工程迁移到另一个工程不应该产生新的学习成本,所有资源应该符合项目规范,消除不必要的歧义和不确定性
遵守规范可以促进对于项目的生产和维护效率,因为团队成员不用去考虑规范问题,只需要去遵守。
本套规范根据诸多优秀经验编写,遵守它意味着可以少走很多弯路。
如果你发现周围有同学不遵守规范,你需要去纠正他,否则最后遭殃的可能是你自己
对于资源的命名的规范应该像法律一样被遵守。一个项目如果有好的命名规范,那么在资源管理、查找、解析、维护时,都会有极大的便利性。
大多数资源的命名都应该有前缀,前缀一般是资源类型的缩写,然后使用下划线和资源名链接。
时刻记住这个命名规范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
。
资源类型 | 资源名 |
---|---|
骨骼模型(Skeletal Mesh) | SK_Bob |
材质(Material) | M_Bob |
反射贴图(Texture (Diffuse/Albedo)) | T_Bob_D |
法线贴图(Texture (Normal)) | T_Bob_N |
资源类型 | 资源名 |
---|---|
静态网格01(Static Mesh (01)) | S_Rock_01 |
静态网格02(Static Mesh (02)) | S_Rock_02 |
静态网格03(Static Mesh (03)) | S_Rock_03 |
材质(Material) | M_Rock |
材质实例(Material Instance (Snow)) | MI_Rock_Snow |
当给一个资源命名的时候,请参照以下表格来决定在资本资源名前后所使用的前缀和后缀
1.2.1 常用类型(Most Common)
1.2.2 动作(Animations)
1.2.4 蓝图(Blueprints)
1.2.5 材质(Materials)
1.2.6 纹理(Textures)
1.2.7 杂项(Miscellaneous)
1.2.8 Paper2D
1.2.9 物理(Physics)
1.2.10 声音(Sound)
1.2.11 用户界面(User Interface)
1.2.12 特效(Effects)
资源类型 | 前缀 | 后缀 | 备注 |
---|---|---|---|
关卡(Level / Map) | 所有地图应该放在Maps目录下 | ||
主关卡(Level (Persistent)) | _P | ||
音效关卡(Level (Audio)) | _Audio | ||
光照关卡(Level (Lighting)) | _Lighting | ||
体积关卡(Level (Geometry)) | _Geo | ||
逻辑关卡(Level (Gameplay)) | _Gameplay | ||
蓝图(Blueprint) | BP_ | ||
材质(Material) | M_ | ||
静态模型(Static Mesh) | SM_ | ||
骨骼模型(Skeletal Mesh) | SK_ | ||
图片/纹理/贴图(Texture) | T_ | _? | 参照纹理 |
粒子系统(Particle System) | PS_ | ||
UI/控件蓝图(Widget Blueprint) | WB_ | WB_. |
资源类型 | 前缀 | 后缀 | 备注 |
---|---|---|---|
瞄准偏移(Aim Offset) | AO_ | ||
瞄准偏移1D(Aim Offset 1D) | AO_ | ||
动画蓝图(Animation Blueprint) | ABP_ | ||
动画合成(Animation Composite) | AC_ | ||
动画蒙太奇(Animation Montage) | AM_ | ||
动画序列(Animation Sequence) | AS_ | ||
混合空间(Blend Space) | BS_ | ||
混合空间1D(Blend Space 1D) | BS_ | ||
关卡序列(Level Sequence) | LS_ | ||
变形目标(Morph Target) | MT_ | ||
Rig | Rig_ | ||
骨骼模型(Skeletal Mesh) | SK_ | ||
骨骼(Skeleton) | SKEL_ |
资源类型 | 前缀 | 后缀 | 备注 |
---|---|---|---|
AI控制器(AI Controller) | AIC_ | ||
行为树(Behavior Tree) | BT_ | ||
黑板(Blackboard) | BB_ | ||
装饰器(Decorator) | BTDecorator_ | ||
服务器(Service) | BTService_ | ||
任务(Task) | BTTask_ |
资源类型 | 前缀 | 后缀 | 备注 |
---|---|---|---|
蓝图(Blueprint) | BP_ | ||
蓝图函数库(Blueprint Function Library) | BPL_ | ||
蓝图接口(Blueprint Interface) | BPI_ | ||
蓝图宏库(Blueprint Macro Library) | BPML_ | 可能的话尽量不要使用蓝图宏 | |
枚举(Enumeration) | E | 没有下划线 | |
结构体(Structure) | F | 没有下划线 | |
用于建表的结构体(Structure) | FST_ | ||
UI/控件蓝图(Widget Blueprint) | WB_ |
资源类型 | 前缀 | 后缀 | 备注 |
---|---|---|---|
材质(Material) | M_ | ||
后期处理材质(Material (Post Process)) | PP_ | ||
材质函数(Material Function) | MF_ | ||
材质引用(Material Instance) | MI_ | ||
材质参数集合(Material Parameter Collection) | MPC_ | ||
次表面描述文件(Subsurface Profile) | SP_ | ||
物理材质(Physical Materials) | PM_ |
以下内容默认 贴图=纹理 蒙版=遮罩,仅仅是不同地方叫法不同,推荐使用贴图、遮罩描述
资源类型 | 前缀 | 后缀 | 备注 |
---|---|---|---|
贴图(Texture) | T_ | ||
基础颜色纹理(Texture (Base Color) | T_ | _D | |
反射贴图(Texture (Diffuse/Albedo/Base Color)) | T_ | _D | |
法线贴图(Texture (Normal)) | T_ | _N | |
粗糙度贴图(Texture (Roughness)) | T_ | _R | |
透明贴图(Texture (Alpha/Opacity)) | T_ | _A | |
环境光遮罩/蒙板贴图(Texture (Ambient Occlusion)) | T_ | _AO | |
自发光贴图(Texture (Emissive)) | T_ | _E | |
这招贴图(Texture (Mask)) | T_ | _M | |
反射贴图(Texture (Specular)) | T_ | _S | |
已打包纹理(Texture (Packed)) | T_ | _* | 参见下面的纹理打包备注. |
体积纹理(Texture Cube) | TC_ | ||
媒体纹理(Media Texture) | MT_ | ||
渲染目标(Render Target) | RT_ | ||
立方体渲染目标(Cube Render Target) | RTC_ | ||
光照描述贴图(Texture Light Profile) | TLP |
<a name="anc-textures-packing"
把多张贴图存于一个贴图文件中是很常见的方法,比如通常可以把自发光(Emissive), 粗糙度(Roughness), 环境光(Ambient Occlusion)以RGB通道的形式保存在贴图中,然后在文件的后缀中,注明这些信息,例如_EGO
一般来说,在纹理的Diffuse信息中附带Alpha/Opacity信息是很常见的,这时在
_D
后缀中加入A
不推荐同时把RGBA四个通道的信息保存在一张纹理中,这是由于带有Alpha通道的纹理要比不带的占用更多的资源,除非Alpha信息是以遮罩/蒙版(Mask)的形式保存在Diffuse/Albedo通道中。
资源类型 | 前缀 | 后缀 | 备注 |
---|---|---|---|
动画向量场(Animated Vector Field) | VFA_ | ||
相机动画(Camera Anim) | CA_ | ||
颜色曲线(Color Curve) | Curve_ | _Color | |
曲线表格(Curve Table) | Curve_ | _Table | |
数据资产(Data Asset) | *_ | 前缀取决于何种类型资源 | |
数据表格(Data Table) | DT_ | ||
曲线(Float Curve) | Curve_ | _Float | |
草地地形类型(Landscape Grass Type) | LG_ | ||
地形层(Landscape Layer) | LL_ | ||
Matinee数据(Matinee Data) | Matinee_ | ||
媒体播放器(Media Player) | MP_ | ||
对象库(Object Library) | OL_ | ||
静态矢量场(Static Vector Field) | VF_ | ||
向量曲线(Vector Curve) | Curve_ | _Vector |
资源类型 | 前缀 | 后缀 | 备注 |
---|---|---|---|
Paper图像序列(Paper Flipbook) | PFB_ | ||
Sprite | SPR_ | ||
Sprite Atlas Group | SPRG_ | ||
瓦片地图/映射(Tile Map) | TM_ | ||
瓦片集合(Tile Set) | TS_ |
资源类型 | 前缀 | 后缀 | 备注 |
---|---|---|---|
物理材质(Physical Material) | PM_ | ||
物理资产(Physical Asset) | PHYS_ | ||
可破坏网格(Destructible Mesh) | DM_ |
资源类型 | 前缀 | 后缀 | 备注 |
---|---|---|---|
对话音效(Dialogue Voice) | DV_ | ||
对话音波(Dialogue Wave) | DW_ | ||
媒体音效(Media Sound Wave) | MSW_ | ||
混响效果(Reverb Effect) | Reverb_ | ||
音效衰减(Sound Attenuation) | ATT_ | ||
音效类(Sound Class) | 没有前缀和后缀,这些资源应该放在SoundClasses目录中 | ||
音效并发(Sound Concurrency) | _SC | 在SoundClass之后命名 | |
Sound Cue | A_ | _Cue | |
音效混合(Sound Mix) | Mix_ | ||
音波(Sound Wave) | A_ |
资源类型 | 前缀 | 后缀 | 备注 |
---|---|---|---|
字体(Font) | Font_ | ||
Slate笔刷(Slate Brush) | Brush_ | ||
Slate控件样式(Slate Widget Style) | Style_ | ||
控件蓝图(Widget Blueprint) | WB_ |
Asset Type | Prefix | Suffix | Notes |
---|---|---|---|
粒子系统(Particle System) | PS_ | ||
后处理材质(Material (Post Process)) | PP_ | ||
Niagara | N_ |
对资源目录的规范管理和资源文件同等重要,都应该像法律一样被严格遵守。不规范的目录结构会导致严重的混乱。
在本套规范中,我们尽量利用了UE4的资源浏览器的过滤和搜索功能来查找资源,而不是按照资源类型来划分目录结构。
|-- Content |-- YuanQi |-- Art | |-- Industrial | | |-- Ambient | | |-- Machinery | | |-- Pipes | |-- Nature | | |-- Ambient | | |-- Foliage | | |-- Rocks | | |-- Trees | |-- Office |-- Characters | |-- Bob | |-- Common | | |-- Animations | | |-- Audio | |-- Jack | |-- Steve | |-- Zoe |-- Core | |-- Characters | |-- Engine | |-- GameModes | |-- Interactables | |-- Pickups | |-- Weapons |-- Effects | |-- Electrical | |-- Fire | |-- Weather |-- Maps | |-- Campaign1 | |-- Campaign2 |-- MaterialLibrary | |-- Debug | |-- Metal | |-- Paint | |-- Utility | |-- Weathering |-- Placeables | |-- Pickups |-- Weapons |-- Common |-- Pistols | |-- DesertEagle | |-- RocketPistol |-- Rifles
使用这种目录结构的原因列在下面
2.4 地图目录(Maps)
2.5 核心资源(Core)
2.7 超大资源(Large Sets)
关于文件夹的命名,有一些通用的规范
2.1.1 使用大驼峰命名法(PascalCase)大小写规范* !
文件夹的命名需要遵守大驼峰命名法规范,也就是所有单词的首字母大写,并且中间没有任何连接符。例如DesertEagle
, RocketPistol
, and ASeriesOfWords
.
参照大小写规范.
永远永远永远在目录名中只使用`a-z`大小写 `0-9`这两类安全的字符,
如果你使用了类似于`元`,`焏`,`@`, `-`, `_`, `,`, `*`, 或者 `#`这样的字符,难免会碰到一些操作系统、源码管理工具或者一些弱智的工具不支持。
同样,也不要把本地工程放在包含有其他符号的目录下面,应该放在类似于`D:\Project`这样的目录里,而不是`C:\Users\My Name\My Documents\Unreal_Projects\元焏`这样的目录。
所有的工程资源都应该保存在一个以工程名命名的目录中。例如工程叫做'YuanQi',那么所有该工程的资源都应该保存在Content/YuanQi
目录中。
开发者目录
Developers
不用受此限制,参照下面的开发者目录中的详细说明。
使用顶级目录的原因有如下。
通常在代码规范中会警告你不要使用全局变量以避免污染全局命名空间。基于同样的道理,不存在于工程目录中的资源对于资源管理会造成不必要的干扰。
每个属于项目资源都应该有它存在的目的。如果仅仅是为了测试或者体验而使用的资源,那么这些资源应该放在开发者
目录中。
当一个团队有多个项目时,从一个项目中把资源拷贝到另一个项目会非常频繁,这时最方便的方法就是使用引擎的资源浏览器提供的Migrate功能,因为这个功能会把资源的依赖项一起拷贝到目标项目中。
这些依赖项经常造成麻烦。如果两个工程没有项目顶级目录,那么这些依赖项很容易就会被拷贝过来的同名资源覆盖掉,从而造成意外的更改。
这也是为什么EPIC会强制要求商城中出售的资源要遵守同样的规定的原因
执行完Migrate资源拷贝后,安全的资源合并方法是使用资源浏览器中的'替换引用'(Replace References)工具,把不属于工程目录中的资源引用替换掉。一旦资源资源完成完整的合并流程,工程目录中不应该存在另一个工程的顶级目录。这种方法可以_100%_保证资源合并的安全性。
正如2.2.2所讲,如果你想把官方范例、模板以及商城中购买的资源放到自己的工程中,那么这些资源都是可以保证不会干扰现有工程的,除非你购买的资源工程和你的工程同名。
当然也不能完全信任商城上的资源能够完全遵守顶级目录规则。
如果工程开发DLC或者子工程,那么这些子工程所需要的资源应该迁移出来放在另一个顶级目录中,这样的结构使得编译这些版本时可以区别对待子工程中的资源。子工程中的资源的迁入和迁出代价也更小。如果想在子项目中修改一些原有工程中的资源,那么可以把这些资源迁移到子工程目录中,这样不会破坏原有工程。
在一个项目的开发期间,团队成员经常会有一个'沙箱'目录用来做测试而不会影响到工程本身。因为工作是连续的,所以即使这些'沙箱'目录也需要进行源码版本控制。但并不是所有成员都需要这种开发者目录的,但使用开发者目录的成员来说,一旦这些目录是在服务器上管理的,总会需要一些麻烦事。
首先团队成员极容易使用这些尚未准备好的资源,这些资源一旦被删除就会引发问题。例如一个做模型的美术正在调整一个模型资源,这时一个做场景编辑的美术如果在场景中使用了这个模型,那么很可能会导致莫名其妙的问题,进而造成大量的工作浪费。
但如果这些模型放在开发者目录中,那么场景美术人员就没有任何理由使用这些资源。UE4资源浏览器的默认设置会自动过滤掉这个目录,从而保证正常情况下不可能出现被误用的情况。
一旦这些资源真正准备好,那么美术人员应该把它们移到正式的工程目录中并修复引用关系,这实际上是让资源从实验阶段'推进'到了生产阶段。
2.4 所有的地图*文件应该保存在'Maps'的目录中 !
无论如何你都应该把所有地图保存在/Content/YuanQi/Maps
中
通过子目录的方法去组织地图资源,避免子关卡的错误加载,例如建立 Maps/StartMap/
(开始关卡) 和 'Maps/PlayMap`(游玩关卡)
这有助于产品的打版本工作,如果工程里的地图保存的到处都是,版本升级时还要到处去找,就让人很恼火了,而把地图放在一个地方,做版本时就很难漏掉某个地图,对于烘培光照贴图或者质量检查都有利。
使用/Content/Project/Core
这个目录用来保存一个工程中最为核心的资源。
例如,游戏模式(GameMode), 角色(Character), 玩家控制器(PlayerController), 游戏状态(GameState), 玩家状态(PlayerState),以及如此相关的一些资源也应该放在这里。
非程序员不要去碰这个目录,策划也只需要使用子类提供的功能就可以工作,负责场景编辑的员工只需要使用专用的的蓝图就可以,而不用碰到这些基础类。
例如,如果项目需要设计一种可以放置在场景中并且可以被捡起的物体,那么应该首先设计一个具有被捡起功能的基类放在Core/Pickups
目录中,而各种具体的可以被捡起的物体诸如药瓶、子弹这样的物体,应该放在/Content/Project/Placeables/Pickups/
这样的目录中。游戏设计师可以在这些目录中定义和设计这些物体,所以其他人不需要也不应该去碰Core/Pickups
目录下的代码,要不然可能无意中破坏工程中的其他功能
因为本来所有目录就是用来保存资源的
资源的文件名本身已经提供了资源类型信息,所以在目录名中再提供资源类型信息就是多余了,而且使用资源浏览器的过滤功能,可以非常便利的提供相同的功能。
比如想查看Environment/Rocks/
目录下所有的静态Mesh资源?只要打开静态Mesh过滤器就可以了,如果所有资源的文件名已经正确命名,这些文件还会按照文件名和前缀正确排序,如果想查看所有静态Mesh和带有骨骼的Mesh资源,只要打开这两个过滤器就可以了,这种方法要比通过打开关闭文件夹省事多了。
这种方法也能够节省路径长度,因为用前缀
S_
只有两个字符,要比使用Meshes/
七个字符短多了。
这么做其实也能防止有人把Mesh或者纹理放在Materials
目录这种愚蠢行为。
这节可以视为针对2.6的补充
对于动画资源和声音资源这一欸数量巨大,而且每个作用都不同的资源。如果你发现有15个以上的资源属于同一个逻辑类型,那么它们应该被放在一起。
举例来说,角色共用的动画资源应该放在Characters/Common/Animations
目录中,并且其中应该还有诸如Locomotion
或者Cinematic
的子目录
如果你的工程中使用了任何基础材质、分层材质,或者任何被重复使用而不属于特定模型的材质和纹理,这些资源应该放在材质库目录Content/Project/MaterialLibrary
。
这样可以很容易管理这些'全局'材质
这也使得'只是用材质实例'这个原则得以执行的比较容易。如果所有的美术人员都只是用材质实例,那么所有的原始材质都应该保存在这个目录中。你可以通过搜索所有不在
MaterialLibrary
中的基础材质来验证这一点。
MaterialLibrary
这个目录并不是仅能保存材质资源,一些共用的工具纹理、材质函数以及其他具有类似属性的资源都应该放在这个目录或子目录中。例如,噪点贴图应该保存在MaterialLibrary/Utility
目录中。
任何用来测试或调试的材质应该保存在MaterialLibrary/Debug
中,这样当工程正式发布时,可以很容易把这些材质从删除,因为这些材质如果不删除,可能在最终产品中非常扎眼。
这一章会专注于蓝图和蓝图的实现。如果可能的话,本规则和Epic官方提供的标准一致。
3.1 编译(Compiling)
3.2 变量(Variables)
3.3 函数(Functions)
3.4 图形节点(Graphs)
需要保证所有蓝图在编译时0警告和0错误。应该尽快修复所有警告和异常,以免它们造成可怕的麻烦。
绝对不要提交那些断开的蓝图,如果你需要通过源码服务器保存,那么必须暂时搁置它们
断开的蓝图有巨大的破坏力,而且会在蓝图之外展现威力,比如造成引用失效,未定义的行为,烘培失败,或者频繁的重新编译。一个断开的蓝图可能会毁掉整个项目。
变量(variable
)和属性(property
)这两个词经常是可以互换的。
3.2.1 命名(Naming)
3.2.2 可编辑行(Editable)
3.2.3 分类(Categories)
3.2.4 权限(Access)
3.2.5 高级(Advanced)
3.2.6 临时变量(Transient)
3.2.7 游戏存档(SaveGame)
3.2.8 配置(Config)
所有非布尔类型的变量必须使用简短、清晰并且意义明确的名词作为变量名。
所有非布尔类型的变量的大小写需要遵守大驼峰命名法(PascalCase)规则。
Score
Kills
TargetPlayer
Range
CrosshairColor
AbilityID
所有布尔类型变量需要遵守大驼峰命名法(PascalCase)规则,但前面需要增加小写的b
做前缀。
例如: 用 bDead
和 bEvil
, 不要 使用Dead
和 Evil
.
UE4的蓝图编辑器在显示变量名称时,会自动把前缀b
去掉
布尔类型变量如果用来表示一般的信息,名字应该使用描述性的单词,不要包含具有提问含义的词汇,比如Is
,这个词是保留单词。
例如:使用bDead
and bHostile
,不要使用bIsDead
and bIsHostile
。
也不要使用类似于bRunning
这样的动词,动词会让含义变得复杂
不要使用布尔变量保存复杂的,或者需要依赖其他属性的状态信息,这会让状态变得复杂和难以理解,如果需要尽量使用枚举来代替。
例如:当定义一个武器时,不要使用bReloading
和 bEquipping
这样的变量,因为一把武器不可能即在reloading状态又在equipping状态,所以应该使用定义一个叫做EWeaponState
的枚举,然后用一个枚举变量WeaponState
来代替,这也使得以后增加新的状态更加容易。
例如:不要使用bRunning
这样的变量,因为你以后有可能还会增加bWalking
或者 bSprinting
,这也应该使用一个枚举来非常清晰的定义这样的状态。
蓝图中的变量命名时需要考虑上下文环境,避免重复不必要的定义。
假设有一个蓝图名为 BP_PlayerCharacter
.
不好的命名
PlayerScore
PlayerKills
MyTargetPlayer
MyCharacterName
CharacterSkills
ChosenCharacterSkin
这些变量的命名都很臃肿。因为这些变量都是属于一个角色蓝图BP_PlayerCharacter
的,没必要在变量中再重复这一点。
好的命名
Score
Kills
TargetPlayer
Name
Skills
Skin
所谓原生变量是指那些最简单的保存数据的变量类型,比如布尔类型、整数、浮点数以及枚举。
字符串(String)和数组(vectors)在蓝图中也属于原生变量类型,但严格来讲它们其实不是。
由三个浮点数组成的vector经常被视为一个整体数据类型,比如旋转向量。
文本类型变量(Text)不属于原生类型,因为它们内部还包含有国际化信息。原生类型的字符串变量类型是
String
, 而不是Text
。
原生类型的变量名中不应该包含变量类型名。
例如:使用Score
, Kills
, 以及 Description
,不要使用ScoreFloat
, FloatKills
, DescriptionString
。
但也有例外情况,当变量的含义包含了"多少个"这样的信息,并且仅用一个名字无法清晰的表达出这个含义时。
比如:游戏中一个围墙生成器,需要有一个变量保存在X轴上的生成数量,那么需要使用NumPosts
或者 PostsCount
这样的变量,因为仅仅使用Posts
可能被误解为某个保存Post的数组
非原生类型的变量是指那些通过数据结构保存一批原生类型的复杂变量类型,比如结构体(Structs)、类(Classes)、接口(Interface),还有一些有类似行为的原生变量比如文字(Text)
和 名字(Name)
也属于此列。
如果仅仅是原生变量组成的数组,那么这个数组仍然属于原生类型
这些变量的名字应该包含数据类型名,但同时要考虑不要重复上下文。
如果一个类中包拥有一个复杂变量的实例,比如一个BP_PlayerCharacter
中有另一个变量BP_Hat
,那么这个变量的名字就不需要包含变量类型了。
例如: 使用 Hat
、Flag
以及 Ability
,不要使用MyHat
、MyFlag
和 PlayerAbility
但是,如果一个类并不拥有这个属性,那么就需要在这个属性的名字中包含有类型的名字了
例如:一个蓝图类BP_Turret
用来顶一个炮塔,它拥有瞄准BP_PlayerCharacter
作为目标的能力,那么它内部会保存一个变量作为目标,名字应该是TargetPlayer
,这个名字非常清楚的指明了这个变量的数据类型是什么。
数组的命名规则通常和所包含的元素的规则一样,但注意要用复数。
例如:用Targets
、Hats
以及 EnemyPlayers
,不要使用TargetList
、HatArray
或者 EnemyPlayerArray
所有可以安全的更改数据内容的变量都需要被标记为Editable
相反,所有不能更改或者不能暴露给设计师的变量都不能表上可编辑标志,除非因为引擎的原因,这些变量需要被标为Expose On Spawn
总之不要轻易使用Editable
标记
对于所有标记为Editable
的变量,包括被标记为 Expose On Spawn
的变量,都应该在其Tooltip
内填写关于如何改变变量值,以及会产生何种效果的说明。
对于可编辑的变量,如果不适合直接输入具体数值,那么应该通过一个滑动条(Slider)并且加上取值范围来让设计师输入。
举例:一个产生围墙的蓝图,拥有一个PostsCount
的变量,那么-1显然适合不合理的输入,所以需要设上取值范围注明0是最小值
如果在构造脚本中需要一个可编辑变量,那么一定要首先定义一个合理的取值范围,要不然可能会有人设上一个非常大的值造成编辑器崩溃。
一个变量的取值范围只有当明确知道其范围时才需要定义,因为滑块的取值范围的确能够阻止用户输入危险数值,但用户仍然能够通过手动输入的方式输入一个超出滑块范围的值给变量,如果变量的取值范围未定义,那么这个值就会变得'很危险'但还是在合理的。
如果一个类的变量很少,那么没有必要使用分类
如果一个类的变量规模达到中等(5-10),那么所有可编辑
的变量应该自己的分类,而不应该放在缺省分类中,通常叫做 Config
如果类中的变量的数量非常大,那么所有可编辑的变量都应该放在Config
分类的子分类下,所有不可编辑的变量应该根据它们的用途建立相关分类保存
通过在分类名中添加字符
|
,你可以直接建立子分类,比如Config | Animations
举例:一个武器的类中的变量分类目录大致如下:
|-- Config
| |-- Animations
| |-- Effects
| |-- Audio
| |-- Recoil
| |-- Timings
|-- Animations
|-- State
|-- Visuals
在C++中,变量的访问类型由类成员的属性决定,Public类型的表示其他类都可以访问,Protetced类型的成员表示子类可以访问,Private类型变量表示只有类内部函数可以访问此变量。
蓝图并没有类似的权限访问设计。
就是视可编辑
类型的变量作为Public类型变量,视不可编辑的变量作为Protected类型变量。
尽量不要把变量生命为private类型,除非变量一开始就打算永远被类内部访问,并且类本身也没打算被继承。尽量用protected
,private类型用在当你有非常清楚的理由要去限制子类的能力。
如果一个变量可以被编辑,但通常不会有人碰到,那么就把它标记为高级显示Advanced Display
。这些变量在蓝图中会缺省隐藏,除非点击节点上的高级显示箭头。
有意思的是,Advanced Display
这个选项本身,在编辑器的变量属性中也是一个高级显示类型的。
Transient类型的变量是指那些不需要被序列化(保存或者加载),并且初始值为0或者null的变量。一般用在引用其他对象,它们的值只有在运行时才知道。这样做能防止编辑器在磁盘上多存储一份多余的数据,以加快蓝图的存盘和加载速度。
因此,所有Transient类型变量都应该被初始化成0或者null。如果是其他值会增加调试bug的时候的难度。
绝对不要将SaveGame
和 Transient
同时使用,这是明显不合理的。
不要使用Config Variable
这个标记,这会让设计师在控制蓝图行为上更加困难。这个标记一般用在C++中,用来标记那些极少被改变的变量,你可以认为它们是那些被标上Advanced Display
的变量
这一节用来解释应该如何管理函数、事件以及事件分发器。除非特殊说明,所有适用于函数的规则,同样适用于事件。
对于函数、事件以及事件分发器的命名极其重要,仅仅从一个名字本身,就有很多条件要考虑,比如说:
- 是纯虚函数吗??
- 是状态查询函数吗?
- 是事件相应函数吗?
- 是远程调用函数吗?
- 函数的目的是什么?
如果命名得当,这些问题甚至更多问题的答案会在名字中体现出来。
所有函数和事件执行者都是需要做一些动作,可能是去获取信息,也可能是数据计算,或者搞点什么事情。因此,所有函数都应该用动词开始,并且用一般现代时态,并且有上下文来表明它们究竟在做什么
OnRep
这样的相应函数,事件具柄和事件派发器的命名不遵守这个规则。
好的例子:
Fire
- 如果类是一个角色或者武器,那么这是一个好命名,如果是木桶,玻璃,那这个函数就会让人困惑了。Jump
- 如果类是一个角色,那么这是个好名字,如果不是,那么需要一些上下文来解释这个函数的含义Explode
ReceiveMessage
SortPlayerArray
GetArmOffset
GetCoordinates
UpdateTransforms
EnableBigHeadMode
IsEnemy
- "Is" 是个动词
不好的例子:
Dead
- 是已经死了?还是死的动作?Rock
ProcessData
- 无意义,这个名字等于没说.PlayerState
- 不能用名词Color
- 如果是动词,那么缺少上下文,如果是名词,也不行.
所有用来响应状态变化的函数应该用OnRep_Variable
的形式,这是由蓝图编辑器强制规定的,如果在C++中写OnRep
函数,应该同样遵守这个规则。
如果一个函数不改变类的状态,并且只是返回信息、状态或者计算返回给调用者yes/no,这应该是一个问询函数。同样遵守动词规则。
非常重要的是,应该假定这样的函数其实就是执行某种动作,并且返回动作是否执行成功。
好的例子:
IsDead
IsOnFire
IsAlive
IsSpeaking
IsHavingAnExistentialCrisis
IsVisible
HasWeapon
- "Has" 是动词.WasCharging
- "Was" 是动词"be"的过去式 用 "was"表示查询以前的状态CanReload
- "Can"是动词
坏的例子:
Fire
- 是查询正在开火?还是查询能不能开火?OnFire
- 有可能和事件派发器函数混淆Dead
- 是查询已经死亡?还是查询会不会死亡?Visibility
- 是查询可见状态?还是设置可见状态?
事件的响应函数和派发函数都应该以On
开头,然后遵守动词规则,如果是过去式,那么动词应该移到最后以方便阅读
在遵守动词规则的时候,需要优先考虑英语中的固定句式
好的例子:
OnDeath
- 游戏中非常常见OnPickup
OnReceiveMessage
OnMessageRecieved
OnTargetChanged
OnClick
OnLeave
坏的例子:
OnData
OnTarget
HandleMessage
HandleDeath
任何时候创建RPC函数,都应该把目标作为前缀放在前面,例如Server
、 Client
或者 Multicast
,没有例外。
前缀之后的部分,遵守上面的其他规则。
好的例子:
ServerFireWeapon
ClientNotifyDeath
MulticastSpawnTracerEffect
坏的例子:
FireWeapon
- 没有使用目标前缀ServerClientBroadcast
- 混淆.AllNotifyDeath
- 用Multicast
, 不要用All
.ClientWeapon
- 没有用动词, 让人困惑.
所有函数内部都应该在结束的地方有返回节点,没有例外。
返回节点明确标注了蓝图到此执行完毕。蓝图中的结构有可能有并行结构Sequence
、循环结构ForLoopWithBreak
或者逆向的回流节点组成,明确结束节点使蓝图易于阅读维护和调试。
如果启用了返回节点,当你的蓝图中有分支没有正常返回,或者流程有问题,蓝图的编译器会提出警告。
比如说,有程序员在并行序列中添加了一个新的分支,或者在循环体外添加逻辑但没有考虑到循环中的意外返回,那么这些情况都会造成蓝图的执行序列出现意外。蓝图编译器会立即给这些情况提出警告。
简单来说,蓝图函数中的节点数应该小于50个,如果函数过于复杂,应该把它分割成几个小一点的函数,以便更好的阅读和维护。
本节包含了关于蓝图图形的内容 This section covers things that apply to all Blueprint graphs.
蓝图中所有连线都应该有清晰的开始点和结束点。你的蓝图让阅读者在一堆乱糟糟的线中翻来翻去。 以下内容帮助你避免龙飞凤舞蓝图产生。
不要试图让节点对齐,对齐的应该是连线。你无法控制一个节点的大小和上面连接点的位置,但你能通过控制节点的位置来控制连线。笔直的连线让整个蓝图清晰美观,歪歪扭扭的连线会让蓝图丑陋不堪。你可以通过蓝图编辑器提供的功能直接让连线变直,方法是选择好节点,用快捷键Q
可接受的例子: 有些节点无论你怎么用对齐工具都无法对齐,这种情况下,就尽量缩短它们之间连线的长度。
如果发现白色执行线和其他数据线无法同时对齐,白色执行线的优先级更高。
1.Airbnb的JS规范 Airbnb Javascript Style Guide
1.thejinchao翻译的ue4-style-guide[https://github.com/skylens-inc/ue4-style-guide]
1.UE4官方代码规范CodingStandard