unity学习笔记

图片还没有哦

unity 第一课(初识unity)

开源框架

unity就是基于mono的基础上,进行游戏引擎开发而实现的跨平台

unity 第二课(unity场景树渲染体系物理体系)

unity组件开发的优点

  • 使用一个开发工具,将所有的工作环节都集成在一起,相互独立的进行开发,开发效率提升。

场景树

GameObject = 节点 + Transform

  • 每一个场景是由节点树组成的(由节点的Transform组成的)
  • 场景树基于Transform组成 而不是游戏节点GameObject

图像世界

渲染关系
Mesh(3D模型网格) + Mesh render + 材质(Material 皮肤、贴图) = 3D模型

物理世界

物理关系
加刚体,运动的物体,可以运动可以受力。
不加刚体,静止的物体。
只有刚体而没有形状的话,相当于幽灵没有实质,所以需要物理形状。
在物理形状上,会有物理材质,就像不同天气,地面会有不同的地面(湿润、干燥)。

  • 刚体(Rigidbody):让一个物体具有重量具有物理属性

物体分为两种

  • 没加刚体就是静态不动物体
  • 加刚体的物体拥有物理属性可受力
  • 物理形状(Collider 碰撞器):让物体拥有形状
    • 物理材质(下雨过后地面很滑,出太阳之后地面很干,这地面可以称为物理材质)

有形状物体与物体才会发生联系

unity 第三课 (Unity C#的基本结构_类_成员_类的函数)

游戏的程序简单运作模式分为三步骤

  • 初始化

  • while循环

    • 处理游戏事件
    • 渲染游戏场景画面
    • 是否需要休眠(cpu性能好的情况才会休眠,如果性能不好就会一直执行循环,休眠是为了节约cpu,不休眠是为了给游戏换取最大的流程程度

      1s要维持60fps 1s 分为60份 1s / 60 = 0.01666…(0.016s)

      cpu的处理能力 如果在0.01s就已经处理完了 (事件+物理计算+渲染画面)cpu就会休眠0.006s
      如果你cpu能力无法在0.016s内完成(事件+物理计算+渲染画面)你的游戏帧数就无法达到60fps
      
      • 这个休眠就是用来维持游戏帧数在60fps上下,根据不同的cpu处理能力来决定休眠的时间
  • 游戏结束处理

手游 帧频到达60(顶峰) 虽然还能继续往上加 但是人眼是分辨不出什么差别的,60帧频就是人眼的极限了看着的画面就很流畅,如果继续增加帧频只会费电。

组件代码入口

MonoBehaviour 组件的基本规则(基类)

  • void Awake(){} 组件实例加载时调用
  • void Start(){} 在第一次update之前调用
  • void Update(){} 游戏每次刷新的时候调用
  • void FixedUpdate() {} 物理引擎每次固定刷新的时候调用,与帧频无关 主要是用于物理计算 (固定机制 根据cpu当前性能得到一个固定的频率)
  • void OnGUI() {} 绘制2D元素入口的时候调用,你如玩家的昵称,血条 绘制GUI元素

组件类和脚本文件名称要保持一致

  • 程序跑起来的时候生成的

    • 栈 在函数执行返回之后进行回收
    • 堆 在没有任何引用指向的时候进行回收
  • 程序加载启动的时候

    • 数据段、代码段 在程序关闭时 回收

unity第四课 (Unity C#表达式_条件_循环_函数传参_out关键字)

out和ref

同:
1、都能返回多个返回值。

2、若要使用 ref 和 out 参数,则方法定义和调用方法都必须显式使用 ref 和 out 关键字。在方法中对参数的设置和改变将会直接影响函数调用之处 (参数的初始值)。

异:
1、ref 指定的参数在函数调用时候必须初始化,不能为空的引用。而 out 指定的参数在函数调用时候可以不初始化;

2、out 指定的参数在进入函数时会清空自己,必须在函数内部赋初值。而 ref 指定的参数不需要。

unity第五课 (Unity_C#构造函数调用基类继承_多态_重载)

虚函数

  • 基类的引用变量来保存子类的实例
  • 使用 virtual 关键字修饰的函数就是一个虚函数,在实例类里,他会检查这个实例类的定义中是否有重新实现该虚函数(通过 override 关键字)所以说 子类如果要重写父类函数想实现C++的多态的话还得在子类函数前面用 override 关键字进行修饰。
  • 虚函数的实现:每一个类都有一个虚函数表,当一个函数为虚函数的时候就会添加进表中,当new 这个实例的时候,会生成一个指向虚函数表的引用变量,当基去调用这个函数的时候,发现为虚函数,就会用这个引用变量去虚函数表中找这个函数,因为每一个类的虚函数表
    图片还没有哦

第六课(Unity_C#数组_string_static_const_泛型编程_名字空间)

string.Format 格式化生成字符串。

1
string str = string.Format("{0} {1} {2}", "xiaohong", 10, "male");

string.Equals 两个字符串是否相等。

1
2
3
4
5
6
7
string str;
str = "Hello";
if(str.Equals("Hello")){
//相等则进
}

// == 表示比较 两个数据对象是否是同一个 如果是字符串值的比较就用这个函数

ToUpper 字符串转换为大写str.ToUpper()

ToLower 字符串转换为小写str.ToLower()

静态变量(static)、const、readonly

类中不需要使用成员变量、纯逻辑而使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class Person{

//static const 全局唯一
//使用类名.xxx
public static int pi = 3.14;
public static const int b = 2;
public const int c = 2; //不加static也行 const 不能修改的变量(常量)

//每一个实例,都会有一个readonly变量
//readonly的变量又一次修改的机会
//在对象构造的时候可进行赋值进行修改 之后就无法进行修改了
public readonly int test_readonly = 4;

//const 修饰累的成员变量,在编译的时候确定的常量,运行的时候无法修改
//readonly实例化的时候确定的常量,可以在构造函数的时候修改,之后就无法修改了
}

Debug.Log(Person.pi);

Debug.Log(Person.b);

Debug.Log(Person.c);

使用 类名.xxx,存在数据段上(全局变量,静态变量)

静态成员变量(全局唯一)
静态成员方法

静态成员函数不需要实例直接用类即可调用,由于没有对象实例,静态成员没有this,我们没法访问对象的成员和调用成员函数。处理纯粹的逻辑例如Math.sin Math.cos

泛型编程(和C++的模板很像)

假如一个类需要实现数值之间的+、-、*、/的计算,如果这个数值有int float double等多种数据类型的话,那么就需要N个类来实现这个方法了,如果使用泛型的话,只需要定义一个泛型类就可以实现N种计算
泛型使用之前需要给定一个数据类型,因为他本是是没有数据类型的
泛型可以是类可以是函数方法,如果成员方法要是泛型,那么类就先要是泛型才行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
//这是一个泛型函数
void Swao<T>(T a, T b){
T temp = a;
a = b;
b = temp;
}

//这是一个泛型类, 整数(int) Point、浮点数(int) Point
//使用之前需要给定一个数据类型 会自动隐式生成相对数据类型的类
class Point<T>{
T xpos;
T ypos;

//这是一个泛型成员函数
public void add_object(T x, T y){
this.xpos = x;
this.ypos = y;
}
}

Point<int> Point_int = new Point<int>();

//隐式生成
// class Point{
// int xpos;
// int ypos;
// }

Point<float> Point_float = new Point<float>();

//隐式生成
// class Point{
// float xpos;
// float ypos;
// }

命名空间()

为了彼此不互相冲突而有的命名空间,一般做库的时候才会使用命名空间

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

using my_namespace;//让命名空间,加入搜索列表

//制作自己的命名空间
namespace my_namespace
{
//外面虽然已经有 Ponit 类了,但是由于命名空间不一样所以可行
class Point{

}
}

class Point{

}

Point point = new Ponit();

//命名空间.xxx 每一个都加命名空间很麻烦 使用using即可解决
//加了using 即可Point mypoint = new Ponit();这样使用

my_namespace.Point mypoint = new my_namespace.Ponit();

例如上面例子,如果有一样的就会先在当前文件查找 Point,如果找不到因为using 就会去my_namespace命名空间去查找。都找不到就会报错

第七课 (unity_transform组件一)

任何一个组件,都会有一个gameObject指向它挂载的节点实例.
任何一个组件,都有个数据成员指向这个节点的transfrom组件实例的;
this.transfrom; 通过任何一个组件来获得每一个节点的transfrom组件。
同理,transfrom它也是一个组件,所以它也有一个gameObject指向它挂的节点

每一个游戏物体 都有transfrom组件 而 每一个组件都有gameObject 所有用this.gameObject 能到到当前组件的实例 this.transfrom.gameObject 也能得到当前组件实例

this.gameObject 与this.transform.gameObject是一样的
this.transform.transform.transform 可以一直这样往后面写,因为this是组件,又一个transform指向它的transform组件实例, this.transform.transform 如果你闲着不累可以写多一点。

1
2
3
4
5
6
7
8
9
//this.gameObject 和 this.transform.gameObject 是一样的

this.gameObject.name //通过挂载的脚本得到name
this.transform.Find("name").gameObject.name
this.transform.Find("name");//通过名字查找节点
this.transform.FindChild("name"); //通过孩子节点的名字查找

// Find("name/name") //可填多重路径
// FindChild("name") //不能是多重路径

相对坐标/绝对坐标

编辑器里面的 postion 是相对坐标,可是代码里面的postion是绝对坐标
编辑器里面的世界坐标等于所有父节点的坐标加自己的相对坐标(绝对坐标)

1
2
3
//这里是代码 和 编辑器里面是相反的
this.transform.position //这个的是绝对坐标(世界坐标)
this.transform.localPosition; //这个的是相对坐标

第八课 (unity_transform组件二)

一个网格的大小为1m

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// 将局部坐标转成世界坐标
// 将 Vector3(0, 0, 0) 这个坐标相对于以 this.transform 为原点的坐标 转为世界坐标
// 假如 this.transform 的坐标为 Vector3(0, 0, 10) 那么 Vector3(0, 0, 0)
// 相对于 Vector3(0, 0, 10) 的世界坐标就为 Vector3(0, 0, 10) 将点
Vector3 w_pos = this.transform.TransformPoint(new Vector3(0, 0, 0));


// 就用 TransformVector(我的南方)就得到了,我的南方在地球的什么方
// 简而言之就是 来获取本地向量 在 世界的什么方向

//我的南方 不等于 地球的南方,我想知道 我的南方 在地球的什么方向 就用 TransformVector(我的南方向量)就得到了 我的南方在地球的什么方向 (TransformVector 可得到,我的本地坐标 在 世界坐标的什么 方向) 将向量
w_pos = this.transform.TransformVector(0, 0, 0);

// 将世界坐标转成局部坐标 将世界坐标的点 假如世界坐标点为 Vector3(0, 0, 10)
// 那么 local_wpos 就为 Vector3(0, 0, 5)
// Vector3(0, 0, 10) 这个点 相对于 Vector3(0, 0, 15)这个点的什么位置 Vector3(0, 0, 5)
// 世界坐标转局部坐标
Vector3 local_wpos = this.transform.InverseTransformPoint(new Vector3(0, 0, 15));

// 平移物体 1:第二个参数为 参考系 2 第一个参数为 每个方向的分量
// Tranlate(new Vector3(0,0,1),this.transform) this.transform 实例的方向
// Tranlate(new Vector3(0,0,1),Space.World) 世界坐标轴方向
// Tranlate(new Vector3(0,0,1),Space.self) 自身坐标轴方向

一个旋转主要就三种表示方法:

  • 矩阵(需要16个元素来存放,耗内存)
  • 欧拉角(万象节锁,没有平滑插值,unity顺序为 ZXY)
  • 四元素(提供平滑插值,欧拉角没有,比欧拉角多一个维度,复杂一点,增加了一个向量+外加多少度)

unity为了防止万向节锁代码里面 用的是四元素,编辑器为了只管看变化用的是欧拉角
unity transform里面为了避免万象节锁,使用的是四元数来存放一个旋转
unity编辑器里面为了直观的来旋转,使用的是欧拉角来表示

unity编辑器里面的旋转 是 欧拉角 代码里面是四元素 (unity中编辑器和代码是不一样的比如之前的position)

物体旋转

  • transform存放一个代表旋转的四元数rotation;
  • 四元数转换成对应的欧拉角eulerAngles;
  • 欧拉角转成四元数Quaternion.Euler(在原来基础上旋转,叠加需要,两个四元素相乘,把每个小变换组成一个大的总的变换,那么是用 * 和矩阵类似);
  • Rotate(旋转欧拉角度函数,在当前的基础上旋转);

第九课 (unity_transform组件三)

Unity 增量时间 Time.deltaTime 详解

  • 能达到一样的效果

    欧拉角旋转
    this.cube.Rotate(new Vector3(0, 45, 0));
    旋转向量 + 角度
    this.cube.Rotate(new Vector3(0, 1, 0), 45);

  • RotateAround

    1
    2
    3
    4
    // RotateAround 围绕一个点 旋转多少度
    Vector3 pos = new Vector3(0, 0, 0);
    // 原点 方向上 角度45/s
    this.sphere.RotateAround(pos, new Vector3(0, 1, 0), Time.deltaTime * 45);
  • LookAT

    跟看一个物体一样,一直看着这个物体不转眼

    1. 头顶方向,旋转时候饶的这个向量,默认为y (0, 1, 0)
    2. 旋转角度
      1
      2
      3
      // 第一个为 要盯着哪一个实例看 第二个为 方向 Y
      // 鼠标点击人物往哪一个方向看 这个函数就起到了关键的作用
      this.transform.LookAt(看的点的位置, new Vector3(0, 1, 0));
  • Quaternion.FromToRotation

    从开始向量,到目标向量的旋转四元素角度

    1
    2
    3
    //起点 终点 会 生成一个旋转角度
    Quaternion rot = Quaternion.FromToRotation(new Vector3(0, 0, 1), new Vector3(1, 0, 1));
    this.cube.rotation = rot;
  • Lerp

    线性插值 直线移动

    1
    2
    3
    4
    5
    // 起点         终点
    // from(0,0,0) to(10,0,0)
    // 用插值也能达到移动物体的目的 下面的代码会让cube的实例移动到(10,0,0)的位置然后停止
    // 最后一个参数用来控制移动的速度
    // this.cube.transform.position = Vector3.Lerp(Vector3.zero, Vector3.right * 10, 速度)
  • Slerp

    球面插值 球面弧线移动

    1
    2
    3
    4
    5
    6
    // 起点         终点
    // 因为球面圆形就是 0 0 0 如果from为 0 0 0就会少一个点那么他就会是直线移动而不是球的弧线
    // from(-10,0,0) 球面插值from这个不能为 0 0 0 to(10,0,0)
    // 用插值也能达到移动物体的目的 下面的代码会让cube的实例移动到(10,0,0)的位置然后停止
    // 最后一个参数用来控制移动的速度
    // this.cube.transform.position = Vector3.Slerp(Vector3.right * -10, Vector3.right * 10, Time.time * 0.1f);

第十课(C#的结构体、属性、ref)

结构体是值类型 用 new 结构体也只是调用结构体的构造函数 他并不会在堆上开堆,用的是栈内存

第十一课(unity_几何体_材质_FBX_package导入导出)

材质的常用的着色模式

  • standard
  • mobile/diffuse(漫反射)

材质mobile/diffuse

  • Tiling 分别x、y铺多少块(会受到纹理Wrap Mode的影响 这个来决定图片怎么铺)
  • Offset 0,0左下角(开始铺) 1,1右上角

纹理

  • Wrap Mode 图片铺的样式(包装模式)
    • Clamp 边缘填充(以边缘的像素进行填充)
    • Repeat 循环填充(用图片循环,平铺)
  • Filter Mode 放大缩小时的插值模式 (Point)表示不用算法会造成放大后图片比较不清晰

FBX模型

  • 模型顶点坐标
  • 纹理坐标
  • 模型动画(可以没有)

package导入导出

  • export导出
  • import导入

unity2017.3.0导入模型,模型无法修改材质问题

导入材质发现无法修改材质
图片暂时没有哦

选择导入的模型 将 location 从 use embedded materials 改成 use external materials (legacy);
图片暂时没有哦
图片暂时没有哦

修改之后再点击材质你就会发现材质可以修改了,然后按照对应的材质手动修改好就行了。

切断与原模型的联系,就算模型源文件删除了也不会影响场景里面的模型
图片暂时没有哦

第十二课(unity_Camera 摄像机详解)

  • Clear Flage
    以什么什么为背景来绘制我们的场景
    图片暂时没有哦

  • Culling Mask
    对哪一些物体是可见的

    • Everything 所有可见
    • Nothing 所有不可见

      根据Layer来勾选是否能看见物体(如果一个物体的Layer为test时Culling Mask中没有对test勾选那么这个物体就是不可见的)

  • Clipping Planes
    摄像机能看到的最近距离和最远距离
    图片暂时没有哦

  • Viewport Rect 视口大小
    图片暂时没有哦

  • Depth
    摄像机的绘制循序 大先绘制 小后绘制

  • Target Display
    可以接多个显示设备

  • Projection投射类型

    • Perspective 透视投影算法 锥形(3D转2D 用相似三角形换算出投影过来的 近的宽,远的窄)
      图片暂时没有哦
      • Field of View 视角大小 控制投影的大小
    • Orthographic 平行投影 立方体
      • Size 在纵向上一半的显示范围 可根据这个值和屏幕宽高比换算出真实宽高

        例如:size为5,屏幕宽高比为 5:4 那么他的宽就是 因为是纵向的一半所有需要乘2,高为(5 2) 5 / 4 = 12.5(算出一个比例为多少 )宽就是 5 * 12.5 = 62.5 (比例 5 : 4) 最后得到的宽高(62.5 : 50)

绘制流水线

  • 模型裁剪,剔除掉不在视椎的范围的物体(视椎裁剪)
  • 世界坐标转为摄像机坐标
  • 摄像机坐标转视口坐标 摄像机的3D坐标转为2D坐标 –> 投影,成像 投影算法:正交投影(2D UI),透视投影
  • 视口坐标转屏幕坐标

遮挡剔除 (Occlusion Culling) 默认开启

先绘制远的物体再绘制进的物体 这样近的物体就会覆盖远的物体.
如果近的物体完全把远的物体挡住了,开启遮挡剔除就不会绘制后面的,如果关闭遮挡剔除那么后面虽然看不到,但是还是会绘制出来,开始有利于游戏性能
都是以Mesh来确定是否来遮挡剔除 建模的时候就要注意区分节点,如果节点是一个那么就没法使用遮挡剔除

第十三课(unity_初识光源一)

光三个因素

  • 颜色
  • 范围
  • 强度

太阳光(平行光源)

  • 颜色
  • 范围(整个世界全局可见)
  • 强度 所有强度一致的
  • 生活参照:太阳

灯泡(点光源)

  • 颜色
  • 范围 球体范围内进行衰减,距离越近强度越强
  • 强度 从中心点开始,像四周减弱;
  • 生活参照:普通的灯泡

台灯或舞台(聚光灯)

  • 颜色
  • 范围 有特定的方向和范围, 像锥形;
  • 强度 从中心轴开始,像四周减弱
  • 生活参照:台灯、舞台灯、聚光灯

光有颜色 255 255 255 白光
假如光的强度只有0.5的白光 128 128 128

光的颜色 + 光的强度就 = 新的颜色 + 材质上的颜色做混合 = 全新的颜色

光属性

Render Mode

  • auto 自动选择
  • important 质量更好 像素进行光照
  • not import 性能更好 顶点进行光照

Cookie 使用一个带Alpha通道的纹理来制作一个遮罩,是光线在不同的地方有不同的强度

Intensity 光的强度
Draw Halo 光晕

  • 太阳光 Directional light

    • Culling Mask和摄像机是一样的

      光照对哪一些层是有效的

  • 点光 Point light
    Range 光的半径(半径越大,光的衰减就越慢,也就越亮)

  • 聚光灯 Spotlight
    Range 椎体高
    Spot Angle 锥角(越大范围越大,越小范围越小)

光源模式

Baking/2017.3.0为(Mode):

  • 实时模式(Realtime)

    优点:光照实时计算出来,可以不断的变化
    缺点:所有光照实时计算出来,消耗CPU

  • 烘焙模式(Baked)游戏静态物体,又需要阴影的可以用烘焙来达到更好的游戏性能

    优点:预先计算出来,性能好,烘焙出来之后去掉之前的光也会有光
    缺点:不能够动态的改变(烘焙出来的影子,物体移动阴影不会发生改变)

烘焙步骤:

  1. 场景中的3D物体设置成静态光照的模式;
    图片暂时没有哦
  2. 烘焙的光源设置成Baked;
    图片暂时没有哦
  3. 打开Lighting窗口,在Scene Tab下点击Build;
    图片暂时没有哦
    图片暂时没有哦
  • 混合模式(Mixed) 对动态的物体使用实时光照,对静态的物体使用烘焙

第十四课(unity_UGUI_Canvas详解)

做2D有三套UI

  • 自带的GUI
  • NGUI(广泛用来做2D界面 开源的第三方包) 需要安装

    unity5.x后 把NGUI作者找到 开发了UGUI

  • UGUI 内置的

3D世界显示2D元素

  1. 使用正交摄像机
  2. 使用透视摄像机,将2D元素移动到合适的距离
  • 像素换算米

    像素变成米然后后面再来计算,640960 单位为100 最后得到6.4 9.6
    图片暂时没有哦
    图片暂时没有哦

2D元素充满屏幕(3D摄像机做2D)

计算正确的Z距离(zeye)

tan(摄像机视角角度 0.5) = (h / 2) / zeye;
最佳Z值 zeye = h / (2
tan(30))
Z / zeye = 最佳缩放比例

  1. 用z来设置,计算出最佳的z距离(Z得固定)

    tan(30) 2 = 1.1547
    假如分辨率为 h 640
    w 960 = h 6.4 * w 9.6
    6.4 / 1.1547 = 5.54为最佳的z距离

如果2D与3D共存,上面的Z固定了可能会造成遮挡的情况,所有用下面的可以解决此问题
不管Z为多少,用Z / 最佳z距离 即可得出2D元素的缩放比例,即可完成图片铺满全屏效果

  1. 使用缩放比例进行调整设置

    假如z = 10
    z / 最佳z距离(5.54) = 合适的缩放比例(x 1.8, y 1.8)

UGUI 核心

  • Canvas(做正确的显示的 做比例适配的)
  • 控件(按钮等)
  • 事件响应(自动创建EventSystem 接收事件)

Canvas

  • Render Mode

    • Screen Space-Overlay 覆盖到屏幕的上方,就和电视的菜单一样都是在上方(没办法和3D共存)

      不支持UI组件image的材质球,显示不出来材质

    • Screen Space-Camers 需要指定摄像机 不指定就和上面是一样的

      • Plane Distance 决定2D界面和3D界面的的遮挡关系

        这个值小于3D的Z值就会挡住3D物体 大于3D就会显示3D物体

    • World Spance

      • 2D和3D完全在一起(用自己上面的换算Z和缩放比例
      • (这里的Z可决定2D和3D遮挡关系的Z了)和上面Plane Distance一样
      • Canvas先计算出最佳Z值填上去,遮挡关系要选好不要遮挡了不需要遮挡的物体
      • 再用Z / 最佳Z = 缩放比例填上缩放比例即可这个是修改Canvas的
      • Canvas下面只需要修改像素为米的单位即可完成充满屏幕的匹配(640 960改为6.4 9.6)
        图片暂时没有哦
        图片暂时没有哦
  • Pixel Perfect 勾选画质更好 更完美的填充像素(subpixel)

Canvas Scaler 比例计算

Canvas Scaler and Screen Space-Camers

  • UI Scale Mode
    • Constant Pixel Size 图片有多大就只显示多大到窗口里面(不做任何缩放) 按像素来(显示窗口就要写死分辨率的)
    • Scale With Screen Size 按屏幕来缩放 一般用这个
      • 找好标准屏幕是多少
        • Reference Resolution x 960 y 640 就会按照 960 * 640 来进行缩放

          只有同比例的缩放才会不影响大小

        • 假如我放在一个1920 1080的屏幕里面,得到比例由上面的960 640为比例缩放得到缩放比例,宽获取比例 1920 / 960 = 2, 高获取比例1080 / 640 = 1.6875
        • 如果以高度来当缩放因子,宽度就会明显得不到填充完整,因为宽需要缩放2。
        • 如果以宽度来当缩放因子,高度就会明显显示不完整(出界),因为高只需要1.6875。
        • 用Match滑动条来决定到底是以宽(1)来做缩放因子,还是以高(1)来做缩放因子
          图片暂时没有哦
        • 如果Mathch值为0.5那么就用(宽缩放因子 + 高缩放因子) / 2 = 一个差值,这个差值就是最终的缩放因子,一般我们不是用宽,就是用高来决定

          选定大的因子来做缩放就会充满铺满但是小的那一方可能会显示不完整
          选定小的因子来做缩放,就会有一方会充满不了屏幕

    • Constant Physical Size 按物理尺寸比例比例来进行缩放,如果物理尺寸不一样就会拉大或缩小

Canvas Scaler and World Spance

  • Canvas Scaler 没有作用

image组件

可用材质球制定纹理(Screen Space-Overlay不支持材质球)

  • Raycast Target

    应该认为这是光线投射的目标吗(是否可以为射线的目标)

  • Preserve Aspect

    保持图像现有尺寸(保持图片宽高比)

  • Set Native Size

    将图片尺寸设置为纹理的原始像素大小
    560 * 363最佳Z为3.14

第十五课(unity_UGUI_RectTransform组件与UI屏幕适配)

常用分辨率

  • iphone5
    • 1136 * 640
  • iphone4
    • 960 * 640
  • iPad
    • 768 * 1024
  • 800 * 480
  • 1920 * 1080
  • 1080 * 1920

Pivot

中心点 左下为0,0 右上角1,1
中心点到锚点的距离

Anchors

左下为0,0 右上角1,1
相对于父节点位置的停靠点(百分比),用来做适配用的

Canvas

与屏幕的大小一样

UI界面的屏幕适配

  1. 确定美术的设计分辨率
    图片暂时没有哦
  2. UI界面Canvas节点的配置,设置Canvas参考分辨率
  3. 背景图要能确保充满所有的屏幕,重要的景物在主流屏幕分辨率下的公共区域
  4. UI布局找准停靠点

    只需设置好父节点的停靠点父物体一般大小设置为0,父节点就是用来做停靠点的),这样做适配就不用管子节点的位置,只需要把父节点的停靠点停好,那么子节点是相对于父节点停靠的,这样就很方便了。

第十六课(Unity_UGUI_Image与Sprite(2D_and_UI))

Filter Mode 纹理的缩放模式算法

Image Type 缩放模式

  • simple

    图片缩放到目标大小

  • tiled

    图片平铺填充到目标大小

  • slice

    图片按照九宫格缩放(指定缩放区域,一些特殊的,不缩放的,比如QQ的气泡)

  • Filled

    指定区域显示,垂直,水平,圆周(cd动画)

    • Fill Method 显示的类型(圆周360)
    • Fill Origin 开始显示的方位 Botton Top Left Right
    • Fill Amount 图片显示的百分比 (0 - 1)
    • Clock wise 是否顺时针显示

Sprite Edit 配合 slice这个使用

精灵九宫格拉伸区域

PackTag

指定打包时候的标志,自动打包的标志,优化

Pixels Per Unit

以多少为一个unity单位(米) 默认100为1个单位

第十七课(Unity_UGUI_RawImage与Texture和Button)

Texture

会把贴图转为2^N次方

  • 用于显示图片的贴图(Texture/Sprite(2D and UI))
  • Alpha from Grayscal
    将灰度作为Alpha通道的值
  • WarpMode
    • 纹理寻址超过范围的填充模式
      • Clamp(边缘填充)
      • Repeat(循环填充)

RawImage

  • UV Rect 贴图纹理寻址
  • 怎么贴
    图片暂时没有哦

RawImage于Image的区别,UV Rect贴图寻址可以做一些效果如:滚动的地图,其他没有什么这种需求建议使用Image

RawImage于Image的区别

  1. Sprite只能用在Image组件上做2D and UI
  2. Sprite可以做九宫格
  3. Sprite一般用作小图,可以打Atlas(图集所有小图的集合) Packing Tag
  4. Texture基于纹理寻址模式,不能打包Atlas
  5. UI需要修改UV Rect和利用寻址模式的(滚动地图),用Texture,否者用Sprite

Button

Interactable 是否使用按钮

按钮的4种过度效果

  • Transition 鼠标的过度效果
    • None 没有任何效果
    • Color Tint 颜色过度
    • Sprite Swap 精灵过度
    • Animation 动画过度

      正常效果
      划过效果
      按下效果
      禁用效果

第十八课(Unity_UGUI_Mask_布局_Text组件)

Mask

  • Rect Mask 2D 矩形mash
    1. 创建一个空节点,指定节点的大小用于裁剪
  1. 添加Rect Mask组件
  2. 把需要裁剪的图片放在这个组件下面,就会通过这个矩形区域被裁剪掉。

    Mask节点下面的孩子只能通过mash区域可见,其他区域会被参见,只能在这个矩形区域内可见

  • Mask(很像蒙版)
    1. 创建Mash图片创建Image对象(这个图片要是不透明的,比如一个圆形头像)
  1. 在这个节点上加入Mask组件
  2. 将图像加入到这个Image孩子节点下(即可通过这个圆形头像看到圆形区域子节点的图像)
  3. 加入的孩子节点只能通过不透明区域看到

    在这个节点下面的孩子只能通过不透明的区域看到
    有毛边的话可以用一个头像外边框

Vertical Layout Group 组件

垂直布局 自动把很多个元素,垂直进行布局,不用我们去手动操作

  • Spacing
    每个元素之间的间隔
  • Padding
    排版是从哪儿开始的,距离起点的距离
  • Child Alignment
    怎么布局
  • Child Force Expand
    width和height可防止父节点大小过大时会把图片拉伸,去掉勾即可

Horizontal Layout Group 组件

水平布局 自动把很多个元素,垂直进行布局,不用我们去手动操作

  • Spacing
    每个元素之间的间隔
  • Padding 局部边缘填充
    排版是从哪儿开始的,距离起点的距离
  • Child Alignment
    孩子排版的方式
  • Child Force Expand 自适应宽高度
    width和height可防止父节点大小过大时会把图片拉伸,去掉勾即可

Grid Layout Group 组件

  • Cell Size

    每一个的大小

  • Spacing

    之间的间隔

  • Start Corner 第一个元素的位置

    从哪儿开始排的

  • Start Axis

    垂直还是水平

  • Constarint
    • Fiexible
    • Fixed Column Count 固定列数
    • Fixed Row Count 固定行数

Text 组件

1:Text 显示的文本
2: Font 使用的文字的字体;
3: FontStyle: 文字字体样式;
4: LineSpacing: 行间距; 只对垂直溢出有效
5: Alignment: 对齐方式;
7: Horizontal 水平溢出;
8: Vertical 垂直溢出;
9: RichText 多格式文本 \<color=blue><\/color>

  • Best Fit 最佳匹配方式
    • Min 字体最小能被缩小到多小
    • Max 字体最大能被放大到多大

第十九课(Unity_UGUI_Solider_Toggle_InputTextfiled组件)

Slider
1: 修改滑动条背景色 –> Background,指定图片或颜色;
2: 修改滑动条进度的颜色–>Fill Area–>Fill 指定图片或颜色;
3: 修改滑动点与按钮一样: 颜色/图片/动画/过度效果
3: 滑动的值被改变: 抛出on value Change事件;
4: 获得当前滑动条的进度值[0, 1],

可以修改这个值[min, max];

Toggle
1: 勾选框背景–>Background 颜色或图片;
2: –>Background–>Checkmark 勾选框 颜色或图片;
3: 组件节点的RectTransform 大小是响应时间的范围;
4: 获得勾选框的值isOn;
5:Toggle 勾选过度 渐变/None;
6: 每次修改,抛出on value change事件;

InputField
Selection Color 选中文本时的颜色
Read Only 勾选表示该内容无法被修改

第二十课(Unity_Recttransform_stretch_预制体_ScrollView)

更新数据

  • 预制体数据同步到物体用 Revert
  • 物体数据同步到预制体用 Apply

第二十一课(Unity_Input输入详解)

监听Input事件都是在Update里面监听

  • Input.mousePosition

    监听当前鼠标位置(以屏幕左下角为原点)

  • Input.anyKey

    持续啊亲下,直到弹起(可以是键盘也可以是鼠标)

  • Input.anyKeyDown 当前按下

    当前按下

  • Input.inputString

    返回输入的assic字符

  • Input.acceleration

    如果没有重力传感器,那么就是返回的(0,0,0)返回重力加速度传感器的值,加速度的方向

  • touches

    返回当前触摸事件

获取虚拟轴的值

  • Input.GetAxis(0 - 1有差值)/Input.GetAxisRaw(0 - 1没有差值)返回虚拟轴的值

    Eidt –> ProjectSetting –> Input
    Input.GetAxis(“Horizontal”)
    水平虚拟轴

GetAxis(“Mouse X”)
GetAxis(“Mouse Y”)

距离上一次鼠标的变化 dx dy

虚拟按键 判断按键有没有按下

  • Input.GetButton(“Firel”)

    持续按下,Firel鼠标左键

  • Input.GetButtonDown(“Firel”)

    当前按下

  • Input.GetButtonUp(“Firel”)

    当前弹起

  • Input.GetKey()

    持续按下

  • Input.GetKeyDown()

    当前按下

  • Input.GetKeyUp()

    当前弹起

  • KeyCode.xx

    键盘上的按键

判断鼠标有没有按下

Input.GetMouseButtonDown(0 1 2)左键 右键 中键
鼠标当前按下
Input.GetMouseButtonUp(0 1 2)左键 右键 中键
鼠标当前抬起
Input.GetMouseButton(0 1 2)左键 右键 中键
鼠标持续按下

GetTouche() 返回当前触控Touch对象

Input Manager参数

1: Name: 轴的名字;
2: Descriptive: 正向方向描述;
3: Button:正向反向按钮和附加按钮;
4: Gravity:复位的速度,用于按键和鼠标;
5: Dead: 小于该值的输入值, 都会被视为0,用于摇杆。
6: Sensitivity(灵敏度): 对于键盘输入,该值越大则响应时间越快,该值越小则越平滑。对于鼠标输入,设置该值会对鼠标的实际移动距离按比例缩放
7: Snap对齐: 如果启用该设置,当轴收到反向的输入信号时,轴的数值会立即置为0,仅用于键/鼠标 输入。
8:Invert 反转: 启用该参数可以让正向按钮发送负值,反向按钮发送正值。
9:Type 类型: 所有的按钮输入都应设置为 键/鼠标 (Key / Mouse) 类型,对于鼠标移动和滚轮应设为 鼠标移动(Mouse Movement)。摇杆设为摇杆轴 (Joystick Axis),用户移动窗口设为窗口移动 (Window Movement)。
10:Axis 轴: 设备的输入轴(摇杆,鼠标,手柄等)
11:Joy Num 摇杆编号: 设置使用哪个摇杆。默认是接收所有摇杆的输入。仅用于输入轴和非按键。

  • Touch

1:fingerID; 手指ID
2: position 手指位置
3: deltaPosition 距离上一次的偏移;
4: phase: 触摸相位TouchPhase.Began Moved, cancel, ended;
5: Touch事件同时会触发 GetMouseButton(0)事件,能够使用
Input.GetAxis(“Mouse X/Y”)获取触摸偏移位置;
6: 到底用Touch还是用Mouse,如果对触摸的准确性要求不高,可以使用Mouse,否则还是区别对待,使用Touch,如果要多点触控,那么自己判断,直接自己使用Touch

  • Input.Touch
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    //只有移动设备有这个touch
    if (Input.touchCount > 0) {
    Touch t = Input.GetTouch(0);
    // t.position; //触摸的位置;
    // t.deltaPosition; // 距离上一次,位置的偏移
    // t.phase // 触摸的相位,-->状态, 按下,滑动,弹起,cancel;
    // Touch,只在移动设备上能用,所以统一,鼠标和这个Touch,
    // Touch同时也会发送一个 鼠标左键的事件,模拟一次鼠标左键;
    // GetMouseButton(0), 鼠标,和触摸都可以当作鼠标事件来处理,那么
    // 我们的代码就只要写一次。单点触摸的时候。
    // 多点触摸,自己处理Touch事件。

    // 单点触控
    // t.position, --> mousePostion;
    // t.deltaPosition --> Input.GetAxis("Mouse X/Y");
    // Begin(Down 当前按下), ended(Up 当前抬起), moved(Button 持续按下)
    // end
    }

第二十二课(Unity_帧动画播放组件)

帧动画基本介绍

  • 美术准备好一个连续动作的离散图片
  • 程序在准确的时间来切换这个图片
  • 优点
    • 简单、速度快
  • 缺点
    • 资源占用相对过大

代码控制image组件

1
2
3
4
// 当前代码强制要求要加入一个Image组件
// 如果没有Image,就会自动加上。如果有就使用
// 在MonoBehaviour外面定义
[RequireComponent(typeof(Image))]

如果代码要求这个节点会依赖某一个(必须)组件[RequireComponent(typeof(Image))]可以加上这句话,如果你忘记添加该组件,不用担心这句代码会自动给你加上该组件,如果已有组件那么就使用这个组件,不会造成冲突,很实用

该播放第几张图片 = (向下取整)(经过总时间 / 图片切换间隔时间)

如果需要循环播放,直接用(把数值调整为初始化状态)

如果直接赋值为初始化可能会造成动画不流畅,所以这里用了减法,把之前叠加的数据都减去即可

  • 经过总时间 -= (切换间隔 * 总图片张数)
  • 图片索引 -= 总图片张数

第二十三课(Unity定时器事件委托节点操作)

  • 定时器 (不支持参数)

    • 一次性定时器

      • Invoke(“function_name”,time);

        多少秒后调用,只会调用一次

    • 循环定时器

      • InvokeRepeating(“function_name”,time, t);

        time秒之后开始调用,每隔t秒调用一次

      • CancelInvoke(“function_name”);

        取消一个循环定时器

  • 委托

    和订阅号一样,如果订阅号新发布了文章,那么就会推送给,每一个订阅了他的人
    订阅号发布文章 – > 订阅号告诉订阅中心(委托) –> 然后订阅中心 –> 向已经订阅了的人推送这篇文章

我们触发这个事件,我们只需要告诉委托就好了,并不需要知道,多少人需要这个事件,我们告诉委托有这个事件之后,那么他就会把这个事件推送给需要的人。

触发者:不需要关心多少人在监听(不需要关系多少人在等我们的文章,我们只需要把写好的文章发布出来,就会通知委托,然后委托把这个文章(事件)推送给需要的监听者,监听者自然也就监听到了自己感兴趣的文章(事件))
监听者:不需要关心触发者是谁,何时会发送,谁会发送,如果我们监听感兴趣的东西有了,那么就自动会收到新来的文章(就能调用得到相关事件的函数,我们只会对自己感兴趣的来进行处理)

监听者不需要管触发者,只要是自己感兴趣的触发者,自然他发的东西,监听就会自动获取得到。

监听者:设置需要委托的函数,告诉委托,有这个事件触发了,请你调用我这个函数。
委托者:当有这个事件触发以后,委托者就会调用,每一个监听者委托的函数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
// 定义一个委托类型,定义这个委托能委托什么样的函数
public delegate void EventHandler(string name, int age);

// 定义一个委托实例的引用变量
public EventHandler e;

// public event EventHandler e; event修饰的委托只能是实例内部触发

// 实例化委托实例 一般不这样写 原理是这样的 下面 用 e = a 方便一些
// e = new EventHandler(a);

// 下面是隐式创建了委托实例和这个一样 e = new EventHandler(a);
e = a
// e = a + b
// e = a + b - a
// e += a;

//委托可用 + - += -= 来增加或减少委托函数

/*
需要委托的函数 参数需要和 定义委托类型一样 string name, int age
例如

这个函数就是监听者,准备要委托的函数
void a(string name, int age){
Debug.Log("a" + name + age);
// 委托函数
}

这个函数就是监听者,准备要委托的函数
void b(string name, int age){
Debug.Log("a" + name + age);
// 委托函数
}
*/

// 触发者
// 触发委托,就会调用相对应委托的函数
e("小明", 10); //打印 小明10

// 委托可跨脚本 只要委托函数参数是委托类型的那么就可以委托
// 当触发的时候,就会调用这个委托函数

// 跨脚本也可以触发委托事件,因为获得了脚本对象的那个委托
// 如果有个需求,就是我在外部能添加委托,但是我无法触发委托呢? 在委托前面加 event
// event 修饰的委托,就只能是在它的成员函数里面触发,达到了上面的需求
  • 系统封装的委托类

    System 命名空间下
    Action

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    public Action e; // 没有参数的委托
    public Action<string> e //模板委托 最多4个参数模板 多于4个就自己定也可以
    // public delegate void EventHandler(xxx);

    // 一般用法
    // public 外部能加入委托
    // event 外部只能添加不能触发委托,委托只能由内部触发
    public event Action<int> action = null;

    // 添加委托函数
    action += b;
    void b(int num){
    Debug.Log("num = " + num);
    }

    // 触发 action
    if(this.action != null){
    //触发委托
    this.action(10); // 打印 num = 10;
    }
  • 删除节点/删除组件

    • 删除自己所在的节点,节点上所有组件全部删除
      MonoBehaviour.Destroy(this.gameObject);
    • 删除组件(删除别的节点 先获取再删除)
      MonoBehaviour.Destroy(this.Getponent().gameObject);
    • 删除组件(删除自己)
      MonoBehaviour.Destroy(this);
    • 删除组件(删除别的组件上面的组件 先获取再删除)
      MonoBehaviour.Destroy(this.Getponent());

      删除节点的时候会把挂载的所有组件都删除

上面的删除会调用OnDestroy

1
2
3
4
5
// 一般在OnDestroy里面删除委托
void OnDestroy(){
// 监听了委托删除组件的时候记得删除相应的委托
// -= 即可
}

  • 隐藏自己/禁用节点

    this.gameObject.SetActive(false);

    1
    2
    3
    4
    5
    // (禁用节点)隐藏节点时调用
    // 禁用节点之后节点的事件函数也不会执行 除非是用定时器之前定的才会执行调用
    void OnDisable(){

    }
  • 显示自己/激活节点

    this.gameObject.SetActive(true);

    1
    2
    3
    4
    // (激活节点)显示节点时调用
    void OnEnabal(){

    }

第二十四课(Unity_BmpFont的使用和第一个编辑器扩展功能)

BmpFont制作工具:GlyphDesigner(Mac系统)

  • GlyphDesigner(Mac系统)
    • 导入字库
    • 调整好想要的大小和颜色
    • 导出即可得到.fnt和对应的.png
  • 字库
    • 系统字库

      优点:省空间
      缺点:效果中规中矩,无法做特定文字

    • 自定义字库(无线字符个数)

      优点:样式自定义、灵活
      缺点:unity需要携带字库文件,占用太大空间

    • BmpFont(位图文字 有限的字符个数)

      优点:速度快,占用空间小(因为是有限的)
      能做出特殊效果的文字
      缺点:也正因为有限所有成了他的缺点

手动计算字模

BmpFont 有个图片.png 有个文件.fnt()

  • 先创建 一个字库(命名和*.fnt文件一样)
    图片暂时没有哦
  • 创建字体库 材质球(命名和*.fnt文件一样)
    图片暂时没有哦

    再拖上对应需要使用的png文件

  • 使用材质球
    图片暂时没有哦
  • 设置字体需要使用的数量
    图片暂时没有哦
  • 填写UV等参数
    图片暂时没有哦

    例如
    char id=52 x=2 y=2 width=25 height=33 xoffset=0 yoffset=0 xadvance=20 page=0 chnl=0 letter=”4”
    UV公式
    X = x / 贴图宽(2/128 = 0.015625) ; Y = 1 - y / 贴图高(1- 2/128 = 0.984375)
    W = width / 贴图宽(25/128 = 0.1953125) ; H = -1 height / 贴图高 (-33/128 = -0.2578125)
    Vert
    X = xoffset (0) ; Y = yoffset (-0.5
    height)
    W = width(25) ; H = height(33) advance = xadvance (20);

    图片暂时没有哦

扩展编辑器自动计算字摸(字体模型库)

创建文件

  • Editor(扩展编辑器代码名字一定不能错)

    Editor里面的代码不会混合项目代码
    用来扩展我们的编辑器
    一定要是Editor(这里面的代码都表示是扩展编辑器用的,而不是实际项目中用的)

  • 加入扩展编辑器的脚本CreateFontEditor.cs

  • 打开vs2017可以发现多了个带Editor的项目
    图片暂时没有哦

  • 入口菜单(点菜单就会调用 CreateFont)

    没有那个路径会生成一个新的菜单路径
    图片暂时没有哦

  • 就可以使用插件进行自动计算了
    • 图片暂时没有哦
    • 图片暂时没有哦

第二十五课(Unity物理引擎的使用(一))

Unity2D物理引擎基于Box2D封装而成

  • 刚体

    控制物体运动和受力

    • 刚体的物理参数设置:
      • UseAutoMass/Mass:刚体质量,根据密度*面积计算出来
      • Linear Drag: 线性阻尼(阻力);
      • Angular Drag: 旋转阻尼;
      • Gravity Scale: 重力缩放因子(重力);
      • is Kinematic: 静态刚体和动态刚体(是否为静态刚体,静态不动的刚体);
      • Sleep 模式: 刚体休眠模式,没有参与受力就会休眠,也可以设置永久不休眠;
      • collistionDetectionMode: 刚体碰撞检测的模式(连续/分离)
        • discrete(分离)
        • Continuous(连续)一般用于速度很快的物体,一般用于子弹检查
      • Constraints
        • Freeze Position(X Y) 这个为true则刚体不能进行移动,锁轴
        • Freeze Rotation 这个为true 刚体则不能旋转
  • 碰撞器

    是物体的形状 + 碰撞的物理参数(物理材质、摩擦力、弹力)

    • 碰撞器的物理参数设置:
      • Is Trigger 触发器 勾选表示物体是一个虚体,只会触发相应事件,不会发生物理运动
    • 矩形碰撞器
    • 圆形碰撞器(Edge)
    • 边界碰撞器(Circle)
    • 多边形碰撞器(Polygon)

第二十六课(Unity刚体组件键盘控制人物跑动跳跃物理地形编辑_Camera跟随玩家)

第二十七课(Unity碰撞管理与碰撞检测)

  • 物理引擎设置
    • 图片暂时没有哦
    • 图片暂时没有哦

物体碰撞器

节点碰撞发生的时候

它会去查询所有组件里面有没有对应的事件函数,如果有就调用这个节点上挂载事件函数

  • 碰撞开始事件
    void OnCollisionEnter2D(Collision2D c){}

    Collision2D 碰撞信息
    c.gameObject 获取与我们碰撞的节点

  • 碰撞结束事件
    void OnCollisionExit2D(Collision2D c){}
  • 碰撞持续事件
    void OnCollisionStay2D(Collision2D c){}

## 物体触发器

  • 开始触发
    void OnTriggerEnter2D(Collider2D c)

    Collider2D 碰撞信息
    c.gameObject 获取与我们碰撞的节点

  • 结束触发
    void OnTriggerExit2D(Collider2D c)
  • 持续触发
    void OnTriggerStay2D(Collider2D c)

(二十八&二十九)一个2D项目(飞机大战)

  • Screen.height(逻辑宽度,显示的高度)
  • 如果是设置的640 * 960

    图片暂时没有哦

1
2
3
4
5
6
// Canvas设置的是 设计宽高
// Game视图的是 逻辑宽高
// Screen获取的就是 逻辑宽高
// 因为屏幕适配的问题(如果设计宽高和逻辑宽高不一样,那么就需要适配一下,再来获取屏幕高度了,不然会导致对应不上) 640.0f / Screen.width = 得到缩放因子 宽度比例
// 真正的高度等于 Screen.height * 得到的缩放因子 宽度比例
// SetParent(this.bullet_root, 是否使用世界) 这个函数后面使用false 不然可能会出现大小或位置不对 用局部

[RequireComponent(typeof(Componentname))]

写好之后重新挂载到脚本才会去生成组件,如果先挂载脚本到实例,写了这个代码的话,他也不会自动帮你生成对应的组件,这点要注意。先写代码,再挂实例!

如果预制体制作需要重复工作且每一个不一样,可用Break Prefab Instance暂时切断他们的联系然后重命名再加上新的元素即可

第030课(Unity3D物理引擎(一)刚体碰撞器材质)

  • 刚体

    • Mass 质量
    • Drag 阻力
    • Angular Drag 旋转阻力
    • Use Gravity 是否受重力影响 true 不受重力影响
    • Is kinematic (是否受牛顿运动学影响): false,正常的物理计算,true运动只会在代码和动画里面受影响,普通的碰撞等都不会改变它的运动状态;
    • Interpotate: (物体运动插值模式)
      None(最近计算值) Interpolate(内插值) extrapolate(外插值)
    • Collision Detection
      • Discrete(离散模式 资源少,静止,低速)
      • Continuous(连续检测,高速体积小)
      • Continus Dynamic(动态连续物体)被使用了Continusous检测撞击的对象,使用Continus Dynamic模式;

        如果子弹使用了连续监测,那么子弹撞击的那面墙就是使用Continus Dynamic这个离散模式

    • Constraints 锁轴

    • 常用参数

      • 角速度(angularVelocity);
      • 线性速度(velocity);
      • 重心(center of mass)
      • 碰撞检测开关(detectCollisions): 默认为true, 关闭检测碰撞 false;

        把物理形状的盒子全部关闭也是可以达到上面的效果,因为只要关闭就不会发生物理碰撞了

      • intertiaTensor惯性张量 intertiaTensorRotation 惯性张量旋转;
      • 最大角速度(maxAngularVelocity);
      • 最大穿透速度(maxDepenetrationVelocity)
      • position: 刚体的世界坐标,与图像的transform.position尽量保持一致;

        物体也有个position 大致相等,刚体带动物体进行移动
        刚体先有position带动物体(并不完全一样,有微小的差异)

      • rotation: 刚体在世界坐标的旋转;
      • useConeFiction是否使用锥形摩擦,一般false; true一般不使用;
      • mass 质量
    • 常用方法
      • AddForce 给刚体一个力,方向是世界坐标
      • ForceMode 类型: t(即0.02s 系统指定)
      • f•t=m•v (force) f•t=1.0•v (Acceleration)
      • f•1.0=m•v(Impulse) f•1.0=1.0•v(VelocityChange)
      • AddForceAtPosition postion是世界体系坐标,确保坐标在物体内;
      • AddRelativeForce力方向 相对于物体坐标
      • MovePosition/MoveRotation 调整刚体到指定的位置/旋转
      • AddTorque/AddRelativeToque: 施加一个力矩(相对力矩);
      • 设置密度SetDensticy,体积是碰撞器体积;
      • Sleep: 强制刚体休眠; Wakeup 唤醒刚体;
      • SweepTest/SweepTestAll 扫描检测,返回射线碰撞到的刚体;

AddForce(世界坐标方向的力)

AddForce(力的方向 力的大小(质量 xxx),力的模式(ForceMode))

  • Force力计算模式 力会持续0.02s
    用刚体的质量给它加一个连续的力。时间0.02f
    力 f
    持续时间 t = 0.02f
    质量 m
    初速度 v0
    作用力 v1
    f t = m (v1 - v0)
    力跟质量有关系 一般用这个
  • Acceleration 力计算模式 力会持续0.02s

    把一个连续的加速度加到刚体上,忽略它的质量。时间0.02f
    持续时间 t = 0.02
    m 质量 不会受质量的影响 质量为1.0f
    f t(1.0f) = m(1.0f) (v1 - v0)

  • Impulse力计算模式 力会持续1s

    利用刚体的质量,给它加上一个瞬间的力脉冲。时间1.0f
    持续时间 t = 1.0f
    f t = m (v1 - v0)

  • VelocityChange 力计算模式 力会持续1s

    向刚体添加瞬时速度变化,忽略其质量时间1.0f
    持续时间 t = 1.0f
    m = 1.0f
    f t(1.0f) = m(1.0f) v(VelocityChange)
    它这个力只会和速度有关系

## AddForceAtPosition(世界坐标方向的力)

默认给力到重心
AddForceAtPosition(力的方向 力的大小(质量 xxx) * 给力到哪一个点(确保是在物体内),力的模式)
就像一个力打上一个球的边缘一样,会使用这个球旋转

AddRelativeForce(相对坐标方向的力,模型坐标)

AddTorque(世界坐标方向力)

AddTorque(Vector3.right * 10); 力矩方向给右边 会绕x轴旋转
给个力矩就会使这个物体做圆周运动
给绕哪一个轴旋转的力矩 就会绕哪个轴旋转

AddRelativeTorque(相对坐标方向力,模型坐标)

物理管理器

  • Gravity 重力,设置重力的大小和方向;
  • Default Material:为每个物体给一个默认的物理材质;
  • Bounce Threshold 反弹阈值,低于这个不进行反弹计算;
  • SleepThreshold 休眠阈值能量低于这个阈值休眠 E = (sqrt(v) + sqrt(A)) * 0.5

    v 速度 A 角度

  • Default Contact offset 默认接触偏差,低于该值认为刚体已经接触必须>0;
  • Solver Iteration Count 关节和连接迭代次数;
  • Raycasts Hit Triggers 射线是否命中触发器, true检测命中, false忽略触发器;
  • Enable Adaptive Force 允许自适应力 修正模拟运动状态的数值偏差;碰撞矩阵,配置层级间的碰撞关系;

物理形状/碰撞器

  • Box Collider 盒子碰撞器;
  • Sphere Collider 球体碰撞器
  • Capsule Collider 胶囊碰撞器;
  • Mesh Collider 网格碰撞器(使用这个的物体不能直接加刚体 unity 5.0);

    或勾选刚体的 Is Kinematic

  • Terrian Collider 地形碰撞器;
  • Wheel Collider 车轮碰撞器;

物理材质

每种物体的弹力,摩擦力等物理参数可能不一样,unity使用物理材质来描述它们;

  • Dynamic Friction 滑动摩擦(动态摩擦)
  • Static Firction 静态摩擦
  • Bounciness 表面弹性;
  • Friction Combine 摩擦力的混合方式
  • Bounciness Combine 弹力的混合方式;
  • Bounce Combine
    • Average 平均值

      如果一个球弹力为1 地面为0 那么弹一次 就会取他们直接弹力的平均值 弹力有衰减

    • Maximun 最大值

      球弹力为1 地面为0 最大值为1 那么球就会一直弹下去 弹力一直为1

第三十一课(Unity3D物理引擎(二)刚体碰撞检测配置与触发器配置)

## Physics

类 物理设置 代码设置物理设置

忽略两个层之间的碰撞
Physics.IgnoreLayerCollision(8, 9);

第三十二课(unity_自带摇杆与车轮碰撞器的使用)

  • WheelCollider

    x 用来控制方向 y 用来控制向前还是向后

    • motorTorque:车轮移动的力矩,为正向前,为负向后
    • steerAngle: 车轮的转向角;
    • rmp: 每分钟转多少转;
      (rmp * 360 / 60) 1s转多少度
  • 车轮

    车轮碰撞器如果需要阻力(在刚体上加)目前只知道的唯一方法

第三十三课(unity关节的基本介绍和基本使用)

unity的帮助手册 组件手册/物理组件/关节组件相关介绍

关节必须加刚体

关节

链接刚体的

  • 铰链关节(hinge Joint)

    将两个刚体束缚在一起,在两者之间产生铰链效果;

    使用

    Axis 绕选轴开始旋转

    • 创建一个圆柱体与一个立方体;
    • 调整他们的大小类是与门的形状;
    • 分别添加刚体组件;
    • 为圆柱体创建一个铰链关节(Hinge Joint)
    • 冻结圆柱体的位置和旋转;
    • 给立方体一个冲量;
  • 固定关节(Fixed Joint)

    将两个刚体束缚在一起, 相对位置保持不变,永远不会变化;

    使用

    • 创建2个球体,分别对2个球体加上刚体组件;
    • 在其中一个球体里面加入固定关节(Fixed Joint),并关联好另外的刚体;
    • 给其中一个刚体一个冲量
  • 弹簧关节(Spring Joint)

    将两个刚体束缚在一起, 相对位置保持不变,永远不会变化;

    使用

    • 创建1个立方体(墙)和1个球体,分别对2个物体加上刚体组件;
    • 在其中一个球体里面加入弹簧关节(Spring Joint),并关联好另外的刚体;
    • 给球体一个力;
  • 角色关节(Character Joint)

    应用广泛的基本关节,角色关节配合Ragdoll使用,是一个扩展的球窝关节;

    使用

    • 创建2个球体,分别对2个球体加上刚体组件;
    • 在其中一个球体里面加入角色关节(Character Joint),并关联好另外的刚体;
  • 可配置关节(Configurable Joint)

    参数可配置,可以穿件很多灵活的关节;
    可配置关节属性

    可配置关节使用

    • 创建一个立方体,创建一个球体,分别加上刚体组件;
    • 选中立方体,加入可配置关节
    • X Motion, Y Motion, Z Motion 修改为Locked;
    • 修改立方体的参数,让它固定位置;

第三十四(关节案例分析)

固定关节如果链接多了,关节质量又不一样的话,会有回弹的效果
机械手夹物体
导入导出不会导出配置的物理配置/层级关系

第三十五(蒙皮网格与布料组件的介绍和基本使用)

例如要模拟衣服,随风摆动,模拟布料需要用到蒙皮网格和布料;
蒙皮网格可以模拟出非常柔软的网格体,用于布料和角色的蒙皮功能;
蒙皮网格 + 布料组件能模拟出布料效果;

  • Skinned Mesh Renderer 蒙皮网格

    是一种网格渲染器,是一种渲染网格的方式;

  • 布料是Cloth组件;

第三十六课(粒子系统和基本使用)

  • unity创建一个粒子

    • GameObject–> Particle System;
    • 创建一个节点–>添加一个ParticleSystem组件;
  • 粒子参数

    • 粒子系统主体;

      • Duration: 粒子喷射周期;
      • Looping: 是否循环喷射;
      • Prewarm: 预热(Loop状态下预产生下一周期的粒子);
      • StartDelay: 粒子喷射延迟,Prewarm无法延迟;
      • Start Lifetime: 粒子生命周期;
      • Start speed: 粒子喷射速度;
      • Start Rotation: 粒子大小;
      • Start Color: 粒子颜色;
      • Gravity Modifier: 相对与重力加速的的重力密度(缩放比);
      • Inherit Velocity: 新生粒子的继承速度;
      • Simulation Space: 粒子系统的模拟空间;
      • Play On Awake: 是否在加载的时候播放;
      • MaxParticles: 一周内发射的例子数,多与此数目停止发射;
    • 喷射(Emission);

    • 形态(shape);

      决定了例子系统喷射的范围;

      • 主要的形状有:
        • 球体(Sphere)
        • 半球体(HemiSphere)
        • 圆锥体 Cone
        • 盒子(Box)
        • 网格(Mesh)
        • 环形(Cricle)
        • 边线(Edge)
    • 生命周期内的速度偏移(velocity over lifetime);

    • 生命周期内的限制速度(limit velocity over lifetime);
    • 生命周期内的受力偏移(Force velocity over lifetime);
    • 生命周期内的颜色(Color velocity over lifetime);
    • 颜色随速度的变化(Color by Speed);
    • 生命周期内的大小(Size over lifetime);
    • 大小随速度变化(Size by speed);
    • 生命周期内的转速(Rotation over lifetime);
    • 角速度随速度变化(Rotation by Speed);
    • 外部作用力(External Forces)
    • 碰撞(Collision)
    • 子发射系统(Sub Eimitters);
    • 纹理层动画(Texture Sheet Animation);
    • 渲染器(Render);

第三十七课(初识shader)

shader概述

  • Shader是给GPU执行的程序,中文叫做着色器;
    • 着色器

      在GPU画面绘制

  • 着色器是运行在图形处理单元上,可以让开发人员直接操作图形硬件渲染功能;
  • shader能开发出很多好的效果,UV动画,水, 雾 等一些特效, 这些用程序开发出来比较困难,性能还不好;
  • 渲染流水线, 模型投影, 定点着色;
  • shader一般主要有: 固定管线着色器, 顶点片元着色器, 表面着色器;
    • 固定管线着色器(慢慢会被淘汰);
    • 顶点shader: 干预模型形态的shader;
    • 像素shader: 干预像素着色的shader;

      模型定点运算的时候,可以加入顶点shader来干预顶点的位置;顶点着色的时候,加入像素shader来干预像素的上色;

GPU编程语言

  • 什么是Direct3D和opengl;
  • 目前面向GPU的编程语言主要有三种:
    • HLSL 语言 通过Direct3D编写的着色器程序,只能在Direct3D里面使用;
    • Cg 语言 NVIDIA和微软合作提供的语言,与C相似,Direct3D和opengl都支持;
    • GLSL语言 支持OpenGL上编写Shader程序;
  • Unity使用ShaderLab来进行着色程序的编写,对不同的平台进行编译,重点支持Cg语言;
  • Direct3D
    win微软
  • opengl
    安卓、Linux、win以外的平台等
  • 定义shader
    1
    2
    3
    4
    5
    6
    7
    8
    9
    // 定义一个Shader,每一个着色程序都要有一个Shader
    Shader “name” { // name shader名字
    // 定义的一些属性,定义在这里的会在属性查看器里面显示;
    [Propeties]
    // 子着色器列表,一个Shader必须至少有一个子着色器;
    Subshaders: {....}
    // 如果子着色器显卡不支持,就会降级,即Fallback操作;
    [Fallback]
    }

Properties

display name是在属性检查器的名字;
type: 这个属性的类型
值: 只这个属性的默认值;

  • name(“display name”, type) = 值;

    name指的是属性的名字,Unity中用下划线开始_Name;

    • 类型(type):
      • Float
      • Int
      • Color(num, num, num, num)(0 ~ 1)
      • Vector(4维向量)
      • Range(start, end)
    • 纹理属性
      • 2D: 2D纹理属性;
      • Rect: 矩形纹理属性;
      • Cube: 立方体纹理属性;
      • 3D: 3D纹理属性;
  • name(“displayname”, 2D) = “name” {options}

    • Options: 纹理属性选项
      • TexGen:纹理生成模式,纹理自动生成纹理坐标的模式;顶点shader将会忽略这个选项;
      • ObjectLinear, EyeLinear, SphereMap, CubeReflect CubeNormal
      • LightmapMod: 光照贴图模式如果设置这个选项,纹理会被渲染器的光线贴图所影响。
  • 定义

    • _Range (“range value”, Range(0, 1)) = 0.3; // 定义一个范围
    • _Color(“color”, Color) = (1, 1, 1, 1); // 定义一个颜色
    • _FloatValue(“float value”, Float) = 1 // 定义一个浮点
    • _MainTex (“Albedo”, Cube) = “skybox” {TexGen CubeReflect} // 定义一个立方贴图纹理属性;

Subshaders

  • SubShader {[Tags], [CommonState], Pass {} }子着色器由 标签(Tags),通用状态,通道列表组成,它定义了一个渲染通道列表,并可选为所有通道初始化需要的通用状态;

    • Tags {“标签1” = “value1” “key2” = “value2”}
      • 标签的类型:
        • Queue tag 队列标签;
        • RenderType tag 渲染类型标签;
        • DisableBatching tag 禁用批处理标签;
        • ForceNoShadowCasting Tag 强制不投阴影标签;
        • IgnoreProjecttor 忽略投影标签;
        • CanUseSpriteAtlas Tag,使用精灵图集标签;
        • PreviewType Tag预览类型标签;
    • Pass
      • subshader 包装了一个渲染方案,这些方案由一个个通道(Pass)来执行的,SubShader可以包括很多通道块,每个Pass都能使几何体渲染一次;
      • Pass基本语法:
        • Pass { [Name and Tags] [RenderSetup] [Texture Setup]}Pass块的Name引用此Pass,可以在其它着色器的Pass块中引用它,减少重复操作,Name命令必须大写;
  • SubShader渲染的时候,将优先渲染一个被每个通道所定义的对象。

  • 通道的类型:

    • RegularPass
      • Lighting 光照: 开启关闭定点光照 On/Off
      • Material{材质块}: 材质,定义一个使用定点光照管线的材质;
      • ColorMaterial: 颜色集 计算定点光照的时使用顶点颜色;
      • SeparateSpecular: 开光状态 开启或关闭顶点光照相关的镜面高光颜色,On/Off;
      • Color 设置定点光照关闭时的所使用的颜色;
      • Fog{雾块}: 设置雾参数;
      • AlphaTest: Alpha测试
      • ZTest: 深度测试模式;
      • ZWrite: 深度写模式;
      • Blend: 混合模式 SourceBlendMode, DestBlendMode, AlphaSourcesBlendMode, AlphaDstBlendMode;
      • ColorMask 颜色遮罩: 设置颜色遮罩,颜色值可以由RGB或A或0或R,G,B,A的组合,设置为0关闭所有颜色通道渲染;
      • Offset偏移因子: 设置深度偏移;
    • 特殊通道(UsePass/GrabPass)
      • UsePass
        • UsePass: 插入所有来自其它着色器的给定名字的通道;
        • UsePass ”Shader/Nmae”, Name为着色器通道;
        • UsePass “Specular/BASE” // 插入Specular中为Bass的通道;
      • GrabPass
        • GrabPass {}: 一种特殊通道类型,他会捕获物体所在的位置的屏幕的内容,并写入一个纹理中,这个纹理能被用于后续通道中完成一些高级图像特效,后续通道可以使用_GrabTexture进行访问(访问上一次截取到的);
        • GrabPass{“纹理名称”} 捕获屏幕内容到指定纹理中,后续通道可以通过纹理名称来访问;
  • 在通道中定义状态同时对整个子着色器可见,那么所有的通道可以共享状态;

通道示例:

1
2
3
4
5
6
7
SubShader {
Tags {“Queue”, “Transparent” }
Pass {
Lighting Off // 关闭光照
....
}
}

shader通过通道来进行达到不同显示的渲染

Fallback

  • 降级: 定义在所有子着色器之后,如果没有任何子着色器能运行,则尝试降级;
  • Fallback “着色器名称”;
  • Fallback Off;
    没有降级,并且不会打印任何警告;

Category分类

  • 分类是渲染命令的逻辑组。例如着色器可以有多个子着色器,他们都需要关闭雾效果,和混合
    1
    2
    3
    4
    5
    6
    7
    8
    // 所有的shader将把 这个给设置为Mode Off
    Shader “xxxx” {
    Categroy {
    Fog { Mode Off }
    SubShader {...}
    SubShader {...}
    }
    }

第三十八课(unity_顶点片元shader与第一个shader)

坐标空间

  • 物体空间: 3D物体自己的坐标空间 一般设计时几何体以中心为原点,人物以双脚为原点;
  • 世界空间: 3D物体在场景中的世界坐标, 整个游戏场景的空间;
  • 摄像机空间: 以观察摄像机为原点的坐标系下的坐标空间;
  • 投影成像 3D坐标转换到屏幕空间;

Unity坐标系转换

  • transform.localToWorldMatrix 局部转世界的矩阵;
  • transfrom.worldToLocalMatrix 世界坐标转局部坐标矩阵;
  • MultiplyPoint, MultiplyPoint3x4 MultiplayVector 来进行坐标变换;
  • shader中 左乘unity_WorldToObject矩阵来实现世界坐标转局部坐标变换;
  • shader中左乘unity_ObjectToWorld矩阵来实现局部转世界的转换;
  • UNITY_MATRIX_MV 基本变换矩阵 x 摄像机矩阵;
  • UNITY_MATRIX_MVP 基本变换矩阵x摄像机矩阵x投影矩阵;
  • UNITY_MATRIX_V 摄像机矩阵;
  • UNITY_MATRIX_P 投影矩阵;
  • UNITY_MATRIX_VP摄像机矩阵x投影矩阵;
  • UNITY_MATRIX_T_MV (基本变换矩阵 x 摄像机矩阵) 转置矩阵;
  • UNITY_MATRIX_IT_MV(基本变换矩阵 x 摄像机矩阵) 的逆转置矩阵;
  • UNITY_MATRIX_TEXTURE0 纹理变化矩阵;

GPU管道流水线

图片暂时没有哦

顶点片元着色器

  • 优点
    • 控制灵活
  • 缺点
    • 不能参与光照计算;
  • 在着色器中插入Cg代码段,编写在 CGPROGRAMENDCG 之间;
  • 编译指令: #pragma控制 着色器代码编译;
    • #pragma vertex name 将名称为 name 的函数编译为顶点着色器;
    • #pragma fragment name 将名称为 name 的函数编译为片元着色器;
  • 参数和返回值有语义修饰

常用语义

  • POSITION : 位置
  • TANGENT : 切线
  • NORMAL: 法线
  • TEXCOORD0: 第一套纹理
  • TEXCOORD1: 第二套纹理
  • TEXCOORD2: 第三套纹理
  • TEXCOORD3: 第四套纹理
  • COLOR: 颜色

  • 为什么加语义

    加语义是为了获取上一个工位的参数,完成之后也要语义,要传给下一个工位

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    SubShader{
    // 渲染通道
    Pass {
    // 代码的入口
    CGPROGRAM
    // 顶点着色器的入口
    #pragma vertex my_vertex
    // 怎么获取上一个工位的参数(语义绑定:看你需要上一个工位的什么工参数 就语义绑定什么参数)
    // 需要获取上一个工位的 position
    // 怎么把结果返回下一个工位(语义绑定:看你需要传递什么参数给下一个工位 就语义绑定什么参数)
    // 需要把结果 position 给下一个工位

    float4 my_vertex(float4 pos : POSITION) : POSITION{
    // UNITY_MATRIX_MVP 基本变换矩阵x摄像机矩阵x投影矩阵;把这个pos转换为 投影矩阵给下一个工位的position

    // unity5.6以前的写法
    // o.vertex = mul(UNITY_MATRIX_MVP,v.vertex);

    // unity5.6以后的写法
    // o.vertex = UnityObjectToClipPos(v.vertex);
    return UnityObjectToClipPos(pos);
    }


    // 片元着色器的入口
    #pragma fragment my_fragment

    // fixed4能保存颜色的数据类型
    // 把 color 返回给下一个工位 所有用来语义绑定 : COLOR
    fixed4 my_fragment() : COLOR{
    // 把红色返回给下一个工位进行渲染
    return fixed4(1.0, 1.0, 0.0, 1.0);
    }

    ENDCG
    }
    }

第三十九课(unity_Cg基础知识和基本使用)

基本类型表达式

* 语法和C语言类是,有对应的编译器,程序是给显卡运行;
* 可以从渲染流水线中获得对应的输入;
* 指定的输出能流入下一个流水线模块;
* 操作符号和C语言一样,可以使用 +, -, * /  <, >, <=, >= 等运算;
* Cg提供了float half double 浮点类型;
* Cg 支持定点数 fixed来高效处理 某些小数;
* Cg使用int来表示整数;
* bool 数据类型来表示逻辑类型;
* sampler*,纹理对象的句柄, sampler/1D/2D/3D/CUBE/RECT
* 内置向量数据类型: float4(float, float, float, float), 向量长度不能超过4;
* 内置矩阵数据类型: float1x1 float2x3 float4x3 float4x4;不能超过4x4;
* 数组类型float a[10]; 10个float, float4 b[10], 10个float4;
* 语义绑定 float4 a : POSITION,返回值也可以语义绑定;

## 结构体与语义

1
2
3
4
5
6
7
struct name {
// 类型 名字;
// 尽量不要使用;
返回值 函数名称(参数) {
// 如果成员函数里面使用,数据成员,该成员定义在函数前;
}
};
  • 输入语义与输出语义:
    • 语义: 一个阶段处理数据,然后传输给下一个阶段,那么每个阶段之间的接口, 例如:顶点处理器的输入数据是处于模型空间的顶点数据(位置、法向量),输出的是投影坐标和光照颜色;片段处理器要将光照颜色做为输入;C/C++用指针,而Cg通过语义绑定的形式;
    • 输入语义: 绑定接收参数,从上一个流水线获得参数;
    • 输出语义: 绑定输出参数到下一个流水线模块;
    • 语义: 入口函数上有意义(顶点着色入口,像素着色入口),普通的函数无意义;

标准内置函数

  • abs(num)绝对值;
  • 三角函数;
  • cross(a, b) 两个向量的叉积;
  • determinant(M)矩阵的行列式;
  • dot(a, b) 两个向量的点积;
  • floor(x)向下取整;
  • lerp(a, b, f), 在a, b之间线性插值;
  • log2(x) 基于2为底的x的对数;
  • mul(m, n): 矩阵x矩阵, 矩阵x向量, 向量x矩阵;
  • power(x, y) x的y次方;
  • radians(x) 度转弧度;
  • reflect(v, n) v 关于法线n的反射向量;
  • round(x) 靠近取整;
  • tex2D(smapler, x) 二维纹理查找
  • tex3Dproj(smapler, x) 投影三维纹理查找;
  • texCUBE 立方体贴图纹理查找;
  • distance 计算点的距离

Unity自带函数

  • 引用Unity自带的函数库: #include “UnityCG.cginc” Unity-->Edit-->Data-->CGIncludes;
  • TRANSFORM_TEX: 根据顶点的纹理坐标,计算出对应的纹理的真正的UV坐标;
  • 使用属性的变量: 在shader里面需要使用属性变量还需要在shader中定义一下这个变量的类型和名字;名字要保持一致;
  • 外部修改shader的编辑器上的参数值;

第四十课(顶点片元Shader实例_正波_UV动画)

顶点片元 Shader Unlit Shader

float4 fixed4 _Time

  • float4是内置向量 (x, y, z, w); float4 a; 访问单独成员a.x, a.y, a.z, a.w;
  • fixed4 是内置向量(r, g, b, a); fixed4 c; color.r, color.g, color.b, color.a;
  • float3是内置向量(x, y, z);
  • fixed3 是内置向量(r, g, b);
  • float2 是内置向量(x, y);
  • _Time: 自场景加载开始所经过的时间t,4个分量分别是 (t/20, t, t2, t3);
  • _SinTime: t 是时间的正弦值,4个分量分别是 (t/8, t/4, t/2, t);
  • _CosTime: t 是时间的余弦值,4个分量分别是 (t/8, t/4, t/2, t);
  • unity_DeltaTime: dt 是时间增量,4个分量的值(dt, 1/dt, smoothDt, 1/smoothDt),平滑时间,防止时间间隔起伏太大;

第四十一课(表面着色器的基础知识和shader结构)

标准的表面着色器 Standerd Surface Shader

  • 表面着色器包括4个函数:
    • 顶点变换函数;
    • 表面着色函数;
    • 光照模型;
    • 最终颜色修改函数;

      1 3 4可选
      表面着色器最终会被编译为一个复杂的顶点着色程序;

入口函数

  • #pragma surface 入口函数名称 光照模型 [Options]
  • suface 后面跟 表面着色的入口函数 surf(Input IN, inout SurfaceOutput o);
    • Input 结构附加数据
      • Input:包含着色所需要的纹理坐标 uv纹理名字;使用第二张纹理是uv2纹理名字;

        附加数据:

        • float3 viewDir 视图方向。
        • float4 color 每个顶点的颜色插值
        • float4 screenPos 屏幕空间中的位置。
        • float3 worldPos 世界坐标空间;
        • float3 worldRef1 世界空间中的反射向量;
        • float3 worldNormal 世界空间中的法线向量;
        • float3 worldRef1; INTERNAL_DATA 世界坐标反射向量, 但必须表面着色写入o.Normal参数;
        • float3 worldNormal; INTERNAL_DATA 世界坐标法线向量, 但必须表面着色写入o.Normal参数;
    • SurfaceOutput 结构体
      • SurfaceOutput:
        • half3 Albedo: 漫反射的颜色值;
        • half3 Normal: 法线坐标;
        • half3 Emission; 自发光颜色;
        • half Specular; 镜面反射系数;
        • half Gloss; 光泽系数;
        • half Alpha; 透明度系数;
      • SurfaceOutputStandard(继承上方):
        • half Smoothness; // 0=粗糙, 1=光滑
        • half Metallic; // 0=非金属, 1=金属
      • SurfaceOutputStandardSpecular(继承上方):
        • fixed3 Albedo;
        • fixed3 Specular;
        • fixed3 Normal;
        • half3 Emission;
        • half Smoothness; // 0=粗糙, 1=光滑
        • half Occlusion; // 遮挡(默认1)
        • fixed Alpha;
  • 光照模型:
    • 系统内置 Lambert(漫反射光照) BlinnPhong (高光光照)
    • 自定义光照: 名字为Name
      • half4 Lighting(SurfaceOutput s, half3 lightDir, half atten);
      • half4 Lighting(SurfaceOutput s, half3 lightDir, half3 viewDir, half atten);
      • half4 Lighting(SurfaceOutput s, half4 light);
        可选参数(Options):
  • vertex: name vertex顶点着色器入口函数:
    • void (inout appdata_full v) 只需改顶点着色器中的输入顶点数据;
    • half4 (inout appdata_full v, out Input o) 修改输入顶点数据,以及为表面着色器传递数据;
  • finalcolor: name 最终颜色修改函数:
    void (Input IN, SurfaceOutput o, inout fixed4 color);

其它可选参数

  • alpha: Alpha 混合模式,用户半透明着色器;
  • alphatest: varirableName Alpha测试模式,用户透明镂空着色器。
  • exclude_path:prepass 使用指定的渲染路径;
  • addshadow: 添加阴影投射器和集合通道;
  • dualforward: 将双重光照贴图用于正向渲染路径中;
  • fullforwardshadows 在正向渲染路径中支持的所有的阴影类型;
  • decal: add 附加印花着色器;
  • decal: blend 附加半透明印花着色器;
  • softvegetation 使用表面着色器,仅在Soft Vegetation 开启时被渲染;
  • noambient 不使用任何光照
  • novertexlights 在正向渲染中不适用球面调和光照或逐点光照;
  • nolightmap 在这个着色器上禁用光照贴图;
  • nodirlightmap 在这个着色器上禁用方向光照贴图;
  • noforwardadd 禁用正向渲染添加通道;
  • approxview: 对于有需要的着色器,逐顶点而不是逐像素计算规范化视线方向。
  • halfasview: 将半方向传递到光照函数中。

Unity坐标系转换

  • transform.localToWorldMatrix 局部转世界的矩阵;
  • transfrom.worldToLocalMatrix 世界坐标转局部坐标矩阵;
  • MultiplyPoint, MultiplyPoint3x4 MultiplayVector 来进行坐标变换;
  • shader中 左乘unity_WorldToObject矩阵来实现世界坐标转局部坐标变换;
  • shader中左乘unity_ObjectToWorld矩阵来实现局部转世界的转换;
  • UNITY_MATRIX_MV 基本变换矩阵 x 摄像机矩阵;
  • UNITY_MATRIX_MVP 基本变换矩阵x摄像机矩阵x投影矩阵;
  • UNITY_MATRIX_V 摄像机矩阵;
  • UNITY_MATRIX_P 投影矩阵;
  • UNITY_MATRIX_VP摄像机矩阵x投影矩阵;
  • UNITY_MATRIX_T_MV (基本变换矩阵 x 摄像机矩阵) 转置矩阵;
  • UNITY_MATRIX_IT_MV(基本变换矩阵 x 摄像机矩阵) 的逆转置矩阵;
  • UNITY_MATRIX_TEXTURE0 纹理变化矩阵;

第四十二课(通用管道的通用指令(一) LOD与渲染队列)

LOD

根据不同的LOD版本使用不同版本的shader(subshader可以写多个,但是只会执行一个,如果都不执行,那么就会执行fallback “Diffuse”)

外部设置LOD值,每个Subshader有个内部LOD值,(找到第一个大于或等于)当外部设置maximumLOD这个值大于等于这Subshader内部LOD值的时候就执行这个Subshader(根据设置LOD值来取决于执行哪一个Subshader)
也可以说Subshader内部LOD值小于等于 外部设置maximumLOD 最大值的时候就执行这个Subshader

  • LOD Level of Detail, 根据LOD来设置使用不同版本的Shader;
  • 着色器中给SubShader一个LOD值,程序来设置这个shader的LOD值,只有第一个大于等于LOD值subShader才会被执行;
  • 每个shader最多只会有一个SubShader被使用;
  • 通过Shader maximumLOD来设置最大的LOD值;
  • 设置全局的LOD值,Shader.globalMaximumLOD;
  • Unity内置着色器分LOD等级:
    • VertexLit kind of shaders 100
    • Decal, Reflective VertexLit 150
    • Diffuse 200
    • Difuse Detail 250
    • Bumped, Specular 300
    • BumpedSpecular 400
    • Parallax 500
    • Parallax Specular 600
      如果需要写多个Subshader并想通过修改maximumLOD来进行改变使用哪一个shader一定要注意
      内部Subshader的LOD值一定要从大到小排序(找到第一个小于或等于的颜色 可能进行覆盖),不然可能会造成和想象不一样的结果

渲染队列

先绘制前面和2D(后向前)不一样

  • 渲染队列(Queue)标签可选值:
    • Background 背景,对应的值为1000;
    • Geometry(default) 几何体对应的值为2000, 这个队列是默认的渲染队列,大多数不透明的物体;
    • AlphaTest Alpha测试,对应值为2450, alpha测试的几何体使用这种队列,它是独立于 Geometry的队列,它可以在所有固体对象绘制后更有效的渲染采用Alpha测试的对象;
    • Transparent:透明,对应值3000, 这个渲染队列在Geometry被渲染,采用从后向前的次序;
      任何有alpha混合的对象都在这个队列里面渲染;
    • Overlay 覆盖对应值为4000, 这个渲染队列是最后渲染的物体;
  • Unity 渲染模式:

    可通过shader设置 "Queue"="Geometry+100"+不能有空格带上这个shader的物体将会被先渲染(ZTest off 的情况 关闭深度测试)

    • 普通物体从前向后, Alpha从后向前;
    • 渲染队列的数值决定了Unity在渲染场景物体时的先后顺序(越大越先),关闭深度测试的情况下;

第四十三课(unity_渲染通道通用指令(二))

混合模式、Alpha测试、深度测试、通道遮罩、面剔除

混合模式

  • 在所有计算完成后,决定当前的计算结果输出到帧缓冲区时,如何混合源和目标,通常用来绘制半透明的物体;
  • Blend Off 关闭混合
  • Blend 源因子,目标因子: 配置并开启混合,产生的颜色和因子相乘,然后两个颜色相加
  • Blend 源因子,目标因子, 源因子A,目标因子A: 源因子与目标因子用户混合颜色值,源因子A,与目标因子A,用于混合alpha
  • BlendOp操作命令: 不是将颜色混合在一起,而是对他们进行操作,主要有:
    Min, Max, Sub, RevSub
  • 混合因子的类型:
    • One 使用源或目标色完全显示出来;
      • OneMinusSrcColor 阶段值 (1-源颜色的值)
    • Zero 删除源颜色或目标颜色;
      • OneMinusSrcAlpha 阶段值 (1-源颜色的Alpha值)
    • SrcColor 这个阶段的值*源颜色值;
      • OneMinusDstColor 阶段值 (1-目标颜色的值);
    • DstColor 这个阶段的值* 帧缓冲颜色值;
      • OneMinusDstAlpha 阶段值 * (1-目标颜色Alpha值)
    • DstAlpha 这个阶段的值 * 帧缓冲源Alpha值
    • SrcAlpha 这个阶段的值 * 源颜色Alpha值
  • 一般放在放在Pass通道里面

    设置混合模式: Blend SrcAlpha OneMinusSrcAlpha;(再设置"Queue"="Transparent" 可见Alpha)
    源因子 [SrcAlpha 这个阶段的值 * 源颜色Alpha值] + 目标因子 [OneMinusSrcAlpha 阶段值 (1-源颜色的Alpha值)]

Alpha测试

  • Alpha测试: 阻止片元被写到屏幕的最后机会, 最终渲染出来的颜色计算出来后可通过透明度和最后一个固定值比较,如果通过测试则绘制次片元,否则丢弃此片元;
  • AlphaTest Off/On: 开启/关闭Alpha测试,默认是关闭的;
  • 比较测试值的模式:
    • Greater >, GEqual >=, Less <, LEqual <=, Equal ==, NotEqual !=,
    • Always (永远渲染), Never(从不渲染);
  • AlphaTest 条件 [变量] / 常数,

    变量加上[变量名]

  • 一般放在放在Pass通道里面;

## 深度测试

  • 为了使近距离的物体挡住远距离的物体,当片元写入到缓冲的时候,需要将片元的深度值与缓冲区的深度值进行比较,测试成功写入帧缓冲区;
  • ZWrite 深度写开关, 控制是否将深度Z的片元写入缓冲区中,如果不绘制透明物体设置为On, 否则的话设置为Off,默认为On;
  • ZTest 深度测试模式: 设置深度测试的执行方式,默认为LEqual,深度测试的模式:
    Less <, Greater >, LEqual <= , GEqual >=, Equal ==, NotEqual !=, Always 总是绘制,关闭深度测试;
  • ZTest 条件
  • 一般放在放在Pass通道里面;

通道遮罩

  • 通道遮罩可以是开发人员指定渲染结果输出的通道,而不是通常情况下的RGBA四个通道;
  • 可选的是RGBA的任意组合以及0,如果为0意味着不会写入到任何通道;
  • ColorMask RGBA(可选)
  • ColorMask 0什么也不输出

面剔除

  • 通过不渲染背对摄像机的几何体的面来提高性能优化错误,所有的几何体都包含正面和反面
  • 面剔除操作,大多数都是封闭的物体,所以不需要绘制背面;
  • 面剔除操作:
    • Cull Back: 不绘制背对摄像机的面,默认项
    • Cull Front, 不绘制面向摄像机的面,
    • Cull Off, 关闭面剔除操作

第四十四课(unity_Shader抓屏通道_多条件编译_Shader注意事项)

GrabPass

  • 使用抓屏通道, GrabPass {} 或 GrabPass { “ 纹理名称”}; _GrabTexture 变量访问
  • 后续的Pass通道使用这个抓屏;
  • 编写案例
    • 创建一个顶点片元着色器;
    • 将这个着色器放到Overlay队列(保证绘制在最上层)
    • 使用GrabPass通道截屏,并定义好变量来接收
    • 设置顶点的UV坐标;
    • 着色使用截图的纹理

常用的gcinc

  • cginc文件: 宏,帮助函数等,放在CGIncludes下面,开发人员可以开发自己的cginclude文件
  • 常用的cginc文件:
    • HLSL.Support.cginc 协助多平台开发的一些宏等,自动包含
    • UnityShaderVarirables.cginc 全局变量,自动包含;
    • UnityCG.cginc 常用的帮助函数;
    • AutoLight.cginc 光照和阴影功能;
    • Lighting.cginc 表面着色器的光照模型;
    • TerrainEngine.cginc 地形植被的光照着色函数;

UnityCG.gcinc常用函数

  • UnityWorldSpaceViewDir: 给定对象空间的顶点位置朝向摄像机方向的世界坐标空间方向;
  • ObjSpaceViewDir: 给定对象空间的顶点位置朝向摄像机方向的对象空间方向;
  • ParallaxOffset: 计算用于视差法线贴图的UV偏移量;
  • Luminance: 将颜色转为亮度;
  • DecodeLightmap: 从光照贴图中解码颜色;
  • float EncodeFloatRGBA(float4 rgba): 将RGBA颜色编码为[0,1)的浮点数;
  • float4 DecodeFloatRGBA(float v): 将一个浮点数解码为RGBA的颜色;
  • UnityWorldSpaceLightDir 给定对象空间的顶点位置到光源的世界坐标空间方向;
  • ObjSpaceLightDir: 给定对象空间的顶点位置到光源的对象空间方向;

UsePass(通道) 复用

使用复用之后的shader就算通过代码修改值,他们也只有一种shader效果,复用就算复用不能产生两种效果

  • 编写过的pass可以重复使用,借助UsePass "ShaderPath/PASS_NAME"
  • PASS名字要大写;
    1
    2
    3
    4
    5
    6
    // 先给一个Pass(通道)一个名字 ONE
    Pass {
    name "ONE"
    }
    // 复用Pass(通道)
    UsePass "Custom/ShaderName/ONE"

multi_compile(编写不同版本的shader)

  • 通过multi_compile编译多个版本的shader;
  • 定义编译多个版本的开关 定义两个版本开关
    • #pragma multi_compile MY_multi_1 MY_multi_2;
  • 编写控制
    • #ifdef MY_multi_1 #endif
    • #ifdef MY_multi_2 #endif
  • 使用控制shader编译出不同的版本 C#中控制
    • Shader.EnableKeyword("MY_multi_1"); 打开 MY_multi_1
    • Shader.DisableKeyword("MY_multi_2"); 关闭 MY_multi_2

移动平台优化

  • 代码优化:
    • 预先计算好对应的值 sqrt(2) –> 根号2 –> 1.414..;
    • 放心的使用向量相关操作,叉积,点击,基本都是硬件实现,很高效;
    • 尽量减少函数调用减少开销;
  • 尽可能的计算放在顶点着色器中,顶点着色器的调用频率远低于片着色器;
  • 几何复杂度考量:在IOS平台视口内的顶点数不要超过100K个,IOS默认的缓冲区就是就是这么大,超过这个数字,底层会做一些操作消耗更多的资源;
  • 纹理大小为 2^n次方大小, 16, 64, 128, 256, 512, 1024;
  • 使用适当的数据类型float < half < fixed(定点数); 性能
  • 尽量慎用透明效果,透明效果GPU要逐像素渲染;

第四十五课(unity_天空盒3D拾取本地存储)

天空盒(也是种材质 skybox)

  • 一个场景是由6幅正方形的纹理图无缝拼接而成, 在视野看来位于真实的视野一样;
  • 两种天空盒:
    • 场景天空盒 Window->Lighting->Scene->Skybox,切换摄像机场景不改变;
    • 摄像机天空盒: 摄像机上添加天空盒组件,切换摄像机,天空盒被切换;

3D拾取

  • 游戏中需要用户触摸/点击 操作3D世界里面的3D物体,那么需要判断用户点击的是3D中的哪个物体;
  • 3D拾取的原理: 从摄像机到屏幕空间的触摸点发出一条射线,这条射线第一个撞到哪个3D物体就会认为哪个3D物体被用户选择;
  • 代码编写
    • 发射一条射线: Ray ray = Camera.main.ScreenPointToRay(Touch.position);
    • 检测撞到那个物体: Raycast hit; bool Physics.Raycast(ray, out hit);
    • hit.transform, 获得物体的transform组件, name可以获得被碰撞的物体的名字;
    • Camera.main获取当前我们的主Camera
    • 如果要拾取,需要有一个碰撞器

本地存储

  • PlayerPrefs类: 游戏开发中需要存储本地数据,借助这个能够实现本地存储;
  • PlayerPrefs主要方法:
    • SetInt/SetFloat, SetString: key–>value
    • GetInt/GetFloat, GetString: key–>value;
    • DeleteKey/DeleteAll 删除一个key/所有数据;
    • HasKey 判断一个Key是否存在;
    • Save 保存数据;

第四十六课(unity_2D_3D声音的使用)

2D全局声音,3D有距离衰减

  • 声音
    • 背景音乐
    • 音效;
  • 声音文件支持的格式
    • ogg
    • mp3
    • wave
    • AIFF
  • 音频管理器

    Edit-->ProjectSetting--> Audio

    • Volume: 全局播放的音量;
    • RolloffScale: 衰减因子,越大,声音衰减越快;
    • Doppler Factor: 多普勒因子;模拟多普勒效应的监听效果:0关闭, 1 高速物体的多普勒效应会 比较明显的监听的到;
    • Default Speak Mode: 设想扬声器模式;默认值为2(立体声, AudioSpeakModer);
    • SampleRate: (输出采样率);
    • DSPBufferSize: 调整DSP缓冲区大小优化延迟和性能;
    • Virutal(虚拟)/RealVoliceCount(真实): 同时播放的真实声音的数量;
    • DisableAudio: 警用音频;

音频监听器

Audio Listener,在主摄像机上面已经有了
就跟我们耳朵一样,有距离而衰减(远小近大)

  • 音频监听器在3D世界中扮演话筒的角色,他接受场景中输入的音频源,通过设备的扬声器来播放声音;
  • 当一个音频监听器挂载到场景中的一个游戏对象上,任何音源如果接近音频监听器,都会输出到计算机的扬声器中,每个场景中只能有一个音频监听器,一般会默认的添加到主摄像机上;
  • AudioClip: 声音文件

AudioSource

来帮助我们播放AudioClip的

  • 音频源: 在场景中播放音频剪辑,如果一个音频剪辑是一个3D,那么音频源就会在给定的位置,然后随着距离进行衰减,还可以在3D和2D之间进行切换;
  • 创建一个音频源:
    • 导入要播放的声音文件;
    • 创建一个节点,并加上Audio–>Audio Source组件;
    • 将AudioClip加入到AudioSource中;
    • 代码控制播放;
  • 属性:
    • AudioClip: 要被播放的文件;
    • Output: 音频剪辑通过音频混合器输出;
    • Mute: 是否静音;
    • Play On Wake:唤醒是否播放;
    • loop: 是否循环播放;
    • Priority: 播放的优先级,0最高,256最低(可以把很重要的优先级设置高一些)
    • Volume 音量 Pitch 音调 Stereo Pan立体声(-1左声道, 1右声道)
    • Min/Max Distance 衰减距离的 开始结束, 最小距离(声音保持最大量),最大距离(不再衰减)
    • patial Blend(空间混合), 0为2D音效(不会随距离而衰减), 1为3D音效
    • Spread: (3D)立体声在扬声器空间中的传播速度;
    • Min Distance(3D) 声音衰减的最小距离
    • Max Distance(3D) 声音衰减的最大距离

      Audio Listener 到 音频的距离 如果他们距离超过最大距离,就会很小或听不见

    • 衰减模式: 对数,线性核自定义模式;

AudioSettings类对声音全局设置类

1
2
3
4
5
// 获取配置
AuidoConfiguratio audio_config = AudioSettings.GetConfiguration
// ... 省略修改
// 修改之后用Reset设置好就行了
AudioSettings.Reset(audio_config);

# 第四十七课(unity_水和雾特效)

  • Unity 自己实现了水的特效,帮助我们解决游戏中水的问题
  • Unity的水集成在了Environment的环境资源包里面,导入方法是 Assert-->Import-->Environent导入,需要正式版的才能看到这个菜单,我们使用别人导出来的package;
  • 水分为Water与Water(Basic)两个文件夹,两个文件夹中都有一个Prefab预制体的文件,文件夹下面的有两个文件,对应Daytime(白天的水)和NightTimer(晚上的水),BasicX效果要差一些,但是占CPU比较低

    带Bsic的水质量低一些,相对的性能肯定好一些

  • 水是由Shader来实现的,所以要看一下水对应的shader,可以自己调节参数

## 雾

开启Unity的雾模式

window-->Lighting-->scene勾选住Fog

  • Fog的模式:
    • Linear 线性(默认)
      • 雾从start开始,接近end越浓
    • Exponential
      • 可配置参数Density,雾的浓度,浓度越大,雾越大
    • Exponential Squared
      • 可配置参数Density 越大表示雾越浓
  • Fog Color雾的颜色:可以配置雾的颜色
  • RenderSettings类
    • fogMode
      • 调整雾气的模式enum FogMode
    • fog的密度fogDensity
    • start/end可通过fogStartDistance/fogEndDistance来设置
    • 关闭雾
      • fogMode = 0

第四十八课(unity_光照(二))

  • 光照的本质:就是光的颜色和物体纹理的颜色的混合;

    光源类型

  • 点光源
  • 定向光源
  • 聚光灯
  • 区域光源(只会对static的物体有效)

    区域光的范围会在场景中用黄色的光显示出来;z轴是光的方向; 光的强度会随距离衰减
    只能配合烘培GI使用

    发光材质(必须使用在static物体上才可以)

    材质–> Emission
    Global Illumi –> Baked
    使用发光材质的物体就变成了一个发光体,只能对标记为static的物体有影响

    • 发光材质也算是一种光源,通过给物体添加特殊的着色器,调节其自发光参数可以得到一个柔和的灯光效果。发光材质可以让物体表面发光,
    • 发光材质也只能作用在被标记为static, 或LightStatic的物体上。光源的强度以2的次方速度衰减;

区域光源和发光材质都是需要依赖于静态物体,只能配置烘焙GI使用

Light组件的参数

  • Type: 灯光当前的类型;
    • Directional: 光源为平行光
    • Point: 光源为点光源;
    • Spot: 光源为聚光灯;
  • Baking: 全局光照模式 Realtime/Baked/Mix模式;
  • Range: 灯光所影响的最大访问,平行光不需要;
  • Color: 灯光发出光线的颜色;
  • Intensity: 灯光发射的明亮程度,0为关闭,1为最亮;
  • Bounes Intensity: 控制全局光照中光线的反弹强度。
  • Shadow Type: 灯光投影的阴影类型;
  • Cookie: 使用一个带有Alpha的纹理来制作一个遮罩,使光线在不同的地方有不同的亮度,当光源为点光源是必须为立方图纹理;
  • Draw Halo: 绘制光晕,如果勾选,光源会带有球形光晕;
  • Flare: (可选) 灯光耀斑,在光源位置绘制;
  • RenderMode: 灯光的渲染模式;
    • Auto自动渲染模式,更具灯光亮度和当前设置的质量在运行时确定;
    • Important: 灯光会按照逐个像素渲染,用于重要的灯光特效;
    • Not Important: 灯光一最快的速度渲染;
  • Culling Mask: 选择某些层(Layer)不受光源影响;

光照贴图

光照烘培出来的贴图,贴到模型物体上,减少运算负担,对静态物体有很好的效果;

  • 光照贴图的烘培
    • 将不同的物体,和光源预先烘焙出来,生成一个光照贴图。
    • 将要做光照烘培的物体配置成Light static模式;
    • 将光源的Light组件上的Baking选为Baked;
    • 打开烘培窗口Window-->Lighting
    • 确认无误后,在Lighting窗口中的Scene面板中选择Build进行烘培, 将Auto(自动烘培)关闭;

光照烘培的参数详解

  • Light过滤按钮:
    • type: 设定灯光的类型,可以将灯光设置成平行光,点光源,区域光,聚光灯;
    • Baking: 光源的烘培模式;
    • Realtime: 对场景的物体都采取实时光照;
    • Baked: 对静态物体采用烘培光照,对非静态物体不起作用;
    • Mixed:对静态物体使用烘培光照,对非静态物体使用实时光照;
    • Bounce Intensity: 调节间接光的强度(从一个物体反射到另外一个物体上的光)
  • Render过滤按钮:
    • Lightmap static: 游戏对象是否为static /Lightmap static,如果是游戏对象参与到GI计算光照;
    • Scale In Lightmap: 该值影响了用于选中对象的lightmap的像素数目,默认值为1.0,每个对象所占的光照图像素的比例,可以通过它来优化光照,不重要对象减少比例,重要物体来获得更多的光照图像素来优化场景;
    • Preserve UV: 保护光照图UV,若模型没在3DMax等建模软件中展示UV,必须勾选住;
    • AutoUV Max Distance: 手动设置UV最大距离;
    • AutoUV Max Angle: 手动设置UV最大角度;
    • Important GI: 让自发光物体的照射范围更大;
    • Advance Parameters: 设置光照的质量;
  • 环境光照选项(Environment)
    • Skybox: 场景中使用的天空盒;
    • Sun: 场景中的太阳光,可以为其指定一个固定的平行光源;
    • Ambient Source: 环境光的来源(默认天空盒);
    • Ambient Intensity: 环境光的强度;
    • Ambient GI: 指定环境光的光照模式是实时光照还是烘培,若两种GI模式都没有开启,该选项没有效果;
    • Reflection Source: 反射源,可以指定反射源氏天空盒或一个自定义的立体纹理图;
    • Reflection Instensity: 反射强度,可以设置来自天空盒或立体纹理图的反射强度;
    • Reflection Bounce: 反射计算的次数;
  • Bake GI参数:
    • Bake Resolution: 烘培的分辨率: 若该值为10,代表每一个单位分布10个纹理像素;
    • Blake Padding: 在LightMap中不同物体烘培图的间距;
    • Compress: 是否压缩光照贴图;
    • Ambient Occlusion: 烘培光照图产生一定数量的环境阻光,环境阻光计算每一点被一定距离内的其他物体或一定距离内自身物体的挡住的遮挡程度(用来模拟物体表面环境光以及阴影的覆盖比例,达到全局光照的效果);
    • Final Gather: 控制从最终聚焦点发射出的光线的数量,较高数值可以达到更好的效果;
  • Other 设置可以设置光晕的效果;

第四十九课(unity光照(三)法线贴图与阴影设置详解)

法线贴图

  • 法线贴图是凹凸贴图技术上 的一种应用,有时也称为Dot3(仿立体)凹凸纹理贴图;
  • 法线贴图是在不增加多边形的情况下,增强模型的细节;
  • 法线贴图是高精度模型导出来的一种贴图,作用到低精度jing’d度模型上面,增强低精度模型的细节同时,又能获取很好的性能;
  • 法线贴图的使用:

    • 模型资源:

      美术提供

      • 低精度的模型
      • 法线贴图(normal)
      • 漫反射贴图(diffuse);
  • 创建两个Shaders材质:

    • Legacy Shaders/Diffuse(漫反射贴图)
    • Legacy Shaders/Bumped Diffuse(法线贴图)
      • 法线贴图的材质类型一定要是 normalmap
  • 代码切换材质:
    • 获取到材质然后materials使用new数值来确定需要使用多少个材质,需要多少就new多少个

阴影的设置(光照)

  • unity可以通过修改阴影的参数来对阴影的质量进行设置;
  • unity使用阴影贴图来显示阴影的,阴影贴图可以看作是灯光投射到场景的阴影通过纹理贴图的形式表现出来;
  • 阴影的质量取决于两个方面:

    • 贴图分辨率(Resolution)
    • 阴影的类型(Type)
      • Hard 硬件(GPU绘制 性能更好 不是所有显卡都支持硬件阴影绘制)
        • 硬阴影是GPU运算的,不影响CPU的性能和内存;hard比较生硬,但是软阴影比硬阴影要消耗更多的资源
      • softer 软件(会影响CPU)
        • 处理更好,但是消耗也就更大
          Resolution设置(默认设置的是Quality设置里面的):
      • 低质量(Low Resolution)
      • 中等质量(Medium Resolution)
      • 高质量(High Resolution)
      • 极高质量(Very High Resolution)

        越高,越清晰,消耗也越大;

  • 用户将阴影设置为 Use Quality Settings使用的是全局的设置参数:
    Edit-->Project Settings->Quality 中和阴影相关的参数。

  • Shadows Type: 设置阴影的类型;

  • Shadow Resolution 阴影设置分辨率,分辨率越高,开销越大
  • Shadow Projection 阴影投射, 平行光的投射投影有两种:
    • Close Fit渲染高分辨率阴影
    • Stable Fit渲染低分辨率阴影
  • Shadow Distance: 相机阴影可见的最大距离,超过这个距离阴影不会被计算;提升性能,不会绘制超过范围的阴影
  • Shadow Casades : 阴影重叠,重叠数目越高,质量越好,开销越大;

阴影的优化

  • 静态物体: 尽量使用光照贴图,将阴影预先烘培出来,不实时的计算阴影;
  • 设置分辨率和阴影类型,适当降低开销,硬件阴影的消耗要比软件阴影的小;
  • 设置阴影在摄像机范围的显示距离;
  • 并非所有的显卡都支持硬件阴影,要做好测试;

第五十课(unity光照(四)渲染路径_颜色空间_Cookies_Flare_光照过滤)

渲染路径和颜色空间

Edit-->ProjectSetting Player-->Inspector-->Other Setting --> Rendering Path

  • Unity光影效果可以通过设置 渲染路径和颜色空间
  • 渲染路径(Rendering Path): 计算光照的着色方式;
    • forward: 着色时根据对应影响的灯光,每个光源着色一次,多个光源作用时会着色多次;
      • 优点: 快速,硬件要求低,快速的处理透明;
      • 缺点: 每个光源都要有对应的成本,大量光源反而降低;
    • Deferred: 延迟渲染路径,将光的颜色着色到几何缓冲器,生成一个屏幕空间的贴图
      • 优点:大量 realttime 光源模式的时候更真实;
      • 缺点:需要硬件水平要求高;
    • Legacy Vertex Lit: 顶点照明,所有的光照只会在顶点上计算
      • 优点:速度快,最广泛的硬件支持。
      • 缺点:但是不支持阴影,法线贴图,灯光遮罩,高精度的高光等;
    • Legacy DefferredDefferfed类似,但是是不一样的算法;Path`
  • 颜色空间(Color Space): 色彩空间决定采用哪种算法计算照明或材质加载时候的颜色混合

    • Linear颜色空间
    • Camma颜色空间
  • Cookies(光照过滤)

    会通过一张纹理图的Alpha通道来决定光照能透过什么光照射下来
    Cookies size 越小值越密集(控制密度)

    • 设置一下导入的纹理,在纹理属性的面板上选择Cookies
      • 配置好Cookies对应的光源类型(Light Type);
      • 可勾选住Alpha from Grayscale选项;
    • 在平行光源中只要把一张带着透明通道的纹理图或者灰度图拖动到光源上的Cookies上;

镜头光晕

  • 镜头光晕又叫耀斑,模拟摄像机镜头内的一种光线折射的效果太阳对准摄像机镜头才有的效果));
  • 耀斑的制作: creator-->Lens Flare来制作一个耀斑,一般由美术和特效人员完成;
  • 耀斑的使用(物体也可以加光晕):
    • 打开光源组件,将耀斑文件拖入到光源的Flare选项里面;
    • 物体中添加耀斑组件Lens Flare然后关联耀斑文件资源;
  • 图片暂时没有哦

第五十一课(unity光照(五)Stander着色器mobilediffuse着色器光探头的原理和使用)

  • Mobile Diffuse

    • 漫反射着色器, 最简单的3D着色模式;
    • 模型顶点,到顶点的纹理坐标,到着色的时候选择纹理上的颜色着色;
    • 在光照之前,你把纹理本来的颜色着色上去,参数光照,漫反射部分;
  • 标准着色器(Standard)

  • 基于物理学的着色是用模拟现实的方式呈现出材质和灯光之间的相互作用, 基于物理学的着色器给用户营造出连续性。;
  • 为了表现出真实的灯光, 标准着色器模仿了能量存储(物体反射的光源不大于它接收的光源), Fresnel反射(视线不垂直于物体表面时夹角月小,反射越明显), 表面遮蔽;
  • Stander着色器的参数:

    • Rendering Mode: 四种渲染模式;
    • Albedo: 漫反射纹理图,也可以设置颜色和透明度, 纹理颜色 + 调和颜色;
    • Metallic: 金属性,值越高,发射效果越明显;
    • Smoothness:影响反射时表面的光滑程度,值越高,反射效果越清晰;
    • Specular: 高光。颜色可以自行设置;
    • Normal Map: 法线贴图;
    • Height Map: 高度图, 通常是灰度图;
    • Occlusion: 环境遮罩贴图。
    • Emission: 自发光属性,开启后类是于一个光源,可以调节GI模式;
    • Detail Mask: 细节遮罩贴图。当某些地方不需要细节图,可以使用遮罩图进行设置。
    • Tiling: 贴图的重复次数;
    • Offset: 贴图的偏移量;
    • Secondary Maps: 细节贴图

      增强效果和细节

  • Rending Mode

    • Standard Shader有四种渲染模式;
      • Opaque 模式:这种模式代表该着色器不支持透明通道, 物体是完全不透明的;
      • Cutout 模式:这种模式下支持透明通道,要么就透明,要么就完全不透明,图片内容是否透明,由Albedo的Alpha值和Alpha Cutoff决定的。适合制作叶子,草灯,带有透明通道的图片但不希望出半透明效果的材质;
      • Fade 模式: 褪色模式,改模式下可以操作Albedo的Color的Alpha值来操作材质的透明度,比较适合做物体渐渐淡出的动画效果, 当Alpha值降低了以后,表面的高光和反色也会变淡;
      • Transparent 模式: 这种模式下的材质通过Albedo的Color的Alpha值来操作材质的透明度
        当物体为半透明的时候,表面的高光和反色不会变淡;

        Fade和Transparent的区别在于 表面的高光和反色前者会变淡,后者不会变淡

  • Occlusion Map

    遮挡图,是一种模型的表面应该接受多少间接反射的图片
    一个表面凹凸不平的物体,在其凹下的地方应该接受较少的间接光照,遮挡图是一张灰度图。

    • 白色(255)表示完全的间接照明
    • 黑色(0)表示完全不接受间接照明;
      通过灰度图来控制间接光照的多少,来让光照的模型更加真实
  • Secondary Maps

    • 又叫Detail Map,材质的次级贴图,又叫细节贴图,它的作用是展示第一组贴图中没有显示的材质细节效果, Unity允许用户在一个材质上添加一个次级的漫反射贴图和法线贴图;

光探头

如果场景使用了光照贴图(对静态物体进行了烘焙),这时候我的游戏人物走到这个静态物体边,不会受到周围环境光照的影响,这肯定是不符合逻辑的,又不能使用实时计算光照(效率消耗比较高),光探头就是用来做这个事情的。预先把这几个场景点放若干个采集点,人物就算走到已经使用了光照贴图的环境也会受光照的影响。
光探头作用:主要用作在静态物体烘焙之后,使我们动态物体能更好的融入静态烘焙之后的环境,更加逼真的效果。

  • 光照贴图都是应用于静态物体,如果一个非静态物体在烘培好的Lighting map的场景中移动,这样的不能很好的融合到烘培好的场景中,最理想是实时计算,但是达不到理想的效果,针对这种提出光探头;
  • Light Probes 的原理是在场景中放上若干采样点,收集采样点的周围的光暗信息;然后在附近的几个点围城的区域内进行插值,将插值结果作用到动态物体上;
  • Light Probes应用细节:
    • 没有必要在光影无变化的区域内布置多个采样点;
    • 在动态物体的活动空间来进行部署,不必要全部空间都部署;
    • 在我们的一个节点上,添加Light Probes Group组件,来进行部署光d探头
      • Add Probe 添加探头
      • Duplicate Selected 复制探头

        添加探头之后需要重新烘焙才能看出效果

    • 在不同光影的区域内布置
  • MeshRender上的光探头选项:
    • Light Probes 选项的3个:
      • off(关闭)
      • Blend Probes(默认)反射探针将被启用。
      • Use Proxy Volume(光照探头代理)

        指定光照探头。

第五十二课(unity光照(六)反射探头的使用)

在反射探头内的物体可被反射出来

反射探头

制作倒影

  • 镜子金属等具有光滑表面的物体都会反射,而游戏中计算实时反射非常消耗CPU的资源,
    unity5.0新增了一个反射探头的技术,通过采样点,生成反射Cubemap,然后通过特定的着色器从Cubemap中采样,就能显示反射效果了;
  • 反射探头(Reflection Probe)的参数:
    • type: 设置反射探头的类型
      • baked 烘焙模式 静态
      • custom 烘焙模式多一些选项
      • realtime 实时模式
    • Dynamic Object: 将场景中的没有标识为 Static的对象烘培到发射纹理中;
    • Cubemap: 烘培出来的立方体纹理图;
    • Refresh Mode 刷新模式:
      • OnAwake只在唤醒时刷新
      • EveryFrame 每帧刷新
      • Via Scripts 有脚本控制;
        Time slicing: 反射画面刷新频率:
      • All face at once: 9帧完成一次刷新(中等)
      • Individual Faces: 14帧完成刷新(性能消耗低)
      • no time slicing: 每帧刷新(性能消耗最大);
    • Importance: 权重,根据权重来混合不同Probe的反射情况;
    • Intensity: 反射纹理的颜色亮度;
    • Box Projection: 若是勾选此项, Probe的Size和Origin会影响反射贴图的映射方式;
    • Size:该探头的区域大小,在该区域内所有的物体都会应用反射(需要Standard着色器);
    • Probe Origin: 反射探头原点,会影响到捕捉到的Cubemap;
    • HDR 生成Cubemap中是否使用高动态范围图像(High Dymainc Range)这也会影响探头的数据存储位置;
    • Shadown Distance: 在反射图中的阴影距离,超过该距离阴影不会被反射;
    • ClearFlags: 设置反射图中的背景是天空盒或者是单一的颜色;
    • Background: 设置背景的颜色;
    • Culling Mask: 反射剔除,决定哪些层的物体是否进行反射;
    • Using Occlustion Culling: 烘培时,是否启动遮挡剔除(一个物体被挡住看不见,是不会绘制的);
    • Clipping Plances: 反射的裁剪平面
      • near
      • far

反射探头模式

  • Baked烘培模式: 类似于光照烘培,把反射探头设置好后,将反射信息烘培到CubeMap中,当游戏运行的时候直接使用烘培好的CubeMap;
  • Custom(自定义模式): 与Baked模式的探头的用法相同,需要手动烘培才能看到效果。Custom提供了更多的参数可以设置,如Dynamic Object,将非静态的物体烘培到发射图中,但是不会随着物体的移动而改变;CubeMap选项可以制定烘培出来的Cubemap;
  • Realtime模式: 可以实时的更新反射图,在这种类型的反射头不需要将被反射的物体勾选为Static, 能实时, 但是性能要求高;

位置大小

  • 反射探头是由物体挂载Relfection Probe来决定的,位置设置完毕后需要设置大小;
    • 根据需要反射物体的大小对探针进行摆放,场景的中心,墙壁的角落, 若有一些物体比较小又有强烈的视觉效果(比如篝火), 就需要探头距离它很近能得到很好的反射效果;
    • 放好探头后要设置探头的大小,探头的形状似一个轴对称的立方体,如果在立方体内有对应的着色器,其反射效果就会根据其所在的反射探头的区域进行显示。若有多个探头,根据权重进行混合;
    • 默认情况下探头的原点是几何中心,可以通过Probe Origin参数进行偏移,一个大的物体来反射一个从边缘接近他的点。

循环反射

  • 循环反射: 两个物体可以都反射,那么就会形成循环反射 interReflection
  • Unity不能无限循环下去,可以设置循环次数: window-->Lighting-->Environment Lighting Reflection Bounes来控制反射的次数, 最大反射次数为5次;

第五十三课(unity_光照系统(七)_Realtime_BakedGI_预计算全局光照._全局光照详解)

全局光照 GI

  • Realtime每帧都会计算光照,实时光照是不会反射的,所以它的光影显得单调;
  • Baked GI:通过烘培光照贴图的方式获得很好的光照效果,无法实时的改变光照;
  • 预先计算全局光照: Precumputed Realtime GI

    实时光照不能很好的显示间接光照的效果, 比如实时光照到一个红色的物体上。
    红色物体发生反射,本来如果是实时不会有反射,如果使用预先光照,那么会预先计算好静态物体的反射, 让物体显示出间接光效果;

  • 环境光

预先计算全局光照(物体必须为Static,才能看到效果)

如果有光照射在一个物体上,这个物体会有反射光
预先计算全局光照弥补了实时光照没有反射效果

  • 使用实时光照,编写场景;
  • 开启预先光照选项 Window --->Lighting-->Scene面板-->Precomputed Realtime GI;
  • Bealtime resolution 代表间接光照分辨率,值越高间接光照效果越明显;
  • 运行场景,看到光源角度变化后,场景中的光影效果也是实时的;
  • 通过预先光照,有效的降低了原本要在游戏中实时计算的全局光照运算数量,若用户需要在游戏中频繁的对光源修改颜色,光源方向,光源强度,一般使用预先光照;
  • 预先光照也是针对静态物体而言;对于动态物体时不会计算预先光照的;
  • 实时光照 + 预先光照 对静态物体生成反射效果;

第五十四课(unity_Mesh网格的详解)

3D建模软件

1:Autodesk 3D Studio Max 支持mac os windows;
2: Autodesk 3D Maya 支持windows
3: Cinema4D 支持mac os windows
4: Blender 开源跨平台的全能三维制作软件, 支持mac os windows, linux;

Blender 开源电影

5: Cheetah3D: 支持mac os
6: Unity与建模软件的单位比例:
unity系统单位为m, 建模软件的m的尺寸大小不一样,所以导入的时候有差异:

软件名 内部米 导入unity后的尺寸/m 与Unity单位的比例关系
3Dmax 1 0.01 100:1
Maya 1 100 1:100
Cinema 4D 1 100 1:100
Light Wave 1 0.01 100:1

网格Mesh

  • Unity提供一个Mesh类,允许脚本来创建和修改,通过Mesh类能生成或修改物体的网格,能做出非常酷炫的物体变形特效;
  • Mesh Filter 网格过滤器从资源中拿出网格并将其传递给MeshRender,用于绘制, 导入模型的时候,Unity会自动创建一个这样的组件;

    Mesh Filter 网格是从这个模型文件里面来读取我们这个模型的数据,然后通过Mesh网格绘制出来

  • Mesh 是网格过滤器实例化的Mesh, Mesh中存储物体的网格数据的属性和生成或修改物体网格的方法

    Mesh 网格负责绘制这个物体的

  • 顶点数组: 每个顶点的x, y, z坐标。Vector3对象,面与面有共用的顶点,所以为了节约内存,先存顶点,然后再存三角形;
  • 三角形索引数组: Mesh里面每个三角形为一个面,由于面与面的顶点公用,所以,用索引来表示三角形的一个面,可以节约模型内存空间, 0, 1, 2面,对应的顶点时在顶点数组中的索引,三角形顶点的顺序为逆时针为正面
  • 顶点法线: 面的法线是与面垂直的线, 严格意义上讲,点是没有法线的, 在光照计算的时候,使用法线来进行光照计算,如果一个面上所有的法线都是一样,那么光着色也一样,看起来会很奇怪,所以通过某种算法,把多个面公用的顶点的法线根据算法综合插值,得到顶点法线;
  • 顶点纹理坐标: 顶点对应的纹理上的UV坐标;
  • 顶点切线 顶点切线;

    Mesh的重要属性

  • Mesh重要的属性:
    • vertices 网格顶点数组;
    • normals 网格的法线数组;
    • tangents 网格的切线数组;
    • uv 网格的基础纹理坐标;
    • uv2 网格设定的第二个纹理坐标;
    • bounds 网格的包围盒;
    • Colors 网格的顶点颜色数组;
    • triangles 包含所有三角形的顶点索引数组;
    • vectexCount 网格中的顶点数量(只读的);
    • subMeshCount 子网格的数量,每个材质都有一个独立的网格列表;
    • bonesWeights: 每个顶点的骨骼权重;
    • bindposes: 绑定姿势,每个索引绑定的姿势使用具有相同的索引骨骼;
  • Mesh重要的方法:
    • Clear 清空所有的顶点数据和所有的三角形索引;
    • RecalculateBounds 重新计算网格的包围盒;
    • RecalculateNormals 重新计算网格的法线;
    • Optimze 显示优化的网格;
    • GetTriangles 返回网格的三角形列表;
    • SetTriangles 为网格设定三角形列表;
    • CominMeshes 组合多个网格到同一个网格;

空物体通过用代码绘制模型

获取Mesh Filter然后获取到模型数据,然后通过代码获取到Mesh模型数据,通过赋值重现模型

  • 先清空 Clear
  • 再赋值
    • vertices 网格顶点数组;
    • normals 网格的法线数组;
    • triangles 包含所有三角形的顶点索引数组;
    • uv 网格的基础纹理坐标;
    • tangents 网格的切线数组;
  • 再重新计算包围盒 RecalculateBounds
  • 再配置MeshRender组件,配置好材质

扩展三角形面

还没有图片哦

第五十五课(unity_旧版动画系统)

旧版动画系统

动画在Fbx模型里面,需要用美术给的动画帧数来切分动画。

  • 导入一个包含多个动画的模型文件;
  • 选中模型文件的Rig tab Animation –> Legacy模式(传统的/旧版);
  • 根据美术给出的时间点,来分割动画;
  • Animation组件动画播放:
    • Animation 启用自动播放时,播放的默认动画;
    • Animations 可以从脚本的访问的动画列表;
    • Play Automatically: 是否自动播放
    • Animate Physics 动画是否与物理交互;
    • Culling Type 设置动画的剔除模型:
      • Always Animate总是播放动画,
      • Base On Renders 只有渲染在屏幕上的对象才会播放动画;

代码播放

  • Animation类的Play: 播放指定名称的动画;
  • Animation类的Stop: 停止播放指定名称的动画;
  • Animation类的CrossFade动画融合: 以融合模式来切换动画;
    • 动画融合能确保动画完美的过渡,两个动作之间不会有突然切换;
    • 第一个参数下一个播放的名字, 切换的时间间隔, 淡入淡出的模式;
    • 淡出的模式:
      • PlayMode.StopSameLayer 淡入新动画时,只淡出与name同一层的动画,
      • PlayMode.StopAll 淡入新动画时,淡出所有的动画;
  • 动画混合:
    动画混合能消减游戏创建动画数量,让一些动画只应用给身体的一部分,和其它的动画配合一起使用。一个挥手动画,空闲挥手,和行走挥手,如果没有动画混合,就要为这个挥手创建2个动画,一个是空闲的挥手,一个是行走的挥手。
    AddMixingTransform 进行动画混合;

第五十六课(unity_mecanim人形动画系统(一)Avatar动画控制器)

Mecanim动画

  • 旧版动画系统只能通过代码来控制动画播放,随着动画种类变多,代码复杂度也会增加,同时动画过渡也需要非常繁琐的代码控制,为了让有经验的动画师开发动画,unity推出了针对人物角色的Mecanim动画系统;
  • Mecanim支持运动重定向(Retargeting)功能,即把动画从一个角色模型应用到另一个角色模型;
  • Mecanim允许使用“肌肉”来控制不同骨骼的运动范围;
  • Mecanim动画系统三要素:
    • Avatar: 从Mecanim系统的简化人形骨架结构到用户实际提供的骨架结构的映射;
    • Animator Controller: 动画控制器用来控制动画的播放和过渡条件
    • Animation Clip: 动画剪辑;

配置Avatar

  • 导入模型后,将模型的动画模式配置成Humanoid模式。
    • None: 无模式,
    • Legacy: 旧版动画模式,
    • Generic 其他动画模式,
    • Humanoid 人形角色动画模式;
  • 系统将会自动生成模型对应的Avatar文件, 并作为其子对象;
  • 大部分情况下Mecanim都能正确的生成Avatar文件, 图片还没有哦

    Configure这个前面会有一个勾,点击Config,能查看到
    映射情况,如果有错误,就没有勾,点击Config,手动做好人形骨骼的映射。
    完整的是绿色的,有红色的说明就是没有关联好的

  • Mapping:

    • Clear清除映射,
    • AutoMap自动映射;
  • 如果骨骼绑定正确,但是角色姿势不对(不是标准的T形),
    在Scene中可看到消息“Character not in T-Pose”,解决此问题
    可通过Pose->Enforce T-Pose或旋转其它的骨骼。

    图片还没有哦

  • Muscle配置:对于过于夸张的一些动作,可以通过对骨骼限定范围来进行调整,让这个骨骼运动在这个范围内,这样,就不用重新制作动画;

    • 点击要限制的骨骼;
    • 调整骨骼的运动范围的参数;

动画控制器配置

  • 动画控制器
  • 动画层
  • 动画状态机
    • 动画状态机必然包含有3个动画状态单元 Any State, Exit, Entry;
    • 动画状态机可以包含多个动画状态单元;
    • 还可以包含子动画状态机;
  • 动画状态单元的创建:
    1. Animator窗口中鼠标右键 在菜单中选择 Create State–>Empty;
    2. 将动画文件拖入到Animator窗口;
  • 过渡条件连接:
    (1)将鼠标放在状态单元上,鼠标右键–>Make Transition,创建动画过渡条件,并再次点击在另一个状态单元上,完成过渡条件连接;
  • 默认动画单元:
    • idle被设置为默认动画并显示为黄色, 其他动画显示为灰色,
    • 也可以在任意非默认单元上右键 Set As Default来设置默认的动画;

过渡条件

  • 动画状态机搭建完成后,需要编写过渡条件,对过渡条件设置来播放和过渡;
  • Mecanim支持过渡参数类型Float, Int, Bool, Trigger, Parameters添加对应类型的参数
  • 选中任意一个过渡条件,在属性视图中的Conditions列表中点击”+”添加参数,并为参数添加对比条件。
    Greator, Less;
  • 代码里面设置过渡条件,来控制代码播放;

第五十七课(unity_人形动画重定向与动画混合树)

重定向

  • 在人形动画系统中,所有的角色动画都属于unity定义的统一的人形,而我们的角色动画都是征询这个规矩做的,Avatar文件做好映射,那么如果我们是基于人形而调的骨骼动画,动画师调出了角色A的动画,那么角色B也可以使用这个动画师做好的动画,他们使用同样的动画和动画控制器,这样简化了动画的工作,解绑了动画师和建模师之间的工作依赖。
  • 传统的动画,每个模型要做一个动画,而人形动画可以解决这个问题。
  • 能够使包体大小变小。

角色动画的混合

  • 游戏开发过程中,有时候会有将两个动画混合成一个动画的需求,比如编写一个边跑边招手的动作,开发好了跑步的动作,和招手的动作,按照传统的方式需要重新再开发一个动作,Unity提供了角色动画的混合,能把两个动画合成一个新的动画;
  • 混合动画基于混合动画树完成,混合动画树是 动画状态的其中一种,可以加入到动画状态机,可以看作是混合后新的一个动画;
  • 创建步骤:
    • 创建Blend Tree, create—> From New Blend Tree,
    • 添加要混合的动画到动画列表,可以添加,也可以删除;
    • 配置混合参数,来进行混合。
    • BlendTree支持子树

动画混合算法

  • 1D混合方式: 最简单常用的混合方式,每个被混合的子动画都会被分配一个可以修改的Float的值,开发人员可以修改这个值来改变混合动画的效果,混合结果中比例越大,就越靠近那个动画;
  • 2D Simple Directional混合方式:以两个混合参数作为混合结果的横竖坐标值,混合的动画以正方形的方式混合在面板中。各自的混合比例用正方型外围的圆圈表现出来;每个动画的分布也以颜色的深浅表现出来;
  • 2D Freeform Directional混合方式:每个源动画都有一个放射性的显示面板,颜色越白权重越大。反之越小,可以通过移动源动画点,对现实面板进行调整;
  • 2D Freeform Cartesian混合方式: 源动画相连渐变表示,混合面板中颜色的深浅表示了各自动化的在混合动画中的权重;

第五十八课(unity_动画单元代码控制_代码生成动画控制器)

动画状态代码控制

  • 每个动画状态,比如进入状态,离开状态, 等都有可能需要代码来参与和处理,比如,进入这个动画单元后做哪些事情,来开这个动画单元后做哪些事情,为了解决这个问题,unity允许每个动画单元来绑定一个脚本代码,这个脚本代码必须继承于StateMachineBehaviour;
  • 可以在动画状态的Add Behaviour上添加挂载一个脚本到动画状态;
  • StateMachineBehaviour主要接口:
    • OnStateEnter: 当动画开始播放的时候被调用;
    • OnStateUpdate: 每帧都会被调用;
    • OnStateExit: 当动画结束播放的时候被调用;
    • OnStateMove: 当动画被移动的时候调用;
    • OnStateIK: 当动画触发逆向运动学时调用此方法;

代码生成动画控制器

10个状态,每2个状态之间需要建立两两的联系,那么这个动画控制器就会有100个过渡条件,那么这个时候最好的方式是代码自动的生成动画控制器;

  • 扩展编辑器,来动态生成这个动画控制器,不用手动的去修改;
  • 扩展编辑器的相关的API:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    // CreateAnimCtrl.cs
    using UnityEngine;
    using System.Collections;
    using UnityEditor;
    using UnityEditor.Animations;

    // 添加菜单选项 // 例如 Assets/Create/CreateAnimCtrl
    [MenuItem("菜单路径")]

    static void Run(){
    // 入口函数
    Debug.Log("called...");

    // Step1 生成我们的动画控制文件(动画控制器)
    // Assets/res/code_anim.controller 创建的文件路径以及名字
    AnimatorController ctrl = UnityEditor.Animations.AnimatorController.CreateAnimatorControllerAtPath("Assets/res/code_anim.controller");
    // Step2 获取我们的动画状态机
    AnimatorStateMachine state_machine = ctrl.layers[index].stateMachine;

    // Step3 创建我们的动画状态
    AnimatorState[] state = new AnimatorState[10];
    for(int i = 0;i < 10;i++){

    // 添加进状态机里面
    state[i] = state_machine.AddState("state" + i);

    // 加载AnimationClip: path动画路径文件名
    AnimationClip anim = AssertDatabase.LoaderAssetAtPath("path", typeof(AnimationClip)) as AnimationClip;
    // 为每个状态指定动画:
    state[i].motion = anim;
    }

    // 两两动画组合过度
    for(int i = 0;i < 10;i++){
    for(int j = 0;j < 10;j++){ // i --> j Transition
    // 创建每个过渡的控制变量
    ctrl.AddParameter(i + "switch" + j, AnimatorControllerParameterType.Trigger);

    // 添加每一个过度动画需要的条件
    AnimatorStateTransition trans = state[i].AddTransition(state[j], false);
    trans.AddCondition(AnimatorConditionMode.If, 0, i + "switch" + j);
    }
    }
    // 设置一个默认动画状态;
    state_machine.defaultState = state[0];
    }

第五十九课(unity_地形的创建)

地形的基本组件

  • 创建一个地形Object: GameObject—> 3D Object–>Terrain;
  • 这个节点包含了两个组件,Terrain 和 Terrain Collider;
  • Terrain Collider 地形与物理引擎方面的组件,实现了地形的物理模拟,似的其他的挂载了物理碰撞器的物体能够与地形进行物理交互;
  • Terrain Collider的参数含义:
    • Material: 该地形的物理材质, 通过改变参数可以分别开发出像戈壁滩,软草地之类的效果;
    • Terrain Data: 地形数据,用户存储该地形的地势以及其他的信息;
    • Enable Tree Colliders: 开启树木参与碰撞检测;(一般是去掉的)
  • Terrain 组件中有一排按钮分别对应了地形引擎的各项操作和设置;

Terrain

Raise And Lower Terrain(升高/下降地形)

  • Brushes: 画笔样式,使用不同样式绘制对应的地形;
  • Brush Size: 画笔大小,其实际含义为画笔直径长度,单位为m;
  • Opacity: 画笔透明度,值越大,调整的强度越大,反之越小;
  • 点击和拖动鼠标可以使得鼠标点过的地方突起,同时按下shift,可以实现下凹的功能;
  • 没有经过操作的是没有办法拉低的,水平面是动不了的;

Paint Height(涂料高度)

  • Brushes: 画笔样式。使用不同的画笔样式,可以绘制出相应样式的地形;
  • BrushSize: 画笔的大小长度为米为单位;
  • Opacity: 画笔的透明度,值越大,调整的强度越大,反之越小;
  • Height: 制定高度值;
  • Flatten: 使得整个地形的高度值都设置为指定的高度值,使得整个上移和下沉;

Smooth Height(平滑高度)

  • Smooth Height:在处理过程中某些地形比较突兀,山峰过于锐利,需要对地形做平滑处理
  • Brushes: 画笔样式。使用不同的画笔样式,可以绘制出相应样式的地形;
  • BrushSize: 画笔的大小长度为米为单位;
  • Opacity: 画笔的透明度,值越大,调整的强度越大,反之越小;

地形灰度图(导入灰度图制作地形,可以用psd文件)

  • Unity将内置的地形引擎将地形的高度信息以灰度的形式保存到一张灰度图里面;
  • 优点:
    • 存储空间小;
    • 和其他的地形工具配合使用;
  • 灰度图的使用:
    • 打开Photoshop, 设置图片的宽度为33x33(1 + 32 * x, 当x = 1时为33) 32的整数倍 + 1;
    • 制作高度图片后,将图片保存为Raw的格式;
    • 点击Terrain Setting: 导入(Heightmap–>Import Raw)刚才生成的Raw格式的灰度图,然后就会生成相应的高度图
    • 也可以把当前的地形到处高度图给其他的人使用;

Paint Texture(地形贴图)

  • 设置好地形的贴图,画笔经过的地方,都会将纹理贴到对应的地形上;
  • Brushes: 画笔样式;
  • Textures: 可绘制的纹理;
  • BrushSize: 画笔大小;
  • Opacity: 画笔透明度,值越大调整的强度就越大,反之越小;
  • Target Strength: 画笔涂抹的强度值,改值得范围为0~1,代表了地形原来的混合比例;
  • Unity也支持psd图片的格式(Photoshop的源文件格式),打包的时候会生成图片。方便了开发;

Place Trees(植树 可用预制体)

  • 可以通过涂画的方式来对树木进行种植,只要提供单个树目,就可以铺设;
  • Place Trees 参数:
    • Trees: 树木对象的预制体对象;
    • Brush Size: 画笔大小,单位为米;
    • Tree Density: 植树密度,每次植树时参生树木的棵数;
    • Tree Height: 树木的高度,可以指定也可以随机分配;
    • Lock Width to Height: 是否锁定横纵比例,保持原始高度;
    • Tree Width: 树木的宽度。

Place Details(植草 可以用psd文件(纹理)、预制体)

  • 功能与Place Trees相似;
  • Place Details 参数:
    • Brushes: 画笔样式;
    • Brush Size: 画笔大小,单位为米;
    • Details: 纹理对象列表。
    • Opacity: 画笔的透明度, 值越大,强度越大;
    • Target Strength: 画笔的涂抹强度,该值的范围为0~1。

TerrainSettings

  • Draw: 是否显示地形;
  • PixelError: 像素误差,地形的绘制精度,值越大细节越少;
  • Base Map Dist 基础图距, 当与地形距离超过该值时,则以低分辨率来显示(远处细节减少,节省性能);
  • Cast Shadows:是否进行阴影投射;
  • Materials: 材质类型: 标准,漫反射, 高光,自定义(自己指定材质);
  • Reflection Probes: 反射探头的类型: 关闭,混合探头, 混合以及天空盒探头,一般;
  • Tickness:物理引擎中该地形可碰撞的厚度;
  • Draw: 是否显示花草树木;
  • Bake Light Probes For Tree: 烘培光照探头到树木上;
  • Detial Distance: 细节距离,与相机键的细节可现实的距离值(超过这个距离不显示);
  • Collect Detail Patches: 进行细节补丁的收集;
  • Detail Density: 细节的密度程度;
  • Tree Distance: 树木的可视距离;
  • Billboard Start: 标志板的起点(公告板,总是在最前,有的树可以调整距离让它一直在前,节约性能,因为只用了一张图片就可做出树木的效果),比标志版形式出现的树木与摄像机的距离;
  • Fade Length: 渐变长度;
  • Max Mesh Trees:允许出现的网格类型的树木的最大数量;
  • Speed: 风吹过草地的风速;
  • Size: 模拟风能影响的范围;
  • Bending:草被封能吹弯的程度;
  • Grass Tint: 草地总着色量的值;
  • Terrain Width/Height/Length 地形的宽度/高度/总长度;
  • Heightmap Resolution地形灰度值的精度;
  • Detial Resolution: 细节精度值,越大,细节越精细;
  • Detial Resolution per patch: 每小块地形设置的精度值;
  • Control Texture Resolution: 将不同的纹理插值绘制到地形上时设置的精度值;
  • Base Texture Resolution:在地形上绘制基础纹理时采用的精度值;
  • Heightmap 高度图

第六十课(拖尾渲染器的使用)

拖尾渲染器(Trall Renderer)

  • 游戏中炮弹后面的拖尾, 以及汽车轮胎拖痕等绚丽特效,unity提供了拖尾渲染器;
  • 拖尾渲染器的属性:
    • Materials: 用户渲染拖尾的材质数组。
    • Size: 在材质数组总共有多少元素。
    • Element 0:用户渲染拖尾的材质的引用;个数由Size决定;
    • Time: 拖尾的长度,以s为单位;
    • Start Width: 开始位置拖尾的宽度;
    • End Width: 结束位置的拖尾宽度;
    • Colors: 拖尾长度颜色渐变的拖尾数组,也可以在这些颜色中使用Alpha;
    • Color0 ~Color4 拖尾的颜色,从开始到结束;
    • Min Vertex Distance: 拖尾锚点之间的最小距离(越小精度越高,性能也就差一些,越大精度越低);
    • AutoDesturct: 将这一项设置为允许,来使物体在静止时候后拖尾即将被销毁;

使用事项

  • Materials材质:
    使用一个包含粒子着色器的材质,材质使用的贴图必须是平方尺寸,在size属性中可以设置材质的个数,在Element属性中添加材质;
  • Trail Width: 拖尾宽度,配合时间属性可以调节他显示和表现;
  • Trail Colors: 通过5种颜色和透明度组合循环变化拖尾,使用这些颜色能控制头部和尾部之间进行渐变;
  • Min Vertex Distance最小顶点距离;
    最小顶点距离决定了包含拖尾的物体在一个拖尾段实例化之前必须经过的距离,较小的值将更频繁的创建拖尾段, 生成更平滑的拖尾,性能有损失,较大的值将显示出更多的锯齿段,找一个满足效果的最大的值;
  • 使用注意:
    • 使用拖尾渲染器不能使用其他的渲染器,(一般创建一个空节点,添加拖尾渲染组件,将拖尾要跟随的物体设置为拖尾的父亲)
    • 最好使用粒子材质,这样可以达到更好的效果;
      还没有图片哦

第六十一课(unity_navmesh网格导航寻路)

烘焙网格导航

  • 将地图元素标记为 Navigation Static
  • 调出Navigation 导航窗口,Window-->Navigation, 在改窗口下按下Bake按钮,进行网格导航烘培;
    还没有图片哦
    还没有图片哦
  • 被标记为Navigation Static的对象,都会出现青色的导航网格层,同时在Assets目录下会生成Pathing文件NavMesh.asset
  • 代理器,角色或NPC(非玩家控制角色)关联好这个组件就能够使用这个组件在在地图上行走;
  • nav mesh agent 参数:
    • Radius 代理器半径;
    • Speed代理器移动速度;
    • Acceleration 代理器加速度;
    • Angular Speed代理器角速度;
    • Stop distance 代理器到达时与目标的距离;
    • Auto Tranver OffMesh Link 是否穿过自定义路线;
    • AutoBaking 是否自动停止无法达到目的地的路线;
    • Auto Repath: 原有路线发生变化的时候,是否重新寻路;
    • Height: 代理器的高度;
    • Base Offset: 代理器相对导航网格的偏移;
    • Obstacle AvoidanceType: 代理器回避级别;
    • Avoidance Priority 代理器回避优先级;
    • Area Mask: 代理器可使用的导航网格层,unity对导航网格层以2^0, 2^1次对第0层第1层进行编码, Walkable是各层数据的和,比如3,可以在第0层和第1层移动;
  • 为了满足复杂的地形而提供的特殊组件,开发人员可以自行设计所需路线,该路线会并入到导航网络中;一并参与寻路计算;
  • Off Mesh Link含义:
    • Start: 定义路线的起始位置信息(有一个小圆圈,表示路搭好了);
    • End 自定义路线的目标位置信息(有一个小圆圈,表示路搭好了);
    • Cost Override: 自定义路线的成本覆盖;
    • Bi directional 自定义路线是否允许双线穿越(可走向start也可以走向end);
    • Activated: 是否激活改路线;
  • 自定义Area Type:
    • Walkable: 这个区域可以行走;
    • Not Walkable: 这个区域不可以行走;
    • Jump: 可跳过,将会自动生成auto-generated Off-Mesh Links

      动态障碍物(Nav mesh obstacle)

  • 导航代理在移动过程中会忽略碰撞体,所以就会穿越动态的障碍物,为了防止这个情况的发生,Unity 3D提供了一个NavMesh Obstacle组件来提供对动态障碍物的支持,这样就可以设置英雄不被穿越的效果;
  • Navmesh obstacle 参数含义:
    (1) Radius: 动态障碍物的半径;
    (2) Height: 动态障碍物的高度;
    (3) Move Threshold: 动态障碍物移动阈值;
    (4) Carve: 是否允许被代理穿越;

第六十二课(unity_协程_多线程_WWW类)

协程

  • 在主进程中开启另外一段逻辑处理,来协同当前程序的执行,但与多线程不同都是在主线程里面执行的,通过StartCoroutine方法来启动一个协程;
  • StartCoroutine是MonoBehaviour的一个方法,改方法可以启动一个协程,协程必须要是一个
    IEnumerator 作为返回值的方法;

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    // 用协程的方式来执行这个函数(这个协程)
    StartCoroutine(fun_name());

    // 协程的入口
    IEnumerator fun_name(){
    // 协程的代码
    // yield中断协程程序
    yield return null;
    // 协程结束以后的代码

    // 协程里面也可以启动协程
    StartCoroutine(fun_other());
    }


    // 协程的入口
    IEnumerator fun_other(){
    // 协程的代码
    // yield中断协程程序
    // 等3s再中断协程
    yeild return new WaitForSeconds(3);
    // 协程结束以后的代码 等待3s再执行后面的代码
    }
  • 协同程序可以使用yield 关键字来中断协同程序;

  • 协程也可以启动一个协程;
  • WaitForSeconds(): 等待多长时间后中断协程;

多线程

  • using System.Thread;
  • 创建一个线程: (函数名就行了,不用括号)
    • Thread r = new Thread(callback_name);
    • r.start(); 启动运行线程;
  • 线程回掉函数 void run() {}
  • 多个线程访问同一个数据的时候,会发生”冲突”(没有锁,如果两个线程同时修改这个公共数据,没有办法确定到底是哪一个线程修改的),需要线程安全的方式来访问;
  • 线程锁是在访问公共数据的时候,先去获得这个锁,没有获得锁的线程将会被挂起,指导这个锁被释放。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    /*
    死锁
    A线程拿了锁A,再等拿锁B
    B线程拿了锁B,再等拿锁A
    A等着,B也等着,就造成了死锁
    解决方案:如果多个线程里面要获取多个锁的时候,为了避免死锁,要按相同的循序拿谁抢到就谁先执行
    A线程拿了锁A,再等拿锁B
    B线程拿了锁A,再等拿锁B(谁抢到就谁先执行)
    */

    // 创建一个锁
    public static Object o = new Object();

    lock(o) // 如果没有拿到这个锁 线程就会挂起等待,只有获取到了锁才会继续执行
    { // 获得了这个锁
    //线程安全的执行代码
    };
    // 括号结束以后,就释放了这个锁,其他的线程就可以拿到这个锁
  • 线程休眠: Thread.Sleep(单位为ms);

  • 多线程之间要避免死锁;

WWW网络类

  • WWW w = new WWW(url); 创建一个w
  • WWW(url)会在后台下载url数据;
  • yeild return w来在协程里等待下载完成;
  • 完成后可以继续处理;
    1
    2
    3
    4
    5
    6
    7
    8
    9
    IEnumerator http_baidu(){
    // 抓取百度网页
    WWW w = new WWW("www.baidu.com");

    // 网络抓取是需要时间,在后台抓取网络数据,一段时候以后,才能完成抓取完整个数据
    yeild return w;

    // 抓取完成网络数据
    }

第六十三课(unity_AssetBundle的使用详解)

AssetBundle

  • Unity能用户存储资源的一种压缩格式的打包集合,他可以存任意一种Unity引擎可以识别的资源: 模型,音频,纹理图,动画, 开发者自定义的二进制文件; 安装包小,更新资源;
  • AssetBundle开发步骤:
    • 创建AssetBundle: 项目的资源打包AssetBundle的集合里面;
    • 部署到web服务器, 让客户端下载我们的AssetBundle;
    • 加载AssetBundle, 加载里面的资源;
    • 卸载AssetBundle, 压缩包,镜像;

AssetBundle创建

  • Assets窗口的资源才可以打包;
  • 创建一个AssetBundle文件,它的名字固定式小写如果有大写系统也会换成小写;
  • AssetBundle可以设置一个Varaint(资源类型),就是一个后缀。可以通过后缀来设置不同分辨率的资源;

配置AssetBundle打包(将要打包的资源配置好即可)

  • 将一个资源打入到AssetsBundle: 点击资源,选择对应的AssetBundle就可以了;
    • 还没有图片哦
  • 编写代码导出AssetBundle文件:
    • using UnityEditor; 引入编辑器操作的名字空间; 继承Editor
    • 调用Api:
      • BuildPipeline.BuildAssetBundles(outpath, BuildAssetBundleOptions, BuildTarget);
        • outpath 打包好的资源路径

          文件夹的路径需要手动创建,否者会报错;

        • BuildAssetBundleOptions 一般设置为None就好
        • BuildTarget 打包好的资源准备在哪一个平台上用(windows、ios、android…)

使用buildmap来对指定的资源进行打包;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// outpath 打包好的资源路径 文件夹的路径需要手动创建,否者会报错;
BuildPipeline.BuildAssetBundles(outpath, AssetsBoundlesBuild[], BuildAssetBundleOptions, BuildTarget);

//定义AssetBuild数组
AssetBundleBuild[] buildMap = new AssetBundleBuild[2];

//打包的资源包名称,开发者可以随便命名
buildMap[0].assetBundleName = "resources";

//定义字符串,用来记录此资源包文件名称
string[] resourcesAssets = new string[2];
//将需要打包的资源名称赋给数组
resourcesAssets[0] = "resources/1.prefab";
resourcesAssets[1] = "resources/MainO.cs";

buildMap[0].assetNames = resourcesAssets;//将资源名称数组赋给AssetBuild

//打包资源并导出
BuildPipeline.BuildAssetBundles("Assets/AssetBundles", buildMap, BuildAssetBundleOptions.None, BuildTarget.StandaloneWindows64);

部署服务器

  • 创建一个webserver, 配置好静态路径的位置,将生成的AssetBundle拷贝到服务器上;
  • 生成Url路径后测试AssetBundle下载;
  • 课程以node.js的express框架搭建的webserver为例来进行部署;
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    // 创建服务器目录-->在目录打开命令窗口安装 npm install express 安装 express
    // www_root 放置资源的文件夹
    // 创建js文件编写代码进行部署服务器
    var path = require("path");
    var express = require("express");
    var app = express();
    // 把静态文件路径 设置成服务器路径
    app.use(express.static(path.join(process.cwd(), "www_root")));
    // 启用6868端口
    app.listen(6868); // http://127.0.0.1:6868/bundle_test bundle_test 包名

    // 在命令窗口执行js脚本
    // 使用 http://127.0.0.1:6868/bundle_test 访问看是否能找到要下载的文件 如果找到了 说明部署成功

AssetBundle下载

  • 非缓冲下载: 创建一个WWW的实例来下载AssetBundle;

    • 使用协程下载AssetBundle,
    • 使用WWW的URL接口来下载;

      不能使用 WWW 对象去下载一个已经被加载进来的AssetBundle(使用www之前要确保支援已卸载干净);

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      // 写一个协程准备下载
      IEnumerator use_bundle()
      {
      WWW w = new WWW("http://127.0.0.1:6868/bundle_test");

      yield return w;

      // 内存镜像
      AssetBundle assetBundle = w.assetBundle;
      }
  • 缓冲下载(传一个版本号,版本号不一样才会去下载,这个版本号自己定的):

    下载之后存到本地,如果再次下载版本号没变就从本地获取

    • 使用WWW类的LoadFromCacheOrDownload来实现下载AssetBundle, 当再次下载的时候,只有当版本低或不存在的时候才下载;

      web平台缓冲的大小是50M, IOS/android缓冲的大小为4GB;

AssetBundle加载使用(内存镜像)

1
2
3
4
5
6
7
8
// 内存镜像
AssetBundle assetBundle = w.assetBundle;
// 使用单个资源
// 资源所在的路径名字可在打包好的 `*.manifest` 文件下看`Assets`下面就是文件列表
assetBundle.LoadAsset("路径");

// 加载所有的资源
assetBundle.LoadAllAssets();

使用完记得卸载,看需求是全部卸载还是保留已加载的

AssetBundle卸载

  • 卸载
    • AssetBoundle.Unload(false);

      false: 卸载内存镜像不卸载Asset内存实例;

    • AssetBoundle.Unload(true);

      true: 卸载内存镜像以及Asset的内存实例;

第六十四课(专题(一)DOTween插件使用)

补间动画

安装DOTween

  • 官网
  • 将DOTween解压后复制到项目的任意目录下(Editor, Plugins or Resources除外);
  • 运行DOTween的Setup: 菜单栏的 Tools/Demigiant;
  • 在代码里面 加上 DOTween的名字空间 using DG.Tween;
  • 安装完DOTween插件后,很多组件能够支持Tween;

DOTween组件扩展

  • 其他的组件扩展了Tween接口: 详情见文档
    • AudioMixer
    • AudioSource
    • Camera
    • Light
    • Material:
    • Rigidbody:
    • Rigidbody2D:
    • Transfrom
  • Transfrom组件:

    会返回一个Tweener t

    • 组件实例.DOMove: 平移;
    • 组件实例.DOScale: 缩放;
    • 组件实例.DORotate 旋转;

Tweener常用操作

  • 常用的播放控制函数

    • 组件实例.DOPause(); 暂停一个Tween;
    • 组件实例.DOPlay/DOPlayBackwards/DOPlayForward; 播放/向后/向前
    • 组件实例.DOKill; // 删掉Tween; 默认是自己删掉的
  • 设置循环次数

    • t.SetLoop(num);
      • num为-1,一直循环;
  • Tweener 常用的事件:
    • OnStart(callback_name); 开始的时候
    • OnComplete(callback_name); 完成的时候
    • OnKill(callback_name); 删掉的时候
    • OnUpdate(callback_name); 播放的时候

      OnComplete,o如果是小写,直接 += 函数(跟委托一样),如果是大写就填回调函数

Sequence队列

  • 创建一个队列容器:
    • Sequence seq = DOTween.Sequence()
  • 将Tween加入到容器队列
    • seq.Append(tween);
  • 设置队列容器的循环次数;
    • seq.SetLoops(num); num为-1,一直循环;
  • 插入一个并行的Tween
    • seq.Insert(“插到哪一个位置”,Tween)

Ease缓动效果

  • Tweener设置缓动动画:
    • SetEase(效果类型,缓动的持续时间);
  • 效果类型:
    • OutBack: 快速移动超出目标,然后慢慢回到目标点;
    • …..

第六十五课(专题(二)游戏中的模型描边和Shader切换)

模型描边

1: LOL里面的模型描边效果:
还没有图片哦
2: 可以找到模型描边的Shader,推荐使用(Toony):
一组第三方的Shader, 帮我们解决了模型描边的问题

代码里面切换Shader

加载shader是会有开销的,如果这个shader没有使用,是会被释放的

  • 关联要切换Shader的材质
  • 代码加载对应的Shader: Shader.Find(“Shader的名字”);
  • 切换材质对应的Shader; Matrix.shader = Shader

在运行的时候,去加载shader 耗CPU 预先把这个shader加载好就可以了,unity就有一个机制,把常用的shader设置告诉他,那么他会帮你把这个shader常驻内存。
常用的shader,提前加载好。

  • 将最常用的Shader, 放到Unity的Always Include里 –> 优化检查
  • Edit--> ProjectSetting --> Graphics --> Always-included Shaders

    Specify a list of Shaders that will always be stored along with the project, even if nothing in your scenes actually uses them. It is important to add shaders used by streamed AssetBundles to this list to ensure they can be accessed. 让这些Shader很重要,在开始的时候加载,常驻内存;

第六十六课(专题(三)常用数据结构与JSON处理)

数组(Array)

  • 类型[] 名字 = new 类型[数量]{“初始化的值”, “”, ‘’”}; 如果有初始值也可以省略大小;
  • 优点:
    • 内存连续,速度快;
  • 缺点:
    • 大小固定;
1
2
string[] str = new string[]{"1","2","3"} // string[3]
int[] i = new int[100];

ArrayList

灵活但是相对的性能会对应有所消耗

  • 属于 System.Collections 命令空间
  • ArrayList l = new ArrayList();
  • 操作:
    • 添加 Add(数据), 修改[index] = 内容
    • 删除 RemoveAt(index)
  • 优点:
    • 不用固定大小;
    • 可以存放任意类型;
  • 缺点

    把所有对象都当做Object来处理,读取的时候需要强制转换一下

    • 由于存放不同类型的数据,导致很多看不见的性能消耗, 多次转换等;

List

  • 属于 using System.Collections.Generic; 命令空间
  • List l = new List();
  • 操作:
    • 添加 Add(数据),
    • 修改[index] = 内容
    • 删除 RemoveAt(index);
  • 优点:
    • 不用固定大小;
    • 存储的类型是泛型模板,比较灵活, 每个对象实例只能存储一个类型;

字典 Dictionary<K, T>

  • 属于 using System.Collections.Generic; 命令空间
  • Dictionary<KT, VT> l = new Dictionary<KT, VT>(); key –> value
  • 操作:
    • 添加 Add(数据)
    • 修改[key] = 内容
    • 删除 RemoveAt(index);
  • 优点:
    • 不用固定大小;
    • 存储的类型是泛型模板,比较灵活, 每个dict只能存储一个类型;
    • key也是泛型 string, int ….

json数据格式

  • JSON (JavaScript Object Notation)
    随着JavaScript的流行与互联网应用,JavaScript里面最强大的数据类型Object,使用起来极其的方便,为了能更好的做数据交换,设计了JSON协议,能够将JavaScript里面的Object,变成可以阅读的文本数据及JSON数据格式。实现JavaScript里面的Object与JSON的转换,Object对象转换成JSON数据以后,方便传输与存储,JSON变为Object方便对象重建;
  • python语言, Lua语言等其它的脚本语言都有类是于JavaScript的Object数据结构,所以JSON数据能在其它的语言里面也非常方便的使用;
  • JSON采用完全独立于语言的文本格式(string),易于阅读与编写以及解析与生成,在很多时候数据交换都采用JSON, 数据—>JSON–>传输,存储—>解码JSON–>数据
  • 上面的过程又叫序列化与反序列化;

Json数据格式

  • JSON Object {}; 里面为key: value;
  • value为数字, 11.0, 12, 0,
    • value为bool true, false
    • value为数组 [ 值, bool, 数组, Object]
    • value 为Object { key: value}
  • Unity 5.3.x以后自带的Json数据解析器
  • Unity 5.3以前可以使用第三方的C#库LitJSon;

Unity5.3 JSON序列化

  • 序列化与反序列化: 内存数据–>文件存储介质; 文件存储介质—>内存数据
  • Unity序列化:

    • 把要序列化的对象声明称:

      只会序列化public的

      • [System.Serializable],
      • using System; [Serializable]
        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        21
        22
        23
        [System.Serializable]
        class my_object{
        public int a = 10;
        public int b = 20;
        public string name = "nihao";
        // 不是public 又想序列化
        [SerializeField] // 可序列化的字段
        bool is_object = true;
        public int[] int_array;
        }

        // JSON 序列化
        my_object obj = new my_object();
        obj.int_array = new int[]{1,2,3};

        // 接收转换过来的json字符串
        string json_string = JsonUtility.ToJson(obj);

        // JSON反序列化
        my_object new_obj = new my_object();
        obj.int_array = new int[]{1,2,3};
        // 把字符串json_string转换为Object并赋值到new_obj
        JsonUtility.FromJsonOverwrite(json_string, new_obj);
    • JsonUtility.ToJson(object); // 将对象(object)转成json字符串;

    • 将Json字符串的数据覆盖到对象 JsonUtility.FromJsonOverwrite(json_str, obj);
    • 当Unity序列化你的脚本的时候,它将仅仅序列化公有域。如果作为附加你也想要Unity去序列化你的一个私有域,你可以添加SerializeField(序列化域)属性给这个域。
    • JSON数组:
    • JSON对象:

第六十七课(专题(四)Unity常用目录和代码加载资源)

编辑器扩展目录(Editor)

  • Editor文件夹
    • Editor文件夹可以在根目录下,也可以在子目录里,夹就可以;
    • Editor下面放的所有资源文件或者脚本文件都不会被打进发布包中,并且脚本也只能在编辑时使用;
    • 一些工具类的脚本放在这里,或者是一些编辑时用的DLL, 类似技能编辑器,那么编辑器的代码放在这里;
  • Editor Default Resources
    • Editor Default Resources 注意中间是有空格的, 它必须放在Project视图的根目录下
    • Editor Default Resources: 把编辑器用到的一些资源放在这里,比如图片、文本文件、等等; 和Editor文件夹一样都不会被打到最终发布包里,仅仅用于开发时使用;
      EditorGUIUtility.Load来读取该资源:
      TextAsset text = EditorGUIUtility.Load(“test.txt”) as TextAsset;

Plugins

  • Plugins 文件夹:
    • 如果做手机游戏开发一般 andoird 或者 ios 要接一些sdk 可以把sdk依赖的库文件 放在这里 比如 .so .jar .a 文件
    • 该目录下的文件会被打包到安装包里面;
  • StreamingAssets 文件夹:
    • 这个文件夹下的资源会全都打包在.apk或者.ipa
    • 它不会压缩原封不动的打包进去
    • 并且它是一个只读的文件夹,就是程序运行时只能读,不能写的
    • Application.streamingAssetsPath 它会根据当前的平台选择对应的路径,只可读不可写

Resources目录

  • 可以在根目录下,也可以在子目录里,只要名子叫Resources就可以。比如目录:/xxx/xxx/Resources
  • Resources文件夹下的资源不管你用还是不用都会被打包进.apk或者.ipa,使用代码加载的资源必须放在Resouces目录下
  • Resource.Load :编辑时和运行时都可以通过Resource.Load来直接读取

    尽量不要使用,因为不知道这个资源是否会放在 assetBundle 如果放在这里面那么Resources目录就要移出这个文件,如果移出了,那么使用这个读取到Resources目录下的资源文件不存在就会报错,不利于后期维护,所有我们会自己封装一个资源加载类我们的资源是从Resource目录下读取,还是assetbundle下读取;

  • AssetDatabase.LoadAssetAtPath():它可以读取Assets目录下的任意文件夹下的资源,它只能在编辑时用。它的路径是”Assets/xx/xx.xxx” 必须是这种路径,并且要带文件的后缀名;

    如果打包了,就不能用了

Unity资源管理

  • 不在代码里面动态加载的,或放到assetBundle里的资源,尽量不要放在Resources文件夹下;
  • Resouces文件夹使用和不使用的资源都会被打入包中,所以在打包的时候,要把通过assetbundle来加载的资源或不使用的资源,移出Resources目录,然后再打包;
  • 必须要封装一个资源加载的类,来封装好从Resource目录下读取,还是assetbundle下读取;

第六十八课(专题(五)移动的汽船)

船的虚拟摇杆移动

图片还没有哦

船在水中的摇摆

图片还没有哦

r >= 4 && r <= 180;左区间 r < (360 - 4) && r >= 180 右区间

摄像机跟随

图片还没有哦

第六十九课(unity_专题(六)Unity道具金币拾取)

分层
配置好物体碰撞关系
必须有一个是刚体

第七十课(unity_专题(七)音乐音效管理)

sound_manager

  • 全局唯一的sound_manager;
  • 在场景里面创建一个物体(做为声音的根节点),而且设置这个物体场景切换也不会删除;
  • 编写接口播放背景音乐play_music;
  • 编写接口播放背景音效play_effect; // 2D声音
  • 音乐和音效内部实现都是一样的, 只不过要把url分组管理, 音效为一组,音乐为一组;
  • 提供开关音效接口set_mute(),并将值写入本地;
  • 提供开关背景音乐接口switch_music(),并将值写入本地;
  • 添加一个play_effect接口在指定的坐标出访一个声音, 可以用于3D音效;
  • 添加一个接口停止掉背景音乐stop_music(url);
  • 添加一个接口删除掉播放的背景音乐clear_music(url), clear_effect(url);
  • 声音文件来自Resouce还是assetBundle,可以通过资源管理来封装,目前从Resource里面加载;
  • 加一个脚本,每隔0.5秒扫描一次已经播放完的声音组件,将它disable;

第七十一课(unity_专题(八)Unity漩涡特效切换)

Mesh 材质 Shader

  • Mesh 是网格数据;
  • Shader渲染算法;
  • 材质是给渲染算法的输入数据;
  • 代码修改材质参数,能修改给渲染算法的数据从而获得不同的效果;

纹理会自动对齐到2^N方,如果想要原始大小,可以设置Advance –> Non Power of 2 –> None

旋涡特效

  • 创建一个Shader: Unlit–>Shader;
  • 漩涡特效分析:
    • 纹理坐标的范围[0, 1];
    • 扭曲顶点的纹理坐标, 扭曲的角度+波及的半径;
    • 将扭曲的角度与半径数据绑定到材质;
    • 设置扭曲的角速度, 随着时间的推移加大扭曲角度;
    • 设置波及范围的速度,随着时间的推移不断的加大波及半径;
    • 编写代码来控制参数,实现动态的旋转;
  • 扭曲代码

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    // 把这个两个参数绑定到编辑器
    // shader的输入数据绑定到我们的材质,进行代码动态修改
    Properties{
    radius("Radius",Float) = 0.0
    angle("Angle", Float) = 0.0
    }

    // 外面也要定义
    float radius;// 扭曲的半径
    float angle;// 扭曲的弧度

    // 顶点uv变化
    // uv变化 获得顶点的纹理坐标[0,1]
    float2 uv = v.uv;

    // 扭曲的半径
    radius = 0.5f;
    // 扭曲的弧度
    angle = 1.0f;

    // 需要把这个两个参数绑定到编辑器
    // radius,angle控制我们扭曲的效果 所有如果想要旋涡动画就得不停的修改这两个参数

    // 改变uv坐标的原点
    // 原来是 左下角为原点 0,0 右上角 1,1 改为了中心为原点 左下角-0.5,-0.5 右上角 0.5,0.5
    // 因为旋涡是从中间开始的
    uv -= float2(0.5, 0.5);

    // 计算当前坐标的长度,当前坐标到纹理中心的距离
    float dist = length(uv);
    // 计算出距离百分比
    float percent = (radius - dist) / radius;
    if ( percent < 1.0 && percent >= 0.0) { // 在半径范围之内进行扭曲
    // 扭曲算法
    float theta = percent * percent * angle * 8.0;
    float s = sin(theta);
    float c = cos(theta);
    uv = float2(dot(uv, float2(c, -s)), dot(uv, float2(s, c)));
    }
    // 变换回我们的纹理坐标寻址的原点
    uv += float2(0.5, 0.5);

    o.uv = uv;
  • 旋涡粗糙就扩展顶点就行了

第七十二课(unity_专题(九)Unity场景导出与导入)

天空盒的位置

  • 可以挂载摄像机(场景编辑器看不见,但是游戏画面有天空盒)
  • 可以配置全局(场景编辑器能看到天空盒)

场景导出案例

  • 准备好Unity 4.7赛车游戏的赛车场景;
  • 将属于场景的物体,导出成预制体;
  • 将预制体导入到自己的场景;
  • 检查导入后的正确性;

    模型网格如果出现missing(Mesh)说明就没有正确关联到模型网格,需要手动修改

  • 导入天空盒;
  • 如果有雾的话配置好雾;
  • 打开光源,烘培出静态光照;

第七十四课(unity_专题(十)Unity_FPS第一人称射击类游戏)

AudioSource 的两个方法 Play 和 PlayOneShot 有什么区别

play 和 playOneShot 最大的区别是:

  • play 每次只能播放一次,也就是说假如短时间内假如你有播放多次和多种音效的需求时。play 只会把音效打断,然后重新播放指定音效。
  • playOneShot 就是为了解决播放多种和多次音效的问题而生的(个人观点),这函数不管你目前有没有正在播放音效,它都会另起炉灶播放指定的音效,并且不会打断当前正在播放的音效(这是重点,谨记!)。还能设置它的音量,不会像 AudioSource 组件里的 Volume 属性那样对音量有限制。

    播放点击枪声的时候,play回造成枪声没播放完就重头开始,效果不佳,playOneShot可以解决这个问题

第七十九课(unity_专题(十一)ARPG游戏摇杆控制角色行走)

人物操作方式

  • 在ARPG游戏中,主角人物在摇杆下控制行走;
  • 主角人物遇到障碍物(碰撞器)将不会穿越过去;
  • 摇杆控制主角人物8个方向的行走;
  • CharacterController 角色控制器组件: 让你在受制于碰撞的情况下很容易的进行运动,而不用处理刚体。角色控制器不受力的影响,仅当你调用Move函数时才运动。它执行运动,但是受制于碰撞。
  • 调用角色控制器的Move函数移动角色;
  • 根据摇杆的方向旋转人物动画;

CharacterController组件(角色控制器)

  • 属性面板属性:
    • Slope Limit: 角色碰撞器只能爬比这个指定角度低的斜坡:(degree)
    • Step Offset: 上楼梯模式,小于Step Offset 值得台阶,可以直接上去;
    • Skin Width: 两个碰撞器可以互相渗透深入皮肤宽度, 建议设置成radius的10%;
    • Min Move Distance: 调用Move函数移动的最小移动量,如果移动距离比这个小,将不移动;
    • center: 相对与transform的位置角色叫胶囊体中心;
    • height: 胶囊体高度;
    • Radius: 胶囊体的半径;
  • 碰撞检测:

    只会和使用了CharacterController组件的物体产生碰撞

    • void OnControllerColliderHit(ControllerColliderHit hit) {
      }
  • 重要方法:
    • Move(Vec3 offset): 移动的距离;

弧度(顺时针角度是负的,逆时针角度是正的)

还没有图片哦

unity_安卓打包

java安装好sdk之后新建两个环境变量

  • JAVA_HOME
  • PATH

    目录都选择sdk的目录即可cmd命令窗口输入java或javac即可查看是否配置好如果为出现内部或外部命令,也不是可运行的程序 或批处理文件说明已经配置好了

  • 一般不直接打包apk

    • 配置好之后点击build即可
  • 一般导出一个安卓工程勾选下面的选项然后点击Export即可导出

    • Google Android Project
    • Development Build
  • Run In Background (如果鼠标移出unity点到其他应用,unity程序就会停止,勾选则就是表示可以在后台运行,打包的时候一般都是勾选这个的)

  • Android Studio打包安卓工程文件

Lua

lua 解释器
luac lua字节码编译器 lua代码 –> lua字节码
使用

  • luac o "编译出来的文件名" "需要编译的文件"

lua表中的

返回连续数字索引的长度()

lua数组索引是从1开始的

1
2
3
local a = {0,1,2,3,4}
a[11] = 10;
print(#a); -- 5 因为连续的数字索引只有5个

数组(连续数字索引)

ipairs 遍历lua表中的数组部分(连续的数字索引)
pairs 遍历lua表中所有的数据

table的sort比较函数

lua 排序中的比较函数必须要保证排序是稳定的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
function a(a, b)
return a > b;
--[[ 从大到小 大的返回true
if a > b then
return true
else
return false
end
]]
--[[ 从小到大 小的返回true
if a < b then
return true
else
return false
end
]]
--[[ 排序中的比较函数必须要保证排序是稳定的
if math.random < b then --这样是会报错的 不用这种做随机打乱一个数组数据
return true
else
return false
end
]]
end

解开数组(unpack)

a,b,_,c,d,e,f = unpack(“数组”)

把数组里面的值解开到a对应索引1,b为2,后面的同理,不用的用 _ 去接即可

模块化

require装载lua脚本(不用加后缀)

require(“lua_file_name”) 或 require “lua_file_name”

可以用一个返回值来接收,如果装载的lua脚本有return那么这个返回值就是脚本里面return的数据

require只会装载执行一次,多次require也只会装载并执行一次lua脚本
第二次require的时候,会发现已经装载过了,直接返回第一次装载的返回值

local 关键字的只能在挂载内部使用,如果想在外部使用

  • 第一种就是不适用local关键字
  • 第二种就是使用return 返回一个函数名即可使用这个函数

上面的方法需要在使用模块脚本里面,都要使用require来装载,才可以调用得到装载脚本里面的方法,这样相对如果多个脚本使用就比较麻烦

全局模块module(全局包)

在lua脚本开头写上这个前缀module("模块名字", package,seeall),只要在一个脚本里面使用require装载这个脚本,其他lua脚本无须再装载,也是可以使用这个装载脚本里面的方法的

不过上面再其他脚本使用前需要加上模块名字.xxx

dofile装载脚本(需要加后缀)

dofile会对读入的模块编译执行,每调用dofile一次,都会重新编译执行一次
require 和 dofile 的区别require只会执行一次,dofile调用几次执行几次

lua的Module

self

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
local a = {}
function a.test(self)
print("a.test",self)
end
a.test()

local b = {}
function b:test() -- 隐士传递self参数
print("b:test",self) -- 可以顺利拿到self
end
b:test() -- 调用的是表的实例self 就是这个表的实例 使用 : 会隐士绑定self

function b.test2()
print("b:test",self)
end
b:test2() -- 无法正常拿到self

-- b.test(b) -- 显示传递 也是可以拿到的
b.test() -- 无法正常拿到self 即使是:定义的函数,调用函数的时候不使用 :也是无法正常拿到self的

self机制需要正常运行,需要两个点

  • 定义的函数必须使用 : 才会有隐式的传递self
  • 调用的时候也需要使用:
  • 显示传递

元表

1
2
3
4
5
6
7
8
9
10
local a = {}
local mtable = {
__index = {
a = 100;
}
}
setmetatable(a, mtable) -- 设置元表 a的元表是mtable 设置mtable为a的元表
getmetatable(a) -- 获得a的元表

print(a.a) -- 打印为100 自己表里面找不到就会去元表的__index表里面去找

元表里面有一个非常重要的key : index
特点
当我门搜索一个表的key的使用,如果没有搜索得到,lua解释器就会去这个表里面的元表里面的
index这个key锁对应的表里面来查找

模拟面向对象的语法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
local base = {}
function base:test()
print("base:test",self);
end

-- 成员函数
function base:new(instant)
if not instant then -- 类的实例 没有就创建一个
instant = {}
end

setmetatable(instant, {__index = self}) -- 把这个实例的元表设置成 base 这样 这个实例就相当于有了可以访问base的成员函数

return instant
end

local b = base:new() -- 这个base表就是new的self(实例)
-- 在new函数里面把新创建的实例表的元表变成了self,这样如果在这个实例里面找不到相关的函数,就会去这个元表的__index表里面去找,相应的就找到了self(也就是base表)
b:test() -- 所有可以用b访问base里面的成员函数 这时候的self对象就是我们的实例b

-- 增加数据成员
local c = base:new({
name="name",
age=33,
})

print(c.name) -- 实在{name="name",age=33,}这个基础上扩展的元表所有不用加:也是可以的,访问数据成员不需要:

总结
面向对象的基本步骤

  • 定义一个表(类) – 相当于定义一个类 class
  • 定义一个实例的表 – new_instant = class:new();
  • 为这个实例的表添加一个元表,并且元表__index指向这个定义的表self(这个类)
  • 利用self机制,表的实例调用函数的时候,隐式的帮我们传递了实例的表为self到函数里面
    表的实例(new_instant):表的函数(test)

继承、重载

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
local base = {} -- 基类

function base:test()
print("base:test",self);
end

function base:test2()
print("base:test2",self);
end

function base:test3()
print("base:test3",self);
end


-- 成员函数
function base:new(instant)
if not instant then -- 类的实例 没有就创建一个
instant = {}
end

setmetatable(instant, {__index = self}) -- 把这个实例的元表设置成 base 这样 这个实例就相当于有了可以访问base的成员函数

return instant
end

local man = base:new() -- 把man实例的元表设置成父类(base)

function man:test()
print("man:test",self);
end

-- 重载base的test3函数
function man:test3()
print("man:test3",self);

-- 子类里面调用父类函数
base.test3(self) -- 显示传递一次self即可
end

-- 这个new调用的方法其实是base里面的方法因为man的元表__index已经设置成了base
-- 而new里面的self就不再是base的了,而是实例man的self
local p = man:new() -- 子类的实例p
-- 下面的self 都是p的
p:test() -- p --> man找到打印 man:test
p:test2() -- p --> man --> base 找到打印 base:test2

--p --> man --> base -->一直找下去直到找到,没有找到那么就会报错
p:test3() -- p里面是没有的接下来去man去找,找到了就返回 所有打印的是 man:test3 这样我们就重载了基类的函数

Lua热更新

资源管理与lua框架开发基本原则

全新资源管理模式

  1. unity资源管理方式阶段(打完包之后,资源无法更新了
    • 编辑器编辑资源,我们如果要用代码加载资源就使用Resources.Load来代码加载资源
    • 用代码加载的资源放在resources目录
    • 不用代码直接加载资源放在resources以外的文件夹下
    • 打包的时候,resources的资源,全部会打入发布包
    • 其他文件下的资源,根据场景依赖或者是resources下的资源依赖进行选择性打包,没有被使用的非resources下的资源就不会被打包进去
  2. 虽然外面无法更新代码,我是否有办法更新我的资源呢,再我们不重新安装的情况下 Unity就有了我们的AssetBundle的更新模式
    • 安装包里面带了资源 + 下载AssetBundle包的资源
    • 如果是AB包资源,那么就用ab包模式加载资源,否者就还是走resources模式
  3. 随着游戏越来越复杂,包体积越来越大,2G~3G的美术资源 包体就非常的大,所有我们有了空包,空包里面什么资源都没有,启动的时候去加载ab包,其他时候什么都没有,这样安装包就比较小,很多游戏第一次安装完之后,就需要下载一个很大的资源包
    • a 所有资源再也没有放resources里面
    • b 放resources里面反倒成了一个负担,打空包的时候,你不希望打资源,但是如果放在resources里面,他还是会全部打包进去
    • c 有一些小型游戏(50~60M,资源在包体,渠道出的带宽),资源在服务器上是自己出带宽,为了节约带宽,希望把这个初始资源包带入安装包,那么还是走assetbundle机制,但是我们会选择把assetbundle放在StreamingAssets文件夹下
    • StreamingAssets里面的ab包可以被游戏加载进来,不走三方服务器,只要放在StreamingAssets文件夹下也能加载到游戏资源
  4. 你只更新资源,不更新代码,总归不是个事,比如做个营销活动(世界杯),等活动结束,要恢复回来,就需要更新代码
    • 热更框架:脚本的模式,游戏里面内置一个脚本解释器,解释执行我们的脚本,我们开发就用这个脚本,脚本资源也会是一种”资源”,也会把代码打包在ab包里面,一般我们会代码单独的打包成ab包。有些可能就区分为(框架的ab包 + 业务逻辑的ab包) 根据自己的情况是否需要把所有脚本代码做成ab
    • 热更方式:脚本模式,内置一个脚本解释器 + 运行起来(插入版本检测,来更新最新的代码)动态装载脚本代码,在解释执行
      1. lua:lua解释器 + lua脚本 —>我们采用的
        • 主流使用C#开发,用lua打补丁

          lua打补丁:比如 C#代码set_name有bug,我们就使用lua给这个函数set_name(){lua 代码 不走原来C#的代码了} 进行打补丁,检测到有lua补丁,就不走原来的C#代码直接走lua补丁代码

        • 主流全部用纯lua开发,底层的C#的框架代码(采用lua打补丁),所有的业务逻辑和C#没有关系了,直接使用Lua开发(一般使用这个)
      2. C#:C#解释器(C# light) + C#脚本

基本设计的原理原则

  1. 完全取消resources资源加载方式,采用ab包进行加载资源(但是总不可能在日常开发的时候总是从ab包里面进行资源加载)
    • Editor下的一个开发模式,我们编写一些接口,不真正的去加载ab包(调用加载ab包的接口是没有用的,但是我们会走这个流程)
      • 加载资源:编辑器模式下运行才有效的函数来装载资源AssetDatabase.LoadAssetAtPath加载ab包的时候什么都不干,所有就可以来正常的开发业务逻辑了
      • 正式运行模式:从ab包里面进行读取装载资源,就走标准的模式(标准的流程)
  2. 我们要支持打空包,所以这个时候的资源,不能把依赖打包到我们的安卓包里面
    • 我们会从场景依赖打入资源,场景里面就不能放任何东西
    • resources的依赖,去掉了resources,就不会存在了,打空包就非常方便了
  3. 我们会把资源打成单独的包出来,放单独的文件夹下,假设你要把第一个版本的资源打包进行,这时候我们就会编写脚本,直接将这个资源包复制到StreamingAssets文件夹下
  4. 热更新代码,热更资源,普通节点上不带任何代码
  5. 场景只放启动节点代码,启动代码 –> 启动C#框架 –> 加载lua脚本 –> lua代码业务逻辑
  6. 对于我们的lua开发,完全支持C#的开发习惯也用组件开发,也有Update等

热更与资源管理模块的设计演示

  • 在我们编辑器上我们会设置模式(这个模式存在Editor本地读写里面)
    • Editor模式
    • 开发模式
  • 我们打包发布的时候:ab(平台(android、ios) + 渠道(百度、360)) AssetBundles

    ab包是要区分平台和渠道的

  • 最终我们是要将代码作为资源打包到ab包里面的,我们使用AssetsPackage(资源 + 代码)文件夹来存放资源包 + 代码
    打ab包的时候全部打的是AssetsPackage下的资源,和开发就没什么关系了

    lua代码不是unity的ab包识别的类型.lua的资源 使用工具 把.lua –>转换 .lya.bytes 类型放在AssetsPackage

  • 我们指定我们要打包ab包,打包的时候有一个包的数据库管理(根据一个配置文件来打包)

  • ab包会根据渠道输出到一个单独的路径
  • 查看打包出来的结果
  • 打包资源的时候会生成一个文本资源.map映射(哪个资源在哪个ab包,资源的路径,AssetsMap)
  • 我们会开启一个模式:测试这个资源包

纯lua开发的注意事项与性能瓶颈

  • 做游戏开发的时候80%、90%性能开销、渲染都是(底层)C#写好的组件 + 物理引擎 –> lua(20%~10%) –> 性能是可接受的,lua –> 接近C# lua开发怎么都是解释执行,所有性能要低一些
  • lua开发编写算法(负载的算法)的时候 –> 尽可能使用C#来做算法,不要使用lua来写(核心算法,不会有很大的改变)

    不用lua写算法,使用C#

  • lua调用C#、C#调用lua 经过漫长的数据交互和函数调用的过程 –>性能开销比较大,尽量减少这个lua和C#的交互

    减少lua和C#的交互

做一个纯lua框架 + 完整的ab包资源 + 其他功能(自己扩展)

项目创建与Lua启动流程

  • 创建项目,规范项目目录结构
    • 创建一个AssetsPackage文件夹,用来放资源,所有的资源和资源ab打包我们都会放在这个文件夹下
    • 创建一个Scripts文件夹,用来放C#的代码
    • 创建一个LuaScripts文件夹,开发的时候我们要存放Lua脚本代码
    • Scenes 用来存放场景
    • StreamingAssets 用来存放我们的本地的ab包资源,可以加载ab包,同时可以打包到安装包里面去
    • Editor 编辑器扩展代码
    • 做一个启动脚本,用空物体来挂一个脚本
  • 搭建xLua开发环境
    • xLua github下载源码
    • xLua的Assets文件夹里面的文件copy到untiy项目的Assets文件夹下即可
  • 编写代码启动Lua虚拟机
    • LuaEnv 是lua解释器的上下文的运行环境,lua解释器的运行环境的数据,都会依赖luaenv
    • 重新添加一个lua代码装载器,到lua解释器,那么它装载lua文件的时候,就会使用这个装载器

      因为开发的时候我们载入的是luascripts的lua代码,在发布的时候我们装载的是被打入assetbundle包的lua代码

    • CustomLoader 一个是编辑器模式 一个是assetbundle模式

      装载lua文件的时候就会调用这个函数

  • 重载Lua装载器
    • luaEnv.AddLoader
    • 参数说明:luaEnv.AddLoader(loader)
      loader 类型为 delegate byte[] CustomLoader(ref string filepath)
      当一个文件被 require 时,这个 loader 会被回调,其参数就是require的参数
      如果该 loader 找到文件,可以将其读进内存,返回一个 byte 数组。
      如果需要支持调试的话,而filepath要设置成 IDE 能找到的路径(相对或者绝对都可以)

  • 编写Lua启动脚本
    • luaEnv.DoString(scriptContent)
    • 参数说明:scriptContent 代码文本内容

lua的组件开发模式

lua启动脚本开发入口

  • Update、FixedUpdate、LateUpdate
    • ApplicationQuit
    • Update
    • FixeUpdate
    • LateUpdate

main.lua加上这些入口、xLuaMgr 调用这些接口,实现lua拥有Update、FixedUpdate、LateUpdate函数功能

lua组件化开发模式设计理念

大家都习惯了组件开发
lua调用unity的接口,非常方便

  • 组件化
    • 组件化 + 真正的在unity添加组件,然后通过组件再转载lua脚本
    • 组件化 + 不通过unity + 纯lua组件开发的模式
      • 我们自己定义lua组件,自己”添加”lua组件到gameObject()
      • 我们用lua脚本来驱动update、fixedupdate、lateupdate 通过这种方式然后实现组件实例

lua组件类基类的设计

所有的lua组件实例都继承LuaBehaviour

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
-- 返回一个基类为base的类
function LuaExtend(base)
return base:new();
end

local LuaBehaviour = {}

function LuaBehaviour:new(instant)
if not instant then
instant = {} -- 类的实例
end

setmetatable(instant, {__index = self});

return instant;
end

-- 两个成员函数 尽量和unity 习惯保持一致
function LuaBehaviour:init(obj)
self.transform = obj.transform
self.gameObject = obj
end

return LuaBehaviour;

新建一个LuaComponentMgr的模块来管理lua的组件实例

lua调用unity

unity在C#中的名字空间 UnityEngine
unity在Lua中的名字空间 CS.UnityEgine

lua调用unity

  • 我要获取unity对象里面的数据成员:obj.transform, obj.gameObject, obj.xxxx
  • 调用unity对象里面的成员函数 obj:xxx()

AddComponent(unity组件)

self.gameObject:AddComponent(“填组件名字就行了”) 已废弃
self.gameObject:GetComponent(“填要获取组件名字就行了”)

unity导出接口给lua使用

1
2
3
4
5
6
7
8
9
10
11
using XLua;

// 表示这个文件将要被导出给lua使用
[LuaCallCSharp]
public class ResMgr : UnitySingleton<ResMgr> {

public override void Awake()
{
base.Awake();
}
}

AssetsBundles打包管理的使用

图片还没有哦

GC与AssetsBundles步骤详解

Editor –> XLuaMenu 文件下做的事情

复制lua文件
修改后缀名

  • AssetsPackage所有的资源部 lua代码也会是一个资源 .lua文件 ab包(需要把.lua转为 .lua.bytes 作为二进制打进去)
  • LuaScripts开发模式下的lua代码 拷贝到AssetsPackage 并且将所有.lua转为 .lua.bytes

不同开关模式的详解(Switch Model)

Editor –> AssetBundle –> AssetbundleMenultems.cs 所有的资源管理菜单都在这里

  • Editor模式 直接重LuaScripts里面加载代码
  • 切换到模拟模式,我们是从assetbundle里面进行加载资源
  • 发布模式从assetbundle里面进行加载资源

渠道和版本管理

  • GameChannel.cs 渠道配置文件 项目有哪一些渠道就可以写哪一些渠道
  • Editor –> PackageBuild –> PackageTool.cs 文件 负责包版本的管理 Tool菜单功能
  • EditorUserBuildSettings 编译目标的一个配置打包时候 unity的配置 activeBuildTarget 当前选择的一个build目标
  • 获取当前渠道 PackageUtils –> GetCurSelectedChannel

Assetbundle打包配置与源码详解

  • AssetBundle打包配置操作

    • Editor/AssetBundle/Database/AssetsPackage –> Lua.asset(对象数据直接存到文件里面)

      .asset文件指定了我们的哪个文件打包哪些文件到我们的assetbundle里面、打包的方式有哪些
      如果你的文件夹没有被指定打包生成assetbundle,那么在这个文件夹下就会有一个按钮 –> create AssetBundleDipatch

    • 打包类型:

      • root 就是以这个文件夹为根目录,来打包assetbundle; lua.assetbundle 路径/名字.assetbundle
      • Children 文件夹下的每个孩子
      • Children Folders Only 只会打包文件夹
      • Children files Only 只会打包文件
  • Unity ScriptableObject详解生成.asset
    ScriptableObject C#对象如果你继承自 ScriptableObject 那么你就会很方便的写入数据到.asset文件
    AssetBundleDispatcherConfig.cs –> 打包的时候的一个配置生成.asset
  • 按钮扩展在AssetBundleDispatcherInspector.cs里面

打包的时候,可以做资源检查,Run All Checks 我们就是根据.asset文件来检查我们的资源包下面的文件夹是否都有
(1) 运行Lua脚本拷贝,将我们lua脚本拷贝到我们的AssetsPackages文件夹下
(2) 清理掉不用的assetbundle的名字 重新创建assetbundle的名字

AssetBundle打包流程分析

AssetBundle资源加载

  • AssetBundleManager是一个单例,所以要在开始节点上添加这个节点

lua热更dome

热更流程

  1. 会从私有数据目录下,来读取我们的版本信息(如果没有读到就会去StreamingAssets文件夹去读取)

    优先从我们的热更的下载目录下读,如果没有再从StreamingAssets目录下读取

  2. 提前下载好最新的资源到私有可写目录,那么我们用的加载就是最新的assetbundle
  3. 从本地读取版本信息,就会得到一个版本号
  4. 从服务器读取一个版本信息
  5. 把本地的版本与服务器的版本进行比对,比对完成以后如果要更新,我们就获取这个更新列表
  6. 更新列表:当前的哪些资源变动了,我们可以通过hash值来比较,如果不一样,就放入下载路径,将资源下载下来,下载好了以后就进入游戏,我们游戏里面,从本地加载的ab包是最新的,这样就热更完成了。

搭建一个简单的webserver

  • 安装好node.js
  • 进入命令窗口node-v查看node版本
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    // 创建服务器目录-->在目录打开命令窗口安装 npm install express 安装 express
    // www_root 放置资源的文件夹
    // 创建js文件编写代码进行部署服务器
    var path = require("path");
    var express = require("express");
    var app = express();
    // 把静态文件路径 设置成服务器路径 http://127.0.0.1:6868/Resources —— www_root/Resources
    app.use(express.static(path.join(process.cwd(), "www_root")));
    // 启用监听6868端口
    app.listen(6868); // http://127.0.0.1:6868/Resources/lua.assetbundle

    // 在命令窗口执行js脚本
    // 使用 http://127.0.0.1:6868/Resources/lua.assetbundle 访问看是否能找到要下载的文件 如果找到了 说明部署成功

NGUI

一般NGUI界面开发流程
1)美术设计界面,提供切图,效果图给到程序
2)用NGUI搭建静态界面
3)将搭建好的静态界面拖到Project视图上—–形成一个预制体prefeb
4)通过代码调用 来初始化和显示预先配置好的界面,实现界面的切换

NGUI - 字体

  • ShrinkContent
    • 总是显示所有文字,根据当前的width和height进行缩放
  • ClampContent
    • 一看到Clamp就想起Clamp函数,也就是不管文本多少个字,根据当前的width和height来显示,超出部分 不显示
  • ResizeFreely
    • 会对当前的文字长度和行数来调整,UILabel的width和height,基本上是只在一行显示,超出的部分不显示
  • ResizeHeight
    • 保持宽度不变,必要时增加高度。
  • Spacing

    • X:设置字与字之间到间隔,可以为负数,设置得当可以反序

    • Y: 设置行与行之间的间隔。

  • Gradient :

    • 设置 渐变字
  • Max Lines:

    • 用来控制最多要多少行。用0表示不限制。如果设置成n的话,那么超过n的行的文字将不会显示!
  • 可以使用BBCode标记

1
2
3
4
5
6
7
8
9
10
11
12
[b]Bold[/b]                      粗体
[i]italic[/i] 斜体
[u]underline[/u] 下划线
[s]strikethrough[/s] 删除线
AA[sub]sub[/sub] 下标
BB[sup]sup[/sup] 上标
[00ff00]设置颜色[-] 设置显示颜色

[url=http://www.cnblogs.com/mrzivchu/][u]博客[/u][/url] 链接

例如设置颜色:
UILabel的Text内容为:[99ff00]n[-]gui: tools

NGUI - 精灵/图集

  • IPanel

    • 用来收集和管理它下面所有widget的组件。通过widget的geometry创建实际的draw call。
    • 没有panel所有东西都不能够被渲染出来。如果你对Unity熟悉,你可以把UIPanel当做Renderer。

    • 所有panel都有一个Depth值,会影响所有它包含的widget。如果你的UI有很多窗口,那么最好每个窗口有一个panel。

    • Panel上的depth权重会远远高于每一个widget的depth权重,所以保证panel不要使用同样的depth。如果使用同样的depth在panel上,那么draw call会被自动拆分来保证渲染顺序,所以会增加更多的draw call。

    • Alpha属性影响所有在panel下面的widget。所以可以用它来淡出整个窗口。
    • 如果你的UI需要被灯光影响,需要勾选上Normals。
    • 如果创建了一个有很多geometry的scrollable panel,你需要勾选Cull选项来减少三角形的数目。这样也可能降低性能,因为widget的可视性需要每次update都检验一次。
    • 勾选Static选项来告诉NGUI这个panel下面的widget不会被移动,这样可以提高性能。NGUI会忽略所有的position/rotation/scale改变。所以在运行时移动widget不会有效——所以小心使用。

    • 如果要调试由panel创建的draw calls,Show All选项可能帮助到你。你会看到由panel创建的所有draw call,以渲染顺序排序。每个draw call会包括它使用到material的详细信息,那个widget用的这个material,甚至可以让你关闭某些draw call来让你查询某些问题。

    • Panel会根据dimensions自动Clip所有它的子节点。使用这个功能需要选择Clipping下拉列表中的任意选项,之后调整Scene View中紫色矩形的尺寸,就像调整widget的尺寸一样。通过这样做你可以把一个panel放到Scroll View中,让他轻松的拖拽。

    • 注意clipping的panel不能嵌套。每个panel只能clip自己管理的widget,如果一个panel在另外一个panel里面,只有一个会影响到里面的widget。这个限制以后会去掉。

    • 默认NGUI中panel的Render Queues从3000开始往上增加。你可以通过Render Q来修改。如果你想在两个panel中间增加粒子,只要修改两个panel的render queue一个高于粒子,一个低于粒子即可。如果想要让所有的draw call使用和NGUI 2.x版本的渲染方式一样,使用z轴而不是depth。

    • 那么给panel的Render Q指定为Explicit。(NGUI 2.x用的是3000)。
    • 如果你找和Anchors相关的文档,可以看基类——UIRect。
  • 小贴士

    • 一个动力学Rigidbody会自动增加到你的panel上,因为对于Unity来说这样会提升性能。移动静态的collider会有很多消耗性能的操作,但是移动rigidbody就不会。
  • UIPanel 和 uiwidget 工具

    • UIPanel会产生drawcall,而widget不会,widget依赖于父节点来产生drawcall
    • panel有裁切区域,widget没有
    • 当panel有裁切区域,Anchors对齐功能才可用,widget不用
    • widget下的depth没用,会按它的父panel的depth算,panel下的depth就是自己的
    • 官方文档

NGUI_锚点_帧动画_Tween动画_Checkbox复选框_Button事件回调_Slider滑动条

1
2
3
4
5
6
7
8
// 添加按钮事件
this.GetComponent<UIButton>().onClick.Add(new EventDelegate(this.OnClickButton2));

// 获得按钮物体,添加事件
UIEventListener.Get(this.gameObject).onClick = OnClickButton3;

// 添加按钮事件
EventDelegate.Add(GetComponent<UIButton>().onClick, AddOnClick5);

NGUI_打字机效果Tab商城背包之Scrollview1(基于panel)

  • 打字机效果

    游戏中的新手引导或人物的对话功能中

    • UILabel
    • TypewriterEffect
  • Tab功能

    实现商城功能必备

    • Toggle
    • ToggledObjects + Box Collider
  • ScrollView滚动视图,基于UIPanel实现

    实现商城功能必备

    • Drag Scroll View
    • Center On Click

本文标题:unity学习笔记

文章作者:游戏人生

发布时间:2019年12月23日 - 16:12

最后更新:2020年12月27日 - 12:12

原始链接:http://www.tjl-myblog.cn/unity学习笔记.html

许可协议: 署名-非商业性使用-禁止演绎 4.0 国际 转载请保留原文链接及作者。

-------------本文结束感谢您的阅读-------------