学习笔记——Unity3D与C#基础总结

1.Draw Call 简单来说,就是CPU通过调用图像编程(像OpenGL或者 DirectX)的接口命令GPU执行渲染操作,它们之间通过命令缓冲区连接,CPU向其中发送渲染命令,当GPU完成了上一次的渲染任务之后,会从命令缓冲区的命令队列中再取出一个并执行。CPU每次调用Draw Call之前需要向GPU发送很多内容,包括状态,数据,命令等等。而GPU的渲染能力是很强的,渲染200个网格和2000个网格没什么区别。所以当CPU调用的Draw Call过多时,会造成比较大的性能消耗,命令缓冲区中的命令已经被GPU执行完了,CPU还在准备下一个Draw Call,而GPU处于等待状态,就影响了效率了。而Draw Call并非越小越好,一味的合并图集与网格追求更小的Draw Call,可能会造成CPU需要传输的资源过大,GPU只能等待。

2.渲染流水线中的3个概念阶段:

(1).应用阶段:在CPU中进行,由开发者设置每个模型的渲染状态,材质,纹理,否开启半透明、是否开启光照等等,当这些都准备好以后,CPU调用DrawCall向GPU发起执行渲染的命令,输出渲染图元

(2).几何阶段:在GPU中进行,输入渲染图元,通过顶点着色器进行空间坐标变换,再经曲面细分着色器细分,几何着色器执行逐图元的着色操作或者用于产生更多的图元,然后裁剪剔除摄像机视野范围外的图元,最后经过屏幕映射将三维的空间坐标转换为屏幕空间的二维顶点坐标。

(3).光栅化阶段:三角形设置,三角形遍历产生片元(包含屏幕坐标,深度信息,法线,纹理坐标等)序列,经片元着色器后开始逐片元操作,包括模板测试,深度测试,混合,最后产生屏幕上的像素,渲染出最终图像。开启模板测试:会比较片元的模板值和缓冲区的模板值,测试结果没有通过的话当前片元会被舍弃掉,比较函数可由开发者编辑。一般用于剔除指定区域的像素,比如剔除重复的阴影。开启深度测试:会比较片元的深度值和缓冲区的深度值,测试结果没有通过的话当前片元会被舍弃掉,比较函数可由开发者编辑。深度值即像素点Z坐标与摄像机的距离,图片,模型被覆盖/遮挡的部分是不需要绘制的,所以要开启深度测试剔除。混合:半透明的物体才需要混合片元颜色与缓冲区的颜色,不透明物体不需要开启混合,片元着色器得到的颜色直接覆盖缓冲区颜色。

3.单例模式:确保一个类只有一个实例,在系统中把这个实例当作一个工具来使用,当需要频繁创建销毁某个对象时,单例模式能提高性能。缺点:(1).不能继承(登记式模式可以被继承),难以拓展。(2).滥用单例模式会提高代码之间的耦合度。

观察者模式是为了定义对象间的一种一对多的依赖关系,以便于当一个对象的状态改变时,其他依赖于它的对象会被自动告知并更新。是一种松耦合的设计模式。

4. 引用类型继承自System.Object类,值类型则是继承Systerm.Object的子类System.ValueType类。值类型的数据存储在内存的栈中;引用类型的数据存储在内存的堆(new 出来的对象)中,在栈中存放数据引用或地址。值类型存取速度快,引用类型存取速度慢。值类型表示实际数据,引用类型表示指向存储在内存堆中的数据的指针或引用

5. 指针其实是一个整型的变量,用来存储其它变量的地址,地址表示数据在内存中的位置

6.string是String类的别名,字符串的本质是一个char数组,string类的内部定义了一个char类型的索引器,所有string可以用[ ]去读取内部的每一个字符,对string对象进行修改时,其实要重新分配内存,创建一个新的string对象,所以当需要频繁修改string的值的时候,会使GC频繁的去回收内存,性能消耗比较大;StringBuilder 实例化时可以指定一个容纳量,当修改的值没有超过容纳量的时候不会重新分配内存.

7.装箱是指将值类型转换为引用类型的过程,首先从托管堆中为新生成的引用对象分配内存,然后将值类型的数据拷贝到刚刚分配的内存中,返回托管堆中新分配对象的地址。拆箱是指引用类型转换为值类型的过程,首先获取托管堆中属于值类型那部分字段的地址,这一步是严格意义上的拆箱,然后将引用对象中的值拷贝到位于线程堆栈上的值类型实例中。

8.序列化是指对象的状态信息转换为可以存储或传输的形式的过程。序列化方式一般有两种,二进制和XML。

9.委托上注册了多个函数后,如果委托和事件有返回值,那么调用委托和事件时,返回的将是最后一个注册函数的返回值 . ActionFunc的本质是委托,系统内部定义的,前者为Void类型不具有返回值,后者具有返回值。(当脚本销毁时一定要释放方法)

10.事件对象的调用只能在声明事件的类的内部调用(安全),在类的外部只能对事件进行” +=”或者” -=”操作,相当于被限制了权限的委托。

11.二分查找

public static int binSearch(int[] array,int start,int end,int key)
{
            int mid = (end - start) / 2 + start;
            if (array[mid] == key) return key;
            if (start >= end) return -1;
            else if (key > array[mid]) return binSearch(array, mid+1, end, key);
            else if (key < array[mid]) return binSearch(array, start, mid-1, key);
            return -1;
}

12.快速排序

public static void QuickSort(int[] array, int start, int end)
        {
            if (start > end) return;    
            int index = Partition(array, start, end);
            QuickSort(array, start, index - 1);
            QuickSort(array, index + 1, end);
        }

        private static int Partition(int[] array, int start, int end)
        {
            int pivot = array[start];
            while (start < end)
            {
                while (start < end && array[end] >= pivot) end--;
                array[start] = array[end];
                while (start < end && array[start] <= pivot) start++;
                array[end] = array[start];
            }
            array[start] = pivot;
            return start;
        }

13.反射可以通过程序集,类型,类型实例获取该程序集内所有类型+属性+属性类型,方法,方法的访问类型,参数和返回值等等。使用反射来调用类型或者触发方法,或者访问一个字段或者属性时CLR需要做更多的工作:校验参数,检查权限等等,所以速度是非常慢的。

14.Var用于在声明变量时,无法确定数据类型时使用。(1).必须在定义时初始化。(2).一但初始化完成,就不能再给变量赋与初始化值类型不同的值。(3). 必须是局部变量

15.时间复杂度用于衡量一个算法执行所耗费的时间,空间复杂度用于衡量算法运行过程中临时占用的内存大小

16. GC即垃圾回收,自动管理内存,当我们创建对象时,系统会为对象分配一个内存空间,GC就开始监控这个对象的地址、大小以及使用情况,当对象没有被任何引用指向,或者不再会被用到的时候,GC就把对象当作垃圾回收,释放内存 。但是GC并不是实时性的,GC并不是能释放所有的资源。它不能自动释放非托管资源(正则表达式,数据库连接,定时器)。减少new产生对象的次数 来减少GC。

17.ref 关键字使参数按引用传递。使用 ref 参数,则方法定义和调用方法都必须显式使用 ref 关键字。ref指定的参数在进入方法前必须初始化。out指定的参数在进入函数时会清空自己,必须在函数内部赋初值。如果一个方法采用 ref 参数,而另一个方法采用 out 参数,无法重载这两个方法。

18.数据结构

HashSet:添加元素时,HashSet会先调用元素的hashCode方法得到元素的哈希值 ,然后通过哈希值算出该元素在哈希表中的存储位置,如果该元素的存储位置已经存在元素,会调用该元素的equals方法与该位置的元素比较,若equals返回true即视为重复元素,不允许添加

Hashtable(哈希表又称散列表)本质是数组,通过哈希函数把string类型key值转换为哈希值,即int下标,来访问数组。数组的长度是固定的,通过key转换而来的int下标是有限的,容易造成重复,这就是哈希冲突,值唯一,且不需要指定类型,但可能导致拆箱装箱。

字典是引用类型,本质也是一种哈希表(数组),实现方式与哈希表相同,与Hashtable不同的是,采用拉链法(又称链地址法)解决哈希冲突,但是在声明的时候必须指定key和item的类型,键唯一。

哈希冲突解决方法:

1.开放定址法
①:线性探查法:
线性探查发会查找发生冲突周围+1,-1,+2,-2…地址的储存空间直到找到地址为止。但线性探测法可能会导致数组内相邻单元中的数据元素会趋近形成聚类,从而使后续单元探查时间变长且效率更低。
②:平方探查法:
和上面的线性探查法相同发生冲突会不过是在加上1的平方,减去1的平方…一直查找直到找到储存的地址为止。
2.拉链法
每次发生冲突时,用链表来储存数据,不过缺点是假如很多哈希值相同的数据时会储存到同一个链表中,会使查找变成线性。
3.再哈希法
发生冲突就再次使用另一个哈希函数计算地址直到找到地址为止。
4.建立一个公共溢出区
当发生哈希冲突之后将冲突的值都放在另一个地方(数组等)

数组是最简单的数据结构. 1.顺序存储,数据存储在连续的内存上。2.数组的内容都是相同类型。3.数组可以直接通过下标访问,故索引时间非常快而且是固定的,与数组大小内容无关。

ArrayList不必在声明ArrayList时指定它的长度, 可以存储不同类型的元素, 不是类型安全的。因为把不同的类型都当做Object来做处理,很有可能会在使用ArrayList时发生类型不匹配的情况。会导致大量拆箱,装箱操作

List内部使用数组来实现,即确保了类型安全,也取消了装箱和拆箱的操作,而且长度可以灵活变化。

链表LinkedList不是连续存储,而是靠各对象的指针所决定,所以向链表中插入删除元素的时候不用调整容量,但是访问某个元素的时候只能从头节点开始遍历。

19.Unity3D 协程是在主线程中执行的,在Update()方法之后,LateUpdate()方法之前调用。Unity在每一帧都会去处理对象上的协程,遇到yield return 语句会挂起,直到条件满足才会被唤醒继续执行后面的代码。使用StartCoroutine和StopCoroutine()方法来开启,结束协程,也可以在启动协程时定义一个Coroutine变量去保存协程,然后调用stop方法去停止协程。

20.Unity3D生命周期表

21.动态语言是在运行时才确定数据类型的语言。变量使用之前不需要类型声明,通常变量的类型是被赋值的那个值的类型。 例如PHP、ASP、Python、JavaScript等等。

静态语言是在编译时变量的数据类型就可确定的语言,多数静态类型语言要求在使用变量之前必须声明数据类型。 例如:C、C++、Golang、Java、C#等,优势:由于类型的强制声明,使得IDE有很强的代码感知能力,所以在实现复杂的业务逻辑、开发大型商业系统、以及那些生命周期很长的应用中,依托IDE对系统的开发很有保障;由于静态语言相对比较封闭,使得第三方开发包对代码的侵害性可以降到最低。

脚本语言又被称为扩建的语言,或者动态语言,是一种编程语言,用来控制软件应用程序,脚本通常以文本保存,只在被调用时进行解释或编译。脚本语言有:PHP、Python、JavaScript、Lua、Scala、VBScript,ActionScript,MAXScript,ASP,JSP,SQL,Perl,Shell,Ruby,JavaFX,AutoIt等;它是一种解释性的语言,它不象c/c++等可以编译成二进制代码,以可执行文件的形式存在,脚本语言不需要编译,可以直接用,由解释器来负责解释。

  • 11
    点赞
  • 42
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值