(1) 点乘和叉乘
【点乘】:
定义:a·b=|a|·|b|cos<a,b> 【注:粗体小写字母表示向量,<a,b>表示向量a,b的夹角,取值范围为[0,180]】
几何意义:是一条边向另一条边的投影乘以另一条边的长度.
v1和v2向量的点乘运算:相应元素的乘积的和:v1( x1, y1,z1) * v2(x2, y2,z2) = (x1x2) + (y1y2) + (z1*z2);
给定一个向量u和v,求u在v上的投影向量,如下图。

假设u在v上的投影向量是u’,且向量u和v的夹角为theta。一个向量有两个属性,大小和方向,我们先确定u’的大小(即长度,或者模),从u的末端做v的垂线,
那么d就是u’的长度。而u’和v的方向是相同的,v的方向v/|v|也就是u’的方向。

再求d的长度。

注意 : 结果不是一个向量,而是一个标量。
性质1: ab = |a||b|Cos(θ) ,θ是向量a 和向量 b之间的夹角。
性质2: ab = b*a 满足乘法交换律
关于这里的计算需要插播一个点,当两个单位向量的长度都是1的时候,向量的点乘就是他们夹角的余弦值。在游戏开发中会有归一化的操作,然后直接求夹角的情况,如果对这个知识点没有了解,可能看不懂夹角计算的原理。
我们通过点积公式可以看出来a,b都是标量,都是正数,余弦值会根据角度有正负变化。
当(0-90)°的时候,余弦值是正数,整个点乘公式都是正的。
当90°的时候,余弦值为0,整个公式为0。
当(90-180)°的时候,余弦值是负数,整个公式为负的。
利用这个性质,我们可以根据点乘的正负,做一些判断了。
Unity项目应用:
1.根据点乘计算两个向量的夹角。<a,b>= arccos(a·b / (|a|·|b|))
2.根据点乘的正负值,得到夹角大小范围,>0,则夹角(0,90)<0,则夹角(90,180),可以利用这点判断一个多边形是面向摄像机还是背向摄像机。
3.根据点乘的大小,得到向量的投影长度,反应了向量的长度关系。
【叉乘】:
定义:c = a x b,其中a b c均为向量
几何意义是:得到一个与这两个向量都垂直的向量,这个向量的模是以两个向量为边的平行四边形的面积
推导

v1和v2向量的叉乘运算:相应元素的乘积的和:v1( x1, y1,z1) x v2(x2, y2, z2) = {(y1z2 - y2z1),(x2z1 - x1z2),(x1y2-x2y1)};
性质1:c⊥a,c⊥b,即向量c与向量a,b所在平面垂直
性质2:模长|c| = |a||b| sin<a,b>
性质3:(数学上)满足右手法则, a x b = -b x a,所以我们可以使用叉乘的正负值来判断a,b的相对位置,即b是处于a的顺时针还是逆时针方向。
叉乘的右手定则是用来确定叉乘积的方向的。
Unity项目应用:
1.根据叉乘得到a,b向量的相对位置,和顺时针或逆时针方位。
2.得到a,b夹角的正弦值,计算向量的夹角(0,90),可以配合点乘和Angle方法计算出含正负的方向。
3.根据叉乘大小,得到a,b向量所形成的平行四边形的面积大小,根据面积大小得到向量的相对大小。
简单的说: 点乘判断角度,叉乘判断方向。
形象的说: 当一个敌人在你身后的时候,叉乘可以判断你是往左转还是往右转更好的转向敌人,点乘得到你当前的面朝向的方向和你到敌人的方向的所成的角度大小。
简单来说,在两个物体的位置关系判断中。
点乘可以判断出目标物体在我的前方还是后方。大于零在前方,小于零在后方。
叉乘可以判断出目标物体在我的左边还是右边。大于零在右方,小于零在左方。
在计算机图形学中。
点乘可以用来计算夹角余弦值。
叉乘可以用来计算平面法向量。
=========================================================分割线=========================================================
=========================================================分割线=========================================================
(2)Lua 原表
在 Lua table 中我们可以访问对应的 key 来得到 value 值,但是却无法对两个 table 进行操作(比如相加)。
因此 Lua 提供了元表(Metatable),允许我们改变 table 的行为,每个行为关联了对应的元方法。
例如,使用元表我们可以定义 Lua 如何计算两个 table 的相加操作 a+b。
当 Lua 试图对两个表进行相加时,先检查两者之一是否有元表,之后检查是否有一个叫 __add 的字段,若找到,则调用对应的值。 __add 等即时字段,其对应的值(往往是一个函数或是 table)就是”元方法”。
有两个很重要的函数来处理元表:
setmetatable(table,metatable): 对指定 table 设置元表(metatable),如果元表(metatable)中存在 __metatable 键值,setmetatable 会失败。
getmetatable(table): 返回对象的元表(metatable)。
Lua table有一套hashmap的查找机制,如果访问一个表中并不存在的字段,不会立即返回nil,而是先触发一套查找机制,也是我们用以实现面向对象的方法。
简单的描述:元表就是用于查找的备用表。
【构成类】:
元方法 __index
简单的描述:是当table中一个元素不存在的时候,会触发寻找元表的__index元方法,如果不存在,则返回nil,如果存在,则返回结果。
【只读不写表】:
1,利用元方法__newindex的特性(对字段进行赋值的时候如果表中没有则执行__newindex中的方法),新建一个空表代替原来的表,并对空表进行元方法重写实现table的相应功能。
这样对任何字段的修改,在空表中肯定都找不到,只能只能统一的入口方法__newindex,我们不处理就能实现只读。
2,再利用__index元方法保存原来的表, 因为取值的时候如果找不到就会去__index中取。
1 | function const(t) |
【table的 弱引用和强引用】:
一.强引用table
lua中的table是引用类型,更准确地说,是强引用类型。如下第二段代码,在内存中有一个{name = “123”}的table,并用a和b[1]指向它,然后置空a,此时就只剩下b[1]指向它了。
这种引用方式和我们所认知的引用是一样的。值得一提的是,这里的a = nil为什么不等同于{name = “123”} = nil呢,意思是将指向的这个表删掉呢?
因为lua是具备自动内存管理的,我们只管创建,删除操作是lua自动进行的,因此这里的a = nil并不是删除表,而是指将a对这张表的引用去掉,当没有地方引用这张表时,这张表就会被lua自动清掉。
1 | a = {name = "123"} |
二.弱引用table
如上所说,一般我们创建的table都是强引用table,即key和value都是强引用,强引用可以防止他们指向的对象被回收。如果一个对象存在强引用,那么它就不会被回收。而相对地,就有弱引用,弱引用不能防止对象被回收。
如果一个对象只存在弱引用,那么它就会 被回收。lua中通过弱引用table来实现弱引用。弱引用table有三种形式:
1.key值弱引用。设置方法为setmetatable(b, {__mode = “k”})
2.value值弱引用。设置方法为setmetatable(b, {__mode = “v”})
3.key和value值弱引用。设置方法为setmetatable(b, {__mode = “kv”})
当一个key或者value被收集时,整个key以及对应的value都会从table中移除。
下面新增一句代码,表示b的value对其指向的对象的引用是弱引用,而b的key对其指向的对象的引用仍然是强引用(对于table来说,其key和value可以指向任何类型的对象,除了key不能指向nil)。
{name = “123”}这个table只存在弱应用b[1],所以被回收。
1 | a = {name = "123"} |
=========================================================分割线=========================================================
=========================================================分割线=========================================================
(3)Unity协程原理
使用过Unity的同学一定知道,Unity提供了一套协程机制,简直不要太好用。但是这个协程依赖于Unity引擎,离开Unity就无法使用。
那有没有办法实现不依赖Unity的协程呢?答案是当然阔以。 所谓实现一个协程,就是实现一个迭代器的容器!
什么是IEnumerator
在.NET中,迭代器模式被IEnumerator和IEnumerable及其对应的泛型接口所封装。如果一个类实现了IEnumerable接口,”(那么就能够被迭代)”;
调用GetEnumerator方法将返回IEnumerator接口的实现,它就是迭代器本身。迭代器类似数据库中的游标,他是数据序列中的一个位置记录。迭代器只能向前移动,同一数据序列中可以有多个迭代器同时对数据进行操作。
这个只能向前移动的特性就是我们协程需要用到的特性。所谓实现一个协程,就是实现一个迭代器的容器!
具体函数:
1 | using System.Runtime.InteropServices; |
总结:
就是线程通过调度器检测条件 使用 IEnumerator 中的MoveNext方法移动 和 yield让出权限
=========================================================分割线=========================================================
=========================================================分割线=========================================================
(4)C# GC原理
Ngui 裁剪原理
怎么把模型放到UI前
摄像机层级管理
怎么拆分模型实现边走边打
渲染管线流程
帧同步状态同步
弱联网
资源管理框架
数据结构
数组和列表