zoukankan      html  css  js  c++  java
  • Unity3d笔记

    • 当变量重命名后,已序列化保存的值会丢失,如果希望继续保留其数值,可使用FormerlySerializedAs,如下代码所示:
      [UnityEngine.Serialization.FormerlySerializedAs("hp")]
      public int newHp = 20;
    • Unity有个隐藏的彩蛋:void Main(){},它的调用在Awake() OnEnable()之后Start()之前。

    • unity对文件名以点开头的文件视而不见
    • 当用enum枚举值作为Dictionary的key,在访问字典时则会产生GC。枚举是值类型的它被当作为整形使用,而Dictionary每次判断key时都会调用Object.getHashCode(Object),枚举会被调用到基类引用类型Object的该方法从而导致装箱产生GC,其他如int等值类型因自己的getHashCode不会调用到Object上的方法则作为key不会有GC,建议在需要用到enum作为key时先强转为int再将int作为key,或者在Dictionary的构造函数内传入自定义的IEqualityComparer:
      1 public class MyEnumComparer : IEqualityComparer<MyEnum> {
      2     public bool Equals(MyEnum x, MyEnum y) {
      3         return x == y;
      4     }
      5     public int GetHashCode(MyEnum x) {
      6         return (int)x;
      7     }
      8 }
    • 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(&quot;Test&quot;, 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两个调用
    • 引用一个游戏对象的逻辑,可以在最开始的地方定义它。例如:
      1 private Transform myTransform;
      2 private Rigidbody myRigidbody;
      3 void Start()
      4 {
      5     myTransform = transform;
      6     myRigidbody = rigidbody;
      7 }
    • 尽量减少使用临时变量,特别是在Update等实时调用的函数中。
    • 捕捉Android返回与Home键:
       1 //返回键
       2 if(Application.platform == Runtimeplatform.Android
       3     && Input.GetKeyDown(KeyCode.Escape))
       4 {
       5     //...    
       6 }
       7 
       8 //Home键
       9 if(Application.platform == Runtimeplatform.Android
      10     && Input.GetKeyDown(KeyCode.Home))
      11 {
      12     //...    
      13 }
    • 直接打开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手机分辨率:
       1     private float androidDensity = 1.0f;
       2     void GetDensity()
       3     {
       4 //#if UNITY_ANDROID
       5         if (Application.platform == RuntimePlatform.Android)
       6         {
       7             AndroidJavaClass player = new AndroidJavaClass("com.unity3d.player.UnityPlayer");
       8             AndroidJavaObject activity = player.GetStatic<AndroidJavaObject>("currentActivity");
       9             AndroidJavaObject wm = activity.Call<AndroidJavaObject>("getWindowManager");
      10             if (wm != null)
      11             {
      12                 AndroidJavaObject display = wm.Call<AndroidJavaObject>("getDefaultDisplay");
      13                 if (display != null)
      14                 {
      15                     AndroidJavaObject displayMetrics = new AndroidJavaObject("android.util.DisplayMetrics");
      16                     display.Call("getMetrics", displayMetrics);
      17                     androidDensity = displayMetrics.Get<float>("density");
      18                 }
      19             }
      20         }
      21 //#endif
      22     }
    • 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

    • 调试执行时间:
      1 Stopwatch sp = new Stopwatch ();
      2 sp.Start ();
      3 DoSomething ();
      4 sp.Stop ();
      5 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已优化。

    • LinkedList、List当自定义结构体struct做链表节点,必须实现IEquatable<T>、IComparable<T>接口,否则Remove、Cotains、Find、FindLast每次都有GC产生.
  • 相关阅读:
    public、private、protected继承的规则
    派生类对象的构造函数与析构函数
    类的保护成员
    派生类覆盖(修改)基类成员
    条款03:尽可能使用const
    处理类与类之间的关系
    继承派生基本概念
    条款02:尽量以const,enum,inline替换#define(宁可编译器替代预处理器)
    Redis持久化AOF和RDB对比
    Memcached取模算法
  • 原文地址:https://www.cnblogs.com/suoluo/p/5259961.html
Copyright © 2011-2022 走看看