unity如何unity 创建mesh大量伤

自定义检视面板的使用:
先是定义一个脚本文件,我们来修饰它的检视面板:
[HelpURL("http://www.baidu.com")]
public class Atr : MonoBehaviour
public int
public string N
[Multiline(<span style="color: #)]
public string BackS
public float
public float
public float weaponDamagel, weaponDamage2;
public string shoeN
public int shoeS
public string shoeT
[Space(<span style="color: #0)]
[Range(-<span style="color: #,<span style="color: #)]
public int
void Start ()
health = <span style="color: #;
然后在根目录的Editor文件夹下定义一个用来修饰上面脚本检视面板的类文件:
using UnityE
using System.C
using UnityE
[CustomEditor(typeof(Atr))]
//需要继承自editor,并且引入UnityEditor程序集
public class LearnInspector : Editor
private bool showW
void OnEnable()
//获取当前自定义的Inspector对象
atr = (Atr)
//执行该函数来自定义检视面板
public override void OnInspectorGUI()
//不写默认是垂直布局
EditorGUILayout.BeginVertical();
EditorGUILayout.Space();
EditorGUILayout.Space();
EditorGUILayout.LabelField("Base Info");
atr.id = EditorGUILayout.IntField("Atr ID", atr.id);
atr.Name = EditorGUILayout.TextField("Atr Name", atr.Name);
EditorGUILayout.Space();
EditorGUILayout.Space();
EditorGUILayout.Space();
EditorGUILayout.LabelField("Back Story");
atr.BackStory = EditorGUILayout.TextArea(atr.BackStory, GUILayout.MinHeight(<span style="color: #0));
EditorGUILayout.Space();
EditorGUILayout.Space();
EditorGUILayout.Space();
atr.health = EditorGUILayout.Slider("Health", atr.health, <span style="color: #, <span style="color: #0);
if (atr.health & <span style="color: #)
GUI.color = Color.
else if (atr.health&<span style="color: #)
GUI.color = Color.
GUI.color = Color.
Rect progressRect = GUILayoutUtility.GetRect(<span style="color: #, <span style="color: #);
EditorGUI.ProgressBar(progressRect,atr.health/<span style="color: #0.0f,"Health");
GUI.color = Color.
EditorGUILayout.Space();
EditorGUILayout.Space();
EditorGUILayout.Space();
atr.damage = EditorGUILayout.Slider("Damage", atr.damage, <span style="color: #, <span style="color: #);
if(atr.damage&<span style="color: #)
EditorGUILayout.HelpBox("伤害过低",MessageType.Error);
else if (atr.damage & <span style="color: #)
EditorGUILayout.HelpBox("伤害过高",MessageType.Warning);
EditorGUILayout.HelpBox("伤害适中",MessageType.Info);
EditorGUILayout.Space();
EditorGUILayout.Space();
EditorGUILayout.Space();
EditorGUILayout.LabelField("Shoe");
EditorGUILayout.BeginHorizontal();
EditorGUILayout.LabelField("Name", GUILayout.MaxWidth(<span style="color: #));
atr.shoeName = EditorGUILayout.TextField(atr.shoeName);
EditorGUILayout.LabelField("Size", GUILayout.MaxWidth(<span style="color: #));
atr.shoeSize = EditorGUILayout.IntField(atr.shoeSize); EditorGUILayout.LabelField("Type", GUILayout.MaxWidth(<span style="color: #));
atr.shoeType = EditorGUILayout.TextField(atr.shoeType);
EditorGUILayout.EndHorizontal();
EditorGUILayout.EndVertical();
//绘制字段用到的方法
//EditorGUILayout.LabelField()标签字段
//EditorGUILayout.IntField() 整数字段
//EditorGUILayout.FloatField() 浮点数字段
//EditorGUILayout.TextField() 文本字段
//EditorGUILayout.Vector2Field() 二维向量字段
//EditorGUILayout.Vector3Field() 三维向量字段
//EditorGUILayout.Vector4Field() 四维向量字段
可以看出该修饰类和效果图对应的关系。我们可以方便的定义检视面板来协助游戏的开发调试,让它直观的显示出帮助消息。
更多的信息可以查看帮助文档:
自定义窗口:
2 using UnityE
3 using System.C
4 using System.IO;
5 using UnityE
6 using UnityEditor.SceneM
8 public class MyFirstWindow : EditorWindow
<span style="color: #
<span style="color: #
public Texture TxT
<span style="color: #
string bugReporterName = "";
<span style="color: #
string description = "";
<span style="color: #
GameObject buggyGameO
<span style="color: #
<span style="color: #
MyFirstWindow()
<span style="color: #
<span style="color: #
this.titleContent=new GUIContent("Bug Rt");
<span style="color: #
<span style="color: #
<span style="color: #
<span style="color: #
[MenuItem("Tool/Bug Reporter")]
<span style="color: #
static void showWindow()
<span style="color: #
<span style="color: #
EditorWindow.GetWindow(typeof (MyFirstWindow));
<span style="color: #
<span style="color: #
<span style="color: #
//绘制窗口界面
<span style="color: #
void OnGUI()
<span style="color: #
<span style="color: #
GUILayout.BeginVertical();
<span style="color: #
<span style="color: #
GUILayout.Space(<span style="color: #);
<span style="color: #
GUI.skin.label.fontSize = <span style="color: #;
<span style="color: #
GUI.skin.label.alignment = TextAnchor.MiddleC
<span style="color: #
GUILayout.Label("Bug Report");
<span style="color: #
<span style="color: #
GUILayout.Space(<span style="color: #);
<span style="color: #
bugReporterName = EditorGUILayout.TextField("Bug Name", bugReporterName);
<span style="color: #
<span style="color: #
GUILayout.Space(<span style="color: #);
<span style="color: #
GUI.skin.label.fontSize = <span style="color: #;
<span style="color: #
GUI.skin.label.alignment = TextAnchor.UpperL
<span style="color: #
GUILayout.Label("Currently Scene:"+EditorSceneManager.GetActiveScene().name);
<span style="color: #
<span style="color: #
GUILayout.Space(<span style="color: #);
<span style="color: #
GUILayout.Label("Time:"+System.DateTime.Now);
<span style="color: #
<span style="color: #
<span style="color: #
GUILayout.Space(<span style="color: #);
<span style="color: #
buggyGameObject =
<span style="color: #
(GameObject) EditorGUILayout.ObjectField("Buggy Go", buggyGameObject, typeof (GameObject), true);
<span style="color: #
<span style="color: #
GUILayout.Space(<span style="color: #);
<span style="color: #
GUILayout.BeginHorizontal();
<span style="color: #
GUILayout.Label("Description", GUILayout.MaxWidth(<span style="color: #));
<span style="color: #
description = EditorGUILayout.TextArea(description, GUILayout.MaxHeight(<span style="color: #));
<span style="color: #
GUILayout.EndHorizontal();
<span style="color: #
<span style="color: #
EditorGUILayout.Space();
<span style="color: #
<span style="color: #
if (GUILayout.Button("Save Bug"))
<span style="color: #
<span style="color: #
SaveBug();
<span style="color: #
<span style="color: #
<span style="color: #
<span style="color: #
if (GUILayout.Button("Save Bug With Screenshoot"))
<span style="color: #
<span style="color: #
SaveBugWithScreeshot();
<span style="color: #
<span style="color: #
<span style="color: #
EditorGUILayout.EndVertical();//布局开始和结束相对应,缺少时可能出现窗口中的元素无法自适应的情况
<span style="color: #
<span style="color: #
<span style="color: #
private void SaveBugWithScreeshot()
<span style="color: #
<span style="color: #
<span style="color: #
Application.CaptureScreenshot("Assets/BugReports/" + bugReporterName + "/" + bugReporterName + ".png");
<span style="color: #
<span style="color: #
<span style="color: #
private void SaveBug()
<span style="color: #
<span style="color: #
<span style="color: #
<span style="color: #
<span style="color: #
//IO类,用来写入保存信息
<span style="color: #
void Writer()
<span style="color: #
<span style="color: #
Directory.CreateDirectory("Assets/BugReports/" + bugReporterName);
<span style="color: #
StreamWriter sw = new StreamWriter("Assets/BugReports/" + bugReporterName + "/" + bugReporterName + ".txt");
<span style="color: #
sw.WriteLine(bugReporterName);
<span style="color: #
sw.WriteLine(DateTime.Now.ToString());
<span style="color: #
sw.WriteLine(EditorSceneManager.GetActiveScene().name);
<span style="color: #
sw.WriteLine(description);
<span style="color: #
sw.Close();
<span style="color: #
<span style="color: # }
自定义菜单项:
1 using UnityE
2 using UnityE
3 using System.C
5 /// &summary&
6 /// 工具类改名
7 /// &/summary&
8 public class ChangeName : ScriptableWizard
public string PrefixStr = null;
/// &summary&
/// 当没有任何GameObject被选中的时候,将菜单disable(注意,这个函数名可以随意取)
/// &/summary&
/// &returns&&/returns&
[MenuItem("ChangeName/AddPrefixAndEuipment", true)]
static bool CreateWindowDisabled()
return Selection.activeT
/// &summary&
/// 创建编辑窗口(注意,这个函数名可以随意取)
/// &/summary&
[MenuItem("ChangeName/AddPrefixAndEuipment")]
static void CreateWindow()
//第一个参数窗口标题
// 定制窗口标题和按钮,其中第二个参数是Create按钮,第三个则属于other按钮
// 如果不想使用other按钮,则可调用DisplayWizard的两参数版本
DisplayWizard&ChangeName&(
"AddPrefix",
"Add", "Remove");
/// &summary&
/// 窗口创建或窗口内容更改时调用
/// &/summary&
void OnWizardUpdate()
helpString = "Note: Prefix are not created";
if (string.IsNullOrEmpty(PrefixStr))
errorString = "Please enter Prefix";
isValid = false;
errorString = "";
isValid = true;
/// &summary&
/// 点击Add按钮(即Create按钮)调用
/// &/summary&
void OnWizardCreate()
Transform[] transforms = Selection.GetTransforms(SelectionMode.TopLevel | SelectionMode.OnlyUserModifiable);
foreach (Transform transform in transforms)
transform.name = PrefixStr + transform. 71
Debug.Log("AddPrefixAndEuipment " + PrefixStr+"successed!");
/// &summary&
/// 点击Remove(即other按钮)调用
/// &/summary&
void OnWizardOtherButton()
Transform[] transforms = Selection.GetTransforms(SelectionMode.TopLevel | SelectionMode.OnlyUserModifiable);
foreach (Transform transform in transforms)
if (transform.name.Contains(PrefixStr))
transform.name=transform.name.Replace(PrefixStr, "");
Debug.Log("ReMove prefix " + PrefixStr + "successed!");
95     
[MenuItem("ChangeName/AddOrderNume2Name ")]// 为选择的对象名称添加序列号,让重名的物体相区别
static void AddOrder2Name()
int n = <span style="color: #;
<span style="color: #0
Transform[] transforms = Selection.GetTransforms(SelectionMode.TopLevel | SelectionMode.OnlyUserModifiable);
<span style="color: #1
<span style="color: #2
foreach (Transform transform in transforms)
<span style="color: #3
<span style="color: #4
transform.name = transform.name + n.ToString();
<span style="color: #5
<span style="color: #6
<span style="color: #7
<span style="color: #8
<span style="color: #9
[MenuItem("GameObject/Add Child %g")]
<span style="color: #0
static void MenuAddChild()
<span style="color: #1
<span style="color: #2
Transform[] transforms = Selection.GetTransforms(SelectionMode.TopLevel | SelectionMode.OnlyUserModifiable);
<span style="color: #3
<span style="color: #4
foreach (Transform transform in transforms)
<span style="color: #5
<span style="color: #6
GameObject newChild = new GameObject("_Child");
<span style="color: #7
newChild.transform.parent =
<span style="color: #8
<span style="color: #9
<span style="color: #0
<span style="color: #1
<span style="color: #2
[MenuItem("ChangeName/ReNameUILabelX")]
<span style="color: #3
static void ReNameUILableX()
<span style="color: #4
<span style="color: #5
UIButton [] UIButtions = GameObject.Find("CenterMenu").transform.GetChild(<span style="color: #).GetComponentsInChildren&UIButton&();
<span style="color: #6
<span style="color: #7
foreach (UIButton uiButtion in UIButtions)
<span style="color: #8
<span style="color: #9
if (uiButtion.gameObject.activeSelf)
<span style="color: #0
<span style="color: #1
uiButtion.GetComponentInChildren&EptBtnOnClick&().EptName="X_"+ uiButtion.GetComponentInChildren&UILabel&().
<span style="color: #2
<span style="color: #3
<span style="color: #4
<span style="color: #5 <span style="color: #1
<span style="color: #2 }
ScriptableWizard:继承自EditorWindow,主要用来做向导。有2个按钮,一个是Create,另一个是Other。当我们使用它的时候,他始终显示在最上层,不会被unity其他窗口遮挡。
& &SelectionMode:
can be used to tweak the selection returned by Selection.GetTransforms.
Note: This is an editor class. To use it you have to place your script in Assets/Editor inside your project folder. Editor classes are in the UnityEditor namespace so for C# scripts you need to add "using UnityE" at the beginning of the script.
The default transform selection mode is: SelectionMode.TopLevel | SelectionMode.ExcludePrefab | SelectionMode.Editable.
Return the whole selection.
Only return the topmost selected transform. A selected child of another selected transform will be filtered out.
Return the selection and all child transforms of the selection.
Excludes any prefabs from the selection.
Excludes any objects which shall not be modified.
Only return objects that are assets in the Asset directory.
If the selection contains folders, also include all assets and subfolders within that folder in the file hierarchy.
阅读(...) 评论()如何用Unity创建霸气的地形_百度知道
如何用Unity创建霸气的地形
我有更好的答案
打开Mudbox在开始之前,我们需要设置一些变量。调整FOV视角层的FOV参数来匹配你Unity上面的摄像机FOV值。这样就能得到一个好点的刻度调整以及均衡的视角。将工具的雕刻值方向只设置Y。由于高度地图指示存储高度信息,我们只需要多边形向上在Y轴移动。为雕刻工具选择一个好的邮戳图片。可以在制作完基本形状后再做。Bw_cliffFace.tif邮戳在岩石表面运行起来效果不错。开始雕刻地形,直到你满意为止。完成之后,你的地形如下图所示:现在要导出我们地形的高度地图来在机器世界里使用!为此我们导入简单的屏幕OBJ文件到场景中来映射高多边地形数据到平、低多边的平面。之后到UV和地图-&提取纹理地图-&新操作选择移置地图并调整下面的参数:目标模型-&添加低多边平面源模型-&添加高多边平面地形搜索距离-&用最佳猜想试验,如果在高度地图中得到非人工的则进行调整图片尺寸-&我们用分辨率的基本文件名字-&选择你的文件名字并选择TIFF16位整数点击提取来制作你的高度地图制作过程完毕后,你会得到一个的图片。添加地形过程详细信息我们打开机器世界并设置一些节点。处理各种参数的时候最好将分辨率设为一个较低的值(513*513),这样会加速构建时间。一旦你觉得地形不错了你就可以将分辨率增加到.你可以在项目设置菜单里更改分辨率。机器世界里对每个节点都做了很多预设。只需要尝试不同设置即可。创建一个新的高度地图首先我们需要生成器列表中的一个文件输入节点来加载我们的高度地图。双击节点选择你的TIF高度地图文件。你可以谅解不同规格的节点到文件输出模块的高度域最终谅解一输出列表中的高度输出节点到你更改环节中最后的一个模块的高度域输出即可双击高度输出节点并选择一个文件名字选择RAW16作为文件格式,将之前的.r16文件后缀名改为.raw点击写出到硬盘!&创建一个splat地图Splat地图是根据其水平高度来对地形进行纹理化的。因此我必须将地形的高度信息分离到单独的通道来获取器RGBA值。我们会使用选择器列表中的选择坡度和选择高度模块。我们的节点如下所示:坡度选择器可以只选择来自给定高度的数据来配置地形数据。如果最大值和最小值跟其他坡度选择器重叠,你就会获得最好的结果。由于选择器只有一个高度位域输出,我们必须使用合并器列表中的通道合并,然后连接高度位域到通道合并器输入的红、绿、蓝。最后我们连接来自输出列表中的位图输出节点来将我们的splat地图导出为一个16位PNG图片。创建标准地图标准地图是为地形添加结构的一种很好的方式。为了导出一个标准地图我们需要从转换列表中添加一个标准地图制作节点,然后将其连接到一个位图输出。创建颜色地图最后我们需要用转换列表中的颜色节点来制作一个颜色地图。详细情况是我们将通过选择器列表中的选择顶点节点来导出一个曲率地图。为了导出图片文件我们需要连接一个高度输出和一个位图输出节点。我们将用Photoshop将曲率地图和颜色地图合并。不断对透明度和混合模式进行调整直到你满意为止。如果你想更深入的了解使用Mudbox创建地形,在CryEngine和UDK里有一个好的更加深入的教程。下面第二部分处理将地图导入到CryEngine,我们将在下面的环节中涵盖Unity的部分。你处理完毕后应该会得到四个图片和一个高度地图的源数据文件。重要:为了用ats颜色地图终极地形渐变器,你序号将它们在Y轴翻转。将地形和地图导入到Unity中打开Unity并将地图添加到项目里。额外需要添加至少四个纹理,以便在地形中使用。你可以从Unity素材商店里花费10美金购买ats颜色地图终极地形渐变器,我们要用到它的,它可是物超所值哦。用通过游戏对象-&创建其他-&地形来创建一个新的地形。到你新创建地形对象的设置列表中,设置高度地图的分辨率为2049,并点击导入源按钮啦导入你的高度地形的源文件。使用ats颜色地图顶级地形渐变器启动ats颜色地图渐变器有点复杂,但是包里的文档还是不错的。创建好地形最好的方法就是遵循手册里的步骤。无论如何,下面是建立渐变器的大概步骤。至少添加四个地形纹理到地形着色工具中在地形中添加CustomTerrainScriptColorMapUltraU4.cs脚本在添加颜色地图脚本的时候,设定颜色地图并用地形上的新编辑器处理标准地图添加splatmap然后选择应用splatd地图为防止处理splat地图格式时候出错,你可以在Unity里更改图片文件的导入格式通过添加地形纹理的标准地图到槽里来创建合并的标准地图,然后导出新生成的合并标准地图为每个纹理无缝混合生成平均的颜色创建一个新材料的地形&&&&&&&&现在你应该在Unity里得到了一个不错的地形了。如果在使用ats Colormap UL TRA地形渐变器过程中有什么问题,请查看包里的用户手册。里面很详细,你应该可以解决任何问题。下面是我们刚才创建的地形。
资深电脑人
为您推荐:
其他类似问题
unity的相关知识
&#xe675;换一换
回答问题,赢新手礼包&#xe6b9;
个人、企业类
违法有害信息,请在下方选择后提交
色情、暴力
我们会通过消息、邮箱等方式尽快将举报结果通知您。努力加载中,稍等...
暂无新消息
努力加载中,稍等...
已无更多消息...
这些人最近关注了你
努力加载中,稍等...
已无更多消息
努力加载中,稍等...
已无更多消息
& Unity的50个使用技巧(2016 Edition)
50 Tips and Best Practices for Unity (2016 Edition)
征集热心朋友翻译文章,奖励规则:每100汉字奖励10QB,30天内互动奖励0 - 50QB.
翻译请求:申请翻译,号前完成
该文章来自用户转载
I published the original
about 4 years ago.Although a lot of it is still relevant, a lot has changed since then:Unity got better. For example, I now trust the FPS counter. The ability to use property drawers makes it less necessary to write custom editors. The way prefabs work makes the need for explicit nested prefabs or an alternative less. Scriptable objects are friendlier.Visual Studio integration got better, making it a lot easier to debug, and reducing the need for so much gorilla debugging.Third-party tools and libraries got better. There are now many assets in the Asset Store available that takes care of things such as visual debugging and better logging. Our own (free)
plug-in has much of the code described in the original article (and a lot of it described here too).Version control got better. (Or perhaps, now I know better how to use it effectively). No need to have multiple copies or backup copies of prefabs, for example.I got more experience. In the last 4 years I worked on many U including , production games such as , and our flagship Unity asset .This article is a revised version of the original taking all of the above into account.Before getting on with the tips, here is a disclaimer (essentially the same as the original).These tips don't apply to every Unity project:They are based on my experience with projects with small teams from 3 to 20 people.There's is a price for structure, re-usability, clarity, and so on — team size, project size, and project goal determine whether that price should be paid. , for example.Many tips are a matter of taste (there may be rivalling but equally good techniques for any tip listed here).Unity also has some best practices on their site (although these are mostly from a performance point of view):Best practices for physically based content creation 2D Best practices in Unity Internal Unity tips and tricks Unity Tips and Tricks Workflow1. Decide on the scale from the beginning and build everything to the same scale. If you don't, you may have need to rework assets later (for example, animation does not always scale correctly). For 3D games, using 1 Unity unit = 1m is usually the best. For 2D games that does not use lighting or physics, 1 Unity unit = 1 pixel (at "design" resolution) is usually good. For UI (and 2D games), pick a design resolution (we use HD or 2xHD) and design all assets to scale in that resolution.2. Make every scene runnable. Do this to avoid having to switch scenes to run the game so that you can test faster. This can be tricky if you have objects that persist between scene loads that is required in all your scenes. One way of doing this is to use make persistent objects singletons that will load themselves when they are not present in the scene. Singletons are described in more detail in another tip.3. Use source control and learn how to use it effectively.Serialize your assets as text. It does not in practice really make scenes and prefabs more mergeable, but it does make it easier to see what changed.Adopt a scene and prefab sharing strategy. In general, more than one person should not work on the same scene or prefab. For a small team it may be enough to ask around that no-one else is working on a scene or prefab before starting to work on it. It may be useful to swap physical tokens that denote scene ownership around (you are only allowed to work on a scene if you have the scene token on your desk).Use tags as bookmarks.Decide and stick to a branching strategy. Because scenes and prefabs cannot be smoothly merged, branching is slightly more complicated. However you decide to use branches, it should work with your scene and prefab sharing strategy.Use submodules with care.
can be a great way to maintain re-usable code. But there are a few caveats:Meta-files are not generally consistent over multiple projects. This is not generally a problem for non-Monobehaviour or non-Scriptable object code, but for MonoBehaviours and Scriptable objects using submodules can cause code to get lost.If you work on many projects (including one or more in submodules), you can sometimes get an update avalanche where you have to have to pull-merge-commit-push over various projects for a few iterations to stabilize the code over all projects (and if someone else is making changes while this is going on, it could turn into a sustained avalanche). One way to minimize this effect is to always make changes to submodules from projects dedicated to them. This way, projects that use submodules only they never need to push back.4. Keep test scenes and code separate. Commit temporary assets and scripts to the repository, and remove them from the project when you are done.5. If you upgrade tools (especially Unity), do so simultaneously. Unity is much better at preserving links when you open a project with a different version than it used to be, but links still sometimes get lost when people are working with different versions.6. Import third-party assets in a clean project and export a new package for your own use from there. Assets can sometimes cause problems when you import them directly in you project:There may be collisions (file or name), especially assets that have files in the root of the Plugins folder, or that use assets from Standard Assets in their examples.They may be unorganized on put their files all over your own project. This is especially a problem if you decide not to use it and want to remove it.Follow these steps to make importing assets safer:Make a new project, and import the asset.Run the examples and make sure they work.Organize the asset into a more suitable folder structure. (I usually do not enforce my own folder structure on an asset. But I make sure that all the files are in a single folder, and that there are not any files in important places that could overwrite existing files in my project.)Run the examples and make sure they still work. (On occasion, I had assets break when I move stuff, but generally this should not be a problem).Now remove all the things you won't need (such as examples).Make sure the asset still compiles and that prefabs still have all their links. If there is anything left to run, test it.Now select all the assets, and export a package.Import into your project. 7. Automate your build process. This is useful even for small projects, but it is particularly useful when:you need to build lots of different versions of the game,other team-members with varying degrees of technical knowledge need to make builds, oryou need to make small tweaks to the project before you can build.See
for a good guide on how to do this.8. Document your setup. Most documentation should be in the code, but certain things should be documented outside code. Making designers sift through code for setup is time-wasting. Documented setups improved efficiency (if the documents are current).Document the following:Tag uses.Layer uses (for collision, culling, and raycasting – essentially, what should be in what layer).GUI depths for layers (what should display over what).Scene setup.Prefab structure of complicated prefabs.Idiom preferences.Build setup.General Coding9. Put all your code in a namespace. This avoids code clashes among your own libraries and third-party code. But don't rely on namespaces to avoid clashes with important classes. Even if you use different namespaces, don't use "Object" or "Action" or "Event" as class names.10. Use assertions. Assertions are useful to test invariants in code and help flush out logic bugs. Assertions are available in the
class. They all test some condition, and write an error message in the console if the condition is not met. If you are not familiar with how assertions can be useful, see .11. Don't use strings for anything other than displayed text. In particular, do not use strings for identifying objects or prefabs. There are exceptions (there are still a few things that can only be accessed by name in Unity). In such cases, define those strings as constants in files such as AnimationNames or AudioModuleNames. If these classes become unmanageable, use nested classes so you can say something like AnimationNames.Player.Run.12. Don't use Invoke and SendMessage. These methods of MonoBehaviour call other methods by name. Methods called by name is hard to track in code (you cannot find "Usages", and SendMessage has a wide scope that is even harder to track).It is easy to roll out your own Invoke using Coroutines and C# actions:public static Coroutine Invoke(this MonoBehaviour monoBehaviour, Action action, float time){
return monoBehaviour.StartCoroutine(InvokeImpl(action, time));}private static IEnumerator InvokeImpl(Action action, float time){
yield return new WaitForSeconds(time);
action();} You can use this then like this in your monoBehaviour:this.Invoke(ShootEnemy); //where ShootEnemy is a parameterless void method. If you implement your own base MonoBehaviour, you can add your own Invoke to that.A safer SendMessage alternative is more difficult to implement. Instead, I usually use GetComponent variaties to get components on parents, the current game object, or children, and make the call directly.13. Don't let spawned objects clutter your hierarchy when the game runs. Set their parents to a scene object to make it easier to find stuff when the game is running. You could use an empty game object, or even a singleton (see later in this article) with no behaviour to make it easier to access from code. Call this object DynamicObjects.14. Be specific about using null as a legal value, and avoid it where you can.Nulls are helpful in detecting incorrect code. However, if you make if habit of silently passing over null, your incorrect code will happily run and you won't notice the bug until much later. Moreover, it can manifest itself deep in the code as each layer passes over null variables. I try to avoid using null as a legal value altogether.My preferred idiom is to not do any null checking, and let the code fail where it is a problem. Sometimes, in re-suable* methods, I will check a variable for null and throw an exception instead of passing it on to other methods where it may fail.In some cases, a value can be legitimately null, and needs to be handled in a different way. In cases like this, add a comment to explain when and why something can be null.A common scenario is often used for inspector-configured values. The user can specify a value, but if she doesn't, a default value is used. A better way to do this is with a class Optional that wraps values of T. (It's a bit like Nullable). You can use a special property renderer to render a tick box and only show the value box if it is ticked. (Unfortunately, you cannot use the generic class directly, you have to extend classes for specific values of T).[Serializable]public class Optional{
public bool useCustomV
public T}In your code, you can then use it like this:health = healthMax.useCustomValue ? healthMax.Value : DefaultHealthM 15. If you use Coroutines, learn to use them effectively. Coroutines can be a powerful way to solve many problems. But they are hard to debug, and you can easily code yourself into a mess that no-one, not even yourself, can understand.You should know:How to execute coroutines in parallel.How to execute coroutines in sequence.How to make new coroutines from existing ones.How to make
using CustomYieldInstruction.//This is itself a coroutineIEnumerator RunInParallel(){
yield return StartCoroutine(Coroutine1());
yield return StartCoroutine(Coroutine2());}public void RunInSequence(){
StartCoroutine(Coroutine1());
StartCoroutine(Coroutine1());}Coroutine WaitASecond(){
return new WaitForSeconds(1);}
16. Use extensions methods to work with components that share an interface. It is sometimes convenient to get components that implement a certain interface, or find objects with such components.The implementations below uses typeof instead of the generic versions of these functions. The generic versions don't work with interfaces, but typeof does. The methods below wraps this neatly in generic methods.public static TInterface GetInterfaceComponent(this Component thisComponent)
where TInterface : class{
return thisComponent.GetComponent(typeof(TInterface)) as TI} 17. Use extension methods to make syntax more convenient. For example:public static class TransformExtensions {
public static void SetX(this Transform transform, float x)
Vector3 newPosition =
new Vector3(x, transform.position.y, transform.position.z);
transform.position = newP
...} 18. Use a defensive GetComponent alternative. Sometimes forcing component dependencies through RequiredComponent can be a pain, and it may not always be possible or desirable, especially when you call GetComponent on somebody else's class. As an alternative, the following extension of GameObject can be used when a component is required to print out an error message when it is not found.public static T GetRequiredComponent(this GameObject obj) where T : MonoBehaviour{
T component = obj.GetComponent();
if(component == null)
Debug.LogError("Expected to find component of type "
+ typeof(T) + " but found none", obj);
}} 19. Avoid using different idioms to do the same thing. In many cases there are more than one idiomatic way to do things. In such cases, choose one to use throughout the project. Here is why:Some idioms don't work well together. Using one idiom forces design in one direction that is not suitable for another idiom.Using the same idiom throughout makes it easier for team members to understand what is going on. It makes structure and code easier to understand. It makes mistakes harder to make.Examples of idiom groups:Coroutines vs. state machines.Nested prefabs vs. linked prefabs vs. god prefabs.Data separation strategies.Ways of using sprites for states in 2D games.Prefab structure.Spawning strategies.Ways to locate objects: by type vs. name vs. tag vs. layer vs. reference ("links").Ways to group objects: by type vs. name vs. tag vs. layer vs. arrays of references ("links").Ways to call methods on other components.Finding groups of objects versus self-registration.Controlling execution order (Using Unity's execution order setup versus yield logic versus Awake / Start and Update / Late Update reliance versus manual methods versus any-order architecture).Selecting objects / positions / targets with the mouse in-game: selection manager versus local self-management.Keeping data between scene changes: through , or objects that are not Destroyed when a new scene is loaded.Ways of combining (blending, adding and layering) animation.Input handling (central vs. local)20. Maintain your own time class to make pausing easier. Wrap Time.DeltaTime and Time.TimeSinceLevelLoad to account for pausing and time scale. It requires discipline to use it, but will make things a lot easier, especially when running things of different clocks (such as interface animations and game animations).21. Custom classes that require updating should not access global static time. Instead, they should take delta time as a parameter of their Update method. This makes these classes useable when you implement a pausing system as explained above, or when you want to speed up or slow down the custom class's behavior.22. Use a common structure for making WWW calls. In games with a lot of server-communication, it is common to have dozens of WWW calls. Whether you use Unity's raw WWW class or a plugin, you can benefit from writing a thin layer on top that does the boiler plate for you.I usually define a Call method (one for each Get and Post), a CallImpl coroutine, and a MakeHandler. Essentially, the Call method builds a super hander from a parser, on-success and on-failure handlers using the MakeHandler method. It also calls the CallImpl courutine, which builds a URL, make the call, wait until it's done, and then call the super handler.Here is roughly how it looks:public void Call(string call, Func parser, Action onSuccess, Action onFailure){ var handler = MakeHandler(parser, onSuccess, onFailure); StartCoroutine(CallImpl(call, handler));} public IEnumerator CallImpl(string call, Func handler){ var www = new WWW(call);
handler(www);}public Func MakeHandler(Func parser, Action onSuccess, Action onFailure){ if(NoError(www))
var parsedResult = parser();
onSuccess(parsedResult); } else {
onFailure("error text"); }} This has several benefits.It allows you to avoid having to write a lot of boilerplate code.It allows you to handle certain things (such as displaying a loading UI component or handling certain generic errors) in a central place.23. If you have a lot of text, put it in a file. Don't put it in fields for editing in the inspector. Make it easy to change without having to open the Unity editor, and especially without having to save the scene.24. If you plan to localize, separate all your strings to one location. There are many ways to do this. One way is to define a Text class with a public string field for each string, with defaults set to English, for example. Other languages subclass this and re-initialize the fields with the language equivalents.More sophisticated techniques (appropriate when the body of text is large and / or the number of languages is high) will read in a spread sheet and provide logic for selecting the right string based on the chosen language.Class Design25. Decide how to implement inspectable fields, and make it a standard. There are two ways: make the fields public, or make them private and mark them as [Serializable]. The latter is "more correct" but less convenient (and certainly not the method popularized by Unity itself). Whichever way you choose, make it a standard so that developers in your team know how to interpret a public field.Inspectable fields are public. In this scenario, public means "the variable is safe to change by a designer during runtime. Avoid setting its value in code".Inspectable fields are private and marked Serializable. In this scenario, public means "it's safe to change this variable in code" (and hence you should not see too many, and there should not be any public fields in MonoBehaviours and ScriptableObjects).26. For components, never make variables public that should not be tweaked in the inspector. Otherwise they will be tweaked by a designer, especially if it is not clear what it does. In some rare cases it is unavoidable. In that case use a two or even four underscores to prefix the variable name to scare away tweakers:public float __aV27. Use property drawers to make fields more user-friendly.
can be used to customize controls in the inspector. This allows you to make controls that better fit the nature of the data, and put certain safe-guards in place (such as limiting the range of the variables).28. Prefer property drawers over custom editors. Property drawers are implement per field type, and is therefore much less work to implement. They are also more re-suable – once implemented for a type, they can be used for that type in any class. Custom editors are implemented per MonoBehaviour, and are therefor less re-usable and more work.29. Seal MonoBehaviours by default. Generally, Unity's MonoBehaviours are not very inheritance-friendly:The way Unity calls message-methods like Start and Update makes it tricky to work with these methods in subclasses. If you are not careful, the wrong thing gets called, or you forget to call a base method.When you use custom editors, you usually need to duplicate the inheritance hierarchy for the editors. Anyone who wants to extend one of your classes has to provide their own editor, or make do with whatever you provided.In cases where inheritance is called for, do not provide any Unity message-methods if you can avoid it. If you do, don't make them virtual. If necessary, you can define an empty virtual function that gets called from the message-method method that a child class can override to perform additional work.public class MyBaseClass{
public sealed void Update()
CustomUpdate();
... // This class's update
//Called before this class does its own update
//Override to hook in your own update code.
virtual public void CustomUpdate(){};}public class Child : MyBaseClass{
override public void CustomUpdate()
//Do custom stuff
}}This prevents a class from accidentally overriding your code, but still gives it the ability to hook into Unity's messages. One reason that I don't like this pattern is that the order of things becomes problematic. In the example above the child may want to do things directly after the class has done its own update.30. Separate interface from game logic. Interface components should in general not know anything about the game in which they are used. Give them the data they need to visualize, and subscribe to events to find out when the user interacts with them. Interface components should not do gamelogic. They can filter input to make sure it's valid, but the main rule processing should happen elsewhere. In many puzzle-games, the pieces are an extension of the interface, and should not contain rules. (For example, a chess piece should not calculate its own legal moves.Similarly, input should be separated from the logic that acts on that input. Use an input controller that informs your actor o the actor handles whether to actually move.Here is a stripped down example of a UI component that allows the user to select a weapon from a list of choices. The only thing these classes know about the game is the Weapon class (and only because Weapon is a useful source for the data this container needs to display). The game also knows nothing all it has to do is register for the OnWeaponSelect event.public WeaponSelector : MonoBehaviour{
public event Action OnWeaponSelect { }
//the GameManager can register for this event
public void OnInit(List
foreach(var weapon in weapons)
var button = ... //Instantiates a child button and add it to the hierarchy
buttonOnInit(weapon, () =& OnSelect(weapon));
// child button displays the option,
// and sends a click-back to this component
public void OnSelect(Weapon weapon)
if(OnWepaonSelect != null) OnWeponSelect(weapon);
}}public class WeaponButton : MonoBehaviour{
private Action&& onC
public void OnInit(Weapon weapon, Action onClick)
... //set the sprite and text from weapon
this.onClick = onC
public void OnClick() //Link this method in as the OnClick of the UI Button component
Assert.IsTrue(onClick != null);
//Should not happen
onClick();
} 31. Separate configuration, state and bookkeeping. Configuration variables are the variables tweaked in the inspector to define your object through its properties. For example, maxHealth.State variables is the variables that completely determines your object's current state, and are the variables you need to save if your game supports saving. For example, currentHealth.Bookkeeping variables are used for speed, convenience, or transitional states. They can always completely be determined from the state variables. For example, previousHealth.By separating these types of variables, you make it easier to know what you can change, what you need to save, what you need to send / retrieve over the network, and allows you to enforce this to some extent. Here is a simple example with this setup.public class Player{
[Serializable]
public class PlayerConfigurationData
public float maxH
[Serializable]
public class PlayerStateData
public PlayerConfigurationD
private PlayerState stateD
//book keeping
private float previousH
public float Health
public get { return stateData. }
private set { stateData.health = }
}} 32. Avoid using public index-coupled arrays. For instance, do not define an array of weapons, an array of bullets, and an array of particles, so that your code looks like this:public void SelectWeapon(int index){
currentWeaponIndex =
Player.SwitchWeapon(weapons[currentWeapon]);}public void Shoot(){
Fire(bullets[currentWeapon]);
FireParticles(particles[currentWeapon]);} The problem for this is not so much in the code, but rather setting it up in the inspector without making mistakes.Rather, define a class that encapsulates the three variables, and make an array of that:[Serializable]public class Weapon{
public GameO
public ParticleS
public B} The code looks neater, but most importantly, it is harder to make mistakes in setting up the data in the inspector.33. Avoid using arrays for structure other than sequences. For example, a player may have three types of attacks. Each uses the current weapon, but generates different bullets and different behaviour.You may be tempted to dump the three bullets in an array, and then use this kind of logic:public void FireAttack(){
/// behaviour
Fire(bullets[0]);}public void IceAttack(){
/// behaviour
Fire(bullets[1]);}public void WindAttack(){
/// behaviour
Fire(bullets[2]);}Enums can make things look better in code…public void WindAttack(){
/// behaviour
Fire(bullets[WeaponType.Wind]);}…but not in the inspector.It's better to use separate variables so that the names help show which content to put in. Use a class to make it neat.[Serializable]public class Bullets{
public Bullet fireB
public Bullet iceB
public Bullet windB}This assumes there is no other Fire, Ice and Wind data.34. Group data in serializable classes to make things neater in the inspector. Some entities may have dozens of tweakables. It can become a nightmare to find the right variable in the inspector. To make things easier, follow these steps:Define separate classes for groups of variables. Make them public and serializable.In the primary class, define public variables of each type defined as above.Do not initialize these variables in Awake or S since they are serializable, Unity will take care of that.You can specify defaults as before by assigning valuThis will group variables in collapsible units in the inspector, which is easier to manage.[Serializable]public class MovementProperties //Not a MonoBehaviour!{
public float movementS
public float turnSpeed = 1; //default provided}public class HealthProperties //Not a MonoBehaviour!{
public float maxH
public float regenerationR}public class Player : MonoBehaviour{
public MovementProperties movementP
public HealthPorperties healthP} 35. Make classes that are not MonoBehaviours Serializable even when they are not used for public fields. This allows you to view the class fields in the inspector when the Inspector is in Debug mode. This works for nested classes too (private or public).36. Avoid making changes to inspector-tweakables in code. A variable that is tweakable in the inspector is a configuration variable, and should be treated as a run-time constant and not double as a state-variable. Following this practice makes it easier to write methods to reset a component's state to the initial state, and makes it clearer what the variable does.public class Actor : MonoBehaviour{
public float initialHealth = 100;
private float currentH
public void Start()
ResetState();
private void Respawn()
ResetState();
private void ResetState()
currentHealth = initialH
}}Patterns
Patterns are ways to solve frequently occurring problems in a standard way. Bob Nystrom's
(readable free online) is a useful resource to see how patterns apply to problems that arise in game programming. Unity itself use a lot of these patterns: Instantiate is an example of t MonoBehaviours follow a version of the template pattern, UI and animation use the observer pattern, and the new animation engine uses state machines.These tips relate to using patterns with Unity specifically.37. Use singletons for convenience. The following class will make any class that inherits from it a singleton automatically:public class Singleton : MonoBehaviour where T : MonoBehaviour{
protected static T
//Returns the instance of this singleton.
public static T Instance
if(instance == null)
instance = (T) FindObjectOfType(typeof(T));
if (instance == null)
Debug.LogError("An instance of " + typeof(T) +
" is needed in the scene, but there is none.");
}} Singletons are useful for managers, such as ParticleManager or AudioManager or GUIManager.(Many programmers warn against classes vaguely named XManager because it points at a class either poorly named, or designed with too many unrelated tasks. In general, I agree with this. However, we have a small number of managers in every game, and they do the same in every game, so that these classes are in fact idioms.)Avoid using singletons for unique instances of prefabs that are not managers (such as the Player). Not adhering to this principle complicates inheritance hierarchies, and makes certain types of changes harder. Rather keep references to these in your GameManager (or other suitable God class
).Define static properties and methods for public variables and methods that are used often from outside the class. This allows you to write GameManager.Player instead of GameManager.Instance.player.As explained in other tips, singletons are also useful for creating default spawn points and objects that persist between scene loads that keep track of global data.38. Use state machines to get different behavior in different states or to execute code on state transitions. A light-weight state machine has a number of states, and for each state allows you to specify actions to run when entering or existing the state, and an update action. This can make code cleaner and less error prone. A good sign that you could benefit from a state machine is if your Update method's code has an if- or switch-statement that changes what it does, or variables such as hasShownGameOverMessage.public void Update(){
if(health &= 0)
if(!hasShownGameOverMessage)
ShowGameOverMessage();
hasShownGameOverMessage = //Respawning resets this to false
HandleInput();
} With more states, this type of code c a state machine can make it a lot cleaner.39. Use fields of type UnityEvent to set up the observer pattern in the inspector. The
class allows you to link in methods that take up to four parameters in the inspector using the same UI interface as the events on Buttons. This is especially useful for dealing with input.40. Use the observer pattern to detect when a field value changes. The problem of executing code only when a variable changes crops up frequently in games. We have baked a general solution of this in a generic class that allows you to register for events whenever the value changes. Here is an example with health. Here is how it is constructed:/*ObservedValue*/ health = new ObservedValue(100);health.OnValueChanged += () =& { if(health.Value &= 0) Die(); }; You can now change it everywhere, without doing checking in each place where you check it, for example, like this:if(hit) health.Value -= 10; Whenever the health hits a point below 0, the Die method is called. For further discussion and an implementation, see this .41. Use the Actor pattern on prefabs. (This is not a "standard" pattern. The basic idea is from Kieran Lord in .)An actor is the main com usually the component that provides the prefab's "identity", and the one higher-level code will most often interact with. The actor uses other components – helpers – on the same object (and sometimes on children) to do its work.If you make a button object from the menu in unity, it creates a game object with a Sprite and Button component (and a child with a Text component). In this case, Button is the actor component. Similarly, the main camera typically has several components (GUI Layer, Flare Layer, Audio Listener) in addition to the Camera component attached. Camera is the actor.An actor may require other components to work correctly. You can make your prefab more robust and useful by using the following attributes on your actor component:Use
to indicate all the components your actor needs on the same game object.
(Your actor can then always safely call GetComponent, without the need to check whether the value returned was null.)Use
to prevent multiple instances of the same component to be attached. Your actor can then always call GetComponent without having to worry what the behavior shoud be when there is more than one component attached).Use
if your actor object has children. This makes it easier to select in the scene view.[RequiredComponent(typeof(HelperComponent))][DisallowMultipleComponent][SelectionBase]public class Actor : MonoBehaviour{
...//} 42. Use generators for random and patterned data streams. (This is not a standard pattern, but one we found to be extremely useful.)A generator is similar to a random generator: it is an object with a Next method that can be called to get a new item of a specific type. Generators can be manipulated during their construction to produce a large variety of patterns or different types of randomness. They are useful, because they keep the logic of generating a new item separate from where you need the item, so that your code is much cleaner.Here are a few examples:var generator = Generator
.RamdomUniformInt(500)
.Select(x =& 2*x); //Generates random even numbers between 0 and 998var generator = Generator
.RandomUniformInt(1000)
.Where(n =& n % 2 == 0); //Same as abovevar generator = Generator
.Iterate(0, 0, (m, n) =& m + n); //Fibonacci numbersvar generator = Generator
.RandomUniformInt(2)
.Select(n =& 2*n - 1)
.Aggregate((m, n) =& m + n); //Random walk using steps of 1 or -1 one randomlyvar generator = Generator
.Iterate(0, Generator.RandomUniformInt(4), (m, n) =& m + n - 1)
.Where(n &= 0); //A random sequence that increases on average We have use generators for spawning obstacles, changing background colors, procedural music, generating letters sequences likely to make words in word games, and much more. Generators also work well to control co-routines that repeat at non-constant intervals, using the construct:while (true){
//Do stuff
yield return new WaitForSeconds(timeIntervalGenerator.Next());} To find out more about generators, see this .Prefabs and Scriptable Objects43. Use prefabs for everything. The only game objects in your scene that should not be prefabs (or part of prefabs) should be folders. Even unique objects that are used only once should be prefabs. This makes it easier to make changes that don't require the scene to change.44. Lin do not link instances to instances. Links to prefabs are maintained when dropping a links to instances are not. Linking to prefabs whenever possible reduces scene setup, and reduce the need to change scenes.As far as possible, establish links between instances automatically. If you need to link instances, establish the links programmatically. For example, the player prefab can register itself with the GameManager when it starts, or the GameManager can find the Player prefab instance when it starts.45. Don't put meshes at the roots of prefabs if you want to add other scripts. When you make the prefab from a mesh, first parent the mesh to an empty game object, and make that the root. Put scripts on the root, not on the mesh node. That way it is much easier to replace the mesh with another mesh without losing any values that you set up in the inspector.46. Use scriptable objects for shared configuration data instead of prefabs.If you do this:scenes are smalleryou cannot make changes to a single scene (on a prefab instance) by mistake.47. Use scriptable objects for level data. Level data is often stored in XML or JSON, but using scriptable objects instead has a few advantages:It can be edited in the Editor. This makes it easier to validate data and is friendlier to non-technical designers. Moreover, you can use custom editors to make it even easier to edit.You don't have to worry about reading / writing and parsing of the data.It is easier to split and nest, and manage the resulting assets, and so compose levels from building blocks rather than from a massive configuration.48. Use scriptable objects to configure behavior in the inspector. Scriptable objects are usually associated with configuring data, but they also allow you to use "methods" as data.Consider a scenario where you have an Enemy type, and each enemy has a bunch of SuperPowers. You could make these normal classes and have a list if them in the Enemy class… but without a custom editor you would not be able to set up a list of different superpowers (each with its own properties) in the inspector. But if you make these super powers assets (implement them as ScriptableObjects), you could!Here is how it looks:public class Enemy : MonoBehaviour{
public SuperPower superP
public UseRandomPower()
superPowers.RandomItem().UsePower(this);
}}public class BasePower : ScriptableObject{
virtual void UsePower(Enemy self)
}}[CreateAssetMenu("BlowFire", "Blow Fire")public class BlowFire : SuperPower{
override public void UsePower(Enemy self)
///program blowing fire here
}} There are a few things to be aware of when following this pattern:Scriptable objects cannot reliably be made abstract. Instead, use concrete bases classes, and throw NotImplementedExceptions in methods that should be abstract. You can also define an Abstract attribute and mark classes and methods that should be abstract with it.Scriptable objects that are generic cannot be serialized. However, you can use generic base classes and only serialize sub-classes that specify all the generics.49. Use scriptable objects to specialize prefabs. If two objects' configuration differ only in some properties, it is common to put two instances in the scene and adjust those properties on the instances. It is usually better to make a separate class of the properties that can differ between the two types into a separate scriptable object class.This gives you more flexibility:You can use inherit from your specialization class to give more specific properties to different types of object.Scene setup is much safer (you merely select the right scriptable object, instead of having to adjust all the properties to make the object into the desired type).It is easier to manipulate these objects at runtime through code.If you have multiple instances of the two types, you know their properties will always be consistent when you make changes.You can split sets of configuration variables into sets that can be mixed-and-matched.Here is a simple example of this setup.[CreateAssetMenu("HealthProperties.asset", "Health Properties")]public class HealthProperties : ScriptableObject{
public float maxH
public float resotrationR}public class Actor : MonoBehaviour{
public HealthProperties healthP}If the number of specializations is large, you may want to define the specialization as a normal class, and use a list of these in a scriptable object that's linked to a suitable place where you can get hold of it (such as your GameManager). There is a bit more glue necessary to make it safe, just the bare minimum is shown below.public enum ActorType{
Vampire, Wherewolf}[Serializable]public class HealthProperties{
public ActorT
public float maxH
public float resotrationR}[CreateAssetMenu("ActorSpecialization.asset", "Actor Specialization")]public class ActorSpecialization : ScriptableObject{
public List healthP
public this[ActorType]
get { return healthProperties.First(p =& p.type == type); } //Unsafe version!
}}public class GameManager : Singleton
public ActorSpecialization actorS
...}public class Actor : MonoBehaviour{
public ActorT
//Example usage
public Regenerate()
+= GameManager.Instance.actorSpecialization[type].resotrationR
}} 50. Use the
attribute to add ScriptableObject creation automatically to the Asset/Create menu. Debugging51. Learn how to use Unity's debugging facilities effectively. Add context objects to
statements to see from where they are generated.Use
to pause the game in the editor (useful, for example, when you want to an error condition occurs and you want to examine component properties in that frame).Use
functions for visual debugging (for example, DrawRay is very useful when debugging why ray casts are not hit).Use
for visual debugging. You can also supply gizmo renderers outside mono behaviours using the
attribute.Use the debug inspector view (to see the values of private fields at runtime in Unity using the inspector). 52. Learn how to use your IDE debugger effectively. See for example .53. Use a visual debugger that draws graphs of values over time. This is extremely helpful to debug physics, animation, and other dynamic processes, especially sporadic glitches. You will be able to see the glitch in the graph, and be able to see which other variables change at the same time. The visual inspection also makes certain types of strange behavior clear, such as values that change too often, or drift without apparent cause. We use , but there are several available.54. Use improved console logging. Use an editor extension that allows you to color-code output according to categories, and allows you to filter output according to those categories. We use , but there are several available.55. Use Unity's test tools, especially to test algorithms and mathematical code. See for example the
tutorial, or the post .56. Use Unity's test tools to run "scratchpad" tests. Unity's test tools are not only suitable for formal tests. They can also be exploited for convenient scratch-pad tests that can be run in the editor without having to run a scene.57. Implement shortcuts for taking screen shots. Many bugs are visual, and are much easier to report when you can take a picture. The ideal system should maintain a counter in PlayerPrefs so that successive screenshots are not overwritten. The screenshots should be saved outside the project folder to avoid people from accidentally committing them to the repository.58. Implement shortcuts for printing snapshots of important variables. This makes it easy to log some information when something unexpected happens during the game that you can inspect. Which variables depends on the game, of course. You will be guided by the typical bugs that occur in your game. Examples are positions of the player and enemies, or the "thinking state" an AI actor (such as the path it is trying to follow).59. Implement debug options for making testing easier. Some examples:Unlock all items.Disable enemies.Disable GUI.Make player invincible.Disable all gameplay.Be careful not to commit debug
changing debug options can mystify other developers on your team.60. Define constants for debug shortcut keys, and keep them in one place. Debug keys are not normally (or conveniently) processed in a single location like the rest of the game input. To avoid shortcut-key collisions, define constants in a central place. An alternative is to process all keys in one place regardless of whether it is a debug function or not. (The downside is that this class may need extra references to objects just for this).61. Draw or spawn small spheres at vertices when doing procedural mesh generation. This will help you make sure your vertices are where they are supposed to be and the mesh is the right size before you start messing with triangles and UVs to get your mesh to display.Performance62. Be wary of generic advice about design and construction for performance reasons.Such advice is often based on myths and is not backed by tests.Sometimes the advice is backed by tests but the tests are faulty.Sometimes the advice is backed by correct tests, but they are in an unrealistic or different context. (For example, it's easy to show how using arrays are faster than generic lists. However, in the context of a real game this difference is almost always negligible. Similarly, if the tests apply to different hardware than your target devices, their results may not be meaningful to you.)Sometimes the advice is sound, but out of date.Sometimes, the advice applies. However, there is a tradeoff. Slow games that ship are sometimes better than fast ones that don't. And heavily optimized games are more likely to contain tricky code that can delay shipping.Performance advice can be useful to keep in mind to help you track the source of actual problems faster using the process outlined below. 63. Test regularly on target devices from early-on. Devices have very different perform don't get surprised by them. The earlier you know about problems the more effectively you can address them.64. Learn how to use a profiler effectively to track the cause of performance problems. If you are new to profiling, see .Learn how to define your own frames (using
and ) for fain-grained analysis.Learn how to use platform-specific profiling, such as the .Learn to
in built players and
in the profiler.65. Use a custom profiler for more accurate profiling when necessary. Sometimes, Unity's profiler cannot give you a clear picture of what is going on: it may run out of profile frames, or deep-profiling can slow down the game so much that tests are not meaningful.
We use our own in-house profiler for this, but you should be able to find alternatives on the Asset Store.66. Measure impact of performance enhancements. When you make a change to increase performance, measure it to make sure the change is a real improvement. If the change is not measurable or negligent, undo it.67. Don't write less-readable code for better performance. Unless:You have a problem, identified the source with a profiler, you measured a gain after the change, and the gain is high enough compared with the loss in maintainability.ORYou know what you are doing.Naming Standard and Folder Structure68. Follow a documented naming convention and folder structure. Consistent naming and folder structure makes it easier to find things, and to figure out what things are.You will most probably want to create your own naming convention and folder structure. Here is one as an example.Naming General PrinciplesCall a thing what it is. A bird should be called Bird.Choose names that can be pronounced and remembered. If you make a Mayan game, do not name your level QuetzalcoatisReturn.Be consistent. When you choose a name, stick to it. Don't call something buttonHolder in one place and buttonContainer in another.Use Pascal case, like this: ComplicatedVerySpecificObject. Do not use spaces, underscores, or hyphens, with one exception (see Naming Different Aspects of the Same Thing).Do not use version numbers, or words to indicate their progress (WIP, final).Do not use abbreviations: DVamp@W should be DarkVampire@Walk.Use the terminology in the design document: if the document calls the die animation Die, then useDarkVampire@Die, not DarkVampire@Death.Keep the most specific descriptor on the left: DarkVampire, not VampireD PauseButton, not ButtonPaused. It is, for instance, easier to find the pause button in the inspector if not all buttons start with the word Button. [Many people prefer it the other way around, because that makes grouping more obvious visually. Names are not for grouping though, folders are. Names are to distinguish objects of the same type so that they can be located reliably and fast.]Some names form a sequence. Use numbers in these names, for example, PathNode0, PathNode1. Always start with 0, not 1.Do not use numbers for things that don't form a sequence. For example, Bird0, Bird1, Bird2 should be Flamingo, Eagle, Swallow.Prefix temporary objects with a double underscore __Player_Backup.Naming Different Aspects of the Same ThingUse underscores between the core name, and the thing that describes the "aspect". For instance:GUI buttons states EnterButton_Active, EnterButton_InactiveTextures DarkVampire_Diffuse, DarkVampire_NormalmapSkybox JungleSky_Top, JungleSky_NorthLOD Groups DarkVampire_LOD0, DarkVampire_LOD1Do not use this convention just to distinguish between different types of items, for instance Rock_Small, Rock_Large should be SmallRock, LargeRock.StructureThe organization of your scenes, project folder, and script folder should follow a similar pattern. Here are some abridged examples to get you started.Folder StructureMyGame
Scratchpad
DarkVampire
LightVampire
Structures
UI MyLibray
...PluginsSomeOtherAsset1SomeOtherAsset2...Scene StructureMainDebugManagers CamerasLightsUI
Structures
...Gameplay
...Dynamic ObjectsScripts Folder StructureDebugGameplay
...FrameworkGraphicsUI...
Unity的50个使用技巧(2016 Edition)
版权所有,禁止匿名转载;禁止商业使用;禁止个人使用。
翻译:高磊(稳定心态)
校审:罗倩(蘑菇)
大约四年前,我发布了关于Unity开发的50个技巧的初始版本。 虽然最新版本与初始版本仍有许多关联,但在初始版本之后,我修改了许多内容:
Unity更好用。例如,我现在信赖FPS计数器。使用property drawer的功能可以降低编写customeditors的必要性。同时Prefab的工作方式也降低了显式嵌套Prefab或替代件的需求。Scriptable objects更为友好。
VisualStudio集成度更佳,从而使调试操作更简便,同时减少了对于大量Gorilla调试操作的需求。

我要回帖

更多关于 unity如何创建文件夹 的文章

 

随机推荐