##《Unity知识点》发布说明:程序员
++++这是立钻哥哥对Unity知识点的一个梳理和拓展,打造最权威的参考。算法
++++这里咱们碰到了一个问题,那就是从什么地方入手比较合适,做为这个问题,一般立钻哥哥就顺其天然了,因此,咱们这边就没有特别的顺序和章节归类。spring
##Unity知识点目录:数据库
#知识点0001:什么是协同程序?编程
#知识点0002:ArrayList和List的区别?设计模式
#知识点0003:MeshRender中material和sharedmaterial的区别?数组
#知识点0004:对象池(Object Pool)技术。安全
#知识点0005:链条关节(Hinge Joint)网络
#知识点0006:PlayerPrefs数据结构
#知识点0007:Unity3d脚本生命周期
#知识点0008:LOD技术
#知识点0001:什么是协同程序?
++++public Coroutine StartCoroutine(IEnumerator routine);
++++public extern void StopCoroutine(string methodName);
++++IEnumerator:
namespace System.Collections{
public interface IEnumerator{
object Current{ get; } //Properties
bool MoveNext(); //Methods
void Reset();
}
}
++++协程称为协同程序,即在主线程运行的同时开启另外一段逻辑,来协助当前程序的执行,StartCoroutine是开启协程,StopCoroutine是关闭协程,协程的返回值是IEnumerator类型,而在协程内部必须有yield return **。
++++IEnumerator用来记录执行到yield return的位置,每次执行协程时均是从当前位置向后执行,而不是从协程的最开始位置执行,除非只有一个yield return。
++++在一个主程序中开启多个协程。(开启协程就是开启一个线程,能够用来控制运动、序列以及对象的行为。)
++++关于协程:方法的返回值必须为IEnumerator。(协程不是多线程。)(协程中能够yield return各类值,其中包括开启另外一个协程。)(yield return 0,null,n的效果同样,不是等待多少帧。)
++++什么是协程:Unity的协程系统是基于C#的一个简单而强大的接口。(协程就是能够把一个方法拆分红屡次执行的一种接口。)
++++协程简单示例:
IEnumerator ShowTime(){
Debug.Log(“立钻哥哥Print: First Frame”); //第一帧执行
yield return 0; //等待下一帧
Debug.Log(“立钻哥哥Print: Second Frame”); //第二帧执行
yield return 0; //等待下一帧
Debug.Log(“立钻哥哥Print: Third Frame”); //第三帧执行
}
++++如何开启协程:
StartCoroutine(ShowTime()); //经过传入方法开启协程
StartCoroutine(“ShowTime”); //经过传入字符串类型的方式名称开启协程
++++如何中止协程:
StartCoroutine(“ShowTime”);
StopCoroutine(“ShowTime”); //中止协程
++++注意:StopCoroutine只能中止以字符串方式开启的协程。
++++Yield:当咱们“yield”一个方法时,至关于:“如今中止这个方法,而后在下一帧中从这里继续开始!”(用0或者null来yield的意思是告诉协程等待下一帧,直到继续执行为止。)
++++利用协程模拟Update功能:
//在Update调用后,每帧调用一次
IEnumerator MyUpdate(){
//死循环
while(true){
Debug.Log(“立钻哥哥Print: MyUpdate”);
//方法在这个地方挂起,等下一帧来了,方法从这个位置继续往下执行。
yield return 0;
}
}
++++yield以后还能够加一些其余有意思的东西:
--yield return new WaitForSecond(2f);
--yield return StartCoroutine(otherIEnumerator());
--...
++++利用协程制做定时器:
//时间间隔
float timeInterval = 2;
IEnumerator MyTimer(){
while(true){
yield return new WaitForSeconds(timeInterval);
Debug.Log(“立钻哥哥Print: 延迟2秒”);
}
}
++++协程注意事项:
--注意1:在程序中调用StopCoroutine()方法只能终止以字符串形式启动(开始)的协程。
--注意2:多个协程能够同时运行,它们会根据各自的启动顺序来更新。
--注意3:协程能够嵌套任意多层。
--注意4:协程不是多线程(尽管它们看上去是这样的),它们运行在同一线程中,跟普通的脚本同样。
--注意5:IEnumerator类型的方法不能带ref或者out类型的参数,但能够带被传递的引用。
++++协程,线程的区别:线程拥有本身独立的栈和共享的堆(共享堆,不共享栈),线程由操做系统调度。(协程和线程同样共享堆,不共享栈,协程由程序员在协程的代码里显示调度。)(协程避免了无心义的调度,由此能够提升性能,但程序员必须本身承担调度的责任,同时,协程也失去了标准线程使用多CPU的能力。)
++++协程优缺点:
--优势1:跨平台。
--优势2:跨体系架构。
--优势3:无需线程上下文切换的开销。
--优势4:无需原子操做锁定及同步的开销。
--优势5:方便切换控制流,简化编程模型。
--优势6:高并发+高扩展性+低成本:一个CPU支持上万的协程都不是问题,因此很适合用于高并发处理。
--缺点1:没法利用多核资源:协程的本质是个单线程,它不能同时将单个CPU的多个核用上,协程须要和进程配合才能运行在多CPU上。(固然咱们平常所编写的绝大部分应用都没有这个必要,除非是CPU密集型应用。)
--缺点2:进行阻塞(Blocking)操做(如IO时)会阻塞掉整个程序:这一点和事件驱动同样,可使用异步IO操做来解决。
++++Unity协程执行原理:unity中协程执行过程当中,经过yield return XXX,将程序挂起,去执行接下来的内容,注意协程不是线程,在为遇到yield return XXX语句以前,协程的方法和通常的方法是相同的,也就是程序在执行到yield return XXX语句以后,接着才会执行的是StartCoroutine()方法以后的程序,走的仍是单线程模式,仅仅是将yield return XXX语句以后的内容暂时挂起,等到特定的时间才执行。那么挂起的程序何时才执行,这就要看MonoBehavior的生命周期了:
++Unity3D的协程和C#线程之间的区别是什么?
++++多线程程序同时运行多个线程,而在任一指定时刻只有一个协程在运行,而且这个正在运行的协同程序只在必要时才被挂起。(除主线程以外的线程没法访问Unity3D的对象、组件、方法。)
++++Unity3d没有多线程的概念,不过Unity也给咱们提供了StartCoroutine(协同程序)和LoadLevelAsync(异步加载关卡)后台加载场景的方法。(LoadLevelAsync则容许咱们在后台加载新资源和场景,再利用前台loading条或动画提示玩家游戏未卡死,同时后台协同处理加载的事宜asynchronous.synchronous同步。(加载的进度条。))
++++StartCoroutine为何叫协同程序呢?所谓协同,就是当咱们在StartCoroutine的函数体里处理一段代码时,利用yield语句等待执行结果,这期间不影响主程序的继续执行,能够协同工做。
++Unity3D是否支持写成多线程程序?若是支持的话须要注意什么?
++++Unity支持多线程,若是同时处理不少事情或者与Unity的对象互动小能够用thread,不然使用coroutine。
++++注意1:虽然支持多线程,可是仅能从主线程中访问Unity3D的组件,对象和Unity3D系统调用,因此若是使用的话须要把组件中的数值传到开启的新线程中。
++++注意2:C#中有lock这个关键字,以确保只有一个线程能够在特定时间内访问特定的对象。
++Unity协程原理与线程的区别?
++++进程拥有本身的独立的堆和栈,既不共享堆,亦不共享栈,进程由操做系统调度。
++++线程拥有本身独立的栈和共享堆。(共享堆,不共享栈。)(线程亦由操做系统调度。)
++++协程和线程同样共享堆,不共享栈,协程由程序员在协程的代码里显示调度。
++++一个应用程序通常对应一个进程,一个进程通常有一个主线程,还有若干个辅助线程,线程之间是平行运行的,在线程里面能够开启协程,让程序在特定的时间内运行。
++++协程和线程的区别是:协程避免了无心义的调度,由此能够提升性能,但程序员必须本身承担调度的责任,同时,协程也失去了标准线程使用多CPU的能力。
++++Unity协程执行原理:Unity中协程执行过程当中,经过yield return XXX,将程序挂起,去执行接下来的内容。
++Unity协程使用指南
++++协程是程序组件来生成非抢占式多任务子函数,生成的子函数容许在程序里挂起和唤醒操做。
++++一般协程能够很方便实现延时操做,以及异步加载操做。
++++使用场景举例1:延时操做
void Start(){
StartCoroutine(MyIEWait());
}
IEnumerator MyIEWait(){
Debug.Log(“立钻哥哥Print: start time: ” + Time.time);
yield return new WaitForSeconds(1);
Debug.Log(“Second time: ” + Time.time);
yield return new WaitForSeconds(2);
Debug.Log(“Third time:” + Time.time);
}
++++使用场景举例2:异步加载资源
void Start(){
System.Action<string> myCallBack = delegate(string text){
Debug.Log(text);
};
StartCoroutine(MyIELoadRes(myCallBack));
}
IEnumerator MyIELoadRes(System.Action<string> callBack){
WWW myWww = new WWW(“http://www.VRunSoft.com”);
yield return myWww;
if(string.IsNullOrEmpty(myWww.error)){
callBack(myWww.text);
Debug.Log(“立钻哥哥Print: load success!”);
}else{
Debug.Log(“立钻哥哥Print: load failed!”);
}
}
#知识点0002:ArrayList和List的区别?
++++ArrayList:
namespace System.Collections{
public class ArrayList : IList, ICollections, IEnumerable, ICloneable{
....
public ArrayList(int capacity);
public ArrayList();
internal ArrayList(bool trash);
public ArrayList(ICollection c);
....
}
}
++++List:
namespace System.Collections.Generic{
public class List<T> : IList<T>, ICollection<T>, IEnumerable<T>, IList, ICollection, IEnumerable{
....
public List();
public List(int capacity);
public List(IEnumerable<T> collection);
....
}
}
++++ArrayList存在不安全类型的(ArrayList会把全部插入其中的数据都当作Object来处理)装箱拆箱操做。(List是泛型,能够指定特定的类型,避免过多的装箱拆箱操做,减小对内存的消耗。)
++++ArrayList是非泛型列表,存储数据时把全部的数据都当成object类型存储,存在装箱问题,取出来使用的时候存在拆箱问题,装箱拆箱会使性能变差,并且存在数据安全问题,可是优势在于可让值类型和引用类型相互转换。(List是泛型列表,在使用的时候才去定义数类型,泛型避免了拆箱装箱的问题,存入读取速度较快,类型也更安全。)
++++数组是一种高效的可是不太方便的数据存储方式,由于固定长度没法修改。(为了充分利用内存,就有了动态数组(ArrayList)的概念。)
++++C#中动态数组的实现就是集合接口IList。(ArrayList和List都继承了接口IList)
++++ArrayList对类型没有要求,是由于ArrayList中存储的类型都是object类型,而其余类型与object类型进行转换的过程就会产生拆箱装箱。(拆箱装箱是一笔不小的开销。)
++++List泛型在声明时就已经限制了存储内容的数据类型,因此不存在跟object类型的转换也就没有了装箱和拆箱的操做,而且是类型安全的。(平时使用时,若是只是为了使用动态数组,就不要考虑ArrayList了,推荐使用List)
++++List<T>类表示可经过索引访问的对象的强类型列表,提供用于对列表进行搜索、排序和操做的方法。
++ArrayList动态数组:
++++1、动态地增长和减小元素。
++++2、实现了ICollection和IList和IEnumerable接口。
++++3、灵活地设置数组的大小。
++++4、不安全的集合数组。
++++5、其元素为值类型时,效率不高(装箱和拆箱耗性能)。
++++ArrayList经常使用方法与描述:
--Add():将对象添加到ArrayList的结尾处。
--Insert():将元素插入ArrayList的指定索引处。
--Remove():从ArrayList中移除特定对象的第一个匹配项。
--RemoveAt():移除ArrayList的指定索引处的元素。
--Reverse():将整个ArrayList当中元素的顺序反转。
--Contains():肯定某元素是否在ArrayList中。
--Clear():从ArrayList中移除全部元素。
++List<T>泛型动态数组:
++++1、List类是ArrayList类的泛型等效类。
++++2、一样继承了IList接口,IEnumerator接口和ICollection。
++++3、与ArrayList不一样的是,声明集合时须要声明集合内部的数据类型,即T的类型。
++++4、安全的集合类型。
++++5、在处理值类型时处理速度比ArrayList快的多。
++++List<T>经常使用方法与描述:
--Add():将对象添加到List<T>的结尾处。
--Insert():将元素插入List<T>的指定索引处。
--Remove():从List<T>中移除指定对象的第一个匹配项。
--RemoveAt():移除List<T>指定索引处的元素。
--Reverse():将整个List<T>当中元素的顺序反转。
--Contains():肯定某元素是否在List<T>中。
--Clear():从List<T>中移除全部元素。
--IndexOf(T):搜索指定对象,并返回整个List<T>中第一个匹配项的从0开始的索引。
++++List:
namespace System.Collections.Generic{
public class List<T> : IList<T>, ICollection<T>, IEnumerable<T>, IList, ICollection, IEnumerable{
....
public int Capacity{ get; set; } //Properties
public int Count{ get; }
....
public T this[]{ get; set; } //Indexer
....
public List(); //Constructors
public List(int capacity);
public List(IEnumerable<T> collection);
....
public void Add(T item); //Methods
public void AddRange(IEnumerable<T> collection);
public void Clear();
public bool Contains(T item);
public void CopyTo(T[] array);
public void CopyTo(int index, T[] array, int arrayIndex, int count);
public void CopyTo(T[] array, int arrayIndex);
public T Find(Predicate<T> match);
public int FindIndex(int startIndex, int count, Predicate<T> match);
public int FindIndex(int startIndex, Predicate<T> match);
public int FindIndex(Predicate<T> match);
public List<T> GetRange(int index, int count);
public int IndexOf(T item, int index, int count);
public int IndexOf(T item);
public int IndexOf(T item, int index);
public void Insert(int index, object item);
public void InsertRange(int index, IEnumerable<T> collection);
public int LastIndexOf(T item);
public int LastIndexOf(T item, int index);
public int LastIndexOf(T item, int index, int count);
public int Remove(T item);
public int RemoveAll(Predicate<T> match);
public void RemoveAt(int index);
public void RemoveRange(int index, int count);
public void Reverse();
public void Reverse(int index, int count);
public void Sort(int index, int count, IComparer<T> comparer);
public void Sort(IComparer<T> comparer);
public void Sort();
public T[] ToArray();
....
}
}
++++Predicate<T>:
namespace System{
public delegate bool Predicate<T>(T obj);
}
++List拓展:下列代码在运行中会发生什么问题?如何避免?
List<int> ls = new List<int>(new int[]{ 1, 2, 3, 4, 5});
foreach(int item in ls){
Console.WriteLine(item * item);
ls.Remove(item);
}
++++产生运行时错误,在ls.Remove(item)这行,由于foreach是只读的。不能一边遍历一边修改。
++List(链表)和数组的区别在哪里?
++++从逻辑结构来看:
--数组必须事先定义固定的长度(元素个数),不能适应数据动态地增减的状况。当数据增长时,可能超出原先定义的元素个数;当数据减小时,形成内存浪费;数组能够根据下标字节存取。
--链表动态地进行存储分配,能够适应数据动态地增减的状况,且能够方便地插入、删除数据项。(数组中插入、删除数据项时,须要移动其余数据项,很是繁琐),链表必须根据next指针找到下一个元素。
++++从内存存储来看:
--(静态)数组从栈中分配空间,对于程序员方便快速,可是自由度小。
--链表从堆中分配空间,自由度大可是申请管理比较麻烦。
++++若是须要快速访问数据,不多或不插入和删除元素,就应该用数组。(若是须要常常插入和删除元素就须要用链表数据结构了。)
++常见的集合
++++常见的集合包括:非泛型集合和泛型集合。
++++非泛型集合(各类经常使用的System.Collection命名空间)的类:
--动态数组(ArrayList):它表明了可被单独索引的对象的有序结合。它基本上能够替代一个数组。可是,与数组不一样的是,咱们可使用索引在指定的位置添加和移除项目,动态数组会自动从新调整它的大小。它也容许在列表中进行动态内存分配、增长、搜索、排序各项。
--堆栈(Stack):它表明了一个后进先出的对象集合。当咱们须要对各项进行后进先出的访问时,则使用堆栈。(当咱们在列表中添加一项,称为推入元素,当咱们从列表中移除一项,称为弹出元素。)
--队列(Queue):它表明了一个先进先出的对象集合。当咱们须要对各项进行先进先出的访问时,则使用队列。(当咱们在列表中添加一项,称为入队,当咱们从列表中移除一项时,称为出队。)
--哈希表(HashTable):它使用键来访问集合中的元素。当咱们使用键访问元素时,则使用哈希表,并且咱们能够识别一个有用的键值。(哈希表中的每一项都有一个键/值对。键用于访问集合中的项目。)
++++泛型集合(各类经常使用的System.Collection.Generic命名空间)的类:
--Dictionary<TKey, Tvalue>:同哈希表同样,标示根据键进行的键值对的集合,不一样的是对键值进行了类型限定。
--List<T>:同ArrayList同样,标示经过索引访问对象的列表,不一样的是对存储的对象进行了类型限定。
--Stack<T>:同Stack同样,不一样的是对存储的对象进行了类型限定。
--Queue<T>:同Queue同样,不一样的是对存储的对象进行了类型限定。
++++常见集合和列表实现接口:
--IEnumerator<T>:这个接口定义了方法GetEnumerator(),返回一个实现了IEnumerator接口的枚举。若是将foreach语句用于集合,就须要实现该接口。
--ICollection<T>:ICollection<T>接口由泛型集合类实现。使用这个接口能够获取集合中的元素个数(count),把集合复制到数组中(CopyTo()),还能够添加和删除元素。
--IList<T>:IList<T>接口用于可经过位置访问其中的元素列表,这个接口定义了一个索引器,能够在集合指定位置插入或删除某些项。(IList<T>接口派生自ICollection<T>接口。)
--IDictionary<TKey,TValue>:IDictionary<TKey,TValue>接口由包含键和值的泛型集合类实现。使用这个接口能够访问全部的键和值。使用键类型的索引器能够访问某些项,还能够添加或删除某些项。
++List简单实例:请用C#实现一个函数,取出整型数组中的重复元素,相同的元素只保留一个。
++++代码参考:
int[] array = new int[]{ 6, 7, 8, 1, 8, 9, 5, 2, 4, 4, 7 };
List<int> myList = new List<int>();
for(int i = 0; i < array.Lenght; i++){
if(myList.Contains(array[i])){
continue;
}
myList.Add(array[i]);
}
++List简单实例:在一个int列表中插入1~10,10个整数,而后删除其中的9,5,2,7四个数。
++++代码参考:
List<int> myList = new List<int>(new int[10]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10});
List<int> deleList = new List<int>(new int[4]{9, 5, 2, 7});
myList.RemoveAll(delegate(int num){
return deleList.Contains(num);
});
++ArrayList的用法
++++ArrayList就是传说中的动态数组,就是Array的复杂版本,它提供了:动态的增长和减小元素;实现了ICollection和IList接口;灵活的设置数组的大小。
++++ArrayList例子:
ArrayList myArrList = new ArrayList();
for(int i = 0; i < 10; i++){
myArrList.Add(i); //给数组增长10个Int元素
}
myArrList.RemoveAt(5); //立钻哥哥:将第6个元素移除
for(int i = 0; i < 3; i++){
myArrList.Add(i + 20); //再增长3个元素
}
//返回ArrayList包含的数组
Int32[] values = (Int32[])myArrList.ToArray(typeof(Int32));
++用List作动态数组
++++有时咱们须要一个动态数组,例如但愿string[]可以动态添加数据。(这时可使用List集合,List集合能够动态添加元素,最后使用List.toArray()方法转成string[])
List<string> myToolNameList = new List<string>();
foreach(MyTool item in EquToolsList){
//根据条件筛选元素
if(item.type == “立钻哥哥”){
myToolNameList.Add(item.Name.ToString());
}
}
string[] toolNames = myToolNameList.ToArray();
#知识点0003:MeshRender中material和sharedmaterial的区别?
++++修改sharedMeterial将改变全部物体使用这个材质的外观,而且也改变存储在工程里的材质设置。不推荐修改由sharedMaterial返回的材质。(若是咱们想修改渲染器的材质,使用material替代。)
++++经过GetComponent<MeshRenderer>.material.color来改变颜色(材质的参数)。
++++经过GetComponent<MeshRenderer>.sharedmaterial.color来改变颜色(改变材质)。
++++MeshRenderer(网格渲染器)从MeshFiler(网格过滤器)得到几何形状,并根据Mesh进行渲染,而渲染所须要的贴图信息就来自于Material。
++++MeshRenderer的Material类型的变量有两个:material和sharedMaterial。
++++sharedMaterial是公用Material,全部用到这个材质的MeshRenderer都会引用这个Material。(改变sharedMaterial的属性也会改变mat文件。)
++++material是独立的Material,改变material的属性不会影响到其余对象,也不会影响mat文件。
++++当只修改材质的参数的时候,使用material属性,确保其余对象不会受影响。(当须要修改材质的时候,直接赋值给sharedMaterial,不然赋值给material会产生内存泄露。)
++++当使用Renderer.material的时候,每次调用都会生成一个新的material都内存中去,这在销毁物体的时候须要咱们手动去销毁该material,不然会一直存在内存中。(能够在场景替换的时候使用Resources.UnloadUnusedAssets去统一释放内存。)
++++当使用Render.sharedMaterial的时候并不会生成新的material,而是直接在原material上修改,而且修改后的设置就会被保存到项目工程中。(通常不推荐使用这个去修改,当某个材质球只被一个gameObject使用的时候可使用这个去修改,而且最好在修改以前把原属性设置保存,当使用完毕后当即恢复原设置,防止下次加载后的gameObject上还会残留以前的设置信息。)
++++若是是玩家主角这一类gameObject身上须要修改材质的属性或者shared属性比较多的时候,能够第一次使用material,这样能够动态的生成一个material的实例,而后再使用sharedMaterial,动态地修改这个新生成的material,并且不会建立新的material。
++MeshRender中material和Shader的区别?
++++MeshRender是模型渲染的组件,由此组件物体才能显示出来。
++++Material是材质球,实际就是shader的实例,并进行赋值,贴图、纹理、颜色等。
++++Shader是着色器,其实是一段程序,还能够用来实现一些仅靠贴图不容易实现的效果,如玻璃。
++++Shader大体分为:1、表面着色器;2、顶点和片元着色器;3、固定功能着色器。
++Render的做用?描述MeshRender和SkinnedMeshRender的关系与不一样?
++++Render是渲染器,渲染器可使物体显示在屏幕上。
++++MeshRender是网格渲染,SkinnedMeshRender是蒙皮网格渲染器。
++++Mesh就是指模型的网格(同名组件是用于调整网格属性的),MeshFilter通常是用于得到模型网格的组件,而MeshRender是用于把网格渲染出来的组件。
++Unity里的Mesh属性
++++Mesh是Unity内的一个组件,称为网格组件。
++++Mesh网格:是指模型的网格,建模就是建网格。(细看Mesh,能够知道Mesh的主要属性内容包括顶点坐标,法线,纹理坐标,三角形绘制序列等其余有用的属性和功能。)(建网格,就是画三角形;画三角形就是定位三个点。)
++++Mesh Filter网格过滤器:内包含一个Mesh组件,能够根据MeshFilter得到模型网格的组件,也能够为MeshFliter设置Mesh内容。
++++Mesh Render网格渲染器:是用于把网格渲染出来的组件。(MeshFilter的做用就是把Mesh扔给MeshRender将模型或者说是几何体绘制显示出来。)
++++Mesh、Mesh Filter、Mesh Render之间的关系大概就是:Unity中的对象就是GameObject,每一个GameObject均可以有一个MeshFilter组件(也能够没有),该组件又有Mesh属性(这个必定有),而该属性又有顶点坐标,法线等属性。(而若是GameObject里有MeshFilter,则必需要Mesh Render才能将此网格渲染出来,否则是看不见该网格的。)
++++Mesh的属性:顶点坐标(vertex)、法线(normal)、纹理坐标(uv)、三角形序列(triangle)
--【顶点坐标(Vertex)】:顶点坐标数组存放Mesh的每一个顶点的空间坐标,假设某mesh有n个顶点,则vertex的size为n。
--【法线(normal)】:法线数组存放mesh每一个顶点的法线,大小与顶点坐标对应,normal[i]对应顶点vertex[i]的法线。
--【纹理坐标(uv)】:它定义了图片上每一个点的位置的信息,这些点与3D模型是相互联系的,以决定表面纹理贴图的位置,UV就是将图像上每个点精确对应到模型物体的表面。(Ui[i]对应vertex[i])
--【三角形序列(triangle)】:每一个mesh都由若干个三角形组成,而三角形的三个点就是顶点坐标里的点,三角形的数组的size=三角形个数*3。
++Unity3D中MeshRenderer的使用
++++任何一个模型都是由许多网格面组成的,而面又是由许多三角形组成的。
++++建立网格面的核心就是为其添加2个组件:Mesh Renderer(网格渲染器)和Mesh Filter(网格过滤器)。
++++手动添加:选择一个游戏对象,而后【Component】=>【Mesh】=>【Mesh Filter】/【Mesh Renderer】
++++经过脚本实现:
gameObject.AddComponent<MeshFilter>(); //添加MeshFilter
gameObject.AddComponent<MeshRenderer>(); //添加MeshRenderer
mesh = GetComponent<MeshFilter>().mesh; //得到Mesh
++++mesh示例:
gameObject.AddComponent<MeshFilter>(); //添加MeshFilter
gameObject.AddComponent<MeshRenderer>(); //添加MeshRenderer
list = new List<Vector3>(); //new一个链表
mesh = GetComponent<MeshFilter>().mesh; //得到Mesh
GetComponent<MeshRenderer>().material.color = Color.green; //修改Mesh的颜色
//选择Mesh中的Shader
GetComponent<MeshRenderer>().material.shader = Shader.Find(“Transparent/Diffuse”);
mesh.Clear(); //清空全部点,用于初始化
++SkinnedMeshRenderer
++++相比MeshRenderer组件,SkinnedMeshRenderer多了bones组件。(MeshRenderer不支持骨骼动画,而SkinnedMeshRenderer支持骨骼动画。)
++++Mesh:指定mesh。
++++RootBone:指定哪一个是根节点。
++Unity换装系统
++++每个模型都有一个SkinnedMeshRenderer组件,改变该组件的材质,咱们就能够实现对特定部位的换装。
++++所谓的换装,表面上就是换掉mesh,但若是只是简单地替换mesh,就会出错。
++++SkinnedMeshRenderer示例:
public class ChangeFoot : MonoBehaviour{
private SkinnedMeshRenderer oldSmr = null;
private SkinnedMeshRenderer newSmr = null;
....
void ChangeFeet(){
//加载替换对象的资源文件
newObj = Resource.Load(“Prefab/newFoot”);
newInstance = Instantiate(newObj) as GameObject;
oldSmr = gameObject.GetComponentInChildren<SkinnedMeshRenderer>();
newSmr = newInstance.GetComponentInChildren<SkinnedMeshRenderer>();
....
//替换Mesh数据
oldSmr.bones = bones.ToArray();
oldSmr.sharedMesh = newSmr.sharedMesh;
oldSmr.sharedMaterial = newSmr.sharedMaterial;
//删除无用的对象
GameObject.DestroyImmediate(newInstance);
GameObject.DestroyImmediate(newSmr);
}
}
++++SkinnedMeshRenderer换装示例:
public class ChangeSkin : MonoBehaviour{
public Texture2D[] TextureEyes; //眼睛贴图
public Texture2D[] TextureFace1; //面部贴图-前
public Texture2D[] TextureFace2; //面部贴图-后
//与贴图对应的SkinnedMeshRenderer
SkinnedMeshRenderer MeshEyes;
SkinnedMeshRenderer MeshFace1;
SkinnedMeshRenderer MeshFace2;
void Start(){
//获取SkinnedMeshRenderer
MeshEyes = transform.Find(“eyes”).GetComponent<SkinnedMeshRenderer>();
MeshFace1 = transform.Find(“face-1”).GetComponent<SkinnedMeshRenderer>();
MeshFace2 = transform.Find(“face-2”).GetComponent<SkinnedMeshRenderer>();
}
void OnGUI(){
if(GUILayout.Button(“显示外装1”, GUILayout.Height(30))){
SetSkin(MeshEyes, TextureEyes[0]);
SetSkin(MeshFace1, TextureFace1[0]);
SetSkin(MeshFace2, TextureFace2[0]);
}
if(GUILayout.Button(“显示外装2”, GUILayout.Height(30))){
SetSkin(MeshEyes, TextureEyes[1]);
SetSkin(MeshFace1, TextureFace1[1]);
SetSkin(MeshFace2, TextureFace2[1])
}
}
private void SetSkin(SkinnedMeshRenderer mRenderer, Texture2D mTexture){
mRenderer.material.mainTexture = mTexture;
}
}
--提供两套外装,把脚本拖放到模型上,而后编辑贴图数组:
#知识点0004:对象池技术。
++++有一个池子里都是对象。
++++对象池就是存放须要被反复调用资源的一个空间,当一个对象会大量生产的时候若是每次都销毁建立会浪费时间,经过对象池把暂时不用的对象放到一个池中(也就是一个集合),当下次要从新生成这个对象的时候先去池中查找一下是否有可用的对象,若是有的话就直接拿出来使用,不须要再建立,若是池中没有可用的对象,才须要从新建立,利用空间换时间来达到游戏的高速运行效果。
++++在FPS游戏中常被大量复制的对象包括:子弹、敌人、粒子等。
++++当游戏中须要频繁建立一个物体对象时,须要作一个pool,游戏开始时预先实例化足够的数量,而后用的时候取,不用的时候收回。
++++对象池的优势:复用池中的对象,没有分配内存和建立堆中对象的开销,没有释放内存和销毁堆中对象的开销,进而减小垃圾收集器的负担,避免内存抖动;没必要重复初始化对象状态,对于比较耗时的构造和析构来讲很是合适。
++++对象池的缺点:因为池中的对象的数量有限,势必成为一个可伸缩性瓶颈。(很难正确地设定对象池的大小,若是过小则起不到做用,若是过大,则占用内存资源高。)(设计和使用对象池容易出错,设计上须要注意状态同步这是个难点,使用上可能存在忘记归还。)
++Unity对象池理解与简单应用
++++对象池用于减小内存开销,其原理就是把可能用到的对象,先存放在一个池中,用的时候就调出来,不用就放回去。(而不是要用的时候建立,不用的时候销毁。)
++++子弹对象池类(class BulletsPool)示例:
//立钻哥哥:对象池简单应用:子弹对象池类
using System.Collections.Generic;
using UnityEngine;
public class BulletsPool : MonoBehaviour{
public static BulletsPool bulletsPoolInstance; //子弹池单例
public GameObject bullectObj; //子弹perfabs
public int pooledAmout = 5; //子弹池初始大小
public bool lockPoolSize = false; //是否锁定子弹池大小
private List<GameObject> pooledObjects; //子弹池链表
private int currentIndex = 0; //当前指向链表位置索引
void Awake(){
bulletsPoolInstance = this; //把本对象做为实例
}
void Start(){
pooledObjects = new List<GameObject>(); //初始化链表
for(int i = 0; i < pooledAmout; ++i){
GameObject obj = Instantiate(bulletObj); //建立子弹对象
obj.SetActive(false); //设置子弹无效
pooledObjects.Add(obj); //把子弹添加到链表(对象池)
}
}
//立钻哥哥:获取对象池中可使用的子弹
public GameObject GetPooledObject(){
//把对象池遍历一遍
for(int i = 0; i < pooledObjects.Count; ++i){
//这里简单优化了一下:每一次遍历都是从上一次被使用的子弹的下一个,而不是每次遍历从0开始:例如上一次获取了第4个子弹,currentIndex就为5,这里从索引5开始遍历,这是一种贪心算法
int temI = (currentIndex + i) % pooledObjects.Count;
//判断该子弹是否在场景中激活
if(!pooledObjects[temI].activeInHierarchy){
currentIndex = (temI + 1) % pooledObjects.Count;
return pooledObjects[temI]; //找到没有被激活的子弹并返回
}
}
//若是遍历完一遍子弹库发现没有可用的,就新建立一个
if(!lockPoolSize){
GameObject obj = Instantiate(bullectObj);
pooledObjects.Add(obj);
return obj;
}
//若是遍历完没有并且锁定了对象池大小,返回空
return null;
}
}
++++自动发射子弹类(class AutoFire)代码参考:
//立钻哥哥:自动发射子弹类
using UnityEngine;
pubic class AutoFire : MonoBehaviour{
//public GameObject shotObj; //传统建立子弹方法须要的子弹perfabs
public GameObject shotSpawn; //子弹发射的初始化位置
public float fireRate = 0.2f; //每次发射子弹时间间隔
private float nextFire; //下一次发射子弹的时间
void Update(){
//能够发射子弹
if(Time.time > nextFire){
nextFire = Time.time + fireRate;
//传统建立子弹方法
//Instantiate(shotObj, shotSpawn.transform.position, shotSpawn.transform.rotation);
//获取对象池中的子弹
GameObject bullet = BulletsPool.bulletsPoolInstance.GetPooledObject();
if(bullect != null){
bullect.SetActive(true); //激活子弹并初始化子弹的位置
bullect.transform.position = shotSpawn.transform.position;
}
}
}
}
++++子弹失效,回收子弹类(class DestroyByBoundary):
//立钻哥哥:子弹失效,回收子弹类
using UnityEngine;
public class DestroyByBoundary : MonoBehaviour{
//判断是否出界,这个类放在场景的一个长方体里
void OnTriggerExit(Collider other){
//Destroy(other.gameObject); //传统方法,直接删除子弹
other.gameObject.SetActive(false); //对象池方法,把子弹失效就行了
}
}
++简单易懂的对象池
++++立钻哥哥:对象池,就是一个池子里都是对象。
++++对象池ObjectPool
public static Dictionary<string, GameObject> dic = new Dictionary<string, GameObject>();
--建立一个Dictionary用来保存全部的对象(Dictionary只是一个用于存储的键值对)
++++1、运行过程当中获取对象:
GameObject obj = dic[objectstring]; //立钻哥哥:获取存放的对象
++++2、将对象实例化出来并激活:
Instantiate(obj, position) as GameObject;
++++3、将实例化出来的对象从对象池中移除:
dic.RemoveAt(ObjectIndex);
++++4、若是对象在场景中被销毁,则会从新添加至对象池中.
++Unity对象池(单例对象池和泛型对象池)
++++立钻哥哥:对象池的意义是:将游戏中反复建立销毁的对象进行屡次利用,从而避免大量对象的销毁与建立而形成CPU的负担。
++++对象池的缺点:占用了更多的内存。(在移动平台上针对游戏的优化基本偏向于牺牲空间换取时间。)
++++1、泛型非单例对象池(class YanlzObjPool)
using UnityEngine;
using System.Collections;
using System;
using System.Collections.Generic;
//立钻哥哥:泛型非单例池(代码简洁,通用性强)
public class YanlzObjPool<T> where T : class{
private Action<T> mReset; //重置对象的委托
private Func<T> nNew; //建立新对象的委托
private Stack<T> stack; //存放对象的池子,用List等动态数组也能够,推荐泛型数组
public YanlzObjPool(Func<T> mNew, Action<T> mReset = null){
this.mNew = mNew;
this.mReset = mReset;
stack = new Stack<T>();
}
//立钻哥哥:从池子中获取对象的方法(思路:若池子的数量为0,则调用建立新对象委托建立一个对象返回,不然从池子中拿出一个对象并返回)
pubic T New(){
if(stack.Count == 0){
T t = mNew();
return t;
}else{
T t = stack.Pop();
if(mReset != null){
mReset(t);
}
return t;
}
}
//立钻哥哥:将销毁的对象存入池子
public void Store(T t){
stack.Push(t);
}
//立钻哥哥:清空池子
public void Clear(){
stack.Clear();
}
}
++++2、泛型对象池应用(YanlzObjPoolTest)
using UnityEngine;
using System.Collections;
//立钻哥哥:让咱们来测试一下这个泛型对象池
public class MyObjPoolTest : MonoBehaviour{
public GameObject bullet; //子弹的预设体
private YanlzObjPool<GameObject> pool;
void Start(){
pool = new YanlzObjPool<GameObject>(NewBullet, Reset);
}
//立钻哥哥:实例化新子弹
private GameObject NewBullet(){
GameObject go = Instantiate(bullet) as GameObject; //实例化新子弹
return go;
}
//立钻哥哥:重置对象的方法
private void Reset(GameObject go){
go.transform.position = Vector3.zero;
go.transform.rotation = Quaternion.identity;
go.SetActive(true); //从池子中取出后将物体设为可见
}
//立钻哥哥:销毁对象
private void Destroy(GameObject go){
go.SetActive(false); //放入池子前将物体设为不可见
pool.Store(go);
}
}
++++3、单例对象池(YanlzSingletonPool)
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using System;
//立钻哥哥:单例对象池
public class YanlzSingletonPool{
#region 单例
private static YanlzSingletonPool instance = null;
private YanlzSingletonPool(){ }
public static YanlzSingletonPool GetInstance(){
if(instance == null){
instance = new YanlzSingletonPool();
}
return instance;
}
#endregion
private Dictionary<string, List<GameObject>> poolDic; //存放池子的字典
private Action<GameObject> mReset; //立钻哥哥:重置对象的委托
private Func<GameObject> mNew; //立钻哥哥:建立新对象的委托
//立钻哥哥:从对应字符串池子中取出对象
public GameObject New(string str, Func<GameObject> mNew, Action<GameObject> mReset = null){
//立钻哥哥:若是字典存在该字符串,取出该池
if(poolDic.ContainsKey(str)){
//若是池子里有对象则取出一个对象并返回
if(poolDic[str].Count > 0){
GameObject go = poolDic[str][0];
poolDic[str].Remove(go);
if(mReset != null){
mRest(go);
}
return go;
}else{
//立钻哥哥:若是池子对象则返回一个新建立的对象
return mNew();
}
}else{
//立钻哥哥:若是字典不存在该字符串则新建立一个池子,并返回对象
poolDic.Add(str, new List<GameObject>());
return mNew();
}
}
//立钻哥哥:销毁的对象存入池子
public void Store(string str, GameObject go){
if(poolDic.ContainsKey(str)){
poolDic[str].Add(go);
}
}
//立钻哥哥:销毁对象池的对象
public void DestroyPool(stirng str){
if(poolDic.ContainsKey(str)){
poolDic.Remove(str);
}
}
}
#知识点0005:链条关节(Hinge Joint)
++++链条关节(Hinge Joint):能够模拟两个物体间用一根链条链接在一块儿的状况,能保持两个物体在一个固定距离内部相互移动而不产生做用力,可是达到固定距离后就会产生拉力。
++++Joint属于Unity3D里面的一种物理组件(Component):是模拟物体与物体之间的一种链接关系。
++++说明1:使用“Break Force”可设置关节断裂的力,一旦力超过它,关节将会断裂。(断裂时,经过onjointbreakforce方法可监听相关事件。)
++Unity3d关节物体链接方式
++++立钻哥哥:Joint链接方式:Hinge Joint(链条链接)、Fixed Joint(固定链接)、spring Joint(弹簧链接)、Character Joint(角色关节链接)、Configurable Joint(可配置链接)等。
++++【Hinge Joint(链条链接)】:能够模拟两个物体用一根链条链接在一块儿的状况,能保持两个物体在一个固定距离内部相互移动而不产生做用力,可是达到固定距离后就会产生拉力。(两个物体间连根链条。)(立钻哥哥:将两个物体以链条的形式绑在一块儿,当力量大于链条的固定力矩时,两个物体就会产生相互的拉力,同时铰链关节是由两个刚体组成,约束它们像连在一个铰链上同样运动,适用于:门。)
++++【Fixed Joint(固定链接)】:模拟两个物体间存在一根杆子,固定了两个物体的相对位置和相对朝向。(立钻哥哥:将两个物体永远以相对的位置固定在一块儿,即便发生物理改变,它们之间的相对位置也将不变,同时固定关节基于另外一个物体来限制一个物体的运动。效果相似于父子关系,但不经过层级变换,而经过物理实现的。适用场景:让两个没有父子关系的物体一块儿运动。)
++++【spring Joint(弹簧链接)】:模拟两个物体间有一根弹簧,大于或者小于固定位置的时候产生相对弹力,根据弹性系数距离偏移越大做用力越大。(立钻哥哥:将两个物体以弹簧的形式绑定在一块儿,挤压它们会获得向外的力,拉伸它们将获得向里的力,像被弹簧链接着一块儿运动。)
++++【Character Joint(角色关节链接)】:模拟人体骨头间的关系链接,就是两个物体能根据一个关键点自由的朝一个方向旋转,但固定在一个相对距离,并且能够设置关节的控制。(能够用在蒙皮骨骼模型上作活动关节,这样就能够作到不少游戏引擎里那种各类自由姿式的死法了。)(立钻哥哥:角色关节主要用于实现布娃娃效果。角色关节是扩展的球关节,能够用于限制关节在不一样旋转轴下的旋转角度。)
++++【Configurable Joint(可配置链接)】:万能链接方式,经过配置很是多的参数和限制,咱们能够作到能想到的任何物体与物体间的链接方式,包括上面全部的链接方式,固然配置起来是比较复杂的。(立钻哥哥:能够模拟任意关节的效果,同时可配置关节将PhysX引擎中全部与关节相关的属性都设置为可配置的,所以能够用此组件创造出与其余关节类型行为相关的关节。)
++++Unity Joint关节的简单示例代码:
//立钻哥哥:Joint关节的简单示例
using UnityEngine;
using System.Collections;
public class YanlzDemoJoint : MonoBehaviour{
Component jointComponent = null; //立钻哥哥:关节组件
Rigidbody connectObjComponent = null; //要链接物体的刚体组件
Rigidbody rigidbodyComponent = null; //自身的刚体组件
void Start(){
connectObjComponent = GameObject.FindWithTag(“ConnectObj”).rigidbody;
rigidbodyComponent = gameObject.rigidbody;
connectObjComponent.useGravity = false;
rigidbodyComponent.useGravigy = false;
}
//立钻哥哥:GUI事件
void OnGUI(){
if(GUILayout.Button(“添加链条关节”)){
YanlzResetJoint();
jointComponent = gameObject.AddComponent(“HingeJoint”);
HingeJoint hjoint = (HingeJoint)jointComponent;
hjoint.connectedBody = connectedObjComponent;
connectedObjComponent.useGravity = true;
rigidbodyComponent.useGravity = true;
}
if(GUILayout.Button(“添加固定关节”)){
YanlzResetJoint();
jointComponent = gameObject.AddComponent(“FixedJoint”);
FixedJoint fjoint = (FixedJoint)jointComponent;
connectedObjComponent.useGravity = true;
rigidbodyComponent.useGravity = true;
fjoint.connectedBody = connectedObjComponent;
}
if(GUILayout.Button(“添加弹簧关节”)){
YanlzResetJoint();
jointComponent = gameObject.AddComponent(“SpringJoint”);
SpringJoint sjoint = (SpringJoint)jointComponent;
connectedObjComponent.useGravity = true;
rigidbodyComponent.useGravity = true;
sjoint.connectedBody = connectedObjComponent;
}
if(GUILayout.Button(“添加角色关节”)){
YanlzResetJoint();
jointComponent = gameObject.AddComponent(“CharacterJoint”);
CharacterJoint cjoint = (CharacterJoint)jointComponent;
connectedObjComponent.useGravity = true;
rigidbodyComponent.useGravaity = true;
cjoint.connectedBody = connectedObjComponent;
}
if(GUILayout.Button(“添加可配置关节”)){
YanlzResetJoint();
jointComponent = gameObject.AddComponent(“ConfigurableJoint”);
ConfigurableJoint cojoint = (ConfigurableJoint)jointComponent;
connectedObjComponent.useGravity = true;
rigidbodyComponent.usGravity = true;
cojoint.connectedBody = connectedObjComponent;
}
}
//立钻哥哥:重置关节
void YanlzResetJoint(){
//立钻哥哥:销毁以前添加的关节组件。
//亦能够:jointComponent.active = false.
Destroy(jointComponent);
this.transform.position = new Vector3(2.431f, 8.388f, -7.374f); //位置复原
connectedObjComponent.transform.position = new Vector3(0.604f, 7.388f, -7.374f);
connectedObjComponent.useGravity = false; //取消使用重力
rigidbodyComponent.useGravity = false;
}
}
++Unity3D铰链关节的简单示例
++++立钻哥哥:在Unity物理引擎中,刚体关节组件能够把两个刚体链接起来。(例如:把刚体车轮与动力学汽车的刚体底盘链接起来以实现一块儿移动的目的。)
++++铰链关节Hinge Joint的使用:现实世界中铰链关节常见应用有门上的折页,经过折页能够把门固定在墙上或门框上,而门经过折页上的轴进行旋转。
++++选择组件Component的Physics的Hinge Joint来添加相应关节(会自动添加刚体属性,由于铰链关节需添加在一个刚体上。)
++++经常使用属性:Connected Body、Anchor、Motor、Limits、Break Force、Spring等:
--【Connected Body】:设置关节所链接的刚体,在选中其余刚体后,其余刚体将控制关节及底下的刚体的运动。(此刚体会链接在设置的刚体上)(若是此项不选择对象,则关节以及关节下刚体都将链接在世界物体上。)
--【Anchor】:锚点,设置关节在刚体中的位置。
--【Motor】:动力引擎,可添加力Force和速度Target Velocity。
--【Limits】:限制项。
--【Break Force】:最大力。
--【Break Torque】:最大扭矩。
--【Spring】:至关于在刚体中加弹簧。
++铰链关节(Hinge Joint)(Unity物理引擎)
++++铰链关节由两个刚体组成。(该关节会对刚体进行约束。使得它们好像被链接在一个铰链上那样运动。)(铰链关节由两个刚体组成,约束它们像连在一个铰链上同样运动,适合于门。)
++++铰链关节检视面板:
++++【Connected Body】:链接刚体(链接体)。(用于为关节指定要链接的刚体,若不指定则该关节将与世界相连。)(为刚体指定的关节链接物,如不设定,则与世界相连。)
++++【Anchor】:锚点。(刚体能够围绕锚点进行摆动,这里能够设置锚点的位置,该值应用于局部坐标系。)(主体摇摆围绕的锚点坐标,基于本地坐标系。)
++++【Axis】:轴(坐标轴)。(定义了刚体移动的方向,该值应用于局部坐标系。)(摆动方向的坐标,基于本地坐标系。)
++++【Use Spring】:使用弹簧(是否使用弹簧)。(勾选该项,则弹簧会使得刚体与其链接的主体造成一个特定的角度。)(弹簧使刚体相对它链接的主体达到一个特定的角度。)
--Spring:弹簧。(用于如激活Use Spring的弹簧属性。)
{
Spring:弹簧力。(维持对象移动到必定位置的力。)
Damper:阻尼。(值越大,对象移动越慢)
Target Position:目标角度。(弹簧的目标角度。弹簧拉向这个角度,以角为单位。)
}
++++【Use Motor】:使用马达(是否使用马达)。(Motor使对象旋转。)
--Motor:马达。(用于如激活Use Motor的马达属性。)
{
Target Velocity:目标速度。(对象设法达到的速度。)
Force:做用力。(用于达到目标速度的力。)
Free Spin:自由转动。(若是启用,Motor永远不会破坏旋转,只会加速。)
}
++++【Use Limit】:使用限制。(若是启用,铰链的角度将被限制在最大和最小之间。)
--Limit:限制。(用于如启用Use Limits的边界属性。)
{
Min:最小值。(rotation能达到的最小角度。)
Max:最大值。(rotation能达到的最大角度。)
Min Bounce:最小弹跳。(物体碰触最小限制时的弹跳值。)
Max Bounce:最大弹跳。(物体碰触最大限制时的弹跳值。)
}
++++【Break Force】:破坏力。(容许这个铰链破损的力。)
++++【Break Torque】:破坏扭矩。(容许这个铰链破损的扭矩。)
++++细节1:单独的铰链关节要连在游戏对象上。(铰链会绕着Anchor属性指定的点,沿着指定的Axis属性方向移动。)(不须要给关节的Connected Body属性分配游戏对象。只有但愿关节的Transform依赖附加对象的Transform时,才须要分配游戏对象给Connected Body属性。)
++++细节2:门的铰链如何工做。(Axis在这种状况下是向上的,沿Y轴正向。)(Anchor在门和墙交点的某处。)(不须要指定墙给Connected Body,由于关节默认会和世界相连。)
++++细节3:狗门铰链。(狗门的Axis应该是侧向的,沿着X轴的正向。)(正门应该指定为Connected Body,这样狗门的铰链即依赖于正门的RigidBody。)
++++链条(Chains):多个铰链关节也能够串联来变成一条链条。(给链条的每一环添加一个关节,并附加下一环为其Connected Body。)
++++提示1:不须要指派Connected Body来使关节运转。
++++提示2:用Break Force来制做动态破坏的系统。(这确实很酷,可让玩家用火箭发射器爆破或者用疾驰的汽车撞,来把门从铰链处弄碎。)
++++提示3:调整Spring、Motor和Limits属性能够改变关节的做用。
++铰链使用的一些问题:
++++问题1:添加铰链组件,最好铰链的anchor不要设置在collider内部,不然铰链的行为会不正常。(好比:在一个柜子(box collider)里的一个物体添加configurableJoint(anchor在柜子boxcollider里),只容许一个轴的limited,其余5个locked,在VR中用手柄去拉物体,铰链的约束会跳动。(诡异:若是去掉外部的box collider,则正常。))
#知识点0006:PlayerPrefs
++++PlayerPrefs:Unity3d提供了一个用于本地持久化保存与读取的类。(工做原理很是简单,以键值对的形式将数据保存在文件中,而后程序能够根据这个名称取出上次保存的数值。)
++++PlayerPrefs的存储机制是“Key-Value”。(可存储类型:int、float、string)
++++PlayerPrefs类支持3种数据类型的保存和读取:整型、浮点型、字符串型。
--整型:SetInt()、GetInt();
--浮点型:SetFloat()、GetFloat();
--字符串型:SetString()、GetString();
++++PlayerPrefs类提供方法:DeleteKey(string key)、DeleteAll()、HasKey(string key)等。
--PlayerPrefs.DeleteKey(string key):删除指定数据;
--PlayerPrefs.DeleteAll():删除所有键;
--PlayerPrefs.HasKey(string key):判断数据是否存在。
++++namespace UnityEngine{ public sealed class PlayerPrefs{ } }
//立钻哥哥:PlayePrefs类信息
using System;
namespace UnityEngine{
public sealed class PlayerPrefs{
//Constructors
public PlayerPrefs();
//Static Methods
public static extern void DeleteAll(); //删除全部的key(立钻哥哥:谨慎使用)
public static extern void DeleteKey(string key); //删除对应的key
public static float GetFloat(string key);
public static extern float GetFloat(string key, float defaultValue);
public static extern int GetInt(string key, int defaultValue);
public static int GetInt(string key); //返回key对应的值
public static extern string GetString(string key, string defaultValue);
public static string GetString(string key);
public static extern bool HasKey(string key); //判断是否存在key
public static extern void Save();
public static void SetFloat(string key, float value); //设置由key肯定的参数值
public static void SetInt(string key, int value); //设置由key肯定的参数值
public static void SetString(string key, string value);
private static extern bool TrySetFloat(string key, float value);
private static extern bool TrySetInt(string key, int value);
private static extern bool TrySetSetString(string key, string value);
}
}
++++PlayerPrefs实例:
void YanlzPlayerPrefsExample(){
PlayerPrefs.SetFloat(“Player Score”, 10.0F);
print(PlayerPrefs.GetFloat(“Player Score”));
}
++++PlayerPrefs保存数据:
--立钻哥哥:Unity3D中的数据持久化是以键值的形式存储的,能够看做是一个字典。
--PlayerPrefs.SetInt(“Age”, mAge);
--PlayerPrefs.SetFloat(“Grade”, mGrade);
--PlayerPrefs.SetString(“Name”, mName);
++++PlayerPrefs读取数据:
--立钻哥哥:Unity3D中的值是经过键名来读取的,当值不存在时,返回默认值。
--mAge = PlayerPrefs.GetInt(“Age”, 0);
--mGrade = PlayerPrefs.GetFloat(“Grade”, 0F);
--mName = PlayerPrefs.GetString(“Name”, “DefaultValue”);
#知识点0007:Unity3d脚本生命周期
++++Unity3d脚本经常使用生命周期:【Awake()】=>【OnEnable()】=>【Start()】=>【FixedUpdate()】=>【Update()】=>【LateUpdate()】=>【OnGUI()】=>【OnDisable()】=>【OnDestroy()】
++++Unity3d脚本完整生命周期:
=>【Editor编辑器:Reset()、】
=>【Initialization初始化:Awake()、OnEnable()、Start()、】
=>【Physics物理系统:FixedUpdate()、yield WaitForFixedUpdate()、Internal physics update()、OnTriggerXXX()、OnCollisionXXX()、】
=>【Input events输入事件:OnMouseXXX()、】
=>【Game logic游戏逻辑:Update()、yield null、yield WaitForSeconds()、yield WWW()、yield StartCoroutine()、Internal animation update()、LateUpdate()、】
=>【Scene rendering场景渲染:OnWillRenderObject()、OnPreCull()、OnBecameVisible()、OnBecameInvisible()、OnPreRender()、OnRenderObject()、OnPostRender()、OnRenderImage()、】
=>【Gizmo rendering:OnDrawGizmos()、】
=>【GUI rendering(GUI渲染):OnGUI()、】
=>【End of frame:yield WaitForEndOfFrame()、】
=>【Pausing:OnApplicationPause()、】
=>【Disable/enable(物体激活或禁用):OnDisable()、】
=>【Decommissioning:OnDestroy()、OnApplicationQuit()、】
++++【Editor:Reset():】:Reset是在用户点击检视面板的Reset按钮或者首次添加该组件时被调用。此函数只在编辑模式下被调用。
++++【Initialization:Awake():】:
++++【Initialization:OnEnable():】:
++++【Initialization:Start():】:Start仅在Update函数第一次被调用前调用。
++++【Physics:FixedUpdate():】:若是固定时间步长小于实际帧更新时间,那么每一帧物理周期将可能发生不止一次;
++++【Physics:yield WaitForFixedUpdate():】:
++++【Physics:Internal physics update():】:
++++【Physics:OnTriggerXXX():】:
++++【Physics:OnCollisionXXX():】:
++++【Input events:OnMouseXXX():】
++++【Game logic:Update():】:
++++【Game logic:yield null:】:
++++【Game logic:yield WaitForSeconds():】:若是一个协程以前已经yield了,可是如今因为恢复了,那么将执行剩下的部分。
++++【Game logic:yield WWW():】:
++++【Game logic:yield StartCoroutine():】:
++++【Game logic:Internal animation update():】:
++++【Game logic:LateUpdate():】:
++++【Scene rendering:OnWillRenderObject():】:若是对象可见,则为每一个相机调用一次此函数。
++++【Scene rendering:OnPreCull():】:在相机剔除场景以前调用此函数。相机可见的对象取决于剔除。OnPreCull函数调用发生在剔除以前。
++++【Scene rendering:OnBecameVisible():】:在对象对于相机可见时调用此函数。
++++【Scene rendering:OnBecameInvisible():】:在对象对于相机不可见时调用此函数。
++++【Scene rendering:OnPreRender():】:在相机开始渲染场景以前调用此函数。
++++【Scene rendering:OnRenderObject():】:在完成全部常规场景渲染后调用此函数。此时,可以使用GL类或Graphics.DrawMeshNow绘制自定义几何图形。
++++【Scene rendering:OnPostRender():】:在相机完成场景渲染后调用此函数。
++++【Scene rendering:OnRenderImage():】:(仅限专业版):在完成场景渲染后调用此函数,以便对屏幕图像进行后处理。
++++【Gizmo rendering:OnDrawGizmos():】:OnDrawGizmos只在编辑模式下被调用。(用于在场景视图中绘制小图示(Gizmos),以实现可视化目的。)
++++【GUI rendering:OnGUI():】:OnGUI在每一帧更新时调用屡次。(在每帧上屡次调用此函数,以响应GUI事件。程序首先将处理Layout和Repaint事件,而后再处理每一个输入事件的Layout和keyboard/鼠标事件。)
++++【End of frame:yield WaitForEndOfFrame():】:
++++【Pausing:OnApplicationPause():】:OnApplicationPause在程序检测到暂停时,会在帧的结尾处被调用。
++++【Disable/enable:OnDisable():】:OnDisable在脚本失效时被调用,被销毁时也会被调用。若是再启用的话,OnEnable()会再一次被调用。
++++【Decommissioning:OnDestroy():】:
++++【Decommissioning:OnApplicationQuit():】:
++Unity3d脚本9个经常使用生命周期的回调方法
++++1、void Awake();:用于在游戏开始以前初始化变量或游戏状态。在脚本整个生命周期内它仅被调用一次。Awake()在全部对象被初始化以后调用,因此能够安全的与其余对象对话或用诸如GameObject.FindWithTag()这样的函数搜索它们。每一个游戏物体上的Awake()以随机的顺序被调用。所以,应该用Awake()来设置脚本间的引用,并用Start()来传递信息,Awake()老是在Start()以前被调用。它不能用来执行协同程序。
++++2、void OnEnable();
++++3、void Start();:仅在Update函数第一次被调用前调用。Start()在behaviour的生命周期中只被调用一次。Start()和Awake()的不一样是Start()只在脚本实例被启用时调用。能够按需求调整延迟初始化代码。Awake()老是在Start()以前执行。这容许协调初始化顺序。在全部脚本实例中,Start()函数老是在Awake()函数以后调用。
++++4、void Fixedupdate(); :物理引擎计算:固定帧更新,在Unity导航菜单栏中,点击【Edit】=>【Project Setting】=>【Time】菜单项后,右侧的Inspector视图将弹出时间管理器,其中“Fixed Timestep”选项用于设置FixedUpdate()的更新频率,更新频率默认为0.02s。
++++5、void Update(); :刷新:正常帧更新,用于更新逻辑。每一帧都执行,处理Rigidbody时,须要用FixedUpdate代替Update。(例如,给刚体加一个做用力,必须应用做用力在FixedUpdate里的固定帧,而不是Update中的帧。)(二者帧长不一样)FixedUpdate(),每固定帧绘制时执行一次,和Update()不一样的是Fixedupdate是渲染帧执行,若是渲染效率低的时候FixedUpdate调用次数就会跟着降低。FixedUpdate比较适合用于物理引擎的计算,由于是跟每帧渲染有关。Update就比较适合作控制。
++++6、void LateUpdate(); :摄像机跟随:在全部Update函数调用后被调用,和FixedUpdate同样都是每一帧都被调用,这可用于调整脚本执行顺序。(例如,当物体在Update里移动时,跟随物体的相机能够在LateUpdate里实现。)LateUpdate,在每帧Update执行完毕调用,他是在全部Update()结束后才调用,比较适合用于命令脚本的执行。(官网上的摄像机跟随,都是在全部Update()操做完才跟进摄像机,否则就有可能出现摄像机已经推动了,但视角里尚未角色的空帧出现。)
++++7、void OnGUI();:在渲染和处理GUI事件时调用。(好比,画一个button或label时经常使用)这意味着OnGUI()也是每帧执行一次。
++++8、void OnDisable();:当物体被销毁时OnDisable()将被调用,而且可用于任意清理代码。脚本被卸载时,OnDisable()将被调用,OnEnable()在脚本被载入后调用。(立钻哥哥:OnDisable不能用于协同程序。)
++++9、void OnDestroy();:当MonoBehaviour将被销毁时,这个函数被调用。OnDestroy()只会在预先已经被激活的游戏物体上被调用。(立钻哥哥:OnDestroy()不能用于协同程序。)
++Unity脚本经常使用生命周期
++++初始阶段:Awake()、OnEnable()、Start()、
++++物理阶段:FixedUpdate()、
++++游戏逻辑:Update()、LateUpdate()、
++++场景渲染:OnGUI()、OnBecameVisible()、OnBecameInvisible()、
++++结束阶段:OnDisable()、OnDestroy()、OnApplicationQuit()、
++++【初始阶段:Awake():唤醒】:当物体载入时当即调用1次,经常使用于在游戏开始前进行初始化,能够判断当知足某种条件执行此脚本 this.enable = true。
++++【初始阶段:OnEnable():当可用】:每当脚本对象启用时调用。
++++【初始阶段:Start():开始】:物体载入且脚本对象启用时被调用1次。经常使用于数据或游戏逻辑初始化,执行时机晚于Awake()。
++++【物理阶段:FixedUpdate():固定更新】:脚本启用后,固定时间被调用,适用于对游戏对象作物理操做,例如移动等。(设置更新频率:【Edit】=>【Project Setting】=>【Time】=>【Fixed Timestep】值,默认为0.02s)
++++【游戏逻辑:Update():更新】:脚本启用后,每次渲染场景时调用,频率与设备性能及渲染量有关。
++++【游戏逻辑:LateUpdate():延迟更新】:在Update函数被调用后执行,适用于跟随逻辑。
++++【场景渲染:OnGUI():渲染】:渲染和处理GUI事件时调用。
++++【场景渲染:OnBecameVisible():当可见】:当Mesh Renderer在任何相机上可见时调用。
++++【场景渲染:OnBecameInVisible():当不可见】:当Mesh Renderer在任何相机上不可见时调用。
++++【结束阶段:OnDisable():当不可见】:对象变为不可用和附属游戏对象非激活状态时此函数被调用。
++++【结束阶段:OnDestroy():当销毁】:当脚本销毁或附属的游戏对象被销毁时调用。
++++【结束阶段:OnApplicationQuit():当程序结束】:应用程序退出时被调用。
++Unity脚本事件函数
++++Unity脚本函数涉及:编辑器、初始化、物理、输入、游戏逻辑、渲染、应用程序、协程、其余等。
++++编辑器相关函数:Reset()、OnValidate()、
--【Reset()】:当脚本附加到GameObject上或脚本的右键菜单项里选择了“Reset”命令的时候调用此函数。
--【OnValidate()】:脚本被加载或值被改变,在inspector中时调用。
++++初始化函数:Awake()、OnEnable()、Start()、
--【Awake()】:此函数在脚本附加到GameObject对象上且此对象是被激活的时候调用。而且是在全部Start()函数调用以前调用。(立钻哥哥:就算挂载的脚本没有被启用此函数也会被调用。)
--【OnEnable()】:脚本对象被激活的时候。
--【Start()】:在全部的Update()函数调用以前调用。
++++物理相关函数:FixedUpdate()、OnTriggerEnter()、OnTriggerEnter2D()、OnTriggerExit()、OnTriggerExit2D()、OnTriggerStay()、OnTriggerStay2D()、OnCollisionEnter()、OnCollisionEnter2D()、OnCollisionExit()、OnCollisionExit2D()、OnCollisionStay()、OnCollisionStay2D()、
++++输入相关函数:OnMouseDown()、OnMouseDrag()、OnMouseEnter()、OnMouseExit()、OnMouseOver()、OnMouseUp()、OnMouseUpAsButton()、
++++游戏逻辑相关函数:Update()、LateUpdate()、
--【Update()】:游戏逻辑的更新函数。
--【LateUpdate()】:全部Update函数调用后调用的更新函数。
++++渲染相关函数:OnPreCull()、OnBecameVisible()、OnBecameInvisible()、OnWillRenderObject()、OnPreRender()、OnRenderObject()、OnPostRender()、OnRenderImage()、OnGUI()、OnDrawGizmos()、OnDrawGizmosSelected()、
--【OnPreCull()】:在执行剔除以前执行的函数。
--【OnBecameVisible()】:此对象从隐藏到显示时调用。
--【OnBecameInVisible()】:此对象从显示到隐藏时调用。
--【OnPreRender()】:渲染场景以前。
--【OnRenderObject()】:全部常规的渲染结束后调用。
--【OnRenderImage()】:场景渲染完后调用。
--【OnGUI()】:执行UI的渲染,一帧可能调用几回。
--【OnDrawGizmos()】:执行场景视图中对象的标示图标的绘制。
--【OnDrawGizmosSelected()】:执行场景视图中选中场景的标示图标的绘制。
++++应用程序相关函数:OnApplicationFocus()、OnApplicationPause()、OnApplicationQuit()、
--【OnApplicationFocus()】:应用程序成为当前的应用时调用。
--【OnApplicationPause()】:当应用程序切入后台暂停时调用。
--【OnApplicationQuit()】:应用程序退出时调用。
++++协程相关:yield()、yield WaitForSeconds()、yield WaitForFixedUpdate()、yied WWW()、yield StartCoroutine()、
--【yield()】:延缓到下一帧执行。
--【yield WaitForSeconds()】:这一帧执行完后,等待指定的延迟到。
--【yield WaitForFixedUpdate()】:全部的固定更新完成后。
--【yield WWW()】:www下载完成。
--【yield StartCoroutine()】:等待一个自定义的函数完成。
++++其余函数:OnDisable()、OnDestroy()、OnLevelWasLoaded()、
--【OnDisable()】:脚本对象被禁用时调用。
--【OnDestroy()】:脚本对象被销毁的时候调用。
--【OnLevelWasLoaded()】:一个新的场景加载时调用。
++物理更新通常放在哪一个系统函数里?
++++FixedUpdate(),每固定帧绘制时执行一次,和Update()不一样的是FixedUpdate()是渲染帧执行,若是渲染效率低下的时候,FixedUpdate()调用次数就会跟着降低。
++++FixedUpdate()比较适用于物理引擎的计算,由于是跟每帧渲染有关。(Update()就比较适合作控制。)
#立钻哥哥的Unity学习空间:http://blog.csdn.net/VRunSoftYanlz/
++立钻哥哥推荐的拓展学习连接(Link_Url):
++++立钻哥哥Unity 学习空间: http://blog.csdn.net/VRunSoftYanlz/
++++设计模式简单整理:http://www.noobyard.com/article/p-scmbzocc-hg.html
++++U3D小项目参考:https://blog.csdn.net/vrunsoftyanlz/article/details/80141811
++++UML类图:http://www.noobyard.com/article/p-aakurcwi-bm.html
++++Unity知识点0001:http://www.noobyard.com/article/p-bqmetnys-ep.html
++++U3D_Shader编程(第一篇:快速入门篇):http://www.noobyard.com/article/p-ptwlpwbc-gz.html
++++U3D_Shader编程(第二篇:基础夯实篇):http://www.noobyard.com/article/p-dadqpvvp-hv.html
++++Unity引擎基础:http://www.noobyard.com/article/p-ggcuedyq-ka.html
++++Unity面向组件开发:http://www.noobyard.com/article/p-eiunlkzw-dt.html
++++Unity物理系统:http://www.noobyard.com/article/p-mcqnwufb-kd.html
++++Unity2D平台开发:http://www.noobyard.com/article/p-brjbvtac-hs.html
++++UGUI基础:http://www.noobyard.com/article/p-nvzrvath-mc.html
++++UGUI进阶:http://www.noobyard.com/article/p-tpspjolu-gt.html
++++UGUI综合:http://www.noobyard.com/article/p-nfgrebqx-gg.html
++++Unity动画系统基础:http://www.noobyard.com/article/p-otjpnbzz-dq.html
++++Unity动画系统进阶:http://www.noobyard.com/article/p-hxghrtgb-bp.html
++++Navigation导航系统:http://www.noobyard.com/article/p-skpvrobt-t.html
++++Unity特效渲染:http://www.noobyard.com/article/p-sudpqrhk-bp.html
++++Unity数据存储:http://www.noobyard.com/article/p-ybvcceul-m.html
++++Unity中Sqlite数据库:http://www.noobyard.com/article/p-vxpuqxev-ca.html
++++WWW类和协程:http://www.noobyard.com/article/p-alggjlwu-cy.html
++++Unity网络:http://www.noobyard.com/article/p-bjvfgzwg-dw.html
++++C#事件:http://www.noobyard.com/article/p-dietpjzv-gm.html
++++C#委托:http://www.noobyard.com/article/p-oiohmxtc-gh.html
++++C#集合:http://www.noobyard.com/article/p-vdfpislb-ex.html
++++C#泛型:http://www.noobyard.com/article/p-vujvnprk-ee.html
++++C#接口:http://www.noobyard.com/article/p-emexlwmu-dm.html
++++C#静态类:https://blog.csdn.net/vrunsoftyanlz/article/details/78630979
++++C#中System.String类:http://www.noobyard.com/article/p-uchiaxzw-cq.html
++++C#数据类型:http://www.noobyard.com/article/p-kqtbvoyq-ba.html
++++Unity3D默认的快捷键:http://www.noobyard.com/article/p-gbllyjbs-s.html
++++游戏相关缩写:http://www.noobyard.com/article/p-pzpxsztf-gm.html
++++立钻哥哥Unity 学习空间: http://blog.csdn.net/VRunSoftYanlz/
--_--VRunSoft : lovezuanzuan--_--