编码(Coding)

使用 .NET standards为基础,并在下方进行了更改

命名(Wording)

  • 使用描述性的且准确的名称(即使这使得名称更长),注重可读性而不是简洁性
  • 不要乱用首字母缩略词,仅使用公认标准的首字母缩写

例如:UI、IO

  • 方法名(Method)应该是动词或动词短语
  • 属性名(Property)应该是名词、名词短语或形容词
  • 布尔属性(Boolean)应该是肯定短语。可以在布尔属性前加上"Is",“Has”,can"

例如:IsActive、CanJump

  • 如果多个属性与同一项相关,使用项名称作为前缀并添加属性类型或角色
private Color _gameTitleColor
private String _gameTitleString
private TextMeshProUGUI _gameTitleText
  • 如果名称不在固定列表中,避免使用数字作为名称

例如:animator1、animator2
而是说明两个属性之间的区别
例如:playerAnimator、enemyAnimator

拼写(Capitalization)

  • 骆驼拼写法(camelCase):第一个词的首字母小写,后面每个词的首字母大写
  • 帕斯卡拼写法(PascalCase):每个单词的首字母都是大写的。如果单词是由两个字母组成的首字母缩略词,则两个字母都要大写。如果单词是包含两个以上字母的首字母缩略词,则只有第一个字母大写
  • 类、方法、枚举、名称空间、公共字段/属性使用PascalCase

例如:ClassName、GetValue

  • 局部变量、方法参数使用camelCase

例如:previousValue mainUI

  • 私有字段/属性使用camelCase,但以下划线开头

例如:_inputReader

  • 常量全大写,用下划线分隔单词

例如:GRAVITY_AMOUNT

编程(Programming)

  • 保持字段和方法为private,除非需要它们public
  • 如果想在Inspector中显示字段,而不想让其他类访问这个变量,不要用public,使用private和特性[SerializeField]

注意:这样可能会得到警告"Field is never assigned to, will always have its default value",使用=default为字段指定默认值

  • 尽量避免使用单例(Singletons)。尝试使用ScriptableObjects(1,2)来建立一个类似的、集中的、可以从多个对象中访问的类
  • 声明变量时不要使用var,始终显式地编写它的类型
  • 避免使用静态变量,如果你绝对需要他们,请确保它们与Fast Enter Play Mode兼容
  • 不要在你的代码中使用硬编码的"魔法数字"

例如:角色移动xInput*0.035f,为什么用这个数字?请将这个数字存储在一个有明确名称的字段中——也许再加个注释说明为什么选择这个特殊数字

  • 提交代码之前,删除所有没使用过的using命令

例如:using System

格式(Formatting)

  • 使用空格来缩进代码,不要用Tab
  • 花括号:如果内容为空,写在同一行。如果非空,每个占一行
public class EmptyBraces(){}
public class NonEmptyBraces
{
    // ...
}
  • 相互包含的逻辑单元需要缩进以表示层次关系
public void FunctionName()
{
    if(somethingHappended)
    {
        // ...
    }
}

注释(Comments)

  • 重要:不要为了注释而注释。如果你认为任何人只要看一眼就能理解代码的作用,就不要添加注释,给变量、类、方法良好的名字,让他们解释自己!
  • 使用行内注释为单独的代码行进行补充说明
  • 在每一个类上都要写摘要,以描述类的目的。如果一个类可读性不高或者功能细节不直观时,可以选择性的在摘要中加入这些细节
/// <summary>
/// This class manages save data
/// </summary>
提示:在IDE中输入三个"/"符号,一般会自动生成摘要。看微软官方规范
  • 在方法前写注释,解释它的功能,以防方法名不准确,或者添加其他重要的细节,可以用行内注释
/// <summary> This function does this... </summary>
public float CalculateBoundingBox(){ }
  • 使用以//TODO:开头的注释来表示需要稍后完成的事情,这样你就不会忘记了

注意:这不是在鼓励你推送未完成的功能

  • 不要写#region或者//--------这类注释

场景/层次结构(Scene/Hierarchy)

组织结构(Organisation)

  • 在根(root)上创建空的GameObject并命名,创建视觉上的分隔符

例如:"— Camera —","— Environment —","— Lighting —"等
给这些分隔符添加"EditorOnly"标签,这样打包的时候就不会加入

  • 可以使用空的GameObject作为容器,但不要只包含1-2个物体
  • UI
  • 尽可能使用同一个Canvas,只有在Canvas属性改变的时候才创建多个Canvas
  • 每一个屏幕创建一个面板(Panel)

例如:一个设置标签和它的选项

  • 用面板(Panel)作为容器,将UI元素进行分组

例如:一个设置标签和它的选项
面板能帮忙固定需要固定在一起的元素
例如:一些能量/物品UI在屏幕右下角

命名(Naming)

  • 在GameObject命名中不要用空格
  • 用PascalCase命名

例如:MainCharacter,DoorTrigger

  • 如果仅用PascalCase命名会产生歧义,用下划线连接两个概念

例如:MainHall_ExitTrigger,BossMinion_AttackWaypoints

  • 预制体(Prefabs):如果合理,可以重新命名实例

例如:一个Prefab Variant文件名称为"Protagonist_Scene1Variant",如果你只用它一次,可以直接重命名为"Protagonist"

项目文件(Project files)

命名(Naming)

  • 与 Scene/Hierarchy 规则相同
  • 同一个文件夹下,如果文件相关,使他们名称自然而然地归为一组
  • 一般来说,名字要以该物体所属的事物作为前缀

例如:PlayerAnimationController、PlayerIdle、PlayerRun等

  • 合理的情况下,可以使相似的对象保持在一起,即使他们属于不同事物,或者形容词导致他们分开

例如:在一个道具资产文件夹中,你可以用TableRound、TableRectangular来作为名称,这样它们就呆在一起,如果用RoundTable、RectangularTable,他们就不在一起了

  • 避免在名称中使用文件类型

例如:用ShinyMetal代替ShinyMetalMaterial

文件夹(Folders)

  • 在根目录中,将你的资源放在能够识别游戏区域/系统/位置的文件夹中,可以创建子文件夹来分隔不同类型的资产
  • 场景总是放在Scenes文件夹中
  • 不属于特定系统的脚本放在Scripts文件夹中,可以创建子文件夹来分类
  • 总而言之:如果一个系统/功能只有脚本,那就在Scripts中创建一个文件夹。如果它有其他类型的资产,那么将该文件夹放在根目录中,并根据资产类型添加子文件夹。如果它有其它类型的资产,就把该文件夹放在根目录下,并按照资产类型添加子文件夹
┌── Art
│   ├── Characters
│   │   └── PigChef
│   │       ├── Materials
│   │       ├── Prefabs
│   │       └── Textures
│   ├── Environment
│   │   ├── Interiors
│   │   ├── Nature
│   │   │   ├── Materials
│   │   │   ├── Prefabs
│   │   │   └── Textures
│   │   └── Props
├── UI
│   ├── Materials
│   ├── Scripts
│   └── ScriptableObjects
├── InventorySystem
│   ├── Scripts
│   └── ScriptableObjects
├── Scenes
│   ├── Locations
│   └── Menus
└── Scripts
    └── SceneManagementSystem