• 当变量重命名后,已序列化保存的值会丢失,如果希望继续保留其数值,可使用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. springboot @ConfigurationProperties @EnableConfigurationProperties @Bean @ Component

    https://www.cnblogs.com/duanxz/p/4520571.html https://juejin.im/post/5cbeaa26e51d45789024d7e2 1. Bea ...

  2. 九度OJ 1011:最大连续子序列 (DP)

    时间限制:1 秒 内存限制:32 兆 特殊判题:否 提交:5615 解决:2668 题目描述:     给定K个整数的序列{ N1, N2, ..., NK },其任意连续子序列可表示为{ Ni, N ...

  3. 用CMakeLists.txt组织工程

    1 一个工程会有多个CMakeLists.txt,如何组织这些CMakeLists.txt来构建一个工程? 1.1  最外层一个CMakeLists.txt,是总的CMakeList.txt,在这个里 ...

  4. 一阶 斜率 二阶 原函数的粗糙度 roughness

    1 2 损失函数+惩罚函数 2阶导数

  5. DAICO模式到底是什么?

    自从V神提出DAICO以来,已经有项目围绕DAICO模式落地,但是DAICO到底是什么呢? 所谓DAIC0,其实就是DAO+IC0. DAO是DistributedAutonomous Organiz ...

  6. Java for LeetCode 117 Populating Next Right Pointers in Each Node II

    Follow up for problem "Populating Next Right Pointers in Each Node". What if the given tre ...

  7. [2018-05-27]配置VSTS认证方式使用Personal Access Token

    本文介绍下如何配置VSTS(visual studio team service,其实就是微软SaaS版的TFS)通过Personal Access Token访问其下的Git代码库. 问题 使用gi ...

  8. PAT 甲级 1116. Come on! Let's C (20) 【循环判断】

    题目链接 https://www.patest.cn/contests/pat-a-practise/1116 思路 注意一个细节 如果没有本来 ID 的 后来又查了这个ID 不是输出 checked ...

  9. Cmder配置

    中文乱码和重叠 当我们使用ls命令查看文件目录时,发现,中文被显示成了一些奇怪的乱码,将以下几行代码配置在cmder/config/user-aliases下即可解决问题: l=ls --show-c ...

  10. 使用vscode写typescript(node.js环境)起手式

    动机 一直想把typescript在服务端开发中用起来,主要原因有: javascript很灵活,但记忆力不好的话,的确会让你头疼,看着一月前自己写的代码,一脸茫然. 类型检查有利有敝,但在团队开发中 ...