Unity3D 学习笔记

不是什么技术文章,纯粹是我我的学习是遇到一些以为须要注意的要点,当成笔记。php

 

1.关于调试,在Android下没法断点,Debug也没法查看,已修正参考第37
查看日志方法能够启动adb的log功能,或者本身写个GUI控件直接在屏幕上显示Info 参考30html

 

2.全部自定义的编辑器扩展插件脚本必须放在Editor文件夹里,否则会致使编译程序时出错,
放到Editor文件下,编译成游戏时才会忽略这些脚本node

 

3.打包资源时,假设是在移动设备上使用,打包方式务必选择成:BuildTarget.Android
BuildPipeline.BuildAssetBundle(obj, null, targetPath, BuildAssetBundleOptions.CollectDependencies | BuildAssetBundleOptions.CompleteAssets | BuildAssetBundleOptions.DeterministicAssetBundle, BuildTarget.Android)
不然到安卓上会读不出资源。PC则无此问题。android

 

4.从新打包资源后,加入在LoadAssetBunldes时没给指定读取新版本,例如原来为:LoadFromCacheOrDownload(path, 1);
如今仍然为LoadFromCacheOrDownload(path, 1);那么是没法读取到新包的。由于系统会先从缓存检查有没有版本为1的同名bundle,若是有,则直接使用缓存的,
若是没有,则读取这个新的包。所以,从新打包文件后,应该给文件包升1级,读取的同时也多读一级。这样才会读出来。ios

 

5.若是在测试的时候为了不频繁打包频繁换级形成麻烦,则能够在每次加载时或者打包时选择手动清空缓存:Caching.CleanCache();这样就会强制读取最新的包。
但千万不要在正式版使用,由于这样是清空所有数据,频繁的读取会形成性能消耗。PC就无所谓了。小程序

 

6.想要控制磁盘缓存不超标. 只要设置Caching.maximumAvailableDiskSpace 的值为你预期的容量大小就能够(byte为单位)例如, 想要限定200M的缓存空间能够这样:Caching.maximumAvailableDiskSpace = 200*1024*1024;c#

 

7.画线能够用LineRenderer,或者直接GL画,或者更方便的能够Debug.DrawLine,甚至能够将物理射线画出Debug.DrawRay,可是Debug画出的线只能在调试模缓存

式看获得,编译成游戏后将再也不出现。参见:http://www.cnblogs.com/jeason1997/p/4805825.html服务器

 

8.判断游戏是否联网网络

Application.internetReachability == NetworkReachability.NotReachable
NotReachable  网络不可达
ReachableViaCarrierDataNetwork  网络经过运营商数据网络是可达的。
ReachableViaLocalAreaNetwork   网络经过WiFi或有线网络是可达的。 

 

9.UGUI作在人物头上血条的HUD的制做方法

public Transform follow;
Vector2 position = Camera.main.WorldToScreenPoint(follow.position);
img.rectTransform.position = position;//位置
img.rectTransform.localScale = new Vector2(2,2);//大小

 

10.动态改变相机的Culling Mask

http://answers.unity3d.com/questions/348974/edit-camera-culling-mask.html

 

11.安卓调式时,链接上手机,而后打开epclise就能够看到locat输出Debug内容了。

 

12.AssetBundle依赖关系

若是一个公共对象被多个对象依赖,咱们打包的时候,能够有两种选取。一种是比较省事的,就是将这个公共对象打包到每一个对象中。这样会有不少弊端:内存被浪费了;加入公共对象改变了,每一个依赖对象都得从新打包。AssetBundle提供了依赖关系打包。

//启用交叉引用,用于全部跟随的资源包文件,直到咱们调用PopAssetDependencies
    BuildPipeline.PushAssetDependencies();

    var options =
        BuildAssetBundleOptions.CollectDependencies |
        BuildAssetBundleOptions.CompleteAssets;


    //全部后续资源将共享这一资源包中的内容,由你来确保共享的资源包是否在其余资源载入以前载入
    BuildPipeline.BuildAssetBundle(
        AssetDatabase.LoadMainAssetAtPath("assets/artwork/lerpzuv.tif"),
        null, "Shared.unity3d", options);


        //这个文件将共享这些资源,可是后续的资源包将没法继续共享它
    BuildPipeline.PushAssetDependencies();
    BuildPipeline.BuildAssetBundle(
        AssetDatabase.LoadMainAssetAtPath("Assets/Artwork/Lerpz.fbx"),
        null, "Lerpz.unity3d", options);
    BuildPipeline.PopAssetDependencies();


    这个文件将共享这些资源,可是后续的资源包将没法继续共享它
    BuildPipeline.PushAssetDependencies();
    BuildPipeline.BuildAssetBundle(
        AssetDatabase.LoadMainAssetAtPath("Assets/Artwork/explosive guitex.prefab"),
        null, "explosive.unity3d", options);
    BuildPipeline.PopAssetDependencies();


    BuildPipeline.PopAssetDependencies();

 

13.非MonooBehavior脚本实现协程:

通常要实现多线程功能(即协程)时,通常都是在MonoBehavior脚本里StartCoroutine一个返回值为IEnumerator的函数,

但有些时候,须要在非继承自MonoBehaviro的脚本(例如单例等)里也实现多线程效果,就没法经过自身实现了,我采起这样的方法:

public class CoroutineProvider : MonoBehaviour
{
    private static CoroutineProvider instance = null;

    public static CoroutineProvider GetInstance()
    {
        if (instance == null)
        {
            GameObject go = new GameObject();
            go.name = "CoroutineProvider";
            instance = go.AddComponent<CoroutineProvider>();
        }
        return instance;
    }
}

建立一个继承自MonoBehavior的协程提供者,该提供者初始时并不存在,但有地方须要使用到协程时,就经过:

CoroutineProvider.GetInstance().StartCoroutine(Fuction());

这个时候就会在Scene里实例化一个GameObject,以它做为协程提供者的身份出现。

 

14.建立HTTP服务器并添加自定义格式文件下载支持:

为了让游戏可以自动更新,需每次登录游戏时都向服务器下载版本文件验证游戏版本,若发现版本不一样,

则下载文件更新列表,根据列表下载最新的资源,但默认状况下HTTP只能下载一些默认资源,例如:rar,txt,xml等,

像“.assetbundle”这类的的自定义格式默认状况下HTTP服务器是没法下载的,若是提交请求,例如:http://127.0.0.1/Aseet/Resource.assetbundle,

则结果是404。为了可以下载自定义格式文件,须要配置服务器,好比IIS的作法就是:

打开管理器->点击根节点->在右边找到IIS列表->找到MIME类型->打开并添加自定义格式扩展名

 

15.寻找游戏中的某个对象

为了下降耦合,尽可能避免对象脚本间经过“public value”拖动对象进行互相引用。

但有时候有必须某个对象引用另外一个对象,能够经过在脚本中查找对象。

经常使用的查找方法有:GameObject.Find / FindWithTag 等一系列,这种方法寻找对象虽然最简单,

可是效率却比较低,尽可能避免在Update中使用,最好就是在Start和Awake中使用

还有一种高效的作法,就是将特定对象归类,并集中放到一个List中,使用时查找就快速多了。

 

16.获取拥有某种类型组件的子gameobject,后面的参数的意思是运行获取不活跃的对象,即active = false

gameObject.GetComponentsInChildren<Component> (true);

 

17.只删除gameobject 脚本组件会残留

完全删除方法以下:

GameObject go = new GameObject();

Component c = go.GetComponent<Component >();

MonoBehaviour.DestroyImmediate(c);
MonoBehaviour.DestroyImmediate(go);

 

18.单例模式慎用,若是一个GameObject对象为单例对象,请当心在其余的GameObject的OnDestory里引用该单例

由于在程序关闭前会执行全部GameObject的OnDestory,可是执行的前后顺序不定。

若是GameObject_1在OnDestory时引用GameObject_2(单例对象),假设GO2的OnDestory先运行,这时候它的

Instance为null,被GO1引用时它会再建立一次。

而若是在OnDestory里建立任何对象,该对象都将不会被UnityEngine释放,一直保留下来

 

19.Unity3D 优化技术(3D图形方面)

① http://blog.csdn.net/candycat1992/article/details/42127811

② http://blog.csdn.net/leonwei/article/details/18042603

③ 尽可能不要动态的instantiate和destroy object,使用object pool代替,前者的开销很大。

具体参见:利用缓存池解决Instantiate慢的问题用

 

20.Unity中的.meta文件

每一个资源文件(外部导入和内部建立的)对应一个.meta文件。这个.meta文件中的guid就惟一标识这个资源。

内部建立的资源会使用外部导入的资源,好比 内部资源材质Material使用贴图Textures(预制体、场景中使用了更多的资源)。

材质怎么知道本身使用了那些资源呢? 就在本身的文件中记录着其它资源的GUID。

并且每一个meta里的标识都是随机产生的,并且同一文件屡次生成的meta文件里的标识一不一样。

在多人合做项目中,若是你上传资源时没有同步上传.meta文件,那么别人的机器就会为这个文件生成一个,但标识可能和你不一样,

致使的结果就是Prefab引用的各类组件丢失。

 

21.Editor运行时从Scene视图观察对象

在Hierarchy选择要观察的GameObject,鼠标联系点击4下,

这样的话,Scene视图就会将焦点集中在该GameObject身上,

就跟绑定一个摄影机同样,调式的时候很是方便。

 

22.Profiler分析器

分析器平时只检测关键代码,若是要检测全部代码,
就必须深度检测,但深度检测又很是消耗性能。因此有时候只想检测某一小段代码的时候,
能够设置检测段:
Profiler.BeginSample ("标签");
// 要检测性能的代码
Profiler.EndSample ();
而后就能够在分析器中找到相应的标签了:

 

23.Unity 5.3 C# 部分源码

https://bitbucket.org/xuanyusong/unity-decompiled/src/779abf10e556?at=master

备份地址

 

24.调节脚本执行顺序

Unity的不一样脚本间的执行顺序通常是没有规矩的,不一样脚本的同一函数,例如Start,启动的顺序也不一样。

所以不多在U3D的脚本里的同一函数作前后顺序依赖。例如在脚本A的Start里调用脚本B的某个属性,而该

属性又在脚本B的Start函数里初始化,因为不知道二者的Start的前后顺序,所以通常改成,在脚本B的Awake里

初始化该属性,而不是Start。

但要强制改变脚本的执行顺序也能够:

在这里调节脚本执行顺序,数值越小,越先执行

 edit->projectseeting->script execution order 

 

25.经过命令行在控制台编译U3D项目

官方Manual教程

示例:

(Unity.exe Path) -batchmode -projectPath (Project Path) -executeMethod MyEditorScript.MyBuildMethod -quit -logFile (Log Path)

UnityPath : Windows: C:\program files\Unity\Editor\Unity.exe

UnityPath : Mac OS: /Applications/Unity/Unity.app/Contents/MacOS/Unity

 

26.C# 以及 Uniyt3D 添加全局预编译指令

#define DEBUG

#if DEBUG

  dosome;

#endif

呐,这就是预编译指令拉,要注意,这不叫宏,跟C/C++的宏仍是有必定差距的。

至于如何定义,在代码里定义的话,当疼的C#只能在每一个文件的文件头定义才生效,也就是说,你在test1.cs里定义#define DEBUG的话,

那么你也必须在test2.cs里的头部定义一个才能在test2里生效。若是有一百个文件要使用这个预编译调节,那么你就要手动定义100次。

还好微软留了下后路,其实全局预编译指令能够在项目里的*.CSharp.csproj里设置,

右键项目,在属性里设置便可,或者干脆直接用文本文件打开项目路径下的该文件,直接编辑。

Unity也给咱们留了解决方案,不过藏得比较深:

Editor->ProjectSetting->Player->Other->如图红圈处

须要注意的是,Unity里设置的全局预编译,是按平台分开的,

也就是说,你在安卓平台下设置的指令,在PC平台是无法使用的,除非复制过去。

这里有个插件能够方便地设置预编译指令:

源代码:

下载后把中文部分去掉,扔到“Editor”文件夹下,而后打开使用即是

 

27.Unity3D自定义Debug

Unity的Debug功能比较有限,有时候要自定义一些特殊的Debug,好比封装一个Logger,在Editor平台时打印日志

到控制台,而在其余平台,则打印信息到屏幕或输出到Log文件。功能倒挺容易实现,问题就是,这个封装的Logger底层

部分也是经过Debug实现,在调试的时候要实现Unity控制台那种双击日志就跳到日志输出的地方的功能,就比较麻烦了。

由于就是Logger封装了Debug,双击的时候也不会跳到Logger.Log的地方,而是跳到内部用Debug实现的地方,致使调式

不方便。

解决方法就是,将Logger编译成dll,而后再在Unity内部引用,这时候Debug输出控制台的时候,双击日志就会直接跳到Logger.Log。

雨松也是这么作的:http://www.xuanyusong.com/archives/2782 

 

28.Unity3D里的特殊文件夹以及脚本编译顺序

第一阶段: Standard Assets, Pro Standard Assets 和Plugins文件夹以内的脚本,编译好后为:Assembly-CSharp-firstpass.dll(C#)

第二阶段: 在第一阶段那些文件夹里任何名字为'Editor'的文件夹里的脚本,例如'Plugins/Editor'

第三阶段: 全部其余不在'Editor'文件夹里的脚本,编译好后为:Assembly-CSharp.dll(C#)

第四阶段: 其余剩余脚本,例如'Editor'文件夹里的脚本(注,不是第二阶段那些在特殊文件夹里的Editor),编译好后为:Assembly-CSharp-Editor.dll(C#)

在U3D里,注意脚本的编译顺序有时候极其重要,例如:

要在C#脚本里引用UnityScprite脚本时,或者反过来,那么被引用的那些脚本,就必须在前一阶段先编译,否则会出现‘找不到脚本’的编译错误。

参考官方连接

 

29.Unity AssetBundle打包资源的一个坑:

就是Shader不会被打包进去,若是在对象身上挂有Shader,那么加载AB后,会出现Shader丢失的状况。

解决方法就是对经过AB动态加载的对象在Awake时作个断定,看看自身的Shader列表是否为空,若是为空,

则手动从本地加载。

 

30.经过Application.RegisterLogCallback来捕获Debug输出:

该方法能够监听到unity中全部的log消息,而后你能够选择保存到文件中或者显示到gui上作调试来用。

经过LogType来判断处理哪些类型的消息须要被保存或显示。

LogType以下:

Error

LogType used for Errors.

Assert

LogType used for Asserts. (These indicate an error inside Unity itself.)

Warning

LogType used for Warnings.

Log

LogType used for regular log messages.

Exception

LogType used for Exceptions.

 

using UnityEngine;
using System.Collections;

public class example : MonoBehaviour {
    public string output = "";
    public string stack = "";
    void OnEnable() {
        Application.RegisterLogCallback(HandleLog);
    }
    void OnDisable() {
        Application.RegisterLogCallback(null);
    }
    void HandleLog(string logString, string stackTrace, LogType type) {
        output = logString;
        stack = stackTrace;
    }
}

 

31.物理射线检测之BoxCast

在Physics下的RayCast已经很熟悉了,今天发现有一个BoxCast,起初觉得它的意思就是检测给定立方体内的物体,

若是物体在该Box内,则返回true:

如图,但实际使用起来老是检测失败,仔细看下BoxCast的参数:

第一二个参数很好理解,立方体的中心,立方体的大小,第三个参数就让我有点疑惑了,居然都已经决定好Box的大小了,为什么还有方向这个概念,

直接检测是否在Box里不就好,知道去Google了下网上的用法才知道:

原来这个BoxCast的意思是,在给定的center处,创建一个给定大小的Box,而后往给定的方向一直往前拖/射

出这个Box,一直拉长,而后检测这条路上的全部障碍物,大概效果如图(中间的空隙是我加上便于理解的,实际中检测是无缝的)

2D版

 

using UnityEngine;

public class NewBehaviourScript : MonoBehaviour
{
    public Vector2 pos;
    public Vector2 Size;
    public float angle;
    public Vector2 dir;
    public float dis;

    void Update()
    {
        var hit = Physics2D.BoxCast(pos, Size, angle, dir, dis);
        if (hit.collider != null)
        {
            Debug.Log(hit.point);
        }
    }

    private void OnDrawGizmos()
    {
        Gizmos.color = new Color(1, 0, 0, 0.3f);

        Quaternion rot = new Quaternion();
        rot.eulerAngles = new Vector3(0, 0, angle);
        // 变换矩阵
        Matrix4x4 trs = Matrix4x4.TRS(pos, rot, Vector2.one);
        Gizmos.matrix = trs;
        // 在该Transform的Vector3.zero处绘制一个位置、角度、缩放均与该Transform同样的方块,大小为size
        Gizmos.DrawCube(Vector3.zero, Size);
        // 复位Gizmos设置
        Gizmos.matrix = Matrix4x4.identity;
    }
}

 

32.U3D中的Time

通常Time中有两个比较重要的参数:

Time.realTime: 表示现实中的时间

Time.time: 表示游戏中的时间(受TimeScale影响)

而咱们游戏中的Time.fixedTime,Time.deltaTime都是与游戏时间对应,而不是与现实时间对应。

好比咱们的fixedTime设置为0.2,那就代表游戏中每0.2“秒”会调用一次FixedUpdate,而这个0.2秒并非现实中的0.2秒,

也就是说,FixedUpdate不能保证现实时间中每0.2秒调用一次,他的实际调用还会受到TimeScale等的影响。

若是咱们在一个FixedUpdate里作太多的内容,那么,现实中每个FixedUpdate的时长就会拉长,可是在游戏中,两个FixedUpdate

的间隔仍然是0.2秒(能够理解为TimeScale变化了,但实际上不是),形成的结果就是,咱们看到的游戏比较卡,帧数降低。

结论:

FixedUpdate与Update都不能保证在现实中多久调用一次,只能保证在游戏时间中FixedUpdate固定调用,而Update会受渲染影响

一样的游戏,当FixedUpdate里处理的内容不多时

FixedUpdate处理的内容较多,能够明星地看到卡了

可是这两个例子,Time.fixedTime都是0.2s

同理, deltaTime表示的是游戏中每两个Update中的间隔时间,即完成最后一帧的时间,

因此若是你要让一个物体匀速移动,你应该在Update里与Time.deltaTime相乘。当你乘以Time.deltaTime实际表示:每秒移动物体10米,而不是每帧10米。若是是每帧10米,游戏卡顿会形成物体移动变成非匀速。

 

33.物理中的Collider性能对比

首先,这是一个有700多颗树的场景,每棵树都挂有一个Rigibody组件

没有任何Collider时:

Box: 可见大概涨了1ms

Shpere: 涨幅巨大,大概是Box的6-7倍

Capsule:虽然也涨幅比较大,但没Sphere严重

因而可知,性能上Box >> Capsule > Shpere,

分别耗时:1ms 7.4ms 5ms,

网上有篇试验教程,是09年的,结果跟我彻底反过来,不知道是否是这几年来U3D物理进行了大优化

http://forum.unity3d.com/threads/capsule-vs-box-colliders.34254/#post-222443

 

另外,以上是基于动态Collider(所谓动态Collider,即该Collider挂载在一个非静止的物体的上,即有Rigibody且不为IsKinematic)

若是是在IsKinematic条件下,则性能消耗会进一步降低,好比以上状况,Box的状况为:

若是把Rigibody去掉,那么状况也差很少,主要消耗性能的部分是动态Collider

 

34. Unity里的Transform在SetPos或者SetRot时会形成很大的性能开销的缘由:

这种状况通常是由于该Transform的GameObject上挂有太多的Collider,Collider分为静态与动态部分,

静态的碰撞体,物理系统会批优化处理,他们基本不占多少资源。但若是这个Collider是动态运行的,好比一直在

改变位置或者旋转,那么物理系统会不断从新计算他,若是你的GameObject上挂有太多的Collider,那么在

改变Transform时就会形成额外的巨大开销。

解决方法就是尽可能减小运动的Collider,若是一个物体身上的Collider太多,能够尝试使用MeshCollider的方式来优化,

在运动的状况下,一个MeshCollider的开销比许多个小BoxCollider要小得多。

 

35.物理各类射线检测性能对比

参考:Unity中各种物理投射性能横向比较

 

36.精简缩小程序集

若想缩减移动端的游戏包大小,能够经过在打包选项里作手脚:

Api Compatibility Level 设置为 .NET 2.0 Subset的话,就会只将.NET的子集导出,仅有部分经常使用功能

Stripping Level的话也差很少,设置不一样级别能精简导出不一样的程序集,越精简的话,导出的函数越少,这样虽然包小了,

但若用到的部分功能被剔除的话,说不定程序会crash,所以若要用这功能,先去Unity官方的列表里查哪些函数是包含在哪一个级别里的:

https://docs.unity3d.com/410/Documentation/ScriptReference/MonoCompatibility.html

好比SetAccessControl这条函数,就只有在.NET 2.0原版的程序集中能用,其余的都不行。

缩减的效果很明显(上为.NET 2.0 Subset,下位.NET 2.0):

 

 

37.经过WIFI调试安卓上的Unity项目

1.首先在手机上开启USB调试功能,并安装驱动(这一步不少手机助手均可以完成)。
2.用USB电缆链接手机和电脑。
3.确保手机和电脑在一个局域网内,简单的说就是电脑和手机共用一个路由器,网段同样。
4.打开电脑上CMD窗口,输入如下命令:
adb tcpip 5555(该命令打开手机adb网络调试功能)
正常状况下输入命令后控制台会出现回显
restarting in TCP mode port: 5555
打开手机查看手机的IP地址(不会请百度)假设手机的地址是192.168.1.x输入命令
adb connect 192.168.1.x
若是一切正常控制台会回显如下内容
connected to 192.168.1.x:5555
若是你想查看是否链接成功请输入如下内容
adb devices
控制台会回显链接的设备

5.若是一切链接成功,请拔掉USB电缆,选择File->Build&Run,在编译以前要勾选上Development Build 和Script Debugging这两项(在build setting里面勾选不要忘记不然是不能调试的)电脑会自动编译文件并将APK推送至手机,在手机上赞成并安装。

6.当程序运行后再Monodevelop里面打开Run->Attach to process 会发现你手机的选项,选择手机,在脚本里面添加断点,你发现能够调试了,那叫一个爽!出现问题不再用去瞎猜,或者添加Debuglog了。

7.同时UnityEditor的Profiler也能够用了(要记得在上面输入IP)

8.若想打印安卓的log,则能够在CMD里输入adb logcat -s Unity -d > xxx.txt,“xxx.txt”为具体路径,其中Unity是过滤用的tag,unity中的全部输出都是“Unity”

:除了第5步要用USB才能装好,其余的都在WIFI环境下测试成功,并且貌似远程调试不能用USB,只能用WIFI?

 

38.安卓游戏拆包

有时候在玩一些外国厂商作的比较大型的手游的时候,常常会发现一个状况,就是安装包下来很小,通常也就20来M,而后还要下载一个很大的obb文件放到/SDcard/Android/obb/packName里,

那是由于一些平台限制上传的apk容量,因此开发者无奈只能将游戏的资源分包出来,而后再在游戏运行时下载下来。用压缩文件打开obb,会发现它其实也就是一个压缩文件,像U3D的游戏,里面存放

的通常是/assets/data/里的一些资源,其实咱们手动将obb里的资源解压后放入apk里,而后从新回编译apk,会发现也能够直接玩。

U3D拆包的作法:

勾上最后一项“Split Application Binary”就会将数据包和apk分离,而后在打开apk以后,进入选择下载或直接下载界面,将你的.obb文件从服务器或者其余地方下载下来

先把apk安装到Android设备,而后将对应obb文件更名为:main.<Bundle Version Code>.<包名>.obb

并拷贝到Android设备的“/android/obb/<包名>/ ”路径下。

如在Unity3D编辑中,你能够在工程设置的如图位置处,看到“Bundle Version Code”和包名(即“Bundle Identifier”).

假设“Bundle Version Code”值为2,包名为“com.Demo.ABC”:

- 首先,在Android设备上安装ABC.apk;- 接着,将ABC.obb更名为“main.2.com.Demo.ABC.obb”;

- 而后,将文件“main.2.com.Demo.ABC.obb”拷贝到Android设备的“/android/obb/com. Demo.ABC/”路径下;

- 启动App,你会发现新安装的APP已经能够正常使用了。

如何安装分包app安装包(apk+obb)
 
 

39.Smcs.rsp文件

能够在Unity Assets目录下建立smcs.rsp文件,并向其中添加预编译命令,其会在unity启动时执行,好比新建一个smcs.rsp文件,向其中添加内容:

-define:MYDEF

而后就能够在脚本中加入宏判断:

#if MYDEF
....
#endif

其原理是启动Unity时会执行unity目录下的smcs.exe文件并添加预编译命令,也能够经过cmd运行smcs.exe逐个添加预编译命令。
另外还有能够建立gmcs.rsp文件,对应Editor脚本中的预编译命令。

好比若是想要在C#语言中使用指针,必须标记为unsafe的,默认状况下unity中使用unsafe标记会报错,能够在项目中添加smcs.rsp文件并加入-unsafe预编译命令,就能够编译经过。

注:Unity5.6貌似将smcs.rsp改为smc.rsp

 

40.Unity代码的编译运行流程

早期:

  安卓:c# -> IL -> mono jit -> 机器码

  苹果:c# -> mono aot -> 机器码

如今

  安卓:c# -> IL -> mono jit -> 机器码

     c# -> IL2CPP -> 机器码

  苹果:c# -> IL2CPP -> 机器码

将来?

  安卓:c# -> IL -> LLVM jit -> 机器码

  苹果:c# -> IL -> LLVM aot -> 机器码

mono jit:在运行时,CLR动态将IL代码解释成机器码,而后运行

mono aot:在编译时,将IL代码解释成机器码,运行时直接执行机器码

il2cpp:在编译时,将IL代码转成C++代码,再将C++代码编译成机器码

llvm(一种中间件,mono跟.net core都有对应的实现):http://www.mono-project.com/docs/advanced/runtime/docs/llvm-backend/

之因此早期苹果用aot而如今换成il2cpp,是由于Unity的mono2.6只支持32位aot,而苹果已经限制32位应用了

将来颇有可能用微软的.net core方案,而后用llvm实现对ios平台的支持

 

41.Unity编辑器监听脚本重载事件

[InitializeOnLoad]
public class UnityScriptCompiling:AssetPostprocessor
{
    [UnityEditor.Callbacks.DidReloadScripts]
    static void AllScriptsReloaded()
    {
    }
}

 除此以外,编辑器还能监听其余时间,例如监听资源文件被打开:

 

42.Unity的RuntimeInitializeOnLoadMethod属性

Unity 5.0开始增长了RuntimeInitializeOnLoadMethodAttribute,这样就很方便在游戏初始化以前作一些额外的初始化工做,好比:Bulgy参数设置、SDK初始等工做。

用此属性修饰某个静态方法,则这个方法就会被Unity注册到系统中,游戏启动的时候,会自动检测并运行该方法,就算打包了游戏,依然会生效

注意:该方法是Unity注册的,也就是说,一个已经编译好的游戏,就算你反编译dll,并加入本身的静态方法并用上该属性,也不会生效

(注册方式,估计是经过名字,写在资源文件里了,看能不能找到是注册在哪一个文件里头的)

using UnityEngine;

public class Test
{
    [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]
    static void OnBeforeSceneLoadRuntimeMethod ()
    {
        Debug.Log("Before scene loaded");
    }

    [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.AfterSceneLoad)]
    static void OnAfterSceneLoadRuntimeMethod()
    {
        Debug.Log("After scene loaded");
    }

    [RuntimeInitializeOnLoadMethod]
    static void OnRuntimeMethodLoad()
    {
        Debug.Log("RuntimeMethodLoad: After scene loaded");
    }
}

 

43.Unity中提供的Attribute有不少,若是本身写程序扩展编辑器的功能,就须要了解这些属性。经常使用的有:

一、AddComponentMenu 导航栏菜单

二、ContextMenu 右键菜单

三、HeaderAttribute

四、HideInInspector 可让public变量在Inspector上隐藏,没法在Editor中进行编辑

五、MultilineAttribute 支持输入多行文本

六、RangeAttribute 限定输入值的范围

七、RequireComponent 组件依赖,使用该组件后自动添加依赖组件

八、RuntimeInitializeOnLoadMethodAttribute

九、SerializeField 强制对变量进行序列化,即便变量是private

十、SpaceAttribute 增长空位

十一、TooltipAttribute 提示信息,当鼠标移到Inspector上时显示相应的提示

十二、InitializeOnLoadAttribute 在启动Unity编辑器并打开项目的时候运行编辑器脚本

1三、InitializeOnLoadMethodAttribute

1四、MenuItem 导航栏的菜单项

 

44.Unity层级结构Hierarchy优化

Hierarchy Structure Guidelines

  • If something moves every frame, make sure all its children care about position. Only rendering, physics, audio, or core systems like that should be there.
  • When dynamically creating game objects at runtime, if they do not need to be children of the spawner for the above reasons, spawn things at the root of the scene.
  • You can easily register everything you spawn and pass along the ActiveInHeirarchy state of the spawner using OnEnable and OnDisable.
  • Try to group your moving transforms such that you have around 50 or so GameObjects per root. This lets the underlying system group your TransformChangeDispatch jobs into a fairly optimal amount of work per thread. Not so few that the thread overhead dominates; not so many that you are waiting on thread execution.
相关文章
相关标签/搜索