unity——C#
一.unityC#脚本基础

如图是unity中C#脚本的基本框架,我们来解释一下:
1.using UnityEngine:引入unity引擎的命名空间,如果我们想使用unity引擎自带的功能,那么必须引入这个命名空间。
2.public class NewMonoBehaviourScript : MonoBehaviour:这里MonoBehaviour是所以几乎所有unity脚本的基类,我们编写的unity脚本类一般必须继承这个类,继承自MonoBehaviour的所有类都必须是public的,否则unity无法识别。
1.生命周期方法:
在同一个类中:相同的生命周期方法只能存在一个,无重载,无返回值,无参数,unity编辑器只能识别无参,无返回值的。
1.void Start() { ... }
执行时机:它会在脚本被启用后、第一帧的 Update 执行之前,自动调用一次。
作用:获取其他游戏对象或管理器的引用。
依赖其他脚本初始化完毕后再执行的操作。
启动协程(Coroutine)。
2.void Update() { ... }
每一帧渲染前自动调用一次。执行频率:不固定。完全取决于设备的帧率(FPS),
核心作用:
接收玩家输入(键盘、鼠标、手柄)。
更新非物理的游戏状态(如血量扣减、UI 刷新、动画状态机切换)。
非物理物体的位移(通过 Transform.Translate)。
注意:跳跃逻辑虽然是由施加的物理力实现的,但是为了防止跳跃键失灵,我们通常将其放在Update中,更为严谨的做法是:意图标记法”(Input Buffering / 意图缓存)。
它的核心思想是:Update 只负责“记录意图”,FixedUpdate 负责“执行物理”。
需要注意的是:因为每帧时间不同,任何与“速度/位移”相关的计算,都必须乘以 Time.deltaTime
3.void Awake() { ... }
最早调用的函数,只调用一次,用于获取自身身上的组件引用(GetComponent)。初始化自身的内部变量。设置单例模式(Singleton),需要注意与start的作用区别。
4.void onEnable() { ... }
与void Start() { ... }的区别:
Start():脚本的一生中只执行一次。
OnEnable():脚本每激活一次,就会执行一次。如果物体反复开关,它就会反复调用
5.void FixedUpdate()
默认固定时间间隔0.02秒会调用一次,可以在设置中更改,
核心作用:
所有与 Rigidbody(刚体) 相关的操作。
施加力(AddForce)、设置速度(velocity)、物理移动(MovePosition)。
物理射线检测(如果要求极高的物理碰撞精度)。
注意:不要在 FixedUpdate 里写 Input 输入检测
6.void LateUpdate()
在所有的 Update 都执行完毕之后,渲染画面之前调用,
核心作用:
相机跟随(最经典用法)。
程序化动画(如根据骨骼位置计算 IK、布料结算)。
需要在其他所有脚本计算完位置后,再强制覆盖位置的逻辑。
对于相机跟随:假设玩家移动写在 Update,相机跟随也写在 Update。由于同帧内脚本执行顺序是随机的,如果相机先执行,玩家后执行,相机记录的是玩家上一帧的位置,渲染出来时相机和玩家就会产生一帧的错位
7.void OnDisable()
与void onEnable相似,在脚本被设置为非激活状态会调用一次。
8.void OnDestory()
在脚本销毁时调用一次,Destory实际上是延迟销毁,会继续执行完剩下的一帧,同时Destroy还接收第二个参数,表示延迟的时间,在 Unity 中,OnDestroy 是物体生命周期中的 “遗言”阶段。
用一句话概括它的核心职责:“清理后事(Cleanup)”,而不是“触发新的核心游戏逻辑(如加分、生成掉落物)”
一般函数的调用顺序:
1.Awake —— 最早。不管物体是否激活,只要脚本0实例存在就会调用。只调一次。
2.OnEnable —— 其次。只有在物体处于激活状态时才会调用。每次激活都会调。
3.Start —— 最后。在第一次帧更新前调用。只调一次(前提是物体是激活的)。
需要注意的是:这些生命周期方法使用的类都必须继承MonoBehaviour,继承MonoBehaviour不能被new,unity中所有需要挂载到游戏物体上的脚本都必须继承MonoBehaviour。
注意:start与Awake的区别,lateUpdate,update与fixUpdate的区别。
2.脚本的调用顺序
默认是先创建的脚本先执行,但是如果我们使用start方法进行初始化,这可能会造成初始化问题,所以我们一般使用Awake方法进行初始化,此外,如果我们还想要控制一些脚本的执行顺序,可以在unity中设置。
3.变量的设置
我们可以在C#脚本使用public作用域来声明一个变量,通过这种方法声明的变量可以在unity中直接设定器数值,而如果我们定义一个private的变量,那么只有这个脚本中能够访问,不能在unity中设定
需要注意的是:构造函数 → Awake → Start,对于继承自MonoBehaviour的类,构造函数可能在编译编辑就已经调用,调用时机不可控,所以对于初始化操作,尽量在Start和Awake函数中执行,不要在构造函数中执行,而对于常规的C#类,构造函数可以正常的用于初始化工作。
二.调试方法
1.Debug.Log("test"),普通输出test。
2.Debug.LogWaring("test"),警告输出tset
3.Debug.LogError("test"),错误输出
4.Debug.DrawLine(vector3.zero,vector3.one),画线,基础接收两个参数,表示画出直线的起始位置坐标与结束位置坐标。
5.Debug.DrawRay(vector3.zero,vector3.one),画射线,基础接收两个参数,表示画出的直线的起始位置与方向,4与5相同,也可以接收第三个参数,Color.red/blue等,表示画出线的颜色。
三.vector3
vector3最常见的作用是表示一个三维向量,当然其还能表示坐标,旋转等。
1.初始化方式
(1)基本初始化方式:vector3 v=new vector3(1,2,3)
(2)初始化属性:vector3 v=vector3.zero/one/left/right/foward等;
也可以:v.x,v.y,v.z这样的赋值。
2.常见方法
(1)求两个向量的夹角:vector3.Angle(vector3.zero,vector3.one),接收两个向量。
(2)计算两个坐标之间的距离:vector3.Distance(vector3.zero,vector3.one),接收两个坐标,计算它们之间的距离。
(3)向量点乘:vector3.Dot(vector3.zero,vector3.one),接收两个向量,计算它们点乘的结果。
(4)向量叉乘:vector3.Cross(vector3.zero,vector3.one),接收两个向量,计算叉乘的结果(向量的叉乘就是造出一个与这两个向量都垂直的向量,且这个向量的长度等于原来两个向量围城的面积)。
(5)插值:vector3.Lerp(vector3.zero,vector3.one,0.5f),接收两个向量与一个叉乘系数t(0<t<1)(插值的作用是根据插值系数t,找到一个位于传入坐标之间的等比例的坐标,数学计算公式是a+(b-a)*t,我们这里假设传入的第一个参数是a,第二个是b,常见的插值系数t是0.5,即找到一个位于坐标a,b之间的坐标)
(6)ProjectOnPlane,Vector3的成员方法:作用是将一个向量投影到一个平面上,接收两个参数,需要投影的向量,投影平面的法线向量。可以用于代替y=0来实现移动(通常使用y=0导致地面必须是平的),使用ProjectOnPlane可以根据地面动态的设定方向。
(7)MoveTowards,Vector3/2的成员方法:作用是逐渐的移动,接收三个参数,起始位置,结束位置,每一帧允许移动的最大距离,如果距离过大,会返回,可用与实现平滑移动。
3.常见属性
(1)取向量的模:v.magnitude,获取向量v的模,sqrMagnitude,获取向量模的平方,计算量小于直接取模,可用于比较两个向量的长度大小。
(2)单位化向量/规范化向量:v.normalized,将返回向量v化为单位向量的结果。
4.Vector2
此外,如果我们需要使用二维的向量,如UI的制作,也可以使用Vector2
四.欧拉角与四元数
欧拉角与四元数都是描述旋转的变量,一般在脚本中,考虑到计算的效率问题,我们都使用四元数
欧拉角的表示使用的就是vector3。
四元数:
1.初始化方法
(1)Quaternion q=new Quaternion(x,y,z,w),基础初始化方式,接收数个整数。
(2)初始化属性:Quaternion q=Quaternion.idenity,无旋转的四元数。
利用欧拉角转化为四元数:Quaternion q=Quaternion.Euler(x,y,z)
我们也可以通过看向一个位置进行初始化:Quaternion q=Quaternion.LookRotation(x,y,z)
我们也可以将一个四元数转化为欧拉角:vector3 v=q.eulerAngles;
(3)Slerp插值:专门用于四元数的插值,等速插值,只要是旋转插值,就用 Slerp;只有位置/数值插值才用 Lerp。原因在于:对于旋转直接使用Lerp插值可能会导致万向节死锁,速度不均匀,路径不是最短的问题。性能消耗高于Lerp
Nlerp:Lerp+Normalize,可用于代替Slerp对四元数使用,效果略差,但是性能消耗要少,可看作是低配版的Slerp,Unity 的 Quaternion.Lerp 内部已经自动做了归一化,所以它实际上就是 Nlerp,所以我们不需要手动的normalize
五.物体类的使用
unity中所有游戏物体的类都是GameObject。
1.脚本代码中获取物体
(1)GameObject go=this.gameObject;
(2)我们也可以简写为gameObject,其就是这个游戏物体。
(3)获取其他游戏物体:public GameObject x,获取该物体的子物体命名为x(记得在unity中将该子物体挂载到该物体的脚本中)
(4)我们还可以通过游戏物体的名称来获取游戏物体:GameObject 命名 =GameObject.Find("游戏物体的名称")
(5)通过标签来获取游戏物体:GameObject 命名 =GameObject.FindWithTag("游戏物体的标签"),如果存在多个物体使用同一个标签,会发生覆盖,对于多个物体使用同一个标签,我们可以使用FindGameObjectsWithTag,返回一个物体类类型的数组。
2.常见属性与方法
(1)获取游戏物体的名字:go.name
(2)获取游戏物体的标签:go.tag
(3)获取游戏物体的图层:go.layer
(4)获取游戏物体的激活状态:x.activeInHierarchy/activeSelf(注意这里不要对父物体使用,因为脚本挂载在父物体上,如果父物体未激活的话,那么脚本根本不会运行),
这里activeInHierarchy是获取当前真正的激活状态(或者说父物体的激活状态,因为一旦父物体未激活,其子物体也不会激活),activeSelf获取的是物体自身的激活状态(其不一定真正激活,这取决于其父物体)。
(5)获取游戏物体的Transform组件:如我们所有游戏物体都有Transform组件:Transform t=this.transform(一个游戏组件其实就是一个脚本,对应一个类,所以Transform的类型就是Transform类),与GameObject相同,我们在使用时也可以直接使用:transform.position(获取位置)
(6)获取游戏物体的其他组件:需要通过泛型:模式:该组件的类 命名 =GetComponent<给组件的类>(),GetComponent也可以显示的指定一个游戏物体:GameObject.GetComponent
(7)获取当前游戏物体的子/父物体上的组件:与(6)格式相同,
使用GetComponentInChildren/GetComponentInParent,注意这俩个实际上是:从当前游戏物体向子/父物体遍历获取第一个遍历到的组件
(8)添加一个组件:ganmObject.AddComponent<组件类>();
(9)设置一个游戏物体的激活状态:go.SetActive(true/false);
(10)通过代码将一个预设体实例化为一个真正的物体:
获取预设体:public GameObject x,然后在unity中将预设体挂载到脚本上
实例化预设体:Instantiate(x);
实力化一个预设体并将其作为当前游戏物体的子物体:Instantiate(x,transform);
实力化一个预设体并为其设置位置,旋转等:Instantiate(x,Vector3.zero,Quaternion.indentity);
Instantiate函数还可接收参数用于设定实例化的物体的父物体
(11)设置一个组件的激活状态:组件名.enable=true/false。
销毁生成的游戏物体:Destory(该游戏对象)重载接收第二个参数表示销毁的时间。
注意:对于position存在localPosition与position,对于rotation也存在,loca表示相对于父物体,但是对于Scale缩放,只存在localScale。
六.时间类
unity中时间类是Time。
1.常见属性与方法
(1)获取游戏从开始到现在所花费的时间:Time.time
(2)更改时间缩放值:Time.timeScale,默认为1,可用于实现加速,减速与时间的暂停,为0时暂停。
(3)固定时间间隔:Time.fixedDeltaTime(0.02秒)
(4)上一帧到这一帧所用的游戏时间:Time.deltaTime,可用于制作计时器
七.Application类
常用于在 Unity 中进行存档、读取配置文件或加载外部资源。
1.常见属性与方法
(1)获取游戏数据文件夹路径(Assets文件夹路径):Application.dataPath,返回对应路径字符串,一般情况是只读,程序打包以后该路径中的内容会被加密压缩,但是如果游戏只是pc端的,那么这里是可读,可写的,我们可以通过字符串拼接的方式访问该文件夹中的文件,但是对于移动端来说,该路径位于安装路径下,不可写。
(2)获取持久化文件夹路径:Application.persistentDataPath,可读可写,一般用于游戏的存档
(3)StreamingAssets文件夹路径:Application.streamingAssetsPath,只读,并不会加密压缩
(4)临时文件夹路径:Application.temporaryCachePath,
(5)控制是否在后台运行:Application.runlnBackground;
(6)打开一个链接:Application.OpenURL(" ")打开一个链接
(7)退出游戏:Application.Quit();
八.场景类与场景管理类
使用场景类需要先导入场景管理类的命名空间:using UnityEngine.SceneManagement
场景管理类为SceneManager,场景类为Secene
1.常见属性与方法
(1)同步加载场景:SceneManager.LoadScene(1),接收一个场景索引(记得在unity中将场景添加到场景列表中)
(2)我们也可以使用场景的名称来跳转:SceneManager.LoadScene(" "),也可以接收第二个参数表示替换加载(只显示一个场景)SceneManager.LoadScene("new scene",LoadSceneMode.Single),表示叠加加载(同时显示两个场景):SceneManager.LoadScene("new scene",LoadSceneMode.AddActive)
异步加载:SceneManager.LoadSceneAsync("new scene"),对于大型场景,同步加载会发生卡顿,为了解决这种情况,我们需要使用异步加载,在加载的同时设计一个进度条,同时对于大规模场景,我们一般会对场景进行划分,动态的生成玩家周围的场景,以节省性能。
(3)获取当前激活的场景:Secene s=SceneManager.GetActiveScene();
(4)获取场景的名称:s.name
(5)场景是否被加载:s.isLoaded
(6)场景路径:s.path
(7)查看场景索引:s.buildIndex
(8)获取场景中所有的游戏物体:GameObject [] gos=scene.GetRootGameObject(),返回一个包含所有游戏物体的数组,只能获取最外层的游戏物体,即无法获取子物体
(9)获取当前加载的场景数量:SceneManager.sceneCount;
(10)创建一个新场景:SceneManager.CreateScene("命名")
(11)卸载一个场景:SceneManager.UnloadScene(s)(同步)
SceneManager.UnloadSceneAsync(s)(异步)
(12)可以通过重新加载场景实现重新开始游戏:SceneManager.LoadScene(SceneManager.GetActiveScene().buildIndex);,SceneManager.LoadScene接收两种参数,通过索引加载场景与通过场景名称加载场景,GetActiveScene()是获取当前激活的场景,buildIndex是获取其下标。
2.unity协程简介
假如我们想要异步的加载一个场景,就需要使用到协程,协程与C#的异步不同但相似,都是为了解决线程阻塞的问题,协程的性能开销要比一般函高得多,对于耗时操作,不能直接在start函数中运行,会阻塞进程,需要使用协程
unity协程的返回类型
(1). 引擎有特殊处理逻辑的类型(带 new 的指令)
当你返回这些类型时,Unity 会根据指令的具体含义,在特定的时机恢复协程的执行:
WaitForSeconds(float):暂停指定的秒数后继续执行,常用于定时器或延迟操作。
WaitForFixedUpdate:等待下一次物理引擎更新(FixedUpdate)执行完毕后继续。
WaitForEndOfFrame:等待当前帧的摄像机和 GUI 渲染全部结束后,在当前帧末尾继续执行。
WaitUntil(Func<bool>):持续等待,直到传入的布尔委托返回 true 时才继续执行。
WWW / AsyncOperation:挂起协程,直到异步下载或异步操作(如场景加载)完成后才继续执行。
嵌套协程(StartCoroutine):挂起当前协程,直到被调用的子协程完全执行完毕后,再继续执行父协程的后续代码。
(2). 引擎无特殊处理的类型(等同于 yield return null)
如果你返回的是引擎不认识的类型,Unity 会将其统一视为 yield return null,其效果是暂停当前帧,在下一帧的 Update 之后、LateUpdate 之前恢复执行。
使用协程必须引入using System.Collections;

如图是一个协程的基本框架,也是异步创建场景的实现。
获取加载进度:asyncLoad.progress,一般该操作放在Update函数中,加载进度是从0到0.9。
场景自动跳转设置:asyncLoad.allowSceneActivation=true/false(默认是true表示自动跳转)
3.可以通过材质的偏移量来实现平面的滚动,形成平面动态背景
平面的滚动需要一个vector2表示x,y轴上的滚动速度,
(1)需要获取Renderer渲染器组件,通过currentOffset = rend.sharedMaterial.mainTextureOffset;记录起始材质偏移量,
(2)sharedMaterial是获取共享材质,会对所有使用改材质的产生影响,如果希望支队某一个对象生效,应该使用Material,会获取这个材质的一份拷贝(因为需要拷贝,所有效率较低)
(3)mainTextureOffset主贴图偏移,返回一个该材质的起始偏移量vector2类型,
(4)通过动态修改材质的mainTextureOffset,就能实现平面的滚动
九.Transform类
Transform类控制游戏物体的位置,旋转,缩放以及父子关系。
1.常见方法与属性
(1)获取位置:transform.position,获取游戏物体在世界中的位置,
transform.localposition,获取游戏物体相对于父物体的位置。
(2)获取旋转:transform.rotation,获取游戏物体在世界中的旋转
transform.localRotation,获取游戏物体相对于父物体的旋转,这俩种都是四元数表示(不能直接使用Vector3接收,这与位置position不同)
欧拉角表示:transform.eulerAngles,获取游戏物体在世界中的旋转
transform.localEulerAngles,获取游戏物体相对于父物体的旋转
(3)获取缩放:transform.localScale,获取游戏物体相对于父物体的缩放
(4)获取当前物体的方向:transform.forward,获取前方
transform.right获取右方,transform.up,获取上方,(我们一般将unity中y作为上方,z作为前方,x作为右方),返回的都是一个vector3
(5)让物体看向一点:transform.lookAt(Vector3.zero),接收一个vector3,放在Update中实现时刻看向。
(6)实现旋转:transform.Rotate(Vector3.up,1),接收两个参数,旋转的轴,旋转的角度,放在Update中实现时按帧旋转
(7)实现绕某个物体旋转:transform.RotateAround(Vector3.zero,Vector3.up,5)
(8)实现移动:transform.Translate(Vector.forward*0.1f),按0.1的速度向指定方向移动,需要注意的是这种移动方法默认是朝着移动物体的自身认为的方向移动的即是Space.Self,如果这时我们的物体在旋转,比如朝向某个方向,就会导致移动方向出现问题,这时我们就需要加上Space.World,使其使用世界坐标系的方向移动,同时这里的位移计算是输入向量×速度系数(10)×时间(Δt),为了斜向速度与直线速度的统一我们一般需要将传入的向量单位化.normalized
(9)获取父物体的Transform组件:transform.parent,
获取游戏物体:transform.parent.gameObject
(10)获取子物体的个数:transform.childCount
(11)解除与子物体的父子关系:transform.DetachChildren()
(12)获取子物体的Transform组件:transform.Find("子物体名称")
transform.GetChild(0),接收一个索引。
(13)判断一个物体是否是另一个物体的子物体:child.isChildOf(transform),返回一个bool值
(14)设置父物体:child.SetParent(transform),将transform设置为child的父物体。
(15)获取该物体的名字:transform.name与gameObject.name相同
(16)获取自身的其他的组件:transform.GetComponent<组件名>(),但不能通过transform使用AddComponent,原因是AddComponent是定义在GameObject中的,而GetComponent定义在Component基类中。
十.键盘,鼠标操作
对于键盘,鼠标操作我们需要实时监控,所以我们需要将其写在Update中,对于旧版unity的键盘,鼠标操作,都是在Input类中,而在新版,我们需要通过Player Input组件控制。
1.常见方法与属性
旧版:
(1)鼠标的点击:if(Input.GetMouseButtonDown(0)),(表示如果鼠标右键单击,则执行什么操作,即返回值是一个bool值,0表示鼠标右键,1表示鼠标左键,2表示鼠标滚轮)
(2)持续按下鼠标:Input.GetMouseButton(0),监听是否持续按下鼠标左键,同样返回bool值
(3)抬起鼠标:Input.GetMouseButtonDownUp(0),监听是否抬起鼠标左键。
(4)键盘操作:与鼠标操作类似:包括按下,持续按下,松开,GetKeyDown,GetKey,GetKeyUp
参数我们可以使用枚举KeyCode.A/B等,也可以使用字符串"a"/"b"等,但注意不能使用大写的字符
串。
(5)获取鼠标点击的位置:Input.MousePosition()
新版:
在新版的unity中,我们需要在unity中添加Player Input组件,并创建一个Input Action文件挂载到其上,在Input Action文件中自定义的添加包括键盘,鼠标,虚拟轴,虚拟按键,触摸等。
新版在C#脚本中我们需要直接获取到Player Input组件,进行操作,组件的类为PlayerInput。
(1)开启动作方法:input.CurrentActionMap.Enable()
(2)切换方法:input.SwitchCurrnetAction(" ")
(3)按下动作事件:input.actions["动作命名"].performed+=jump,实际上是一个事件。
(4)触发动作:private void jump(InputAction.CallbackContext obj) { },通过事件触发
(5)松开动作事件:input.actions["动作命名"].cancled+=jump,这样是将这两种都绑定到了同一个事件上,想要区分,我们可以在这个事件中进行判断,即:if(obj.performed),判断其该属性是否为真。
(6)而对于虚拟轴的设置,我们需要将其设置为Vector2类型的量,我们就需要读取其返回的Vector2了:Vector2 vec2=input.actions["动作命名"].readValue<Vector2>();,得到的Vector2就是虚拟轴的表示向量。
(7)获取鼠标点击的位置:Mouse.currnet.position.ReadValue()
十一.虚拟轴与虚拟按键
虚拟轴(Virtual Axis)是 Unity 旧版输入系统(Input Manager)中的一个核心概念,你可以把它理解为一个抽象的、取值范围在 -1 到 1 之间的“数轴”,目的是提高兼容性和简化代码。
1.常见方法
(1)获取虚拟轴:旧unity上的方法:float horizontal = Input.GetAxis("Horizontal"),接收虚拟轴的名称
(2)获取虚拟按键:Input.GetButtonDown(" "),接收一个虚拟按键名称,与键盘相同,同样存在
GetButton,GetButtonUp方法
十二.触摸方法
一般用于手机与平板
1.常见方法与属性
(1)开启多点触摸:Input.multiTouchEnabled=true,默认是false
(2)判断触摸次数:Input.touchCount;如果为1,则为单点触摸
(3)创建触摸对象:Touch touch=Input.touches[0],根据我们触摸的次数返回对应touches数组中存储的Touch对象,对应的对象存储该触摸的信息。
(4)触摸的位置:touch.position
(5)触摸的阶段:
if(Input.touchCount==1)
{
Touch touch=Input.touches[0];
switch(touch.phase)
{
case TouchPhase.Began://触摸开始
Debug.Log("Touch Began");
break;
case TouchPhase.Moved://触摸移动
Debug.Log("Touch Moved");
break;
case TouchPhase.Stationary://触摸静止
Debug.Log("Touch Stationary");
break;
case TouchPhase.Ended://触摸结束
Debug.Log("Touch Ended");
break;
case TouchPhase.Canceled://触摸取消
Debug.Log("Touch Canceled");
break;
}
}
十三.音频类
unity中音频类为AudioClip,音频播放器组件类为AudioSource
1.常见方法与属性
(1)获取音频:public AudioClip music,之后需要在unity中进行音频的挂载。
(2)获取播放器组件:使用获取组件的通用方法:
AudioSource musicPlayer=GetComponent<AudioSource>();
(3)设定播放片段:musicPlayer.cilp=music
(4)设定循环播放:musicPlayer.loop=true
(5)设定音量:musicPlayer.volum,musicPlayer.pitch同时修改播放速度与音调高低,本质上是在改变音频采样率的读取速度。
(6)播放:musicPlayer.Play()(无论暂停与否,都从初始状态开始播放);
(7)控制播放的继续与暂停:
musicPlayer.isPlaying,判断是否正在播放,返回bool值,只读
musicPlayer.Stop()停止播放(会将播放进度归零),musicPlayer.Pause()暂停播放(会保存播放进度),musicPlayer.UnPause()继续播放(与musicPlayer.Pause()联用,从当前保存的进度继续播放)
(8)播放音效:musicPlayer.PlayOneShot(),声音可以重叠
(9)对于存在大量音频与音效的,逐个挂载较慢,我们一般采用动态加载的方式,将所所有音频与音效放在Resourse文件夹下,默认放在这个文件夹下的音频可以直接在脚本中查找到:musicPlayer.clip=Resourse.Load<AudioClip>("音频文件名")
2.唤醒时播放与脚本控制播放
(1). 唤醒时播放(Play On Awake)
适用于:爆炸特效、技能释放特效、物品拾取特效等,.对于背景音乐,一般都是新建一个单独的空物体负责,一般都是选择唤醒时播放
(2). 设计脚本控制播放(AudioSource.Play / PlayOneShot)
适用于:玩家开火、角色移动时的脚步声、坦克的引擎声等。
(3)对于子弹,一般不使用唤醒时播放,所以我们需要为其设计脚本,一般情况,子弹射击不易放在Update中,除非存在按键限制(如按鼠标才会发射子弹),而对于全自动的枪,为了防止射速过快导致的音效重叠,我们要两种方法:计时器/协程
同时对于子弹的音效播放,一般通过枪口(子弹的生成)来实现播放,不能为每一个子弹添加Audio Source和播放脚本,因为子弹的数量很大,这样会导致大量的消耗。
3.Resourse文件夹
对于存储在Resourse文件夹下的资源,我们可以在运行时动态加载,通过Resources.LoadAll<T>(path)或者Resources.Load<T>(path),在脚本中获取。
十四.视频类
视频类的使用需要引入UnityEngine.Vedio命名空间,视频类为VedioClip,视频播放器类为VedioPlayer
1.常见方法与属性
(1)获取视频,视频播放器组件与音频操作相同,不再赘述
(2)视频播放同样存在Play,isPlaying,Stop,Pause等音频相同的方法,同样不再赘述。
十五.角色控制类
unity中角色控制类为CharacterControler
如是根据虚拟轴实现的简单移动:

常见方法:(1)简单移动方法:player.SimpleMove(move*20),接收方向*速度,需要按秒移动的话*Time.deltaTime
十六.碰撞与触发监听
碰撞信息类:Conllision,触发类Collider,碰撞与触发非常相似,但是Conllision是碰撞信息类,获取碰撞器需要:collision.collider,但这里的collider就是碰撞器
1.常用方法:
碰撞与触发的检测都要求碰撞双方都存在碰撞器组件,至少又一方存在刚体组件,如果是触发的话,还需要至少有一方存在触发器。
碰撞:
(1)监听发生碰撞:private void OnCollisionEnter(Conllision collision)
(2)监听持续碰撞:private void OnCollisionStay(Conllision collision)
(3)结束碰撞方法:private void OnCollisionExit(Conllision collision)
可以通过collision.gameObject.name获取碰撞到的物体。
触发:
(1)监听发生碰撞:private void OnTriggerEnter(Collider collider)
(2)监听持续碰撞:private void OnTriggerStay(Collider collider)
(3)结束碰撞方法:private void OnTriggerExit(Collider collider)
注意这两种方法的参数类型不相同,碰撞的参数是Conllision碰撞信息类,获取碰撞组件需要Conllision.Collider,而触发的参数是Collider直接就是触发的物体的碰撞器组件
十七.射线类
unity中射线类是Ray,射线碰撞检测信息类RaycastHit,注意射线检测实际上是碰撞,射线检测的物体必须要有Collider组件。
常见方法属性
(1)创建一条射线:Ray ray=new Ray(Vector3.zero,Vector3.up),接收两个参数,起始位置与方向,
但我们更为常用的是通过摄像机射出(也就是我们从屏幕一点发射的一条射线)一条射线:Ray ray=Camera.main.ScreenPointToRay(Input.mousePosition),接收一个参数,表示鼠标点击的位置。
(2)检测射线是否碰到游戏物体:Physics.Raycast(ray,out hit),返回bool值,表示射线是否碰到带碰撞器的游戏物体,接收两个参数,射线(也可以传入射线的起始位置与方向)与一个射线碰撞检测类型的变量,记录碰撞的信息,射线同时可以接收第三个参数maxDistance为射线设定长度,第四个参数layerMask代表检测的层,第五个参数queryTriggerInteraction表示是否检测触发体(默认情况下不检测)
(3)通过hit碰撞信息,获取射线碰撞物体的坐标位置:hit.point
(4)除了单检测,我们还可以使用多检测:Physics.RaycastAll(ray,100,1<<10),返回一个RaycastHit类型的数组,记录所有检测到的物体的碰撞信息,第二个参数表示检测的射线的距离,第三个参数表示检测第十个图层的物体。
(5)SphereCast表示球形射线,bool hit = Physics.SphereCast( Vector3 origin, // 球体起始位置 float radius, // 球体半径 Vector3 direction, // 发射方向 out RaycastHit hitInfo, // 碰撞信息 float maxDistance // 最大检测距离 );
(6)SphereCast 相当于"带体积的射线检测",比 Raycast 更适合检测有宽度的物体(如角色)在移动过程中是否会碰到障碍物。
(7)camera.ViewportPointToRay从摄像机视窗发射一条射线,接收一个Vector3向量,z轴为0,x,y表示在摄像机的近平面上的二维坐标位置,表示射线发射的位置。
十八.线段与拖尾渲染器
unity中线段渲染器类是LineRenderer
常用方法与属性
(1)获取线段渲染器的方法与通用的获取组件的方法相同
(2)画线:设置线的点的数量:lineRenderer.positionCount=3;
设置点的位置:逐个设置:lineRenderer.SetPosition(0,Vector3.zero),接收两个参数,点的索引与位置。
多个设置:lineRenderer.SetPositions(vec3)接收一个Vector3类型的数组。
(3)其他设置:设置开始颜色:lineRenderer.startColor=Color.white,结束颜色:endColor
开始时的宽度:lineRenderer.startWidth,结束时的宽度:endWidth。
十九.动画类
unity中旧版的动画类为Animation,新版的为Animator
常用方法与属性
旧版:
(1)获取动画播放组件的方法与通用的获取组件的方法相同
(2)播放动画,animation.Play(" "),接收一个参数,要播放的动画的名称。
新版:
(1)获取动画播放组件的方法与通用的获取组件的方法相同
(2)播放动画,animator.Play(" "),接收一个参数,要播放的动画的名称。
(3)触发动画:animator.SetTrigger("动画名称");
(4)获取参数:animator.GetFloat/GetInt/GetBool("要获取的参数名称").
(5)实现帧事件,在模型的Animation中填写帧事件函数,为某一帧或某几帧的动画添加事件(如为一个跑步动画在脚落地时播放脚步声等),我们需要在人物模型的脚本中控制帧事件函数,直接在脚本中public就可以直接访问到。
二十.反向动力学(IK)
常用方法与属性
(1)基础方法:private void Onanimator(int layerdex),关于ik的都写在这个方法中,接收一个参数,表示动画图层的编号。
(2)设置权重:animator.SetLookAtWeight(1),接收一个int类型的参数,表示权重。
(3)设置指向的位置:animator.SetLookAtPosition(target.position)修改头部IK,设置其他部分的Ik需要animator.SetLookAtPositionWeight(AvatarGoal.RightHand,1)接收一个枚举,和一个权重,
设置旋转的话也相同,使用animator.SetLookAtRotationWeight(AvatarGoal.RightHand,1)
二十一.导航
需要引入UnityEngine.AI,代理组件类:NavMeshAgent
(1)获取导航代理组件的方法与通用的获取组件的方法相同
(2)设置一个位置为导航的目标位置:agent.SetDestination(point),接收一个Vector3表示导航到的位置。
(3)isStopped,bool值,人为暂停开关。设为 true 时保留路径但停止移动,常用于 Idle/Attack 站桩,需要注意的是设置isStopped为false不代表代理的对象完全停止移动,代理的对象还保留了速度与惯性,想要完全停止移动需要设置代理的线速度。
(4)ResetPath(),清除当前路径。与 isStopped 不同,它会彻底丢弃现有路线。常用于放弃追击或强制重新规划。
(5)Warp(newPosition):瞬移/重置位置。直接将代理传送到新坐标并更新内部状态。注意: 不要用 transform.position = xxx 直接改坐标,会导致 NavMeshAgent 内部状态错乱、报错或卡死,必须用 Warp。
(6)remainingDistance 属性(float) 到目标的剩余距离。致命陷阱: 当没有有效路径时,该值为 -1。判断到达前必须先检查 hasPath,否则 -1 <= stoppingDistance 会误判为“已到达”。
(7)stoppingDistance 属性(float) 代理认为“到达”的阈值半径。近战怪应设大一点防穿模,远程怪可设为攻击射程。
(8)hasPath 属性(bool) 当前是否有有效路径。配合 remainingDistance 使用,是判断巡逻点是否走完的最安全方式。
(9)pathPending 属性(bool) 路径是否正在异步计算中。调用 SetDestination 后不会立刻有路径,需等此值变 false 后才能安全读取 remainingDistance。
(10)desiredVelocity 属性(Vector3) 代理想要移动的速度向量(含转向)。可用于驱动动画混合树(Blend Tree),比直接用 velocity 更平滑。
(11)velocity 属性(Vector3) 代理实际移动的速度向量。受物理碰撞、避障等影响,可能与 desiredVelocity 不同。
(12)speed 最大移动速度 Patrol 时设正常速度,Sense 到玩家进入 Combat 时可动态加速(如冲刺追击)。
(13)angularSpeed 最大转向角速度(度/秒) 值太小怪物转身像坦克一样慢;值太大则瞬间回头不自然。配合动画的 Root Motion 使用时需注意同步。
(13)acceleration 加速度 控制起步/停止的惯性。设为极大值可实现即时启停(适合像素风或机械单位),较小值则更真实。
(14)autoBraking 自动刹车(bool) 默认 true。接近目标时自动减速。追击移动中的玩家时建议设为 false,否则怪物会在接近玩家时反复减速-加速,产生诡异的“抽搐”现象。
(15)avoidancePriority 避障优先级(int) 值越小优先级越高。Boss 设低值让小怪让路;玩家设最低值让所有怪物避让。
二十二.灯光类
常用方法与属性
(1)获取灯光组件的方法与通用的获取组件的方法相同
(2)灯光的开启与关闭,与组件的激活相同,直接设置组件的激活状态
(3)灯光的强度与范围:light.intensity,light.range,都是数值,修改直接改变其数值
二十三.钢体
钢铁类为Rigidbody
常用方法与属性
(1)获取钢体组件的方法与通用的获取组件的方法相同
(2)修改钢体的速度:rb.linearVelocity(x,y,z),参数接收一个向量,一般情况下操作钢体,无论是加力还是改变速度都必须要写在FixedUpdate中,因为写在updae中两帧直接的频率不固定,会导致修改的速度不稳定,导致物体出现异常抖动,抽搐等问题。
(3)给钢体添加力:rb.AddForce(Vector3.up*200)参数给一个力的方向*力的大小,重载接收第二个参数,ForceMode,存在四种模式:
ForceMode.Force,标准的牛顿第二定律 F=ma,持续的推力,适用场景:角色持续移动、汽车引擎持续加速、持续的风力、水流推力。
ForceMode.Impulse,瞬间冲量:爆发力,适用场景:跳跃(就是你上一段代码用的)、爆炸冲击波、开枪的后坐力、被重锤击中。
ForceMode.Acceleration,直接设定加速度 a,跳过m,适用场景:自定义全局重力(让羽毛和铁球在真空中以相同加速度下落)、太空游戏中的推进器(忽略飞船质量差异)。
ForceMode.VelocityChange(瞬间速度改变:绝对的强制位移),适用场景:强制击退效果(如被盾击弹飞)、平台跳跃中的精确速度控制(如吃到弹簧必须精确弹到 10m/s)、传送带强制速度
(4)刚体角速度:angularVelocity
(5)rb.isKinematic=true,修改物体是否收到物理影响,true为不受。
二十四.UI界面
使用新的UI文本必须引入TMPor,文本为string类型
1.UI组件的控制
下拉组件类为Dropdown
(1)获取下拉组件的方法与通用的获取组件方法相同。
(2)获取组件选项:List<Dropdown.OptionData>option=dropdown.options
(3)修改选项:options.Add(new Dropdown.optinoData("选项的命名"))
dropdown.optinos=options.
按钮组件Button:
button.interactable:一个bool值,用于控制这个按钮是否能够交互
2.新版文本组件的控制
新版文本组件:TextMeshProGUI
(1)获取下拉组件的方法与通用的获取组件方法相同。
(2)动态的修改文本内容有两种方式:通过text.text=" "和通过scoreText.SetText("Score:{0} ",score);,其中SetText可以接收两个参数,应该表示字符串和占位符,一个表示占位的变量。
2.常用UI布局组件
(1)mask遮罩组件:父物体遮罩子物体
(2)文本适配:动态的根据文本的大小修改文本显示框,Content Size Fitter
(3)图像布局组件:解决一个面板中多个图像的布局问题:Vertiacl Layout Group(垂直方向上的布局),Horizontal Layout Group(水平方向上的布局),Grid Layout Group(网格布局)
三种移动控制的比较
根据以上内容,我们知道了三种移动的控制方式,我们简单介绍它们的区别:
1.利用transform组件控制移动
transform.position += ... 或 transform.Translate(...),没有什么物理的属性,实际上是通过每一帧的瞬移来1实现位置1的移动的。对于物理相关的检测需要我们后续自行添加。
常用于摄像机跟随、UI动画、或者不需要碰撞的子弹。
2.利用charactor Control组件
专门用于人物角色,它不是严格的物理刚体,也不是纯粹的瞬移。它是一个胶囊体碰撞器加上专门的移动逻辑,处于物理系统和瞬移之间。
常用于:第一人称射击游戏、第三人称动作游戏的玩家角色
3.利用刚体组件控制移动
物理交互最真实
常用于需要真实物理互动的物体。
unity中的常用其他函数
1.unity中的Random函数
生成指定范围的随机数 (Random.Range(a,b)),需要注意的是:当参数为整数(int)时:左闭右开 [min, max),当参数为浮点数(float)时:左闭右闭 [min, max]。
获取 0 到 1 之间的随机浮点数 (Random.value)
Random.insideUnitCircle:返回半径为 1 的圆内的随机点。
Random.insideUnitSphere:返回半径为 1 的球体内的随机点。
Random.onUnitSphere:返回半径为 1 的球体表面上的随机点。
Random.rotation:生成一个随机的四元数旋转。
Random.ColorHSV:通过 HSV 和 Alpha 范围生成一个随机颜色。
控制随机种子 (Random.InitState):Random.InitState(seed),接收一个随机种子。
2.Mathf函数
unity提供的数学运算的静态工具类:包括插值平滑(Mathf.Lerp/MoveTowards/SmoothDampPingPong等),范围限制:Mathf.Clamp/Clamp01,浮点数取整:Mathf.FloorToInt/CeilToInt/.RoundToInt,角度与三角函数:Mathf.DeltaAngle/Sin/Cos
更多推荐
所有评论(0)