Unity知识点0001

Unity知识点》

##Unity知识点》发布说明:

++++这是立钻哥哥对Unity知识点的一个梳理和拓展,打造最权威的参考。

++++这里我们碰到了一个问题,那就是从什么地方入手比较合适,作为这个问题,通常立钻哥哥就顺其自然了,所以,我们这边就没有特别的顺序和章节归类。

##Unity知识点目录:

#知识点0001:什么是协同程序?

#知识点0002ArrayListList的区别?

#知识点0003MeshRendermaterialsharedmaterial的区别?

#知识点0004:对象池(Object Pool)技术。



#知识点0001:什么是协同程序?

#知识点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或者nullyield的意思是告诉协程等待下一帧,直到继续执行为止。)

++++利用协程模拟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:协程不是多线程(尽管它们看上去是这样的),它们运行在同一线程中,跟普通的脚本一样。

--注意5IEnumerator类型的方法不能带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系统调用,所以如果使用的话需要把组件中的数值传到开启的新线程中。

++++注意2C#中有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!);

    }

}




#知识点0002ArrayListList的区别?

#知识点0002ArrayListList的区别?

++++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。(ArrayListList都继承了接口IList

++++ArrayList对类型没有要求,是因为ArrayList中存储的类型都是object类型,而其他类型与object类型进行转换的过程就会产生拆箱装箱。(拆箱装箱是一笔不小的开销。)

++++List泛型在声明时就已经限制了存储内容的数据类型,所以不存在跟object类型的转换也就没有了装箱和拆箱的操作,并且是类型安全的。(平时使用时,如果只是为了使用动态数组,就不要考虑ArrayList了,推荐使用List

++++List<T>类表示可通过索引访问的对象的强类型列表,提供用于对列表进行搜索、排序和操作的方法。

++ArrayList动态数组:

++++1、动态地增加和减少元素。

++++2、实现了ICollectionIListIEnumerable接口。

++++3、灵活地设置数组的大小。

++++4、不安全的集合数组。

++++5、其元素为值类型时,效率不高(装箱和拆箱耗性能)。

++++ArrayList常用方法与描述:

--Add():将对象添加到ArrayList的结尾处。

--Insert():将元素插入ArrayList的指定索引处。

--Remove():从ArrayList中移除特定对象的第一个匹配项。

--RemoveAt():移除ArrayList的指定索引处的元素。

--Reverse():将整个ArrayList当中元素的顺序反转。

--Contains():确定某元素是否在ArrayList中。

--Clear():从ArrayList中移除所有元素。

++List<T>泛型动态数组:

++++1List类是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~1010个整数,然后删除其中的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的复杂版本,它提供了:动态的增加和减少元素;实现了ICollectionIList接口;灵活的设置数组的大小。

++++ArrayList例子:

ArrayList myArrList = new ArrayList();

for(int i = 0;  i < 10;  i++){

    myArrList.Add(i);    //给数组增加10Int元素

}

 

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();




#知识点0003MeshRendermaterialsharedmaterial的区别?

#知识点0003MeshRendermaterialsharedmaterial的区别?

++++修改sharedMeterial将改变所有物体使用这个材质的外观,并且也改变存储在工程里的材质设置。不推荐修改由sharedMaterial返回的材质。(如果我们想修改渲染器的材质,使用material替代。)

++++通过GetComponent<MeshRenderer>.material.color来改变颜色(材质的参数)。

++++通过GetComponent<MeshRenderer>.sharedmaterial.color来改变颜色(改变材质)。

++++MeshRenderer(网格渲染器)从MeshFiler(网格过滤器)获得几何形状,并根据Mesh进行渲染,而渲染所需要的贴图信息就来自于Material

++++MeshRendererMaterial类型的变量有两个:materialsharedMaterial

++++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

++MeshRendermaterialShader的区别?

++++MeshRender是模型渲染的组件,由此组件物体才能显示出来。

++++Material是材质球,实际就是shader的实例,并进行赋值,贴图、纹理、颜色等。

++++Shader是着色器,实际上是一段程序,还可以用来实现一些仅靠贴图不容易实现的效果,如玻璃。

++++Shader大致分为:1、表面着色器;2、顶点和片元着色器;3、固定功能着色器。

++Render的作用?描述MeshRenderSkinnedMeshRender的关系与不同?

++++Render是渲染器,渲染器可以使物体显示在屏幕上。

++++MeshRender是网格渲染,SkinnedMeshRender是蒙皮网格渲染器。

++++Mesh就是指模型的网格(同名组件是用于调整网格属性的),MeshFilter一般是用于获得模型网格的组件,而MeshRender是用于把网格渲染出来的组件。

++Unity里的Mesh属性

++++MeshUnity内的一个组件,称为网格组件。

++++Mesh网格:是指模型的网格,建模就是建网格。(细看Mesh,可以知道Mesh的主要属性内容包括顶点坐标,法线,纹理坐标,三角形绘制序列等其他有用的属性和功能。)(建网格,就是画三角形;画三角形就是定位三个点。)

++++Mesh Filter网格过滤器:内包含一个Mesh组件,可以根据MeshFilter获得模型网格的组件,也可以为MeshFliter设置Mesh内容。

++++Mesh Render网格渲染器:是用于把网格渲染出来的组件。(MeshFilter的作用就是把Mesh扔给MeshRender将模型或者说是几何体绘制显示出来。)

++++MeshMesh FilterMesh Render之间的关系大概就是:Unity中的对象就是GameObject,每个GameObject都可以有一个MeshFilter组件(也可以没有),该组件又有Mesh属性(这个一定有),而该属性又有顶点坐标,法线等属性。(而如果GameObject里有MeshFilter,则必须要Mesh Render才能将此网格渲染出来,不然是看不见该网格的。)

++++Mesh的属性:顶点坐标(vertex)、法线(normal)、纹理坐标(uv)、三角形序列(triangle

--【顶点坐标(Vertex)】:顶点坐标数组存放Mesh的每个顶点的空间坐标,假设某meshn个顶点,则vertexsizen

--【法线(normal)】:法线数组存放mesh每个顶点的法线,大小与顶点坐标对应,normal[i]对应顶点vertex[i]的法线。

--【纹理坐标(uv)】:它定义了图片上每个点的位置的信息,这些点与3D模型是相互联系的,以决定表面纹理贴图的位置,UV就是将图像上每一个点精确对应到模型物体的表面。(Ui[i]对应vertex[i]

--【三角形序列(triangle)】:每个mesh都由若干个三角形组成,而三角形的三个点就是顶点坐标里的点,三角形的数组的size=三角形个数*3

++Unity3DMeshRenderer的使用

++++任何一个模型都是由许多网格面组成的,而面又是由许多三角形组成的。

++++创建网格面的核心就是为其添加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:对象池(Object Pool)技术。








#立钻哥哥的Unity学习空间:http://blog.csdn.net/VRunSoftYanlz/

++立钻哥哥推荐的拓展学习链接(Link_Url

++++立钻哥哥Unity 学习空间: http://blog.csdn.net/VRunSoftYanlz/

++++设计模式简单整理https://blog.csdn.net/vrunsoftyanlz/article/details/79839641

++++U3D小项目参考https://blog.csdn.net/vrunsoftyanlz/article/details/80141811

++++UML类图https://blog.csdn.net/vrunsoftyanlz/article/details/80289461

++++Unity知识点0001https://blog.csdn.net/vrunsoftyanlz/article/details/80302012

++++U3D_Shader编程(第一篇:快速入门篇)https://blog.csdn.net/vrunsoftyanlz/article/details/80372071

++++U3D_Shader编程(第二篇:基础夯实篇)https://blog.csdn.net/vrunsoftyanlz/article/details/80372628

++++Unity引擎基础https://blog.csdn.net/vrunsoftyanlz/article/details/78881685

++++Unity面向组件开发https://blog.csdn.net/vrunsoftyanlz/article/details/78881752

++++Unity物理系统https://blog.csdn.net/vrunsoftyanlz/article/details/78881879

++++Unity2D平台开发https://blog.csdn.net/vrunsoftyanlz/article/details/78882034

++++UGUI基础https://blog.csdn.net/vrunsoftyanlz/article/details/78884693

++++UGUI进阶https://blog.csdn.net/vrunsoftyanlz/article/details/78884882

++++UGUI综合https://blog.csdn.net/vrunsoftyanlz/article/details/78885013

++++Unity动画系统基础https://blog.csdn.net/vrunsoftyanlz/article/details/78886068

++++Unity动画系统进阶https://blog.csdn.net/vrunsoftyanlz/article/details/78886198

++++Navigation导航系统https://blog.csdn.net/vrunsoftyanlz/article/details/78886281

++++Unity特效渲染https://blog.csdn.net/vrunsoftyanlz/article/details/78886403

++++Unity数据存储https://blog.csdn.net/vrunsoftyanlz/article/details/79251273

++++Unity中Sqlite数据库https://blog.csdn.net/vrunsoftyanlz/article/details/79254162

++++WWW类和协程https://blog.csdn.net/vrunsoftyanlz/article/details/79254559

++++Unity网络https://blog.csdn.net/vrunsoftyanlz/article/details/79254902

++++C#事件https://blog.csdn.net/vrunsoftyanlz/article/details/78631267

++++C#委托https://blog.csdn.net/vrunsoftyanlz/article/details/78631183

++++C#集合https://blog.csdn.net/vrunsoftyanlz/article/details/78631175

++++C#泛型https://blog.csdn.net/vrunsoftyanlz/article/details/78631141

++++C#接口https://blog.csdn.net/vrunsoftyanlz/article/details/78631122

++++C#静态类https://blog.csdn.net/vrunsoftyanlz/article/details/78630979

++++C#中System.String类https://blog.csdn.net/vrunsoftyanlz/article/details/78630945

++++C#数据类型https://blog.csdn.net/vrunsoftyanlz/article/details/78630913

++++Unity3D默认的快捷键https://blog.csdn.net/vrunsoftyanlz/article/details/78630838

++++游戏相关缩写https://blog.csdn.net/vrunsoftyanlz/article/details/78630687

++++立钻哥哥Unity 学习空间: http://blog.csdn.net/VRunSoftYanlz/


--_--VRunSoft : lovezuanzuan--_--

猜你喜欢

转载自blog.csdn.net/vrunsoftyanlz/article/details/80302012