Unity生命周期函数

生命周期函数

/usr/uploads/2019/11/20151014095608805.png


游戏的本质

游戏的本质就是一个死循环,每一次循环处理游戏逻辑就会更新一次画面,之所以能看到画面在动是因为切换画面的速度到达一定时人眼就认为画面是流畅的

一帧就是执行一次循环

Unity底层已经帮助我们做好了死循环


生命周期函数的概念

  • 所有继承Mono的脚本 最终都会挂载到GameObject游戏对象上
  • 生命周期函数 就是该脚本对象依附的GameObject对象从出生到消亡整个生命周期中通过反射自动调用的特殊函数
  • Unity帮助我们记录了一个GameObject对象依附了哪些脚本 会自动得到这些对象 通过反射执行一些固定名字的函数
  • 生命周期函数支持继承多态
  • 物体在非激活状态时不会调用生命周期函数,在激活状态时 这个脚本不管是否激活 都会调用awake函数
  • 生命周期函数 不是基类Mono中的成员 是Unity利用反射调用的
  • Unity帮我们记录了场景上所有的GameObject对象 以及关联的脚本对象 在游戏的特定时机 通过函数名反射得到脚本对象中对应的生命周期函数来执行

生命周期函数

  • 生命周期函数的访问修饰符一般为privateprotected
  • 避免在继承自Mono的类中使用构造函数(在初始化时构造函数会被unity执行多次)
  • Awake:当对象(自己这个类对象)被创建时 才会调用该生命周期函数(类似构造函数)
  • OnEnable:当当依附的对象激活时调用(物体被激活时 会激活脚本 本质上是脚本激活时调用)
  • Start:第一帧更新前调用
  • FixedUpdate:固定时间调用 默认0.02秒 可在Project Setting中的time中设置 主要用来处理物理相关的更新
  • Update:每一帧调用一次
  • LateUpdate:每帧调用一次在update之后执行 通常用于处理 摄像机位置更新相关内容(UpdateLateUpdate之间 Unity进行了一些处理 处理我们动画相关更新)
  • OnDisable:当依附的对象取消激活时调用和OnEnable是对应关系
  • OnDestroy:当对象销毁时调用(只会调用一次)销毁时也会调用OnDisable

屏幕(Screen)

获取屏幕当前分辨率

//获取显示器分辨率和刷新率
Resolution r = Screen.currentResolution;
//获取游戏窗口的宽高
Screen.width;
Screen.height;

屏幕休眠模式

Screen.SleepTimeout = SleepTiomeout.NeverSleep;

不常用

//运行时是否全屏模式
Screen.fullScreen = true;
//窗口模式
//FullScreenMode.ExclusiveFullScreen独占全屏 Windowed 窗口模式
Screen.fullScreenMode = FullScreenMode.ExclusiveFullScreen;

//移动设备屏幕旋转相关
Screen.autorotateToLandscapeRight = true;//允许自动旋转为右横向
Screen.autorotateToLandscapeLeft = true;//允许自动旋转为左横向
Screen.autorotateToPortraitUpsideDown = true;//允许屏幕自动旋转为倒着
Screen.autorotateToPortrait = true;//允许自动旋转屏幕为正向

//指定屏幕显示方向
Screen.orientation = ScreenOrientation.LandscapeLeft;//指定为横屏

设置分辨率 不常用

Screen.SetResolution(1920,1080,true);//前面两个参数分辨率 第三个参数 是否全屏

碰撞和射线检测

范围检测

//Physics.OverlapBox(中心点位置,长宽高的一半,角度 四元数);
//参数4 检测指定层级 填入层级的id  1<<LayerMask.NameToLayer("UI")
//参数5 是否忽略触发器 UseGlobal-使用全局设置 Collide-检测触发器 Ignore-忽略触发器 不填使用UseGlobal
Cokkider[] colliders = Physics.OverlapBox(Vector3.zero,new Vector3(1,2,3));
//球形碰撞器
colliders = Physics.OverlapSphere(Vector3.zero, 5, 1 << LayerMask.NameToLayer("Default"));
//胶囊碰撞器
colliders = Physics.OverlapCapsule(Vector3.zero, Vector3.up, 1, 1 << LayerMask.NameToLayer("UI"), QueryTriggerInteraction.UseGlobal);
//另一个API 
//返回值:碰撞到的碰撞器数量
//参数:传入一个数组进行存储
//Physics.OverlapBoxNonAlloc()
if(Physics.OverlapBoxNonAlloc(Vector3.zero, Vector3.one, colliders) != 0)
{	
	Debug.log(colliders.length);
}

LayerMask位运算

Unity中的层级编号是0~31 刚好32位

每一个编号 代表的都是 二进制中的一位

  • 1« 5 0000 0000 0000 0000 0000 0000 0001 0000 代表第五层
  • 1« 1 0000 0000 0000 0000 0000 0000 0000 0010 代表第一层

如果想要同时检测第五层和第一层 则使用1« 5 | 1« 1 或运算有1则1

碰撞器(Collder)

碰撞产生的条件

两个物体都要有碰撞器 至少有一个物体有刚体组件


什么是碰撞器

碰撞器表示物体的体积(形状) 刚体会利用体积进行碰撞计算,模拟真实的碰撞效果,产生力的作用


3D碰撞器种类

  • 盒状
  • 球状
  • 胶囊
  • 网格
  • 轮胎
  • 地形

通用参数

  • Is Trigger是否是触发器 如果启用此属性 则碰撞体将用于触发事件 并被物理引擎忽略
  • Material 物理材质 可以确定碰撞体和其他对象碰撞时的行为
  • Center 碰撞体在对象局部空间中的中心点位置

常用碰撞器

Box Collider 盒状碰撞器

  • Size 碰撞体在X、Y、Z方向上的大小

Sphere Collider 球状碰撞器

  • Radius 碰撞体的半径大小

Capsule Collider 胶囊碰撞器

  • Radius 胶囊的半径
  • Height 胶囊的高度
  • Direction胶囊在对象局部空间中的轴向

异形物体使用多种碰撞器组合

父对象添加刚体 子对象没有刚体也会参与碰撞检测(作为一个整体)


不常用的碰撞器

Mesh Collider网格碰撞器 性能消耗较高

  • Convex 启用此属性 该Mesh Collider将和其他Mesh Collider发生碰撞 Convex Mesh Collider最多255个三角形 如果启用了刚体就要启用了该属性 才产生力的作用
  • Cooking Options 启用或金禁用影响物理引擎对网络处理方式的网络烹制选项
  • Mesh 引用需要用于碰撞的网络

Wheel Collider环状碰撞器 轮胎碰撞器

  • Mass车轮的质量
  • Radius车轮半径
  • Whell Damping Rate 车轮的阻尼值
  • Suspension Distance 车轮悬架的最大延申距离
  • Force App Point Distance 车轮上的受力点
  • Suspension Spring 悬架尝试通过增加弹簧力和阻尼力来达到目标位置
  • Forward Friction 车轮向前滚动时轮胎摩擦的特性
  • Sideways Friction 侧向滚动时的轮胎摩擦的特性

Terrain Collider地形碰撞器 性能消耗较高

输入(Input)

Unity输入

获取鼠标在屏幕上的位置

//返回的是屏幕坐标
Input.mousePosition

检测鼠标输入

//鼠标按下事件 0左键 1右键 2中建
Input.GetMouseButtonDown(0)
//鼠标抬起事件
Input.GetMouseButtonup(0)
//鼠标当前是否按下
Input.GetMouseButton(0)
//鼠标中键滚动 返回值y -1下 0  1上
Input.mouseScrollDelta

检测键盘输入

//键盘按下事件 KeyCode具体按键 也可以传入字符串"w"要小写
Input.GetKeyDown(KeyCode.W)
//键盘抬起
Input.GetKeyUp(KeyCode.W)
//键盘当前是否按下
Input.GetKey(KeyCode.W)

Unity提供了一种方便控制位移的方法

  • 使用Input.GetAxis方法 参数填InputManager中的字段
  • Horizontal 左右方向 Vertical上下方向
  • Mouse X 鼠标横向移动 Y 鼠标属相移动
//填入Project Settings中的InputManager
//这里面有很多unity自带的热键 也可以自定义
Input.GetAxis("Horizontal"); 返回-1 1
Input.GetAxis("Vertical");

//和GetAxis使用方式相同 但只会返回-1 0 1 没有中间值
Input.GetAxisRaw("Horizontal");

一些其他方法

//是否有任意按或者鼠标长按
Input.anyKey
Input.anyKeyDown
//记录当前按下的按键
Input.InputString    
    
    
//手柄输入相关
//得到连接的手柄的所有按钮名字
string[] strs = Input.GetJoystickNames();
//某一个手柄按键按下
Input.GetButtonDown("Jump")
//移动设备触摸相关
if(Input.touchCount > 0)
{
    Touch t1 = Input.touches[0];
    //位置
    print(t1.position);
    //相对上次位置的变化
    print(t1.deltaPosition);
}

//是否启用多点触控
Input.multiTouchEnabled = false;
//陀螺仪(重力感应)
//是否开启陀螺仪 必须开启 才能正常使用
Input.gyro.enabled = true;
//重力加速度向量
print(Input.gyro.gravity);
//旋转速度
print(Input.gyro.rotationRate);
//陀螺仪 当前的旋转四元数 
//比如 用这个角度信息 来控制 场景上的一个3D物体受到重力影响
//手机怎么动 它怎么动
print(Input.gyro.attitude);

内部类

内部类

  • 在类的内部 声明的类
  • 使用时要用 包裹者点出自己
class Person{
    public int age;
    public string name;
    //内部类
	public class Body{
        Arm leftArm;
        Arm rightArm;
        class Arm{
            
        }
	}
}
//使用
Person.Body body = new Person.Body();

冒泡排序和选择排序

冒泡排序

 //冒泡排序
int[] a = new int[]{3,2,5,1,6,4,2,1,5,6,7,8};
for (int i = 0; i < a.Length; i++)
{
    for (int j = 0; j < a.Length - i - 1; j++)
    {
        //如果下一位更大 则交换
        if (a[j] > a[j+1]) {
            int b = a[j];
            a[j] = a[j + 1];
            a[j + 1] = b;
        }
    }
}
foreach (var item in a)
{
    Console.WriteLine(item);
}
Console.ReadKey();

选择排序

 //选择排序
int[] a = new int[] { 3, 2, 5, 1, 6, 4, 2, 1, 5, 6, 7, 8 };
for (int j = 0; j < a.Length; j++)
{
    //最大值索引
    int maxIndex = 0;
    for (int i = 0; i < a.Length - j; i++)
    {
        if (a[maxIndex] < a[i])
        {
            maxIndex = i;
        }
    }
    //将最大值和最后一个值交换
    if (maxIndex != a.Length - j - 1)
    {
        int temp = a[a.Length - j - 1];
        a[a.Length - j - 1] = a[maxIndex];
        a[maxIndex] = temp;
    }
}

命名空间(namespace)

命名空间

  • 命名空间是用来组织和重用代码的
  • 类声明在命名空间中
  • 命名空间就像是工具包 类就像是工具包中的工具
  • 命名空间可以声明多个一样的 但属于同一个(类似分布类)
    • 同一个命名空间中 不可以有相同名称的类
    • 不同命名空间 可以有同名类 如果要同时使用 需要命名空间.类名 来指明这个类在哪个命名空间中
  • 不同命名空间相互使用 需要引用(using)或指明出处(命名空间.类名)
  • 命名空间 可以包裹命名空间 引用时需要 命名空间.命名空间中的命名空间;
  • 类的访问修饰符 只可以是public或internal(内部的 只能在这个程序集里使用)
using 命名空间;
using 命名空间.命名空间;
//基本语法
namespace 命名空间名
{
	class 类名{
	
	
	}
    
    namespace 命名空间中的命名空间{
        
    }
}

欧拉角和四元数

欧拉角

  • 由三个角度XYZ组成
  • 在特定坐标系下用于描述物体的旋转量
  • 空间中任意旋转都可以分解成绕三个互相垂直轴的三个旋转角组成的序列

欧拉角旋转约定

  • Heading-Pitch-bank是一种最常用的旋转序列约定
  • heading 物体围绕自身坐标系的Y轴 旋转的角度
  • pitch 物体围绕自身坐标系的X轴 旋转的角度
  • bank 物体绕自身的对象坐标系的Z轴 旋转的角度

Unity中的欧拉角

  • Inspector窗口中调节的Rotaion就是欧拉角
  • this.transform.enlerAngles得到的就是欧拉角角度

欧拉角的优缺点

优点:

  • 直观,易理解
  • 存储空间小(三个数表示)
  • 可以从一个方向到另一个方向旋转大于180的角度

缺点:

  • 同一旋转的表示不唯一
  • 万向节死锁
    • 绕一个轴 旋转可能会覆盖住另一个轴的旋转 从而失去一个维度的自由度

四元数

  • 一个四元数包含一个标量和一个3D向量
    • [w,v],w为标量 V为3D向量
    • [w,(x,y,z)]对于给定的任意一个四元数 表示3D空间中的一个旋转量
  • 四元数弥补了欧拉角的缺点
  • 四元数相乘相当于旋转四元数

轴角对

在3D空间中,任意旋转都可以表示 绕着某个轴旋转一个角得到(该轴不是空间中的xyz轴 而是任意一个轴)

对于给定旋转,假设围绕着n轴 旋转β度 n轴为(x,y,z)那么可以构成四元数为

Q=[cos(β/2),sin(β/2)n]

四元数Q则表示围绕轴n 旋转β度的旋转量


Unity中的四元数

//轴角对公式初始化 一般不会使用这种方式
//β为角度 需要转换成弧度传入
Quaternino q = new Quaternino(sin(β/2)x,sin(β/2)y,sin(β/2)z,cos(β/2));
//Quaternino(角度,轴);
Quaternino q2 =  Quaternino.AngleAxis(60,Vector3.right);//Vector3.right也会围绕自己的本地坐标系旋转

四元数的常用方法

单位四元数

垃圾回收(GC)

垃圾回收

  • 垃圾回收 英文简写GC(Garbage Collector)
  • 垃圾回收的过程是在遍历堆内存(Heap)上动态分配的所有对象
  • 通过识别他们是否被引用来确定那些对象是垃圾,那些对象仍要被使用
  • 所谓的垃圾就是没有引用的对象
  • 垃圾就需要被回收释放
  • 垃圾回收有很多算法,比如
    • 引用计数(Reference Counting)
    • 标记清除(Mark Sweep)
    • 标记整理(Mark Compact)
    • 复制集合(Copy Collection)
  • GC值负责堆(Heap)内存的垃圾回收
  • 引用类型都是存在堆内存中的,所以他们的分配和释放都通过垃圾回收机制来管理
  • 栈内存(Stack)上的内存是由系统自动管理的
  • 值类型都是在栈内存(Stack)中分配内存的,他们有自己的声明周期,不用对他们进行管理,会自动分配和释放

C#中内存回收机制的大概原理

0代内存 1代内存 2代内存

代的概念:

  • 代是垃圾回收机制使用的一种算法(分代算法)
  • 新分配的对象都会被配置在第0代内存中
  • 每次分配都可能会进行垃圾回收以释放内存(0代内存满时)

在一次内存回收过程开始时,垃圾回收期会认为堆中全是垃圾,会进行以下两步

  1. 标记对象 从根(静态字段、方法参数)开始检查引用对象,标记后为可达对象,未标记为不可达对象(垃圾)
  2. 迁移对象压缩堆(挂起执行托管代码线程) 释放未标记的对象 搬迁可达对象 修改引用地址

大对象总被认为是第二代内存 目的是减小性能损耗 提高性能

不会堆大对象进行搬迁压缩 85000字节(83kb)以上的对象为大对象

//手动触发垃圾回收
GC.Collect();

枚举(enum)

枚举类型

  • 枚举是一个被命名的整型常量的集合
  • 一般用于表示状态 类型等
  • 可以和Switch语句配合
  • 声明枚举:创建一个自定义的枚举类型
  • 枚举一般声明在namespeace语句块中(class外面)
  • 也可以声明在 class和struct中 但不可以在函数中声明
  • 声明枚举变量:使用自定义的枚举类型 创建一个枚举变量
  • 第一个默认值是0,下面的会依次累加 1 2 3 4 5
//一般用E_名命名
enum E_自定义枚举名
{
	名字1,名字2
}
//可以自定义 枚举的值
enum E_自定义枚举名
{
	名字1,
    名字2 = 100,
    名字3  //101
}
//使用方法
E_自定义枚举名 enumTest = E_自定义枚举名.名字1;

枚举和字符串互相转换

//枚举转字符串
string str = enumTest.ToString();
//字符串转枚举
E_自定义枚举名 enumTest2 =(E_自定义枚举名)Enum.Parse(Typeof(E_自定义枚举名),"名字1");

静态类(static class)

静态类

  • 用static修饰的类
  • 只能包含静态成员
  • 不能实例化(唯一性)
static class TestStatic{
	public static int index;
}

静态构造函数

  • 在第一次使用时被自动调用
  • 不可以使用访问修饰符
  • 不能有参数

静态类中的构造函数

  • 只会自动调用一次(不可以被实例化)
  • 静态类的构造函数也是静态的
static class TestStatic{
	public static int index;
    static TestStatic(){
        index = 5;
    }
}

普通类中的静态构造函数

static class TestStatic{
    public static int index;
    static TestStatic(){
        index = 5;
    }
    //和静态的构造函数不同 不属于重载
    public TestStatic(){
        index = 4;
    }
}

Unity界面基础

Hierarchy对象窗口

  1. Duplicate直接复制粘贴出来一个对象(Ctrl+D)
  2. Copy复制(Ctrl+C) Paste粘贴(Ctrl+V)
  3. Rename修改名字(F2)
  4. Delete删除(Delete)

Scence场景窗口

主要功能

  1. 渲染模式
  2. 2D、3D视图切换
  3. 光源、音效、特效显示开启
  4. 辅助线显示
  5. 辅助功能,控制场景上提示图标等
  6. 搜索
  7. 场景轴向

操作物体位置角度缩放

快捷键:平移(Q),移动(W),旋转(E),缩放(R),2D(T),综合(Y)

快捷键

左键相关

  1. **鼠标单击:**选中单个物体

  2. **鼠标框选:**选中多个物体

  3. **Ctrl+鼠标单击:**多选物体

  4. **长按ALT键+鼠标左键+移动鼠标:**相对观察识图中心点旋转

选中物体后,按F键:

剧中显示物体(或者再层级窗口中双击对象)

右键相关

  1. **鼠标右键按下+移动鼠标:**旋转窗口

  2. **鼠标右键按下+WASD:**漫游场景

  3. **鼠标右键按下+WASD+Shift:**快速漫游场景

  4. **长按ALT键+鼠标右键+移动鼠标:**相对屏幕中心点拉近拉远

中键相关

  1. **鼠标中键:**相对屏幕中心点拉近拉远
  2. **鼠标中键+移动鼠标:**平移观察窗口
  3. **长按ALT键+滚动鼠标中键:**鼠标指哪就朝哪拉近拉远

眼睛:是否可见

手指:是否可选中

Local和Global在场景中的作用

  • Local使用物体自己的坐标轴
  • Global使用世界的坐标轴

Game和Project窗口

Game窗口

Game窗口中显示的内容是场景中摄像机拍摄范围内的游戏对象是玩家通过屏幕能看到的东西

Project窗口

窗口中显示的内容主要是Assets文件夹中的文件主要用来管理资源脚本文件所有游戏资源都会显示在该窗口中

Project工程窗口关键功能

  1. 窗口设置
  2. 创建相关资源文件
  3. 查找
  4. 按资源类型查找
  5. 按名字查找

Project工程窗口默认文件夹

  • 默认文件夹(老版本没有)
  • Scences:
    • 里面有一个默认空场景
  • Packages:
    • 官方拓展包

资源类型

  • 图片格式:jpg、png、tga
  • 模型格式:fbx、max、maya
  • 音效:wav、mp3、ogg
  • 文本:txt、json、bytes
  • 视频:mp4

Inspector和Console

Inspector窗口

  • 显示游戏对象挂载的组件信息和对象基本设置
  • select icon给对象打个标记 方便查找
  • Tag 标签(给一些对象分类 怪物 玩家等等)
  • Layer层级(类似ps图层 渲染层级)

Console控制台窗口

默认未开启

接口(Interface)

接口

  • 接口是行为的抽象规范 使用关键字interface声明
  • 接口只包含方法、属性、索引器、事件
  • 成员不能被实现 并且不可以是私有的
  • 接口只能继承接口
  • 一个类可以继承多个接口
  • 类继承接口后必须要实现接口中所有的成员
  • 接口不可以被实例化,但可以作为容器存储对象 遵循里氏替换原则
//接口的命名规范I开头 帕斯卡命名
interface IFly{
    //方法
	void Fly();
    //属性 不可以写实现
    string Name{get;set;}
    //索引器
    int this[int index]{get;set;}
    //事件
    event Action doSomthing;
}

class Person:Ifly{
     //要实现接口中所有的成员
    public void Fly(){}
    public string Name{get;set;}
    public int this[int index]{get;set;}
    public event Action doSomthing; 
    
}

实现的接口函数 可以加virtual 再在子类重写

class Person:Ifly{
     //要实现接口中所有的成员
    public virtual void Fly(){
        onsole.WriteLine("I want fly!!!");
    }
    public string Name{get;set;}
    public int this[int index]{get;set;}
    public event Action doSomthing; 
    
}

class SuperMan:Person{
    public override void Fly(){
        Console.WriteLine("I can fly!!!");
    }   
}

接口的显式实现

结构体(struct)

结构体

  • 结构体是一种自定义变量类型 类似枚举类型 需要自己定义
  • 他是数据和函数的集合
  • 在结构体中可以声明各种变量和方法
  • 作用:用来表现存在关系的数据集合 比如用结构体表现学生 动物 人类等等

基本语法

  • 结构体一般卸载namespace语句块中
  • 结构体关键字 struct
  • 结构体可以有构造函数(可选)
    • 结构体的构造函数 必须有参数
    • 如果声明了构造函数 那么必须在其对所有变量数据初始化
    • 可以有重载 但必须对所有变量初始化
    • 一般是用于初始化
  • 结构体命名规范 帕斯卡命名法(首字母大写)
  • 不可以在结构体中 包含一个和自己相同类型的结构体
struct StructName{
	public string name;
	public int age;
	public bool sex;
	public void Speak(){
        Console.WriteLine("我的名字是"+name);
    }
}
//结构体的使用
StructName st1;
st1.name = "罗杰";
st1.Speak();

结构体和类的区别

  1. 结构体和类最大的区别是在存储空间上,结构体是值类型的(存在栈上) 类是引用类型(存在堆里)
  2. 结构体不具备继承和多态的特性 所以它也不能使用protected保护修饰符
  3. 语法上的区别
    1. 结构体不能使用protected访问修饰符 类可以
    2. 结构体的成员变量 不能指定初始值 而类可以
    3. 结构体不可以声明无参构造函数 且在申明有参构造函数后 必须在构造函数内初始化所有成员变量
    4. 结构体在声明有参构造后 无参构造不会被顶掉
    5. 结构体内不能有析构函数 类可以
    6. 结构体不可以被继承 类可以
    7. 结构体不可以被static修饰(不存在静态结构体)类可以
    8. 结构体不能在内部声明和自己一样的结构体变量 类可以

结构体和类的选择

光源组件(Light)

光源

光源参数

Type光源类型

  • Spot 聚光灯
    • Range:发光范围距离
    • Spot Angle 光锥角度
  • Directional 方向光(环境光)
  • Point点光源
  • Area面光源
    • baked only 仅烘焙状态下有用

Color 颜色

Mode光源模式

  • Realtime实时光源
    • 每一帧实时计算 性能消耗大
  • Baked 烘培光源
    • 事先计算好 无法动态变化
  • Mixed 混合光源
    • 预先计算+实时运算

Intensity光源亮度

ShadowType 阴影类型

  • NoShadows 关闭阴影
  • HardShadows 生硬阴影
  • SoftShadows 柔和阴影

Cookie 投影遮罩

Draw Halo 球形光环开关

Flare 耀斑

Culling Mast 剔除遮罩层,决定那些层的对象收到该光源影响

不常用:

  • Indirect Multiplier改变间接光的强度(反射) 低于1每次反弹会使光更暗 高于1更亮
  • RealtimeShadows 阴影设置
    • Strength 阴影暗度 0-1 越大越黑
    • Resolution 阴影贴图渲染分辨率
    • Bias 阴影推离光源的距离
    • Normal Bias 阴影投射面沿法线收缩距离
    • NearPanel 渲染阴影的近裁切面
  • Cookie Size 遮罩大小 只有方向光有
  • Render Mode 渲染优先级
    • Auto 运行时确定
    • Important 以像素质量为单位进行渲染 效果逼真 消耗大 Not Importand 以快速模式进行渲染

光面板设置

Window-Rendering-Lighting Settings

刚体(Rigidbody)

碰撞产生的条件

两个物体都要有碰撞器 至少有一个物体有刚体组件

刚体组件

面板参数

  • Mass 质量:默认单位为千克 质量越大惯性越大
  • Drag空气阻力:根据力移动对象时影响对象的空去阻力大小 0代表没有阻力
  • Angular Drag根据扭矩旋转对象时影响对象的空气阻力大小 0代表没有阻力
  • Use Gravity 是否受到重力影响
  • Is Kinematic 如果启用此选项,则对象将不会呗物理引擎驱动,只能通过(Transform)对其进行操作,对于移动平台,或者如果要动画化附加了HingeJoint的刚体,此属性将非常有用
  • InterPolate插值运算 让刚体移动更平滑 (物理系统是在Fixed Update中计算的,如果Fixed Update的更新频率很低 ,可以使用插值运算来平滑移动 一般使用前一帧)
    • None 不应用差值计算
    • Interpolate根据前一帧来平滑移动
    • Extrapolate根据下一帧来平滑移动
  • Collision Detection碰撞检测模式:可用于解决快速移动的对象穿过其他对象而不触发碰撞的问题
    • Discrete离散检测 :对场景中所有其他碰撞体使用离散碰撞检测 默认值
    • Continuous连续检测 :对于刚体 使用离散检测,对于没有刚体的物体 使用连续碰撞检测
    • Continuous Dynamic连续动态检测 :对设置为连续和连续动态的刚体使用 连续检测 对于没有刚体的物体使用连续碰撞检测
    • Continuous Speculutive 连续推测检测:对刚体和碰撞体使用推测性连续碰撞检测,该方法通常笔连续动态碰撞检测成本更低
  • 性能消耗关系:动态连续检测>连续推断检测>连续检测>离散检测
  • /usr/uploads/2025/01/920065697.png
  • Constraints约束 对刚体运动的限制
    • FreezePosition 有选择的停止刚体沿世界XYZ轴移动
    • FreezeRotaition有选择的停止刚体沿世界XYZ轴旋转

刚体加力

获取刚体组件

//获取刚体
Rigidbody rigidBody;
rigidBody = GetComponent<Rigidbody>();

添加力

//世界坐标系 Z轴正方向添加了一个力
//加力过后 对象是否停止移动 是由阻力决定的
rigidBody.AddForce(Vector3.forward * 10);
//本地坐标系
rigidBody.AddRelativeForce(Vector3.forward *10);

添加扭矩力

//世界坐标
rigidBody.AddTorque(Vector3.up * 10);
//本地坐标
rigidBody.AddRelativeTorque(Vector3.up * 10);

直接修改速度

rigidBody.velocity = Vector3.forward * 5;

模拟爆炸效果

//第一个参数力的大小
//第二个参数 爆炸的中心
//第三个参数 爆炸的半径
//只影响调用了这个方法的对象
rigidBody.AddExplosionForce(100,Vector3.Zero,20);

力的模式

  • 主要的作用 主要就是 计算的方式不同
  • 由于计算方式不同 最终的移动速度也会不同
  • ForceMode.Acceleration 给物体增加一个持续的力 忽略其质量
  • ForceMode.Force 给物体增加一个持续的力 与物体的质量有关
  • ForceMode.Impulse 给物体增加一个瞬间的力 与物体的质量有关 忽略时间默认1
  • ForceMode.VelocityChange 给物体一个瞬间的力 忽略质量
rigidBody.AddForce(Vector3.forward * 10,ForceMode.Force);

力场脚本

  • 可在Inspector面板中添加Constant Force组件
  • 添加Constant Force组件时会自动添加一个刚体组件
  • 可以通过面板上的参数 给这个对象一个持续的力

刚体的休眠

在某些情况下 添加了刚体的物体 会不参与物理计算,这是因为Unity为了节约性能 给刚体增加了一个休眠机制 可以通过脚本来判断是否为休眠状态

构造函数

构造函数补充

  • 声明了有参构造函数 就会失去自带的无参构造函数 需要自己再声明
  • 可通过this 重用构造函数代码
public string name;
public int age;
//无参构造
public Persion(){
    name = "小王";
    age = 16;
}

//通过this重用构造函数代码 会先调用this()再调用这个有参的
public Persion(int age,string name):this(){
    
}

继承

继承

  • 子类实例化时 会默认调用父类的无参构造方法(从老祖宗开始 依次向下执行)
  • 如果父类没有无参构造函数
    • 父类始终声明一个无参构造
    • 通过base关键字调用指定父类的构造函数
  • 如果父类和子类有相同的成员
    • 父类装子类:父类
    • 子类装子类:子类
  • 里氏替换原则:任何父类出现的地方,子类都可以替代(父类容器装子类对象)
  • 子类容器不可以装父类对象
class Fater()
{
	public Fater(int i){
		Console.WriteLine(i);
	}
}
class Son() : Fater
{
	//调用指定的父类构造
	public Son(int i):base(i){
		Console.WriteLine(i);
	}
    //调用自己的构造 复用代码
    public Son():this(3){
        
    }  
}

is 和 as

  • 解决父类装子类 不能用父类容器使用子类的方法
  • 类对象 is 类名 返回 bool (用于判断)
  • 类对象 as 类名 返回转换成这个类名的对象
Fater p = new Program();
if (p is Program) {
    (p as Program).Speak();
}

密封类和密封方法

  • 密封类 是使用sealed关键字修饰的类
  • 密封类无法再被继承
  • 也可以修饰方法 使其不可以在被重写
sealed class Fater{
}
//报错
class Son:Fater{}

作用

脚本规则

脚本

创建脚本的规则

  1. 不再VS中创建脚本
  2. 脚本放在Assets文件夹中 通常是Scripts文件夹
  3. 类名和文件名必须一致 否则无法挂载脚本(因为unity通过反射机制创建对象,会通过文件名去找Type)
  4. 建议不要使用中文命名
  5. 没有特殊需求 不用管命名空间
  6. 创建的脚本默认继承自 MonoBehavior

脚本的执行顺序

可在Project Settings中的Script Execution Order设置执行时间 时间小的先执行

在编辑器模式下执行脚本

  • 在类前添加ExecuteAlways特性
  • 可在编辑器模式下执行unity的生命周期函数
  • 让GUI在编辑模式下显示
[ExecuteAlways]
public class Test : Monobehaviour{
}