• 当变量重命名后,已序列化保存的值会丢失,如果希望继续保留其数值,可使用FormerlySerializedAs,如下代码所示:

    [UnityEngine.Serialization.FormerlySerializedAs("hp")]
    public int newHp = ;
  • Unity有个隐藏的彩蛋:void Main(){},它的调用在Awake() OnEnable()之后Start()之前。

  • unity对文件名以点开头的文件视而不见
  • 当用enum枚举值作为Dictionary的key,在访问字典时则会产生GC,应该是内部在对enum值进行查找时产生的,而其他如int等值类型作为key则不会有GC,建议在需要用到enum作为key时先强转为int再将int作为key。
  • C#属性(Property)想要显示在Inspector上且set get能被正常执行:https://github.com/LMNRY/SetProperty

    [SerializeField, SetProperty ("Number")]
    private float number;
    public float Number
    {
    get
    {
    return number;
    }
    private set
    {
    number = Mathf.Clamp01(value);
    }
    }
  • 当你在Unity中编辑场景,突然死机时,可以在项目文件目录中找到Temp文件夹,双击文件夹,找到_Backupscenes文件夹,把后缀为.backup的文件后缀改为.unity,然后拖进Unity的Project界面里面,这样就可以还原死机前场景最后情况。
  • 通过Debug.Log获取执行此语句物件:在脚本的Debug.Log语句中加入gameObject,即Debug.Log("Test", gameObject); 脚本运行时点击Console界面中的输出语句,就能在Hierarchy界面中看到哪个物件执行了这个脚本。
  • Debug.Break()可以在任何地方暂停游戏,调试小技巧。
  • 性能调试时将你想观察的那部分代码放入

    Profiler.BeginSample ("aaa");
    Profiler.EndSample ();
    代码之间就可指定查看该部分代码的开销。

  • 当修改了Prefab并想将该改动应用到所有的物体上:
    若是在Project视图中直接修改的该prefab则一定要记得执行File->Save Project,unity并不会将改动直接保存到磁盘。
    若是在Hierarchy视图中修改的该prefab,必须点击Apply,若同时还需要保存场景时,则尽量记住先执行Save Project操作再执行Save Scene,不然你场景中的prefab很可能跟它本身的prefab永久丢失关联。
  • 屏幕坐标与鼠标位置:
    屏幕坐标系以左上角为原点(0, 0),右下角为(Screen.Width, Screen.Height)。
    Input.mousePosition鼠标位置以屏幕左下角为原点(0, 0),屏幕右上角为(Screen.Width, Screen.Height)。
  • Awake会在物体初始化之后被调用,无论脚本本身是否被启用(是否被禁用,在Inspector视图上脚本前面的复选框是否被勾上,实际上该复选框只有脚本在存在Start(), Update(), FixedUpdate(), and OnGUI()至少一个时才会显示);OnEnable、Start则是在脚本被启动的情况下被调用,其中OnEnable会在脚本或者物体每次被重新启用时都会被再次调用。
  • 父物体与子物体Awake、OnEnable、Start函数执行顺序:
    首先同一脚本中该三个函数的执行顺序是:Awake->OnEnable->Start,然后:
    * 若父子物体都是在场景中已经存在的:子物体的Awake、OnEnable全执行完毕后才会执行到父物体的Awake、OnEnable;当所有父与子物体的Awake与OnEnable全都执行完毕后才开始执行子物体的Start再执行父物体的Start。
    * 若父子物体先不存在于场景中,而是通过Instantiate()动态产生的:则父与子的调用顺序与上面相反,父物体的Awake、OnEnable全执行完毕后才会执行到子物体的Awake、OnEnable;当所有父与子物体的Awake与OnEnable全都执行完毕后才开始执行父物体的Start再执行子物体的Start。
    该结论是实测多次观察出来的,但unity官方没未明确说明Awake OnEnable Start存在固定顺序,故不排除有时不表现为以上所说顺序而是随机顺序,不应该依赖这些执行顺序而应尽量避免初始化的先后的要求。
  • 调用Instantiate()方法动态添加GameObject时,新GameObject的Awake、OnEnable都调用结束后Instantiate()才会返回。
  • 一般在新建类时会产生空的Update函数。如果代码不需要用到该函数,应该该函数进行删除。另外,尽量不要在Update函数内执行Find、FindObjectOfType、FindGameObjectsWithTag这些寻找物体的函数,面应该尽量在Start或Awake函数中执行。
  • 每个脚本中实现Update()回调函数,这是一般做法,更优的做法是只有一个Manger脚本含有Update函数,遍历(Array性能优于List)调用其他脚本的OnUpdate(或其他命名)函数。Unity要维护调用的Update函数越多开销越大,详细测试见:https://blogs.unity3d.com/cn/2015/12/23/1k-update-calls/  从评论中了解到,unity正在开发全新的一套消息机制,喜闻乐见。
    另一方面,有人想到用Coroutines协程代替Update,测试证明Update比协程要快5倍,协程内部至少要处理move next和current两个调用
  • 引用一个游戏对象的逻辑,可以在最开始的地方定义它。例如:
     private Transform myTransform;
    private Rigidbody myRigidbody;
    void Start()
    {
    myTransform = transform;
    myRigidbody = rigidbody;
    }
  • 尽量减少使用临时变量,特别是在Update等实时调用的函数中。
  • 捕捉Android返回与Home键:
     //返回键
    if(Application.platform == Runtimeplatform.Android
    && Input.GetKeyDown(KeyCode.Escape))
    {
    //...
    } //Home键
    if(Application.platform == Runtimeplatform.Android
    && Input.GetKeyDown(KeyCode.Home))
    {
    //...
    }
  • 直接打开app store,同理根据链接不同也可打开Mail或浏览器等:
    void OnRateButtonClick()
    {
    #if UNITY_ANDROID
    Application.OpenURL("market://details?id=YOUR_APP_ID");
    #elif UNITY_IPHONE
    Application.OpenURL("itms-apps://itunes.apple.com/app/idYOUR_APP_ID");
    #endif
    }
  • 获取Android手机分辨率:
         private float androidDensity = 1.0f;
    void GetDensity()
    {
    //#if UNITY_ANDROID
    if (Application.platform == RuntimePlatform.Android)
    {
    AndroidJavaClass player = new AndroidJavaClass("com.unity3d.player.UnityPlayer");
    AndroidJavaObject activity = player.GetStatic<AndroidJavaObject>("currentActivity");
    AndroidJavaObject wm = activity.Call<AndroidJavaObject>("getWindowManager");
    if (wm != null)
    {
    AndroidJavaObject display = wm.Call<AndroidJavaObject>("getDefaultDisplay");
    if (display != null)
    {
    AndroidJavaObject displayMetrics = new AndroidJavaObject("android.util.DisplayMetrics");
    display.Call("getMetrics", displayMetrics);
    androidDensity = displayMetrics.Get<float>("density");
    }
    }
    }
    //#endif
    }
  • Time.timeScale设置为0时Update()仍然会继续执行,FixedUpdate()会停止调用,注意:此时协程WaitForSeconds和Invoke调用都会停止!
    Time.timeScale会影响Time.deltaTime和Time.time,不会影响Time.realtimeSinceStartup, Time.unscaledDeltaTime, Time.unscaledTime;
    Time.time/timeScale = unscaledTime,
    Time.deltaTime / timeScale = unscaledDeltaTime;
    在Update中使用Time.deltaTime,就可以使用timeScale改变运动速率
    在Update中使用Time.unscaledDeltaTime,timeScale就不会影响运动速率
    在后台运行(暂停)和卡顿(如加载大场景时进入场景那一刻)期间,Time.deltaTime和Time.time不会继续计时,Time.realtimeSinceStartup, Time.unscaledDeltaTime, Time.unscaledTime会继续计时,此时Time.realtimeSinceStartup更精准会在游戏恢复时立马得到正常时间,Time.unscaledDeltaTime, Time.unscaledTime则可能要等好几帧之后才会得到正常的时间(即暂停期间经过的时间过几帧才加上来)。
    当手机调整了设备时间后Time.realtimeSinceStartup也同样会受影响。
  • 获取当前animator播放的state时长:anim.GetCurrentAnimatorStateInfo(0).length。然而该函数需要等动画播放后才能正确获取,即使在协程中等待一帧再调用该函数得到的时间也可能是错误的,应该调用anim.Update(0)后再去获取:
    m_animator.Play(animName, -1, 0);
    m_animator.Update(0);
    int length = m_animator.GetCurrentAnimatorStateInfo(0).length;

    若是在5.x版本中则可以通过指定的状态名字获取其时长:anim.runtimeAnimatorController.animationClips.First (x => x.name == "AnimationName").length;

  • Animator.Play(string stateName, int layer = -1, float normalizedTime = float.NegativeInfinity):
    layer:经测试0和-1是一样的。
    normalizedTime:
    0-1为从开始到结束之间某时间点开始播放;
    负无穷时如果play的是当前状态则继续播放当前状态不做任何改变,否则跳到从头开始播目标状态;
    为负数时,时间会保持增长,从负数开始至0期间为暂停的,从0之后开始正常播放;
    为超过1的数时则会以小数部分的时间(等同于0-1的参数)点开始播放,忽略整数部分(实际上整数部分代表重复播放了多少遍)。
  • 简单实现animator反方向播放动画:animator.speed=-1;
  • 发ios包的注意点:
  • 在用unity4.6发iOS包的时候,发现导出的xcode工程出错;其实原因是xcode太新了,为7.2, 而unity还只支持7.1,而两者兼容性不好,导致了很多error的出现。所以,发ios包时要注意xcode的版本和unity是否相符,不然会花太多的时间在上面的,切记!

  • 图形化调试:
    Unity中图形化调试主要4种
    Debug.Draw
    Gizmos.Draw
    Graphic.DrawMesh
    GL
    只需在Scene窗口显示的调试图像
    一直显示的 OnDrawGizmos + Gizmos.Draw
    选中显示的 OnDrawGizmosSelected + Gizmos.Draw
    脚本控制的 Update + Debug.Draw
    需要在实际设备屏幕显示的调试图像
    Update+Graphic.DrawMesh
    OnRenderObject+GL

    Graphic.DrawMesh和Debug.Draw 调用一致,都是在Update系里
    Graphic.DrawMesh和GL 显示类似,都在各个窗口显示,并且可以设置材质。
    详见:http://blog.sina.com.cn/s/blog_471132920101gxzf.html

  • Unity提供了命令行的接口,可以通过Shell调用。进而可以一键打包,一键打AssetBundle,接着上传SVN等操作。文档:http://docs.unity3d.com/Manual/CommandLineArguments.html

  • 调试执行时间:
     Stopwatch sp = new Stopwatch ();
    sp.Start ();
    DoSomething ();
    sp.Stop ();
    Debug.Log (string.Format ("Elapsed:{0} ms", (float)sp.ElapsedTicks/ Stopwatch.Frequency * 1000f));
  • 写Unity编辑器控件时如何获得Unity内置的图标:Texture tex  = (Texture)EditorGUIUtility.Load("PlayButton On");或
    EditorGUIUtility.IconContent("PlayButton On")

    这样就可以获得一个蓝色的播放图标。所有的图标名字集合请转至http://www.xuanyusong.com/archives/3777

  • 您可以通过使用[MenuItem(“CONTEXT / ...”)添加自定义项目到上下文菜单,即使是内置的类
  • 写一个继承自AssetPostporocessor的脚本可在Unity导入或更新各种资源之前或之后增加自定义处理,如更改图片或音频的格式、转换xlsx配置档为bytes文件等等。

  • 在使用缓冲池等需要修改对象的父节点时,正确的顺序应该是首先禁用对象,然后将其父对象重置为对象池,而不是先修改父对象再禁用对象,这样会造成不必要的污染。
  • ??和?.两个操作符表达式对于继承自UnityEngine.Object的类通常是得不到正确结果的,它是纯C#的null检查,它会绕过Unity内部自定义的==null检查,请谨慎使用或避免使用在内置组件中。
    unity为了开发者方便使用调试获得更多错误细节信息,在对C++封装C#时增加了很多额外处理,如果不做这些操作,则用户看到的就是简单的空引用异常的错误,对错误原因和修改方式知之较少。
    如GameObject等在C#仅仅是对引擎内部原生C++ Object的封装,C#的内存管理是GC自动处理,C++则是是仅当切换场景或手动调用UnityEngine.Object.Destroy()时清理,故存在C#引用还存在而底层原生代码已被销毁的情况。故unity重载了==操作符可以判断原生Object是否被destoryed,也因此==null的操作比你想像中更费,它要判断处理的事比较复杂:
    private Transform m_CachedTransform
    public Transform transform
    {
    get
    {
    if (m_CachedTransform == null)
    m_CachedTransform = InternalGetTransform();
    return m_CachedTransform;
    }
    }

    以上代码即会看不出对transform进行缓存有多少性能提升,因==null的操作本身就比较费。

    对object进行判空有两种意图,是确保已进行赋值还是检查引用的底层引擎对象生命周期,以下代码意义不明:

    var go = gameObject ?? CreateNewGameObject();

    当你是想判断实际引用的物体是否被destory需要显示调用==操作符:

    var go = gameObject != null ? gameObject : CreateNewGameObject();
    // Or use the implicit bool conversion operators for the same check
    go = gameObject ? gameObject : CreateNewGameObject();

    当你想确保变量已被初始化赋了正确的值需要显示调用object.ReferenceEquals()(对null的检查该调用已被编译器优化,且速度快于自定义的==操作符):

    return !object.ReferenceEquals(gameObject, null) ? gameObject : CreateNewGameObject();

    详情:https://blogs.unity3d.com/2014/05/16/custom-operator-should-we-keep-it/
    实际代码实践中发现仅如BoxCollider等内置组件不可使用??和?.表达式,自定义类及UGUI相关组件是没问题的,可能unity5或之后某版本unity已优化。

Unity3d笔记的更多相关文章

  1. Unity3D笔记 英保通一

    一.材质和着色器 1.材质和着色器紧密的联系,其中材质相当于是一个框架,而着色器就是框架中中的内容.在材质框架中可以选择不同的Shader并调节不同的 属性 Material和Physic Mater ...

  2. Unity3D笔记 Collect

    一.输入轴 默认输入轴: Horizontal 和 Vertical被映射到w, a, s, d键和方向键 Fire1, Fire2, Fire3被分别映射到Ctrl,Option(Alt)和Comm ...

  3. Unity3D笔记 GUI 三、实现选项卡二窗口

    实现目标: 1.使用个性化Box控件 2.个性化Lable控件 3.添加纵向滚动条 4.新建SelectedItem样式 一.最终效果: 二.主要代码 using UnityEngine; using ...

  4. Unity3D笔记 GUI 一

    要实现的功能: 1.个性化Windows界面 2.减少个性化的背景图片尺寸 3.个性化样式ExitButton和TabButton 4.实现三个选项卡窗口 一.个性化Windows界面 1.1.创建一 ...

  5. Unity3D笔记 愤怒的小鸟<七> 小鸟群准备动画

    要实现的目标: 1.3只小鸟初始动画 2.完善代码slingShot.js 3.完善代码BirdMoving.js 1.实现3个准备动画:Unity3D内置的动画管理器 1.1.先选择GameObje ...

  6. Unity3D笔记 愤怒的小鸟<五> 小鸟动画+Unity3D如何设置断点调式

    前言:实现小鸟的动画,之前吐槽过js写U3D,就改成了C#来写,没想到遇到问题了. 实现的效果 using UnityEngine; using System.Collections; /// < ...

  7. Unity3D笔记 愤怒的小鸟<二> 实现Play界面

    创建Play界面.能个把各个图片组合成一个场景,场景组成后背景能够不停的滚动,当鼠标单击时显示图片手型鼠标 一.GUI Texture 1.创建背景.地面.树木.草 ,这里注意Z轴的排序,一层一层则第 ...

  8. Unity3D笔记 愤怒的小鸟<一>场景切换

    新建3个场景,场景1 Start 十秒后自动切换到场景2 Splash,场景2在二秒后自动切换到场景3 Selection 一.场景一Start 二.场景2 Splash 三.场景3 Selectio ...

  9. Unity3D笔记六 GUI游戏界面

    1.Label:标签控件,可以在游戏中用来展示文本字符串信息,不仅可以写字还可以贴图片. 2.Button:按钮控件,一般分图片按钮和普通的按钮,还有一个连续按钮RepeatButton注意,这个在W ...

  10. Unity3D 笔记二 3D模型基础

    一.3D模型基础 1.Hierarchy 显示的是界面上的游戏对象(GameObject),每个游戏对象都有.至少要有一个Camera,点击Camera就可以在Preview中看到摄像机的视角画面.每 ...

随机推荐

  1. linux 设置静态IP方法

    本系统使用 linux redhat 7.2 1.   修改ip vi   /etc/sysconfig/network-scripts/ifcfg-eno16777736 2. 修改数据项如下 3. ...

  2. mongo 介绍

    [介绍]:MongoDB 是由C++语言编写的,是一个基于分布式文件存储的开源数据库系统.在高负载的情况下,添加更多的节点,可以保证服务器性能.MongoDB 旨在为WEB应用提供可扩展的高性能数据存 ...

  3. HttpModule与HttpHandler详解(转)

    ASP.NET对请求处理的过程:当请求一个*.aspx文件的时候,这个请求会被inetinfo.exe进程截获,它判断文件的后缀(aspx)之后,将这个请求转交给 ASPNET_ISAPI.dll,A ...

  4. Failed to load http://wantTOgo.com/get_sts_token/: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://fromHere.com' is therefore not allowed access.

    Failed to load http://wantTOgo.com/get_sts_token/: No 'Access-Control-Allow-Origin' header is presen ...

  5. Channel (digital image) 通道 色彩深度 Color_depth

    en.wikipedia.org/wiki/Channel_(digital_image) Color digital images are made of pixels, and pixels ar ...

  6. python cookbook第三版学习笔记三:列表以及字符串

    过滤序列元素: 有一个序列,想从其中过滤出想要的元素.最常用的办法就是列表过滤:比如下面的形式:这个表达式的意义是从1000个随机数中选出大于400的数据 test=[] for i in range ...

  7. JavaScript判断图片是否加载完成的三种方式 (转)

    一.load事件 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 <!DOCTYPE HTML> <html> <head>      ...

  8. ABAP 调用webservice 错误

    错误:1.soamanager 配置端口错误: 调整端口后报错: java端回复: 嗯 有问题了我待会儿看看应该是数据有问题 

  9. 【ansible】ansible部署方式以及部署包

    最近研究ansible的使用,在使用pip安装的时候遇到很多奇怪的问题,为此采用了手动安装的方式,并编写了一键安装脚本. ansible要求机器必须安装python2.6以上版本,可以通过一下命令查看 ...

  10. PAT 乙级 1081. 检查密码 (15) 【字符串】

    题目链接 https://www.patest.cn/contests/pat-b-practise/1081 思路 有一个坑点 可能会输入空格 也就是说 要用 geline 或者 gets() 然后 ...