面试笔试杂项积累-Unity

1.update、Lateupdate和FixedUpdate

http://bbs.9ria.com/thread-417976-1-1.html

   MonoBehaviour.Update 更新

        MonoBehaviour启用时,其Update在每一帧被调用。


        MonoBehaviour.FixedUpdate固定更新

        当MonoBehaviour启用时,其 FixedUpdate在每一帧被调用。

        处理Rigidbody时,需要用FixedUpdate代替Update。例如:给刚体加一个作用力时,你必须应用作用力在FixedUpdate里的固定帧,而不是Update中的帧。(两者帧长不同)


        MonoBehaviour.LateUpdate 晚于更新        当Behaviour启用时,其LateUpdate在每一帧被调用。
        LateUpdate是在所有Update函数调用后被调用。这可用于调整脚本执行顺序。例如:当物体在Update里移动时,跟随物体的相机可以在LateUpdate里实现。



        UpdateFixedUpdate的区别:

        update跟当前平台的帧数有关,而FixedUpdate是真实时间,所以处理物理逻辑的时候要把代码放在FixedUpdate而不是Update.

        Update是在每次渲染新的一帧的时候才会调用,也就是说,这个函数的更新频率和设备的性能有关以及被渲染的物体(可以认为是三角形的数量)。在性能好的机器上可能fps 30,差的可能小些。这会导致同一个游戏在不同的机器上效果不一致,有的快有的慢。因为Update的执行间隔不一样了。

        而FixedUpdate,是在固定的时间间隔执行,不受游戏帧率的影响。有点想Tick。所以处理Rigidbody的时候最好用FixedUpdate。

        PS:FixedUpdate的时间间隔可以在项目设置中更改,Edit->ProjectSetting->time  找到Fixedtimestep。就可以修改了。

        

        

        UpdateLateUpdate的区别

        在圣典里LateUpdate被解释成一句话:LateUpdate是在所有Update函数调用后被调用。这可用于调整脚本执行顺序。例如:当物体在Update里移动时,跟随物体的相机可以在LateUpdate里实现。这句我看了云里雾里的,后来看了别人的解释才明白过来。

        LateUpdate是晚于所有Update执行的。例如:游戏中有2个脚步,脚步1含有Update和LateUpdate,脚步2含有Update,那么当游戏执行时,每一帧都是把2个脚步中的Update执行完后才执行LateUpdate 。虽然是在同一帧中执行的,但是Update会先执行,LateUpdate会晚执行。

        现在假设有2个不同的脚本同时在Update中控制一个物体,那么当其中一个脚本改变物体方位、旋转或者其他参数时,另一个脚步也在改变这些东西,那么这个物体的方位、旋转就会出现一定的反复。如果还有个物体在Update中跟随这个物体移动、旋转的话,那跟随的物体就会出现抖动。 如果是在LateUpdate中跟随的话就会只跟随所有Update执行完后的最后位置、旋转,这样就防止了抖动。

        做一个相机跟随主角的功能时,相机的位置调整写在LateUpdate(),老是不明白,看官方的SmoothFollow相机跟随写在Update()中


2.Destroy()与DestroyImmediate()的区别


DestroyImmediate立即对对像进行销毁;

Destroy销毁场景中的物体,但内存中还存在,当令它需要销毁时,只是给一个标识。而内存中它依然是存在的,只有当内存不够,或一段时间没有再次被引用时(或者更多合理的条件满足),机制才会将它销毁并释放内存。
这样做的目的就是为了避免频繁对内存的读写操作。回收器会定时清理一次内存中引用计数为0的对象,很可能你的要销毁的对象在其他地方还有引用而你自己不清楚,直接销毁可能导致其他地方空引用错误


3.定时器

//因此通过它可以让游戏对象按照一个常速进行旋转,而不是依赖于它的帧频:   
function Update()  
{  
   tranform.Rotate(0, 5 * Time.deltaTime, 0);  
}  
   
//同样地移动效果:   
function Update()  
{  
   transform.Translate(0, 0, 2 * Time.deltaTime);  
}  
   
//如果想要一个值根据每帧的变化而变化(增加或减少) ,你应该使用 Time.deltaTime来乘以这个值。这样才能使得变化的效果依赖于单位时间,而不是帧频。这不仅使得游戏的运行独立于帧频,也使得运动的效果符合现实。   
//同理,要让灯光的照射范围在每秒使半径增加 2个单位,可进行如下编码:   
function Update()  
{  
   light.range += 2.0 * Time.deltaTime;  
}  
   
//但是在通过force来处理rigidbody时,一般情况下不要乘以 Time.deltaTime,因为 Unity引擎已经为你进行了处理   
   
2  
IEnumerator wait(int seconds){  
  yield return new WaitForSeconds(seconds);  
 }  
StartCoroutine(wait(2));  // 在Update() 中调用好像无效   
//在JS中可以直接用 yield WaitForSeconds(3);   
   
//3 定时器的使用   
using UnityEngine;     
using System.Collections;     
public class NewBehaviourScript : MonoBehaviour {     
// Use this for initialization      
void Start () {     
   InvokeRepeating("LaunchProjectile", 1,5);//1秒后调用LaunchProjectile () 函数,之后每5秒调用一次      
}     
// Update is called once per frame      
void Update () {     
   if (Input.GetButton ("Fire")) {     
    CancelInvoke();     
   }     
}     
void LaunchProjectile () {     
   print("hello");     
}     
}    

4.协程

http://blog.csdn.net/huang9012/article/details/38492937

yield return是“停止执行方法,并且在下一帧从这里重新开始”

IEnumerator SayHelloFiveTimes()  
{  
    Yield return 0;  
    Debug.Log("Hello");  
    Yield return 0;  
    Debug.Log("Hello");  
    Yield return 0;  
    Debug.Log("Hello");  
    Yield return 0;  
    Debug.Log("Hello");  
    Yield return 0;  
    Debug.Log("Hello");  
}  
   
//This will do the exact same thing as the above function!  
IEnumerator SayHello5Times()  
{  
    for(inti = 0; i < 5; i++)  
    {  
        Debug.Log("Hello");  
        Yield return 0;  
    }  
}  

每一帧输出“Hello”,无限循环。。。

通过在一个while循环中使用yield,你可以得到一个无限循环的协程,这几乎就跟一个Update()循环等同。。。

    //Once started, this will run until manually stopped or the object is destroyed  
    IEnumerator SayHelloEveryFrame()  
    {  
        while(true)  
        {  
            //1. Say hello  
            Debug.Log("Hello");  
       
            //2. Wait until next frame  
            Yield return 0;  
       
        }//3. This is a forever-loop, goto 1  
    }  

	void Start () {
        StartCoroutine("loop");
	}
    IEnumerator loop()
    {
        while (true)
        {
            Debug.Log("aa");
                yield return new WaitForSeconds(1); 
        }

    }

计时

...不过跟Update()不一样的是,你可以在协程中做一些更有趣的事:

    IEnumerator CountSeconds()  
    {  
        int seconds = 0;  
       
        while(true)  
        {  
            for(float timer = 0; timer < 1; timer += Time.deltaTime)  
                Yield return 0;  
       
            seconds++;  
            Debug.Log(seconds +" seconds have passed since the Coroutine started.");  
        }  
    }  

这个方法突出了协程一个非常酷的地方:方法的状态被存储了,这使得方法中定义的这些变量都会保存它们的值,即使是在不同的帧中。还记得这个教程开始时那些烦人的计时器变量吗?通过协程,我们再也不需要担心它们了,只需要把变量直接放到方法里面!


开始和终止协程

        StartCoroutine("loop");
        StartCoroutine(loop());
        StopAllCoroutines();

。。。那我们怎么终止其中的一个协程呢?在这个例子里,这是不可能的,如果你想要终止某一个特定的协程,那么你必须得在开始协程的时候将它的方法名作为字符串,就像这样:

        StopCoroutine("loop");
<p align="left"><span><span class="comment">If you start a Coroutine by name...</span><span>  </span></span><span>
</span></p><p align="left"><span>StartCoroutine(<span class="string">"FirstTimer"</span><span>);  </span></span><span>
</span></p><p align="left"><span>StartCoroutine(<span class="string">"SecondTimer"</span><span>);  </span></span><span></span></p><p align="left"><span><span class="comment">//You can stop it anytime by name!</span><span>  </span></span><span>
</span></p><p align="left"><span>StopCoroutine(<span class="string">"FirstTimer"</span><span>); </span></span></p><p></p> 

协程的参数

抽象化一个协程的第一个方法是给它传递参数,协程作为一个函数方法来说,它自然能够传递参数。这里有一个协程的例子,它在特定的地方输出了特定的信息。


[csharp]  view plain copy
  1. Using UnityEngine;  
  2. Using System.Collections;  
  3.    
  4. Public class TimerExample : MonoBehaviour  
  5. {  
  6.     Void Start()  
  7.     {  
  8.         //Log "Hello!" 5 times with 1 second between each log  
  9.         StartCoroutine(RepeatMessage(5, 1.0f,"Hello!"));  
  10.     }  
  11.    
  12.     IEnumerator RepeatMessage(int count,float frequency,string message)  
  13.     {  
  14.         for(int i = 0; i < count; i++)  
  15.         {  
  16.             Debug.Log(message);  
  17.             for(float timer = 0; timer < frequency; timer += Time.deltaTime)  
  18.                 Yield return 0;  
  19.                
  20.         }  
  21.     }  
  22. }  


嵌套的协程

在此之前,我们yield的时候总是用0(或者null),仅仅告诉程序在继续执行前等待下一帧。协程最强大的一个功能就是它们可以通过使用yield语句来相互嵌套。

眼见为实,我们先来创建一个简单的Wait()程序,不需要它做任何事,只需要在运行的时候等待一段时间就结束。


[csharp]  view plain copy
  1. IEnumerator Wait(float duration)  
  2. {  
  3.     for(float timer = 0; timer < duration; timer += Time.deltaTime)  
  4.         Yield return 0;  
  5. }  


接下来我们要编写另一个协程,如下:


[csharp]  view plain copy
  1. Using UnityEngine;  
  2. Using System.Collections;  
  3.    
  4. Public class TimerExample : MonoBehaviour  
  5. {  
  6.     voidStart()  
  7.     {  
  8.         StartCoroutine(SaySomeThings());  
  9.     }  
  10.    
  11.     //Say some messages separated by time  
  12.     IEnumerator SaySomeThings()  
  13.     {  
  14.         Debug.Log("The routine has started");  
  15.         Yield return StartCoroutine(Wait(1.0f));  
  16.         Debug.Log("1 second has passed since the last message");  
  17.         Yield return StartCoroutine(Wait(2.5f));  
  18.         Debug.Log("2.5 seconds have passed since the last message");  
  19.     }  
  20.    
  21.     //Our wait function  
  22.     IEnumerator Wait(float duration)  
  23.     {  
  24.         for(float timer = 0; timer < duration; timer += Time.deltaTime)  
  25.             Yield return 0;  
  26.     }  
  27. }  


第二个方法用了yield,但它并没有用0或者null,而是用了Wait()来yield,这相当于是说,“不再继续执行本程序,直到Wait程序结束”。

现在,协程在程序设计方面的能力要开始展现了。

控制对象行为的例子

在最后一个例子中,我们就来看看协程如何像创建方便的计时器一样来控制对象行为。协程不仅仅可以使用可计数的时间来yield,它还能很巧妙地利用任何条件。将它与嵌套结合使用,你会得到控制游戏对象状态的最强大工具。

运动到某一位置

对于下面这个简单脚本组件,我们可以在Inspector面板中给targetPosition和moveSpeed变量赋值,程序运行的时候,该对象就会在协程的作用下,以我们给定的速度运动到给定的位置。


[csharp]  view plain copy
  1. usingUnityEngine;  
  2. Using System.Collections;  
  3.    
  4. Public class MoveExample : MonoBehaviour  
  5. {  
  6.     ublic Vector3 targetPosition;  
  7.     ublic float moveSpeed;  
  8.    
  9.     Void Start()  
  10.     {  
  11.         StartCoroutine(MoveToPosition(targetPosition));  
  12.     }  
  13.    
  14.     IEnumerator MoveToPosition(Vector3 target)  
  15.     {  
  16.         while(transform.position != target)  
  17.         {  
  18.             transform.position = Vector3.MoveTowards(transform.position, target, moveSpeed * Time.deltaTime);  
  19.             Yield return 0;  
  20.         }  
  21.     }  
  22. }  


这样,这个程序并没有通过一个计时器或者无限循环,而是根据对象是否到达指定位置来yield

按指定路径前进

我们可以让运动到某一位置的程序做更多,不仅仅是一个指定位置,我们还可以通过数组来给它赋值更多的位置,通过MoveToPosition() ,我们可以让它在这些点之间持续运动。


[csharp]  view plain copy
  1. Using UnityEngine;  
  2. Using System.Collections;  
  3.    
  4. Public class MoveExample : MonoBehaviour  
  5. {  
  6.     ublic Vector3[] path;  
  7.     ublic float moveSpeed;  
  8.    
  9.     Void Start()  
  10.     {  
  11.         StartCoroutine(MoveOnPath(true));  
  12.     }  
  13.    
  14.     IEnumerator MoveOnPath(bool loop)  
  15.     {  
  16.         do  
  17.         {  
  18.             foreach(var point in path)  
  19.                 Yield return StartCoroutine(MoveToPosition(point));  
  20.         }  
  21.         while(loop);  
  22.     }  
  23.    
  24.     IEnumerator MoveToPosition(Vector3 target)  
  25.     {  
  26.         while(transform.position != target)  
  27.         {  
  28.             transform.position = Vector3.MoveTowards(transform.position, target, moveSpeed * Time.deltaTime);  
  29.             Yield return 0;  
  30.         }  
  31.     }  
  32. }  
还加了一个布尔变量,你可以控制在对象运动到最后一个点时是否要进行循环。

Wait()程序加进来,这样就能让我们的对象在某个点就可以选择是否暂停下来,就像一个正在巡逻的AI守卫一样,这真是锦上添花啊!

注意事项:

  • l 在程序中调用StopCoroutine()方法只能终止以字符串形式启动(开始)的协程;
  • l 多个协程可以同时运行,它们会根据各自的启动顺序来更新;
  • l 协程可以嵌套任意多层(在这个例子中我们只嵌套了一层);
  • l 如果你想让多个脚本访问一个协程,那么你可以定义静态static的协程;
  • l 协程不是多线程(尽管它们看上去是这样的),它们运行在同一线程中,跟普通的脚本一样;
  • l 如果你的程序需要进行大量的计算,那么可以考虑在一个随时间进行的协程中处理它们;
  • l IEnumerator类型的方法不能带ref或者out型的参数,但可以带被传递的引用;
  • l 目前在Unity中没有简便的方法来检测作用于对象的协程数量以及具体是哪些协程作用在对象上。


5.优化-主动回收垃圾

给某个 GameObject 绑上以下的代码:

  function Update() { if(Time.frameCount % 50 == 0) { System.GC.Collect(); } }



6.动态加载资源的方式

1.Resources.Load()

可以直接load并返回某个类型的Object,前提是要把这个资源放在Resource命名的文件夹下,Unity不关有没有场景引用,都会将其全部打入到安装包中。Resources.Load();

Resources.Load是本地动态加载资源所用的方法,也就是你想动态加载的时候才用到它,比如子弹,特效,某些实时更换的图片什么的,建议此文件夹不要放太多东西,在打包的时候,它会单独把里面的所有东西都集中打包到一起,不管里面有没有你用的东西,所以大多数资源应该是自己建文件放置

2.AssetBundle

http://www.xuanyusong.com/archives/2405/

即将perfab资源打成 asset bundle 放在服务器或本地磁盘,然后使用WWW模块get 下来,然后从这个bundle中load某个object。AssetBundle

3.AssetDatabase.loadasset

http://www.mamicode.com/info-detail-312000.html

这种方式只在editor范围内有效,游戏运行时没有这个函数,它通常是在开发中调试用的

4.区别与利弊

Resources的方式需要把所有资源全部打入安装包,这对游戏的分包发布(微端)和版本升级(patch)是不利的,所以unity推荐的方式是不用它,都用bundle的方式替代,把资源达成几个小的bundle,用哪个就load哪个,这样还能分包发布和patch,但是在开发过程中,不可能没更新一个资源就打一次bundle,所以editor环境下可以使用AssetDatabase来模拟,这通常需要我们封装一个dynamic resource的loader模块,在不同的环境下做不同实现。


7.Unity3d提供了一个用于保存和读取数据的类

PlayerPrefs

用于本地持久化保存与读取的类,类似HashTable以键值对的形式将数据保存在文件中

Class Functions类函数

  • SetInt
    Sets the value of the preference identified by key.
    设置由key确定的偏好值。
  • GetInt
    Returns the value corresponding to key in the preference file if it exists.
    如果存在,返回偏好文件中key对应的值。
  • SetFloat
    Sets the value of the preference identified by key.
    设置由key确定的偏好值。
  • GetFloat
    Returns the value corresponding to key in the preference file if it exists.
    如果存在,返回偏好文件中key对应的值。
  • SetString
    Sets the value of the preference identified by key.
    设置由key确定的偏好值。
  • GetString
    Returns the value corresponding to key in the preference file if it exists.
    如果存在,返回偏好文件中key对应的值。
  • HasKey
    Returns true if key exists in the preferences.
    如果key在偏好中存在,返回true。
  • DeleteKey
    Removes key and its corresponding value from the preferences.
    从偏好中删除key和它对应的值。
  • DeleteAll
    Removes all keys and values from the preferences. Use with caution.
    从偏好中删除所有key。请谨慎使用。

8.Unity3d的生命周期

http://www.jianshu.com/p/1d93ece664e2

在Unity3D脚本中,有几个Unity3D自带的事件函数按照预定的顺序执行作为脚本执行。其执行顺序如下:

编辑器(Editor)

  • Reset:Reset函数被调用来初始化脚本属性当脚本第一次被附到对象上,并且在Reset命令被使用时也会调用。
    编者注:Reset是在用户点击Inspector面板上Reset按钮或者首次添加该组件时被调用。Reset最常用于在见识面板中给定一个默认值。

第一次场景加载(First Scene Load)
这些函数会在一个场景开始(场景中每个物体只调用一次)时被调用。

  • Awake:这个函数总是在任何Start()函数之前一个预设被实例化之后被调用,如果一个GameObject是非激活的(inactive),在启动期间Awake函数是不会被调用的直到它是活动的(active)。
  • OnEnable:只有在对象是激活(active)状态下才会被调用,这个函数只有在object被启用(enable)后才会调用。这会发生在一个MonoBehaviour实例被创建,例如当一个关卡被加载或者一个带有脚本组件的GameObject被实例化。

注意:当一个场景被添加到场景中,所有脚本上的Awake()和OnEable()函数将会被调用在Start()、Update()等它们中任何函数被调用之前。自然的,当一个物体在游戏过程中被实例化时这不能被强制执行。

第一帧更新之前(Before the first frame update)

  • Start:只要脚本实例被启用了Start()函数将会在Update()函数第一帧之前被调用。

对于那些被添加到场景中的物体,所有脚本上的Start()函数将会在它们中任何的Update()函数之前被调用,自然的,当一个物体在游戏过程中被实例化时这不能被强制执行。

在帧之间(In between frames)

  • OnApplicationPause:这个函数将会被调用在暂停被检测有效的在正常的帧更新之间的一帧的结束时。在OnApplicationPause被调用后将会有额外的一帧用来允许游戏显示显示图像表示在暂停状态下。

更新顺序(Update Order)

当你在跟踪游戏逻辑和状态,动画,相机位置等的时候,有几个不同的事件函数你可以使用。常见的模式是在Update()函数中执行大多数任务,但是也有其它的函数你可以使用。

  • FixedUpdate:FixedUpdate函数经常会比Update函数更频繁的被调用。它一帧会被调用多次,如果帧率低它可能不会在帧之间被调用,就算帧率是高的。所有的图形计算和更新在FixedUpdate之后会立即执行。当在FixedUpdate里执行移动计算,你并不需要Time.deltaTime乘以你的值,这是因为FixedUpdate是按真实时间,独立于帧率被调用的。
  • Update:Update每一帧都会被调用,对于帧更新它是主要的负荷函数。
  • LateUpdate:LateUpdate会在Update结束之后每一帧被调用,任何计算在Update里执行结束当LateUpdate开始时。LateUpdate常用为第三人称视角相机跟随。

渲染(Rendering)

  • OnPreCull:在相机剔除场景前被调用。剔除是取决于哪些物体对于摄像机是可见的,OnPreCull仅在剔除起作用之前被调用。
  • OnBecameVisible/OnBecameInvisible:当一个物体对任意摄像机变得可见/不可见时被调用。
  • OnPreRender:在摄像机开始渲染场景之前调用。
  • OnRenderObject:在指定场景渲染完成之后调用,你可以使用GL类或者Graphics.DrawMeshNow 来绘制自定义几何体在这里。
  • OnPostRender:在摄像机完成场景渲染之后调用。
  • OnRenderImage(Pro Only):在场景徐然完成之后允许屏幕图像后期处理调用。
  • OnGUI:为了响应GUI事件,每帧会被调用多次(一般最低两次)。布局Layout和Repaint事件会首先处理,接下来处理的是是通过
    Layout和键盘/鼠标事件对应的每个输入事件。
  • OnDrawGizmos:用于可视化的绘制一些小玩意在场景视图中。

协同程序(Coroutines)

正常的协同程序更新是在Update函数返回之后运行。一个协同程序是可以暂停执行(yield)直到给出的依从指令(YieldInstruction )完成,写成的不同运用:

  • yield:在所有的Update函数都已经被调用的下一帧该协程将持续执行。
  • yield WaitForSeconds:一段指定的时间延迟之后继续执行,在所有的Update函数完成调用的那一帧之后。
  • yield WaitForFixedUpdate:所有脚本上的FixedUpdate函数已经执行调用之后持续。
  • yield WWW:在WWW下载完成之后持续。
  • yield StartCoroutine:协同程序链,将会等到MuFunc函数协程执行完成首先。

销毁(When the Object is Destroyed)

  • OnDestory:这个函数在会在一个对象销毁前一帧调用,会在所有帧更新一个对象存在的最后一帧之后执行,对象也许会响应Object.Destroy 或一个场景关闭时被销毁。

退出游戏(When Quitting)
这些函数会在你场景中所有的激活的物体上调用:

  • OnApplicationQuit:这个函数在应用退出之前的所有游戏物体上调用,在编辑器(Editor)模式中会在用户停止PlayMode时调用,在网页播放器(web player)中会在网页视图关闭时调用。
  • OnDisable:当行为变为非启用(disable)或非激活(inactive)时调用。

脚本的生命周期流程图




9.MipMap贴图分级细化

Mipmap是一个功能强大的纹理技术,它可以提高渲染的性能以及提升场景的视觉质量。它可以用来解决使用一般的纹理贴图会出现的两个常见的问题:

  • 闪烁,当屏幕上被渲染物体的表面与它所应用的纹理图像相比显得非常小时,就会出现闪烁。尤其当相机和物体在移动的时候,这种负面效果更容易被看到。
  • 性能问题。加载了大量的纹理数据之后,还要对其进行过滤处理(缩小),在屏幕上显示的只是一小部分。纹理越大,所造成的性能影响就越大。加快渲染速度。

Mipmap就可以解决上面那两个问题。当加载纹理的时候,不单单是加载一个纹理,而是加载一系列从大到小的纹理当mipmapped纹理状态中。然后OpenGl会根据给定的几何图像的大小选择最合适的纹理。Mipmap是把纹理按照2的倍数进行缩放,直到图像为1x1的大小,然后把这些图都存储起来,当要使用的就选择一个合适的图像。这会增加一些额外的内存。在正方形的纹理贴图中使用mipmap技术,大概要比原先多出三分之一的内存空间。



10.异步加载场景

http://www.xuanyusong.com/archives/1427/


11.编程-鼠标拖动,滚轮缩放

鼠标拖动:

using UnityEngine;
using System.Collections;

public class Lu : MonoBehaviour
{
    public Camera cam;
    // We need to actually hit an object
    RaycastHit hitt = new RaycastHit();

    // Use this for initialization
    void Start()
    {

    }

    // Update is called once per frame
    void Update()
    {
        Ray ray = cam.ScreenPointToRay(Input.mousePosition);
        Physics.Raycast(ray, out hitt, 100);
        //    Debug.DrawLine(cam.transform.position, ray.direction,Color.red);
        if (null != hitt.transform)
        {
            print(hitt.point);//鼠标点击的坐标
        }
    }
}

滚轮缩放:

        if (Input.GetAxis("Mouse ScrollWheel") > 0)
        {
            Ray ray = cam.ScreenPointToRay(Input.mousePosition);
            Physics.Raycast(ray, out hit, 100);
            if (null != hit.transform)
            {
                hit.collider.gameObject.transform.localScale += new Vector3(0.1f, 0.1f, 0.1f);
            }
        }
        if (Input.GetAxis("Mouse ScrollWheel") < 0)
        {
            Ray ray = cam.ScreenPointToRay(Input.mousePosition);
            Physics.Raycast(ray, out hit, 100);
            if (null != hit.transform)
            {
                hit.collider.gameObject.transform.localScale -= new Vector3(0.1f, 0.1f, 0.1f);
            }
        }


12.点积与叉积

点积:cos

叉积(向量积):两个向量的叉积与这两个向量的和垂直

面试笔试杂项积累-Unity_第1张图片


13.位图与矢量图

前几日有同事总是不时的问我什么是矢量图什么是位图及它们之间有什么区别……今天咱们就共同的看看什么是矢量图什么是位图及它们之间的区别。

工具/原料

  • 矢量图素材

  • 位图素材

方法/步骤

  1.     1、先从概念说起:矢量图是根据几何特性来绘制图形,是用线段和曲线描述图像,矢量可以是一个点或一条线,矢量图只能靠软件生成,矢量图文件占用内在空间较小,因为这种类型的图像文件包含独立的分离图像,可以自由无限制的重新组合;

        位图图像也称为点阵图像,位图使用我们称为像素的一格一格的小点来描述图像。

    面试笔试杂项积累-Unity_第2张图片
  2.      2、最大的区别,矢量图形与分辨率无关,可以将它缩放到任意大小和以任意分辨率在输出设备上打印出来,都不会影响清晰度,而位图是由一个一个像素点产生,当放大图像时,像素点也放大了,但每个像素点表示的颜色是单一的,所以在位图放大后就会出现咱们平时所见到的马赛克状。

  3.     3、位图表现的色彩比较丰富,可以表现出色彩丰富的图象,可逼真表现自然界各类实物;而矢量图形色彩不丰富,无法表现逼真的实物,矢量图常常用来表示标识、图标、Logo等简单直接的图像。

    面试笔试杂项积累-Unity_第3张图片
  4.     4、位图的文件类型很多,如*.bmp、*.pcx、*.gif、*.jpg、*.tif、photoshop的*.psd等;

    矢量图形格式也很多,如AdobeIllustrator的*.AI、*.EPS和SVG、AutoCAD的*.dwg和dxf、Corel DRAW的*.cdr等。

    面试笔试杂项积累-Unity_第4张图片
  5.     5、由于位图表现的色彩比较丰富,所以占用的空间会很大,颜色信息越多,占用空间越大,图像越清晰,占用空间越大;由于矢量图形表现的图像颜色比较单一,所以所占用的空间会很小。

    面试笔试杂项积累-Unity_第5张图片
  6. 6

        6、经过软件矢量图可以很轻松的转化为位图,而位图要想转换为矢量图必须经过复杂而庞大的数据处理,而且生成的矢量图质量也会有很大的出入。


14.判断周围是否有敌人的三种模式


1、只攻击正前方的单位,向前发射一条射线,攻击碰到的单位
        RaycastHit hit;
        //range 射线的长度,即攻击范围,maskTarget敌方单位的mask,只攻击敌方单位
        if (Physics.Raycast(unit.thisT.position, unit.thisT.forward, out hit, range, maskTarget))
        {
            Unit targetTemp = hit.collider.gameObject.GetComponent();
            if (targetTemp != null && targetTemp.HPAttribute.HP > 0)
            {
                target = targetTemp;
                if (attackMode == _AttackMode.StopNAttack)
                {
                    if (attackMethod != _AttackMethod.Melee) unit.StopAnimation();
                    unit.StopMoving();
                }
            }
        }



2、以己方单位为圆心的某一半径长度内

        //返回相交球的所有碰撞体
        Collider[] cols = Physics.OverlapSphere(unit.thisT.position, range, maskTarget);
        //if(cols!=null && cols.Length>0)Debug.Log(cols[0]);
        if (cols.Length > 0)
        {
            Collider currentCollider = cols[Random.Range(0, cols.Length)];
            UnittargetTemp = currentCollider.gameObject.GetComponent();
            if (targetTemp != null && targetTemp.HPAttribute.HP > 0)
            {
                target = targetTemp;
                if (attackMode == _AttackMode.StopNAttack)
                {
                    if (attackMethod != _AttackMethod.Melee) unit.StopAnimation();
                    unit.StopMoving();
                }
            }
        }



3、以己方单位为圆心的扇形范围内
Collider[] cols=Physics.OverlapSphere(unit.thisT.position,range, maskTarget);
//if(cols!=null && cols.Length>0)Debug.Log(cols[0]);
if(cols.Length>0)
{
    Collider currentCollider=cols[0];
    foreach(Collider col in cols)
    {
        Quaternion targetRot=Quaternion.LookRotation(col.transform.position-unit.thisT.position);
        if(Quaternion.Angle(targetRot, unit.thisT.rotation))
        Unit targetTemp=currentCollider.gameObject.GetComponent();
        if(targetTemp!=null &&targetTemp.HPAttribute.HP>0)
        {
            target=targetTemp;
            if(attackMode==_AttackMode.StopNAttack)
            {
                if(attackMethod!=_AttackMethod.Melee)unit.StopAnimation();
                unit.StopMoving();
            }
            break;
        }
    }
}

15.Unity’s Rendering Pipeline

Shaders define both how an object looks by itself (its material properties) and how it reacts to the light. Because lighting calculations must be built into the shader, and there are many possible light & shadow types, writing quality shaders that “just work” would be an involved task. To make it easier, Unity has Surface Shaders, where all the lighting, shadowing, lightmapping, forward vs. deferred rendering things are taken care of automatically.

This document describes the pecularities of Unity’s lighting & rendering pipeline and what happens behind the scenes of Surface Shaders.

Rendering Paths

How lighting is applied and which Passes of the shader are used depends on which Rendering Path is used. Each pass in a shader communicates its lighting type via Pass Tags.

  • In Forward Rendering, ForwardBase and ForwardAdd passes are used.
  • In Deferred Shading, Deferred pass is used.
  • In legacy Deferred Lighting, PrepassBase and PrepassFinal passes are used.
  • In legacy Vertex Lit, Vertex, VertexLMRGBM and VertexLM passes are used.
  • In any of the above, to render Shadows or a depth texture, ShadowCaster pass is used.

Forward Rendering path

ForwardBase pass renders ambient, lightmaps, main directional light and not important (vertex/SH) lights at once. ForwardAdd pass is used for any additive per-pixel lights; one invocation per object illuminated by such light is done. See Forward Rendering for details.

If forward rendering is used, but a shader does not have forward-suitable passes (i.e. neither ForwardBase nor ForwardAdd pass types are present), then that object is rendered just like it would in Vertex Lit path, see below.

Deferred Shading path

Deferred pass renders all information needed for lighting (in built-in shaders: diffuse color, specular color, smoothness,world space normal, emission). It also adds lightmaps, reflection probes and ambient lighting into the emission channel. See Deferred Shading for details.

Legacy Deferred Lighting path

PrepassBase pass renders normals & specular exponent; PrepassFinal pass renders final color by combining textures, lighting & emissive material properties. All regular in-scene lighting is done separately in screen-space. See Deferred Lighting for details.

Legacy Vertex Lit Rendering path

Since vertex lighting is most often used on platforms that do not support programmable shaders, Unity can’t create multiple shader variants internally to handle lightmapped vs. non-lightmapped cases. So to handle lightmapped and non-lightmapped objects, multiple passes have to be written explicitly.

  • Vertex pass is used for non-lightmapped objects. All lights are rendered at once, using a fixed function OpenGL/Direct3D lighting model (Blinn-Phong)
  • VertexLMRGBM pass is used for lightmapped objects, when lightmaps are RGBM encoded (PC and consoles). No realtime lighting is applied; pass is expected to combine textures with a lightmap.
  • VertexLMM pass is used for lightmapped objects, when lightmaps are double-LDR encoded (mobile platforms). No realtime lighting is applied; pass is expected to combine textures with a lightmap.






















你可能感兴趣的:(面试笔试杂项积累-Unity)