工程文件夹

  • Assets:工程资源文件夹(美术资源,脚本等等)
  • Library:库文件夹(Unity自动生成管理)
  • Logs:日志文件夹,记录特殊信息(Unity自动生成管理)
  • obj:编译产生中间文件(Unity自动生成管理)
  • Packages:包配置信息(Unity自动生成管理)
  • ProjectSettings:工程设置信息(Unity自动生成管理)

特殊文件夹(Assets中)

  • Resources资源文件夹
  • StreamingAssets流动资源文件夹
  • persistentDataPath持久数据文件夹
  • Plugins插件文件夹
  • Editor编辑器文件夹
  • Standard Assets默认资源文件夹

脚本基本规则

创建规则

  • 在Unity中创建
  • 可以放在Assets文件夹下的任何位置
  • 必须有继承了Mono的类的类名和文件名一致,不然不能挂载(因为反射机制创建对象,通过文件名去找Type)
  • 不要使用中文名命名
  • 没有特殊需求不用管命名空间
  • 创建的脚本默认继承 MonoBehaviour

MonoBehaviour基类

  • 创建的脚本默认都继承MonoBehaviour,继承了它才能被挂载到GameObject上
  • 继承了MonoBehaviour的脚本不能new实例只能挂载
  • 继承了MonoBehaviour的脚本不需要构造函数,因为不会new
  • 继承了MonoBehaviour的脚本可以在一个对象上挂多个(如果没有加 DisallowMultipleComponent 特性)
  • 继承MonoBehaviour的类也可以被再次继承,遵循面向对象继承多态的规则

不继承MonoBehaviour的类

  • 不继承Mono的类不能挂载到GameObject上
  • 不继承Mono的类想怎么写就怎么写,使用时需要new
  • 不继承Mono的类一般都是单例类(用于管理模块)或者数据结构类(用于存储数据)
  • 不继承Mono的类不用保留Unity周期函数

生命周期函数

  1. 对象出生
  2. Awake():出生时调用,类似构造函数,一个对象只会调用一次
  3. OnEnable():挂载的GameObject对象每次激活时调用
  4. Start():从对象被创建出来之后,第一次帧更新之前调用,一个对象只会调用一次
  5. FixedUpdate():物理帧更新,固定间隔时间执行,间隔时间可以设置
  6. OnTriggerXXX/OnCollisionXXX() 物理碰撞触发相关函数相应
  7. Update():逻辑帧更新,每帧执行
  8. LateUpdate():每帧执行,于Update之后执行
  9. OnDisable():挂载的GameObject对象每次失活时调用
  10. OnDestroy():对象销毁时调用,挂载的GameObject对象被删除时
  11. 对象死亡

Inspector窗口可编辑变量

  • 加上强序列化特性 [SerializeField] 后私有和保护的成员变量也可以被显示和编辑
  • 加上 [HideInInspector] 特性使公共属性不显示
  • 加上 [System.Serializable] 特性使自定义类或结构体可以被显示和编辑
  • 分组说明特性 [Header("xxx")] 可以让属性显示时分组
  • 悬停注释特性 [Tooltip("xxx")] 可以为属性添加鼠标悬停注释
  • 间隔特性 [Space()] 让两个字段间出现间隔
  • 修饰数值的滑条范围特性 [Range(0, 10)]
  • 多行显示字符串特性 [Multiline(5)] ,默认不写参数显示3行
  • 滚动条显示字符串特性 [TextArea(3, 4)] (最少显示3行,最多4行,超过4行显示滚动条)默认不写参数就是超过3行显示滚动条
  • 为变量添加快捷方法特性 [ContextMenuItem("显示按钮名", "方法名")] ,在窗口中右键属性名可执行函数,前提是必须存在添加的函数
  • 为方法添加 [ContextMenu("测试函数")] 特性使方法能够在Inspector中执行。在窗口中点击组件的更多选项可以调用方法

MonoBehaviour

  • 获取依附的GameObject: this.gameObject
  • 获取依附的GameObject的Transform: this.transform
  • 获取脚本是否激活: this.enabled
  • 各种 GetComponent

删除对象的方法

GameObject.Destroy(gameObject, 5)
第二个参数代表延迟几秒钟后删除,可不填。

注:Destroy方法不会立刻删除对象,而是给这个对象添加了一个移除标识,一般情况下它会在下一帧时把这个对象移除并从内存中删除。
GameObject.DestroyImmediate(gameObject) 则是立刻删除对象。

换场景不移除: GameObject.DontDestroyOnLoad(gameObject) 。在切换场景时,场景中的对象会被自动删除掉,如果希望某个对象在更换场景是不被删除就调用这个方法传入不想被移除的对象。


Vector3

  • Vector3.Distance(v1, v2) 算出两个点之间的距离。
  • VA.magnitude V3成员属性,获取向量的模长。
  • VA.normalized V3成员属性,获取单位向量。
  • 向量加减乘除
  • 向量点乘: Vector3.Dot(方向向量, VA - VB)
  • 向量叉乘: Vector3.Cross(VA, VB)
  • 向量线性插值: Vector3.Lerp(Vstart, Vtarget, t)
  • 球型插值: Vector3.Slerp(Vstart, Vtarget, t)

Quaternion

  • 单位四元数: Quaternion.identity
  • 插值运算: Quaternion.Lerp()Quaternion.Slerp() 与Vector3基本无异
  • Quaternion.LookRotation(V3) 将一个面朝向量转为四元数
  • 轴角对: Quaternion.AngleAxis(角度, 轴)
  • 四元数相乘: QC = QA * QB
  • 四元数乘向量,返回向量: VB = QA * VA

Input输入

鼠标输入

  • 鼠标在屏幕的位置: Input.mousePosition
  • 检测鼠标输入(按下瞬间): Input.GetMouseButtonDown() ,0左键,1右键,2中键
  • 检测鼠标输入(抬起瞬间): Input.GetMouseButtonUp() ,0左键,1右键,2中键
  • 鼠标长按按下抬起都会进入: Input.GetMouseButton() ,0左键,1右键,2中键
  • 鼠标中键滚动: Input.mouseScrollDelta ,返回Vector2,鼠标中键滚动会改变y值,-1往下,0没动,1往上

键盘输入

  • 键盘按下: Input.GetKeyDown(KeyCode.W)
  • 键盘按下(传入字符串的重载): Input.GetKeyDown("q") ,必须小写
  • 键盘抬起: Input.GetKeyup(KeyCode.W)
  • 键盘长按: Input.GetKey(KeyCode.W)

检测默认轴输入

  • Input.GetAxis("Horizontal") :当键盘AD按下时返回 -1 到 1 ,相当于左右方向
  • Input.GetAxis("Vertical") :当键盘WS按下时返回 -1 到 1 ,相当于上下方向
  • Input.GetAxis("Mouse X") :鼠标横向移动时返回 -1 到 1,左右
  • Input.GetAxis("Mouse Y") :鼠标纵向移动时返回 -1 到 1,上下
  • GetAxisRawGetAxis 方法使用方式相同,只不过它的返回值只会是 -1 0 1 不会有中间值

Input.anyKeyInput.anyKeyDown 任意键按下

移动端触摸输入

  • Input.touchCount :返回触碰数
  • Input.touches : 返回触碰位置数组
  • Input.multiTouchEnabled = true :启用多点触碰

Screen

  • 当前屏幕分辨率: Screen.currentResolution ,返回 Resolution
  • 当前屏幕窗口宽高: Screen.widthScreen.height
  • 屏幕休眠模式: Screen.sleepTimeout
  • Screen.fullScreen = true 运行时是否全屏模式
  • Screen.fullScreenMode = FullScreenMode.Windowed 窗口模式
  • Screen.fullScreenMode = FullScreenMode.ExclusiveFullScreen 独占全屏
  • Screen.fullScreenMode = FullScreenMode.FullScreenWindow 全屏窗口
  • Screen.fullScreenMode = FullScreenMode.MaximizedWindow 最大化窗口

力的几种模式

rigidBody.AddForce(Vector3.forward * 10, ForceMode.Acceleration)

  • ForceMode.Acceleration 给物体增加一个持续的加速度,忽略其质量
  • ForceMode.Force 给物体添加一个持续的力,与物体的质量有关
  • ForceMode.Impulse 给物体添加一个瞬间的力,与物体的质量有关,忽略时间,默认为1
  • ForceMode.VelocityChange 给物体添加一个瞬时速度,忽略质量

鼠标隐藏锁定

  • 隐藏鼠标: Cursor.visible = false
  • 锁定鼠标: Cursor.lockState = CursorLockMode.Locked
    • CursorLockMode.None 不锁定
    • CursorLockMode.Locked 锁定在屏幕中央(默认隐藏)
    • CursorLockMode.Confined 限制在窗口范围内

设置鼠标图片

Cursor.SetCursor(tex, Vector2.zero, CursorMode.Auto)
参数一:光标图片
参数二:偏移位置,相对图片左上角
参数三:平台支持的光标模式(软件或硬件)


Mathf

  • Mathf是Unity封装好的用于数学计算的工具类,位于 UnityEngine 命名空间中, System.Math 有的它基本上都有,还多了一些适用于游戏开发的方法
  • Mathf.PI
  • Mathf.Abs(-5) 绝对值
  • Mathf.CeilToInt(1.3f) 向上取整
  • Mathf.FloorToInt(1.6f) 向下取整
  • Mathf.Clamp(10, 11, 20) 钳制函数
  • Mathf.Max(1, 2, 3, ...) 取最大值
  • Mathf.Min(1, 2, 3, ...) 取最小值
  • Mathf.Pow(2, 8) 幂运算
  • Mathf.Sqrt(4) 开平方根
  • Mathf.RoundToInt(2.3) 四舍五入
  • Mathf.IsPowerOfTwo(8) 判断一个数是否是2的n次方
  • Mathf.Sign(-10) 判断正负数
  • 插值运算: Mathf.Lerp(stert, end, t)
  • 弧度转角度: anger = rad * Mathf.Rad2Deg;
  • 角度转弧度: rad = anger * Mathf.Deg2Rad;
  • 三角函数: Mathf.Sin(30 * Mathf.Deg2Rad)Mathf.Cos()Mathf.Asin()Mathf.Acos()

延迟函数

延迟函数就是会延时执行的函数,可以自定义执行的函数和延迟的时间,是MonoBehaviour基类中实现好的方法。

  • 延迟函数: Invoke("函数名", 5) 函数名,延迟时间(秒)
  • 延迟重复执行函数: InvokeRepeating("函数名", 5, 5) 函数名,第一次执行的延迟时间,之后每次执行的间隔时间
  • 注:延时函数没法传参,也不能调用其他脚本的函数,想传参或调用只能包裹一层无参函数
  • 取消全部延迟函数: CancelInvoke()
  • 取消指定延迟函数: CancelInvoke("函数名")
  • 是否存在延时函数: IsInvoking()
  • 是否存在指定延迟函数: IsInvoking("函数名")
  • 注:脚本或依附对象失活,延时函数不受影响,可以继续执行,但脚本或依附对象被删除后,延迟函数无法继续执行

协程

  • Unity是支持多线程的,但在Unity中新开的线程无法访问Unity相关对象的内容,可以作为算法计算等不需要访问游戏对象时使用
  • 协程(协同程序)不是多线程
  • 新开的线程时独立的一个管道,和主线程并行执行
  • 新开的协程是在原线程上开启,进行逻辑分时分布执行
  • Unity的协程函数实际上是一个返回 IEnumerator(迭代器) 的函数,而 StartCoroutine() 方法实际上是需要一个 IEnumerator 参数,所以可以通过 Ienumerator ie = MyCoroutineFun() 先定义迭代器,再 StartCoroutine(ie) 调用协程函数。
  • StartCoroutine() 方法会返回一个协程 Coroutine 对象
  • 关闭所有协程: StopAllCoroutines()
  • 关闭指定协程: StopCoroutine(C1)
  • 协程开启后,脚本组件和物体销毁,协程停止执行;物体失活,协程停止执行;脚本组件失活,协程仍然执行

yield return 返回值

  • 下一帧执行: yield return 数字yield return null ,在Update和LateUpdate之间执行
  • 等待指定秒后执行: yield return new WaitForSeconds(num) , 在Update和LateUpdate之间执行
  • 等待下一个固定物理帧执行: yield return new WaitForFixedUpdate() , 在FixedUpdate和碰撞检测相关函数之后执行
  • 等待摄像机和GUI渲染完成后执行: yield return new WaitForEndOfFrame() , 在LateUpdate之后的渲染相关处理完毕之后执行
  • 一些特殊类型的对象,比如异步加载相关函数返回的对象,如异步加载资源,异步加载场景,网络加载,一般在Update和LateUpdate之间执行
  • 跳出协程: yield break

协程的本质

  • 协程可以分为两部分:协程函数的本体和协程调度器
    • 协程函数的本体就是一个能够中键暂停返回的函数
    • 协程调度器是Unity内部实现的,会在对应的时机执行协程函数
  • Unity只实现了协程调度部分,协程的本体本质上就是一个C#的迭代器方法
  • yield return 会把函数分为几部分进行执行,而协程调度器就是利用 yield return 返回的内容决定下一次再何时继续执行迭代器函数中的下一部分
  • 理论上来说,我们可以利用迭代器函数的特点,自己实现协程调度器来取代Unity自带的调度器

碰撞检测

范围检测

盒状范围检测

1
Physics.OverlapBox(Vector3.zero, Vector3.one, Quaternion.AngleAxis(45, Vector3.up), layermask, QueryTriggerInteraction.UseGlobal);
  1. 参数一:立方体中心点
  2. 参数二:立方体三边大小
  3. 参数三:立方体角度
  4. 参数四:检测指定层级(不填检测所有层)
  5. 参数五:是否忽略触发器
    1. QueryTriggerInteraction.UseGlobal 使用全局设置
    2. QueryTriggerInteraction.Collide 检测触发器
    3. QueryTriggerInteraction.Ignore 忽略触发器
    4. 不填默认全局设置
  6. 返回值:在该范围内的触发器对象 Collider[]
    检测碰撞到的碰撞器数量
1
Physics.OverlapBoxNonAlloc(Vector3.zero, Vector3.one, colliders);
  1. 参数:中心点、大小、用来检测的碰撞器数组
  2. 返回值:碰撞到的碰撞器数量

球形范围检测

1
Physics.OverlapSphere(Vector3.zero, 5, layerMask);
  1. 参数一:中心点
  2. 参数二:球半径
  3. 参数三:检测指定层级(不填检测所有层)
  4. 参数四:是否忽略触发器
  5. 返回值:在该范围内的触发器
    检测碰撞到的碰撞器数量
1
Physics.OverlapSphereNonAlloc(Vector3.zero, 5, colliders);

胶囊范围检测

1
Physics.OverlapCapsule(Vector3.zero, Vector3.up, 1, layerMask, QueryTriggerInteraction.UseGlobal);
  1. 参数一:半圆一中心点
  2. 参数二:半圆二中心点
  3. 参数三:半圆半径
  4. 参数四:检测指定层级(不填检测所有层)
  5. 参数五:是否忽略触发器
  6. 返回值:在该范围内的触发器
    检测碰撞到的碰撞器数量
1
Physics.OverlapCapsuleNonAlloc(Vector3.zero, Vector3.up, 1, colliders);

射线检测

射线对象

1
Ray r = new Ray(Vector3.right, Vector3.forward);
  1. 参数一:起点
  2. 参数二:方向

属性

  1. r.origin :起点
  2. r.direction :方向

摄像机发出的射线

1
Ray r = Camera.main.ScreenPointToRay(Input.mousePosition);

得到一条从屏幕位置作为起点,摄像机视口方向为方向,参数位置为终点的射线。

射线检测函数

基础射线检测

1
2
3
4
5
// 准备一条射线
Ray r = new Ray(Vector3.zero, Vector3.forward);

// 进行射线检测
Physics.Raycast(r, 100, layerMask, QueryTriggerInteraction.UseGlobal);
  1. 参数一:射线
  2. 参数二:检测的最大距离,超出这个距离不检测
  3. 参数三:检测指定层级(不填检测所有层)
  4. 参数四:是否忽略触发器
  5. 返回值:bool , 当碰撞到对象时,返回 true ,否则返回 false

一种重载:

1
Physics.Raycast(Vector3.zero, Vector3.forward, 100, layerMask, QueryTriggerInteraction.UseGlobal);

不用传入射线,直接传入七点和方向,也可以用于判断。就是把第一个参数射线变成了射线的两个点,一个起点,一个方向。

获取碰撞的单个物体信息

1
2
3
4
5
6
7
8
// 物体信息类 RaycastHit
RaycastHit hitInfo;

Physics.Raycast(r, out hitInfo, 100, layerMask, QueryTriggerInteraction.UseGlobal);

// 获取碰撞到的物体的name
print(hitInfo.collider.gameObject.name);
// 其他详见RaycastHit属性
  1. 参数一:射线
  2. 参数二: RaycastHit 是结构体(值类型),Unity会通过out关键字在函数内部处理后得到碰撞数据后返回到该参数中
  3. 参数三:距离
  4. 参数四:检测指定层级(不填检测所有层)
  5. 参数五:是否忽略触发器

一种重载:

1
Physics.Raycast(Vector3.zero, Vector3.forward, out hitInfo, 100, layerMask, QueryTriggerInteraction.UseGlobal);

不用传入射线,直接传入起点和方向,也可以用于判断。

获取多个碰撞的物体

1
2
// 使用 RaycastHit 数组接收返回值 
RaycastHit[] hits = Physics.RaycastAll(r, 100, layerMask, QueryTriggerInteraction.UseGlobal);
  1. 参数一:射线
  2. 参数二:距离
  3. 参数三:检测指定层级(不填检测所有层)
  4. 参数四:是否忽略触发器
  5. 返回值: RaycastHit 数组

一种重载……

还有一种重载:

1
Physics.RaycastNonAlloc(r, hits, 100, layerMask, QueryTriggerInteraction.UseGlobal);

返回碰撞的数量,通过out得到碰撞物体数据


—end—