《Unity知识点》
##《Unity知识点》发布说明:
++++这是立钻哥哥对Unity知识点的一个梳理和拓展,打造最权威的参考。
++++这里我们碰到了一个问题,那就是从什么地方入手比较合适,作为这个问题,通常立钻哥哥就顺其自然了,所以,我们这边就没有特别的顺序和章节归类。
##Unity知识点目录:
#知识点0001:什么是协同程序?
#知识点0002:ArrayList和List的区别?
#知识点0003:MeshRender中material和sharedmaterial的区别?
#知识点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或者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的区别?
#知识点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的区别?
#知识点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:对象池(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知识点0001:https://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--_--