Unity系统学习笔记

news/2024/9/20 1:26:27/ 标签: unity, 学习, 笔记

文章目录

  • 1.基础组件的认识
    • 1.0.组件继承关系图
    • 1.1.项目工程文件结构,各个文件夹都是做什么的?
    • 1.2.物体变化组件
      • 1.2.3.三维向量表示方向
      • 1.2.4.移动物体位置
      • 附录:使用变换组件实现物体WASD移动
    • 1.3.游戏物体和组件的显示和禁用
      • 1.3.1.界面上的操作
      • 1.3.2.代码上的操作
    • 1.4.网格组件
    • 1.5.Resources
      • 1.5.1.基础介绍
      • 1.5.2.常用资源类型
      • 1.5.3.单个资源加载
      • 1.5.4.多个资源加载
      • 例子:资源的加载
    • 1.6.音频组件
      • 1.6.1.PlayClipAtPoint() 像特效一样播放一次的方法
    • 1.7.实例化物体和销毁游戏物体
      • 1.7.1.前情提要
      • 1.7.2.实例化游戏物体
      • 1.7.3.销毁游戏物体
      • 1.7.4.给游戏物体上添加组件
      • 例子:实例化和销毁游戏物体以及游戏物体上的组件操作
    • 1.8.Unity的生命周期方法们
      • 1.8.1.开始状态
      • 1.8.2.进行中状态
      • 1.8.3.结束状态
    • 1.9.GameObject 查找 & Transform 查找
      • 1.9.1.GameObject
        • 1.全局名称查找
        • 2.未激活游戏物体
        • 3.查找的时候出现重名
        • 4.路径查找
        • 5.标签查找 ( 一次性查找多个对象)
      • 1.9.2.Transform 查找
        • 1.查找子物体
    • 1.10.时间,数学运算 & 插值运算
      • 1.10.1.Time 时间类
      • 1.10.2.Mathf 数学类
        • 角度与弧度互相转换
      • 1.10.3.插值运算
      • 1.10.4.插值运算的几个实际应用
        • 单轴向位移和旋转
        • 其他插值 API
        • 示例
  • 2.物理系统的初步认识
    • 2.1.刚体组件
      • 2.1.1.刚体的几个属性
      • 2.1.2.刚体的移动
        • 无动力情况
        • 有动力情况
        • 例子
        • WASD移动
    • 2.2.碰撞体和碰撞
        • 2.2.1.组合效果
    • 2.3.物理射线
      • 2.3.1.概述
      • 2.3.2.使用射线
        • **通过摄像机相关 API 构造射线对象:**
        • 检测物理射线:
        • 使用例子
    • 2.4.角色控制器(Character Controller)
      • 2.4.1.常用Api
      • 2.4.2.相关属性
      • 实例使用
  • 3.UI系统
    • 3.1.文字组件
      • 3.1.1TMP 字体文件
    • 3.2.图片组件
      • 3.2.1.导入UI 图片素材
  • 4.Invoke和协程
    • 4.1.延迟方法Invoke()
      • 4.1.1.基本介绍
      • 4.1.1.延迟调用
      • 4.1.2.重复调用
      • 4.1.3.取消调用
    • 4.2.协程
      • 4.2.1.协程介绍
      • 4.2.2.协程语法格式
        • 1.创建协程方法
        • 2.开启协程
        • 3.停止协程
    • 4.3.协程与 Invoke 对比
      • 4.3.1.相同之处
      • 4.3.2.不同之处

1.基础组件的认识

1.0.组件继承关系图

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

1.1.项目工程文件结构,各个文件夹都是做什么的?

  • Assets [资产]:存放项目开发过程中所用到的资源(模型, UI, 声音, 特效…);

    (在 Unity 项目开发中,Assets 文件夹是需要程序员手动管理的文件夹。)

  • Library [库]:存放项目核心库,扩展库,以及动态生成的缓存文件;

  • Logs [日志]:存放项目日志文件;

  • Packages [包]:存放当前项目扩展库的引用目录;

  • ProjectSettings [项目设置]:存放当前项目的设置文件(输入,导航,编辑器…);

  • Temp [临时]:存放当前项目的临时资源;

  • UserSettings [用户设置]:存放当前项目用户相关的自定义设置。

1.2.物体变化组件

1.2.3.三维向量表示方向

  1. 在 Unity 场景视图中,主要有两个坐标系:世界坐标系物体自身坐标系

  2. 这两个坐标系都有六个方向:前-后-左-右-上-下;

  3. Vector3 结构体内,有六个静态只读属性,用于表示世界坐标系六个方向,这六个属性分别是:forwardbackrightleftupdown

  4. 在 Transform 类中,有三个对象可读可写属性,用于表示三个“箭头方向”,这三个属性分别是:forwardrightup

  5. position 是读写属性,直接 new 一个新的 Vector3 数据赋值;

  6. Vector3 有三个公开的字段:x,y,z,我们可以分别获取使用;

    //查找持有对象引用.m_Transform = gameObject.GetComponent<Transform>();//打印输出位置信息.Debug.Log("Cube的位置:" + m_Transform.position);//修改位置信息.m_Transform.position = new Vector3(5, 5, 5);//单独打印x轴向位置数据.Debug.Log("x:" + m_Transform.position.x);//Vector3 三个字段不可以单独赋值,你的游戏物体只需要在单个轴向发生位置改变,你也需要构造一个 Vector3 数据进行整体赋值m_Transform.position.x = 10; //不能这样用.m_Transform.position = new Vector3(10, m_Transform.position.y, m_Transform.position.z); //应该这样用.//向前方移动.m_Transform.Translate(Vector3.forward, Space.Self);

1.2.4.移动物体位置

[void] m_Transform.Translate(Vector3, Space)

对象方法 Translate( )可以控制游戏物体往指定的方向移动。

参数说明

  • Vector3 参数指的是移动方向;

  • Space 参数是一个枚举类型,World[世界坐标系空间],Self[自身坐标系空间]。

附录:使用变换组件实现物体WASD移动

    //向前.if (Input.GetKey(KeyCode.W)){m_Transform.Translate(Vector3.forward * 0.02f, Space.Self);}//向后.if (Input.GetKey(KeyCode.S)){m_Transform.Translate(Vector3.back * 0.02f, Space.Self);}//向左.if (Input.GetKey(KeyCode.A)){m_Transform.Translate(Vector3.left * 0.02f, Space.Self);}//向右.if (Input.GetKey(KeyCode.D)){m_Transform.Translate(Vector3.right * 0.02f, Space.Self);}

1.3.游戏物体和组件的显示和禁用

1.3.1.界面上的操作

  • 显示与隐藏游戏物体:选中某个游戏物体,Inspector 视图最上方,游戏物体名称的左边有一个复选框;
  • 启用与禁用组件:选中某个游戏物体,Inspector 视图可以看到该游戏物体所有的组件,组件标题栏的前边有一个复选框;
  • 组件禁用之后,该组件就处于无效状态; [关闭房间内的灯,灯就处于无效状态。]
  • 游戏物体隐藏后,该游戏物体同样处于无效状态。

1.3.2.代码上的操作

[void] gameObject.SetActive(bool)
  • 设置游戏物体是显示还是隐藏,true 是显示,false 是隐藏;
  • 所有的游戏物体都可以进行 SetActive()操作。
[bool] 组件对象.enabled 
  • //可读写属性

  • 设置组件是启用还是禁用,true 是启用,false 是禁用;

  • 大部分组件对象都可以进行 enabled 属性操作,但是一些核心组件除外。

1.4.网格组件

  • 什么是 Mesh[网格]?

网格就是三维模型的数据文件,美术人员三维建模,主要就是完成模型的网格制作;

三维模型由点,线,面组成,最终形成一个网格的形态。

  • Mesh Filter

Mesh Filter [网格过滤器]组件:用于指定当前模型游戏物体的网格数据。

Mesh [网格]:持有网格数据文件的引用。

  • Mesh Renderer

Mesh Renderer [网格渲染器]组件:用于完成当前模型游戏物体的网格渲染。

网格过滤器组件和网格渲染器组件需要配合使用,一个组件持有网格数据,另外一个组件负责网格数据的渲染。

组件核心控制参数:

Materials [材质球引用]:往游戏物体上拖拽挂载的材质球资源,其实就是拖拽

赋值给了该属性。 [见图 1, 2]

Cast Shadows [投射阴影]:On 开启阴影投射,Off 关闭阴影投射。

[该属性可以控制单个模型物体的阴影,Light 组件可以控制整个场景内的阴影。]

Receive Shadows [接收阴影]:是否接收另外的游戏物体投射的阴影。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

1.5.Resources

1.5.1.基础介绍

Resources [资源]类:主要用途就是资源加载;

在项目工程中,需要手动创建名为“Resources”的文件夹,相关资源存放到该文件夹内,在代码中即可通过 Resources 类相关的 API 实现资源加载。

1.5.2.常用资源类型

  • 预制体资源 [Prefab]:GameObject
  • 材质球资源 [Material]:Matirial
  • 音频剪辑资源 [AudioClip]:AudioClip
  • 模型贴图资源 [Texture]:Texture2D

1.5.3.单个资源加载

[s][T] Resources.Load<T>(string)
  • Load( )是 Resources 类的静态泛型方法, 返回值类型和填写的泛型一致;
  • Load( )参数是字符串类型,填写资源在 Resources 文件夹中的路径;
    • |–资源在根目录,直接写文件名即可[名称不需要带后缀],比如:“Cube”;
    • |–资源在子目录,写“完整路径”,比如:“Player/Cube”;
  • 拼凑出资源在项目中的完整路径:Assets/Resources/ + Player/Cube

1.5.4.多个资源加载

[s][T[]] Resources.LoadAll<T>(string)
  • LoadAll( )静态泛型方法, 返回值类型和填写的泛型一致,返回数组;
  • Load( )方法是加载单一资源,参数是具体资源的“文件名路径”;
  • LoadAll( )方法是加载多个资源,参数是具体资源的“文件夹路径”,将文件夹内的资源全部加载,返回一个数组格式,文件夹内的资源,保持类型一致

例子:资源的加载

   private GameObject m_GameObject;    //预制体.private Material m_Material;        //材质球.private AudioClip m_AudioClip;      //音频剪辑.private Texture2D m_Texture2D;      //模型贴图.private GameObject[] player;         //多个角色.void Start(){m_GameObject = Resources.Load<GameObject>("Player/Cube");m_Material = Resources.Load<Material>("ColorA");m_AudioClip = Resources.Load<AudioClip>("ddz");m_Texture2D = Resources.Load<Texture2D>("Line");Debug.Log(m_GameObject.name);Debug.Log(m_Material.name);Debug.Log(m_AudioClip.name);Debug.Log(m_Texture2D.name);player = Resources.LoadAll<GameObject>("Player");Debug.Log(player.Length);for (int i = 0; i < player.Length; i++){Debug.Log(player[i].name);}}

1.6.音频组件

1.6.1.PlayClipAtPoint() 像特效一样播放一次的方法

[s][void]AudioSource.PlayClipAtPoint(AudioClip, Vector3)

参数说明:

  • PlayClipAtPoint( ):在场景内指定的位置,播放音频剪辑,静态无返回值;
  • AudioClip 参数:代码中加载持有的音频剪辑;
  • Vector3 参数:场景中播放音频剪辑的位置;
  • PlayClipAtPoint( )方法,每执行一次,就会在场景内创建一个 AudioSource游戏物体,名称为:One shot audio [一次性音频],当音频播放完毕之后,该游戏物体会自动销毁。

1.7.实例化物体和销毁游戏物体

1.7.1.前情提要

  • 使用 Resources 类加载到内存中的资源,比如音频剪辑,材质球,模型贴图,在组件对象属性上,可以直接赋值使用;
  • 预制体资源,相较而言比较特殊,因为预制体本身是游戏物体类型,在场景内, 游戏物体是“顶级”场景元素,无法通过赋值的方式来使用。

1.7.2.实例化游戏物体

代码:

[s][T]GameObject.Instantiate<T>(GameObject, Vector3, Quaternion)

参数说明:

  • Instantiate [实例化]方法:使用预制体资源作为模板,在场景内指定的位置,生成一个游戏物体,额外还可以控制生成的游戏物体的旋转信息;
  • Vector3.zero:只读属性,位置在世界原点,XYZ 都为零;
  • Quaternion.identity:只读属性,表示无旋转,XYZ 都为零[欧拉角]。

1.7.3.销毁游戏物体

[s][void]GameObject.Destroy(Object)
[s][void]GameObject.Destroy(Object, float)
Destroy( )方法有两种重载形式,销毁游戏物体和定时销毁游戏物体。

销毁方法也可以销毁游戏物体上某个组件。

1.7.4.给游戏物体上添加组件

[T]gameObject.AddComponent<T>()
//AddComponent [添加组件]方法:泛型格式的对象方法,这里的泛型要写 Unity引擎内的组件类型,以及我们自己编写的脚本组件名称。

例子:实例化和销毁游戏物体以及游戏物体上的组件操作

public class Demo10 : MonoBehaviour
{private GameObject m_GameObject;    //预制体.private Material m_Material;        //材质球.private AudioClip m_AudioClip;      //音频剪辑.private Texture2D m_Texture2D;      //模型贴图.private GameObject[] player;         //多个角色.private GameObject cubeA;void Start(){m_GameObject = Resources.Load<GameObject>("Player/Cube");m_Material = Resources.Load<Material>("ColorA");m_AudioClip = Resources.Load<AudioClip>("ddz");m_Texture2D = Resources.Load<Texture2D>("Line");Debug.Log(m_GameObject.name);Debug.Log(m_Material.name);Debug.Log(m_AudioClip.name);Debug.Log(m_Texture2D.name);player = Resources.LoadAll<GameObject>("Player");Debug.Log(player.Length);for (int i = 0; i < player.Length; i++){Debug.Log(player[i].name);}}void Update(){if (Input.GetKeyDown(KeyCode.Space)){//实例化cubeA = GameObject.Instantiate<GameObject>(m_GameObject, Vector3.zero, Quaternion.identity);GameObject cubeB = GameObject.Instantiate<GameObject>(m_GameObject, new Vector3(0, 0, 10), Quaternion.Euler(new Vector3(0, 0, 90)));cubeA.name = "CubeA";cubeB.name = "CubeB";//将组件添加到游戏物体上AudioSource audioSource = cubeA.AddComponent<AudioSource>();audioSource.clip = m_AudioClip;audioSource.Play();//摧毁游戏物体的组件GameObject.Destroy(cubeA.GetComponent<MeshRenderer>());}if (Input.GetKeyDown(KeyCode.S)){//摧毁游戏物体//GameObject.Destroy(cubeA, 3);}}}

1.8.Unity的生命周期方法们

1.8.1.开始状态

  • Awake [唤醒]:当对象被创建时,执行一次;
  • OnEnable [启用时]:脚本组件被启用,执行一次;
  • Start [开始]:项目运行之后,执行一次;

细节说明:

  1. 在运行之后,场景内存在的游戏物体会首先被实例化成对象,然后执行这些对象脚本中的生命周期方法,执行到实例化游戏物体的代码,实例化成对象,然后在执行这些对象脚本中的生命周期方法
  2. Awake:脚本组件挂载到游戏物体上,并没有启用,也会执行
  3. OnEnable:当脚本组件处于启用状态时,执行一次,后续的生命周期依赖于该状态; [使用组件面板,以及代码,分别启用禁用脚本组件。]
  4. Start:在 Awake 和 OnEnable 之后执行,正式的开始。

1.8.2.进行中状态

  • FixedUpdate [固定更新]:固定时间更新,0.02 秒执行一次;
  • Update [更新]:每帧执行一次,一秒钟大约 60 次;
  • LateUpdate [延迟更新]:在 Update 之后执行,同样每帧执行一次;

细节说明:

  1. FixedUpdate:物理组件控制的持续运动,比如:刚体运动;
  2. Update:需要每帧都执行的代码,比如:按键检测代码;
  3. LateUpdate:在 Update 之后执行,比如:摄像机跟随;

1.8.3.结束状态

  • OnDisable [禁用时]:脚本组件被禁用,执行一次;
  • OnDestroy [销毁时]:脚本组件被销毁,执行一次;

细节说明:

  1. OnEnableOnDisable 分别对应脚本组件的启用状态,禁用状态;
  2. 在代码中,当游戏物体/脚本组件,被销毁时,先执行 OnDisable,再执行OnDestroy,在引擎界面上退出项目运行状态,等同于销毁操作。

1.9.GameObject 查找 & Transform 查找

1.9.1.GameObject

1.全局名称查找
[GameOjbect] GameOjbect.Find(string)
//在场景中查找指定名称的游戏物体
2.未激活游戏物体

场景内的游戏物体有两种状态,Find( )通过名称查找,也有两种结果:

  1. 激活[显示]状态:能查找到,返回游戏物体对象;
  2. 激活[隐藏]状态:查找不到,报错“空引用异常”。

注意:在开发过程中,某些游戏物体需要默认隐藏,同时又需要查找持有它的对象引用。

这种情况,场景中的游戏物体需要默认保持显示状态,然后代码查找持有,再修改成隐藏状态,场景运行后,代码操作瞬间完成。

3.查找的时候出现重名
  1. 在场景中如果有两个游戏物体的名称完全一样,通过 Find()方法查找,返回的是层级视图靠后的那一个
  2. Find()方法通过名称查找到第一个游戏物体之后,并没有直接返回,而是继续向下遍历,找到了最后一个然后返回,这是因为 Find()方法的逻辑是“递归遍历”,它会将整个层级视图内的游戏物体全部遍历一遍。
4.路径查找
  1. Find()方法内的字符串参数,除了填写游戏物体的名称之外,还可以填写“路径”;

  2. 路径可以是相对路径,也可以是绝对路径:

    • 相对路径:游戏物体路径的一部分,比如:Cube5/Cube6;

    • 绝对路径:从根游戏物体一直到具体物体,比如:Cube4/Cube5/Cube6;

  3. 注意事项:

    • 路径中的最后一个名字,必须是你要查找的具体的游戏物体的名字;
    • 如果是相对路径,在路径的开头就不要写“/”,否则空引用异常;
    • “/”开头的路径,表示绝对路径,必须是从根游戏物体开始的路径。
5.标签查找 ( 一次性查找多个对象)
[GameOjbect[]] GameOjbect.FindGameObjectsWithTag(string)
//在场景中查找被赋予了特定标签的游戏物体,查找结果以数组形式返回

1.9.2.Transform 查找

1.查找子物体
[Transform] m_Transform.Find(string)
//Transform 组件对象方法 Find(),在子物体当中查找指定名称的游戏物体,返回该游戏物体的 Transform 组件对象。
  1. 得到 Transform 组件对象,就相当得到了 gameObject 游戏物体对象,因为组件和游戏物体之间有从属关系,可以直接“点出游戏物体”;
  2. Transform 组件对象方法 Find()可以直接以名称查找子物体,但是不能直接以名称查找孙物体(空引用异常),如果需要查找多层嵌套的子物体,需要写完整路径
  3. Find()方法的“前身”是 FindChild()方法(被官方弃用),两个方法的用途完全一样,如果你使用旧版本的 Unity 引擎,可能看到的就只有 FindChild()方法。

1.10.时间,数学运算 & 插值运算

1.10.1.Time 时间类

[float] Time.time //时间. 

静态只读属性,项目从开始运行到现在的总时长,以秒为单位;

[float] Time.deltaTime //增量时间. 

静态只读属性,当前游戏画面渲染完一帧,所需要的时长,以秒为单位;

[float] Time.timeScale //时间缩放. 

静态读写属性,控制虚拟游戏世界中的时间流速,可以用来实现游戏暂停;

  • 1:时间正常;

  • 0:时间暂停;

  • 0.5:时间 0.5 倍慢放;

1.10.2.Mathf 数学类

Mathf“数学类”其实是一个结构体类型,提供了一些基础的数学运算 API, 比如:最大值,最小值,三角函数,角度,弧度…

角度与弧度互相转换

角度 [Degree]:两条线段之间的夹角; [变换组件的旋转属性]

弧度 [Radian]:弧度也是一个角度单位,1 弧度≈57.3 度 1 度≈0.0174 弧度。

[float] Mathf.Deg2Rad //角度转弧度常量. 0.0174. [float] Mathf.Rad2Deg //弧度转角度常量. 57.295. 

在项目开发过程中,经常会遇到“角度”与“弧度”互相转换的情况;

  • 50 度转弧度:50 * Mathf.Deg2Rad ≈ 0.872
  • 0.75 弧度转角度:0.75 * Mathf.Rad2Deg ≈ 42.97

1.10.3.插值运算

确定两个参数 A 和 B,然后从 A 平滑过渡到 B的过程;

[s][float] Mathf.Lerp(float a, float b, float t)

参数说明

  • a:起始值,例如:0;

  • b:目标值,例如:20;

  • t:插值系数,表示 a 和 b 之间的比例,取值范围是 0 ~ 1 之间。

    • t=0,返回 a 参数的值,t=1,返回 b 参数的值;
    • t=0.5,返回 a 和 b 的中间值。
  • Lerp()插值方法,我们最终使用的是 a 值,b 值是相对固定的,在插值运算的过程中,a 值会逐渐递增[也可能是递减]“变成”b 值,插值运算宣告结束。

注意:

  • Lerp()插值方法需要放到 Update 语句块中执行(一般来说,如果不放进去的话,就执行一次就失去了意义)。
  • 将插值运算的结果重新存储到 a 中,再插值的时候就是在新的 a和原有的 b 之间继续插值,a 值才能实现递增。
  • 在插值运算过程中,a 值会逐渐靠近 b 值,当两个值非常近的时候,依然在继续插值,每一次插值的累加步长非常小,但是插值运算不会停止;所以,到达一个很接近的值的时候要停止插值运算,之后进行赋值结束。
  • 插值系数一般用一个很小的数值,这样可以实现 a 值到 b 值之间的平滑过渡,在实际使用过程中,插值系数经常使用“增量时间”:Time.deltaTime。

1.10.4.插值运算的几个实际应用

单轴向位移和旋转
public class CubeLerp : MonoBehaviour
{private float startPos = 0;private float endPos = 20;private float startRot = 0;private float endRot = 50;private float tPos = 0;private float tRot = 0;private Vector3 startVector3;private Vector3 endVector3;private Quaternion startQua;private Quaternion endQua;private Transform m_Transform;private Transform endCube;void Start(){m_Transform = gameObject.GetComponent<Transform>();endCube = GameObject.Find("EndCube").GetComponent<Transform>();startQua = m_Transform.rotation;endQua = endCube.rotation;}void Update(){//单向位移MoveLerp();//单向旋转RotationLerp();}private void MoveLerp(){if (startPos < 19.7f){startPos = Mathf.Lerp(startPos, endPos, Time.deltaTime);m_Transform.position = new Vector3(0, 0, startPos);}}private void RotationLerp(){if (startRot < 49.5f){startRot = Mathf.Lerp(startRot, endRot, Time.deltaTime);m_Transform.rotation = Quaternion.Euler(new Vector3(0, 0, startRot));}}}

上面演示的“单轴向位移”和“单轴向旋转”两个效果,存在两个共有的问题:

  • 插值运算前面快,后面慢; [插值系数固定,前面肯定快,后面肯定慢。]
  • 无法精准的插值到结束值; [临界值的 if 判断语句,停止继续插值。]

能不能让插值运算匀速插值,且“精准”的插值到结束值呢??

优化版:

public class CubeLerp : MonoBehaviour
{private float startPos = 0;private float endPos = 20;private float startRot = 0;private float endRot = 50;private float tPos = 0;private float tRot = 0;private Vector3 startVector3;private Vector3 endVector3;private Quaternion startQua;private Quaternion endQua;private Transform m_Transform;private Transform endCube;void Start(){m_Transform = gameObject.GetComponent<Transform>();endCube = GameObject.Find("EndCube").GetComponent<Transform>();startQua = m_Transform.rotation;endQua = endCube.rotation;}void Update(){//单向位移MoveLerp();//单向旋转RotationLerp();}private void MoveLerp(){if(tPos < 1.0f){tPos += 0.5f * Time.deltaTime;m_Transform.position = new Vector3(0, 0, Mathf.Lerp(startPos, endPos, tPos));}}private void RotationLerp(){if (tRot < 1.0f){tRot += 0.5f * Time.deltaTime;m_Transform.rotation = Quaternion.Euler(new Vector3(0, 0, Mathf.Lerp(startRot, endRot, tRot)));}}}
  1. 每次插值运算完毕,将插值结果存入到起始值中,第二次,在新的起始值和固定结束值之间继续插值,且重复上方操作,改进方案是:起始值和结束值都固定不变,我们改变插值系数;
  2. 插值系数初始值为 0,在 Update 语句块中,每帧累加 Time.deltaTime,插值系数每帧都会“变大”,起始值和结束值是固定不变的,但是插值系数在慢慢变大,一样可以实现插值结果的变大。
其他插值 API
  1. 借助于 Mathf.Lerp()方法,我们可以实现单轴向上的位移动画和旋转动画,但是局限性很大,比如:[3,8, 10]插值移动到[34, 19, 78];
  2. Unity 引擎中,很多类都有自己专用的 Lerp()插值运算方法,接下来我们就演示“向量插值”和“四元数插值”。
[s][Vector3] Vector3.Lerp(a, b, float t)//向量插值
[s][Quaternion] Quaternion.Lerp(a, b, float t)//四元数插值
示例
public class CubeLerp : MonoBehaviour
{private float startPos = 0;private float endPos = 20;private float startRot = 0;private float endRot = 50;private float tPos = 0;private float tRot = 0;private Vector3 startVector3;private Vector3 endVector3;private Quaternion startQua;private Quaternion endQua;private Transform m_Transform;private Transform endCube;void Start(){m_Transform = gameObject.GetComponent<Transform>();endCube = GameObject.Find("EndCube").GetComponent<Transform>();startVector3 = m_Transform.position;endVector3 = endCube.position;startQua = m_Transform.rotation;endQua = endCube.rotation;}void Update(){MoveLerp();RotationLerp();}private void MoveLerp(){if(tPos < 1.0f){tPos += 0.5f * Time.deltaTime;m_Transform.position = Vector3.Lerp(startVector3, endVector3, tPos);}}private void RotationLerp(){if (tRot < 1.0f){tRot += 0.5f * Time.deltaTime;m_Transform.rotation = Quaternion.Lerp(startQua, endQua, tRot);}}}

2.物理系统的初步认识

Unity 引擎内有两套内置的物理系统组件,菜单路径位置如下:

  • 3D 物理组件:Component --> Physics [基于 PhysX 引擎]
  • 2D 物理组件:Component --> Physics 2D [基于 Box2D 引擎]

2.1.刚体组件

2.1.1.刚体的几个属性

  • Mass [质量]

    1. 控制物体的质量,默认值为 1,单位是千克;
    2. 两个物体的质量差距很大,但是下落速度是一样的; [“两个铁球同时着地”]
    3. 两个物体的质量差距很大,在发生碰撞时,会有明显效果。 [各种交通事故]
  • Drag [阻力]

    1. 控制空气阻力,默认值为 0,表示没有空气阻力;
    2. 将该参数值调大,可以降低物体的下落速度。
  • Angular Drag [角阻力]

    1. 当物体发生旋转时的空气阻力,默认值为 0.05;
  • Use Gravity [使用重力]

    1. 当前刚体组件是否开启重力效果,默认是勾选,重力开启状态;
    2. 如果取消勾选,则当前游戏物体关闭重力效果。
  • Is Kinematic [运动学]

    1. 在物理运动和“运动学”运动[Transform]之间切换,默认是不勾选状态;
    2. 勾选该属性,运行之后,物体同样没有重力效果。

2.1.2.刚体的移动

无动力情况
    //刚体组件对象方法 MovePosition()控制当前物体移动到指定位置,Vector3 参数指的是目标位置。[void] m_Rigidbody.MovePosition(Vector3)
有动力情况
  • 使用刚体组件控制游戏物体,主要目的是进行两种运动:平稳运动,剧烈运动。

    • 平稳运动:游戏角色行走,奔跑;

    • 剧烈运动:枪械武器发射子弹,炮弹

这里指的就是剧烈运动。

    //给刚体组件添加力,让刚体组件所在的游戏物体,向某个方向发射出去。Vector3 参数指的是目标方向的力度。[void] m_Rigidbody.AddForce(Vector3) //世界坐标系方向[void] m_Rigidbody.AddRelativeForce(Vector3) //物体坐标系方向
例子
    m_Transform = gameObject.GetComponent<Transform>();m_Rigidbody = gameObject.GetComponent<Rigidbody>();//向上移动位置.m_Rigidbody.MovePosition(new Vector3(0, 5, 0));//向上方发射.[世界方向.]m_Rigidbody.AddForce(Vector3.up * 1000);//向上方发射.[物体自身方向.]m_Rigidbody.AddRelativeForce(Vector3.up * 1000);
WASD移动
    /// <summary>/// 世界坐标系四方向移动./// </summary>private void WorldDirMove(){//向前.if (Input.GetKey(KeyCode.W)){m_Rigidbody.MovePosition(m_Transform.position + Vector3.forward * 0.2f);}//向后.if (Input.GetKey(KeyCode.S)){m_Rigidbody.MovePosition(m_Transform.position + Vector3.back * 0.2f);}//向左.if (Input.GetKey(KeyCode.A)){m_Rigidbody.MovePosition(m_Transform.position + Vector3.left * 0.2f);}//向右.if (Input.GetKey(KeyCode.D)){m_Rigidbody.MovePosition(m_Transform.position + Vector3.right * 0.2f);}}/// <summary>/// 自身坐标系四方向移动./// </summary>private void SelfDirMove(){//向前.if (Input.GetKey(KeyCode.W)){m_Rigidbody.MovePosition(m_Transform.position + m_Transform.forward * 0.2f);}//向后.if (Input.GetKey(KeyCode.S)){m_Rigidbody.MovePosition(m_Transform.position - m_Transform.forward * 0.2f);}//向左.if (Input.GetKey(KeyCode.A)){m_Rigidbody.MovePosition(m_Transform.position - m_Transform.right * 0.2f);}//向右.if (Input.GetKey(KeyCode.D)){m_Rigidbody.MovePosition(m_Transform.position + m_Transform.right * 0.2f);}}

2.2.碰撞体和碰撞

2.2.1.组合效果

在模型游戏物体上使用物理组件,有四种常见的组合方式:

在这里插入图片描述

组合 A: 网格 + 刚体 + 碰撞体

最常用的一种组合方式,游戏内的玩家角色,各种小怪,Boss 都是这种组合方式;

角色游戏物体可以推着“组合 A”移动。

组合 B: 网格 + 碰撞体

游戏场景相关元素,大多使用这种组合方式,比如场景内的各种建筑,墙壁,石头;

角色游戏物体可以和“组合 B”发生碰撞,但是“组合 B”无法移动。

组合 C: 网格

这种组合方式没有使用任何物理相关的组件;

角色游戏物体可以直接穿透“组合 C”,在物理运算角度,“组合 C”不存在。

组合 D: 网格 + 刚体

这种组合方式没有使用碰撞体组件,也就意味着项目运行之后,“组合 D”不会停

留在“地面”上,而是直接穿透地面进行自由落体运动

2.3.物理射线

2.3.1.概述

在 Unity 引擎中,还存在一个名为“射线”的东西,它也可以和碰撞体组件产生物理碰撞,因此“射线”也称之为“物理射线”;

2.3.2.使用射线

  1. 在 Unity 引擎中,射线没有对应的组件,只有一个结构体:Ray [射线];
  2. 在脚本中以 Ray 为类型创建字段,字段的用途是用于持有对象引用;
  3. 射线对象的创建方式主要有如下两种:
    1. 直接 new 关键字构造射线对象;
    2. 通过摄像机相关 API 构造射线对象。
通过摄像机相关 API 构造射线对象:
[Ray] Camera.main.ScreenPointToRay(Vector3)
//摄像机组件对象方法,接收“屏幕上的一个点”作为参数,创建并返回一个射线对象,Vector3 参数指的就是“屏幕上的一个点”;
[Vector3] Input.mousePosition; 
//只读属性,鼠标在屏幕上的位置. 从摄像机所在的位置,向鼠标在屏幕上的位置(方向),创建一根射线。
  1. 场景中默认存在的“Main Camera”称之为:主摄像机;
  2. 之所以能被称之为“主摄像机”,不是因为游戏物体名称的中文翻译,而是因为这个 Main Camera 游戏物体身上默认设置了一个专用的 Tag 标签;
  3. [Camera]Camera.main:只读属性,用于获取设置了 MainCamera 标签的游戏物体身上的 Camera 组件对象;
  4. 该属性在项目工程中任意一个脚本中,都可以直接书写调用。
检测物理射线:
[bool] Physics.Raycast(Ray, out RaycastHit)

物理类中的静态方法 Raycast(),主要用于射线检测,方法接收两个参数:

  • Ray:需要被检测的射线对象;
  • RaycastHit:结构体,用于存放射线与碰撞体组件的碰撞信息;
    • 使用 out 关键字返回数据对象;
    • 结构体对象只读属性 collider,可以获取与射线发生碰撞的碰撞体组件;
  • bool:射线碰撞到了游戏物体(碰撞体组件)返回真,没有发生碰撞返回假。
使用例子
    private Ray ray;            //物理射线.private RaycastHit hit;     //射线碰撞信息.void Start(){}void Update(){if (Input.GetMouseButtonDown(0)){ray = Camera.main.ScreenPointToRay(Input.mousePosition);if(Physics.Raycast(ray, out hit)){GameObject.Destroy(hit.collider.gameObject);}}}

2.4.角色控制器(Character Controller)

2.4.1.常用Api

  1. SimpleMove(Vector3):简单移动

    以一定的速度移动角色,会自动应用重力。[角色控制器不是刚体,但是具备刚体的一些属性]

  2. Move(Vector3):移动

    更为复杂的一种运动,每次都绝对运动,不会应用重力。

  3. OnControllerColliderHit(ControllerColliderHit hit):可以通过 hit 获取到角色碰撞器碰撞到的物体的信息。

2.4.2.相关属性

  1. Slope Limit

    斜率限制,控制角色最大的爬坡斜度。[演示:角色爬坡]

  2. Step Offset

    台阶高度,控制角色可以迈上最大的台阶高度。[演示:角色上台阶]

  3. Skin Width [默认即可]

    皮肤厚度,在角色的外围包裹着一层“皮肤”,设置这层皮肤的厚度。数值调大,最明显的就是角色和地面之间的间距变大,也就是角色皮肤变厚了。

  4. Min Move Distance [默认即可]

    最小移动距离,默认是 0.001,也就是 1 毫米。如果该数值调大,但代码中单位移动速度很慢,角色就不会动。

  5. Center/Radius/Height角色控制器组件在 Scene 面板中体现为一个“胶囊碰撞器”的形状。

Center:控制中心点的位置;

Radius:控制半径;

Height:控制高度。

实例使用

using UnityEngine;
using System.Collections;public class Player : MonoBehaviour {private CharacterController m_CC;void Start () {m_CC = gameObject.GetComponent<CharacterController>();//m_CC.slopeLimit = 10;}void Update () {//Debug.Log(Input.GetAxis("Horizontal"));float horizontal = Input.GetAxis("Horizontal");float vertical = Input.GetAxis("Vertical");m_CC.SimpleMove(new Vector3(horizontal, 0, vertical) * 3);//m_CC.Move(new Vector3(horizontal, 0, vertical) * 0.3f);}void OnControllerColliderHit(ControllerColliderHit hit){Debug.Log(hit.gameObject.name);}
}

3.UI系统

3.1.文字组件

3.1.1TMP 字体文件

  • 字体文件

    • Text 组件和 Text TMP 组件,都是用于文字显示,文字显示依赖字体文件;
      • Text 组件:TTF 格式字体文件; [Windows 系统拷贝字体文件]
      • Text TMP 组件:SDF 格式的 Asset 字体文件,默认无法显示中文。
  • 制作 TMP 字体文件(TMP默认不显示中文)

    1. Window --> TextMeshPro --> Font Asset Creator 打开功能面板;
    2. Source Font File [源字体文件]:支持中文显示的 TTF 字体文件;
    3. Character Set [字符集]:Custom Characters [自定义字符];
    4. Custom Character List [自定义字符列表]:输入你需要显示的中文;
    5. Render Mode [渲染模式]:SDF; [和 TMP 自带的字体保持类型一致]
    6. 点击 Generate Font Atlas [生成字体图集]按钮;
    7. 点击 Save 按钮,将文件存储到指定路径位置;
    8. 当中英文并存时,会自动生成子物体,调用 TMP 默认的字体显示英文。

3.2.图片组件

3.2.1.导入UI 图片素材

  1. 将素材图拖拽导入到 Textures 文件夹中;
  2. Unity 项目工程中的图片,默认用途是“模型贴图”,如果想应用于 UI 界面,需要手动修改图片的属性;
  3. 全选素材图,右侧属性栏参数:Texture Type 修改为 Sprite (2D and UI),Sprite Mode 修改为 Single,参数修改之后,点击 Apply 按钮。

4.Invoke和协程

4.1.延迟方法Invoke()

4.1.1.基本介绍

  • Invoke [调用]函数:它是 MonoBehaviour 类中定义的公开方法;

  • 我们所编写的 Unity 项目脚本,默认都继承至 MonoBehaviour 父类,也就意味着在我们所编写的脚本中,可以直接使用 Invoke 相关 API;

  • Invoke 函数的用途有两个:延迟调用某个方法,重复调用某个方法。

4.1.1.延迟调用

[void] Invoke(string, float) //延迟多少秒之后,调用某个方法. 

参数说明:

string 参数:需要被延迟调用的方法的名字;

float 参数:延迟的时间,单位是秒。

4.1.2.重复调用

[void] InvokeRepeating(string, float, float)

参数说明:

参数列表中前边的两个参数的用途和 Invoke 函数相同;

第三个 float 参数指的是之后每隔多少秒再调用一次该方法。

4.1.3.取消调用

[void] CancelInvoke() //取消当前脚本中所有的延迟调用. 
[void] CancelInvoke(string) //取消当前脚本中指定的延迟调用. 

4.2.协程

4.2.1.协程介绍

  1. 协同程序,简称“协程”[Coroutine];在代码中,协同程序是以“协程方法”的形式存在;
  2. 在使用过程中,需要先创建具有特定功能的协程方法,然后使用对应的 API 对协程方法进行开启和停止。

协程的用途

  1. Unity 引擎所开发的项目,默认是单线程,因为画面渲染,物理运算以及 Unity引擎核心 API 的运行,都需要在主线程内执行;
  2. Unity 项目开发过程中,可以使用 C#多线程相关代码开启子线程,但是在子线程内你无法使用任何 Unity 引擎相关的 API,比如:实例化/销毁游游戏物体;
  3. 协程的用途类似于开启子线程,辅助主线程完成一些额外的代码运行。

4.2.2.协程语法格式

1.创建协程方法
  1. 协程方法跟普通方法语法格式相同,不同点是协程方法会使用到特殊关键字;
  2. 协程返回值类型为:IEnumerator [迭代器接口]
  3. 协程返回数据方式:yield return [产出返回]
  4. 延迟等待:new WaitForSeconds(float) [延迟等待指定秒数]

注意:协程返回值类型是:IEnumerator [迭代器接口],代码编写过程中输入“IEn”出现下拉菜单代码提示,其中有一个 类似的类型:IEnumerable [可枚举接口],切记:协程方法的返回值是“tor”结尾,不是“ble”结尾;

2.开启协程
[Coroutine] StartCoroutine(string)//协程方法的名称作为参数,开启协程. [Coroutine] StartCoroutine(string, object)//协程方法的名称作为参数,开启协程,以 object 为类型传递方法参数给协程. 
3.停止协程
[void] StopCoroutine(string) //停止指定名称的协程方法. 
[void] StopAllCoroutines() //停止当前脚本中所有的协程方法.

4.3.协程与 Invoke 对比

4.3.1.相同之处

  1. 都是 MonoBehaviour 父类中定义的公开方法,在我们自己编写的 Unity 项目脚本中,都可以直接使用;
  2. 都可以延迟一段时间之后,在执行具体的代码。

4.3.2.不同之处

  1. 协程在开启时,可以动态的传递参数,Invoke 只能是无参方法
  2. 协程方法体内,可以多次延迟,Invoke 只能在方法开启时延迟;
  3. Invoke 能实现的效果,协程都能实现,可以理解成一个是免费版,一个是 SVIP付费加强版,在项目实际开发过程中,按需使用即可。

http://www.ppmy.cn/news/1433520.html

相关文章

数据结构 - 顺序表实现通讯录

test.c文件 #define _CRT_SECURE_NO_WARNINGS 1#include "Contact.h" int main() {Con myContacts;ConInit(&myContacts);int choice;int index;char targetName[100];PerInfo contact; // 创建一个新的联系人信息实例while (1) {printf("\n--- 通讯录管理…

PaddleSeg (2) 模型训练

已处理好数据集和配置文件,可以开始模型训练。 启动训练 python tools/train.py --config configs/xxx.yml --do_eval --use_vdl --save_interval 500 --save_dir output/xxx上述训练命令解释:* `--config`

java spring 07 createBean()和doCreateBean()

01.createBean方法 protected Object createBean(String beanName, RootBeanDefinition mbd, Nullable Object[] args)throws BeanCreationException {if (logger.isTraceEnabled()) {logger.trace("Creating instance of bean " beanName "");}RootBea…

引导过程和服务控制

1、Linux系统开机引导过程 1&#xff09;开机自检 检测硬件设备&#xff0c;找到能够引导系统的设备&#xff0c;比如硬盘 2&#xff09;MBR引导 运行MBR扇区里的主引导程序GRUB 3&#xff09;启动GRUB菜单 系统读取GRUB配置文件(/boot/grub2/grub.cfg)获取内…

spring boot 定义启动页 到 login

当前办法只是针对 项目启动后 直接跳转到 指定静态页面 如果有验证身份 安全等问题 可以另外想办法 去添加 &#xff0c;需要的直接 拉过去使用 修改 【"redirect: 需要启动后访问到文件位置得地址 ”】 直接上代码 &#xff1a; import org.springframework.context…

【教程】使用vitepress搭配githubPages构建自己的在线笔记

1. 创建VitePress项目 确保自己已经安装好了node&#xff0c;我这个笔记用的是node 18.16.0, 怎么安装nvm这个可以csdn或者掘金&#xff0c;再或者等我有空了我就更新一下 使用nvm安装node # 查看可用版本 nvm list avaliable # 安装node nvm install 18.16.0 # 切换node nvm …

(C语言)sscanf 与 sprintf详解

目录 1.sprintf函数详解 2. sscanf函数详解 1.sprintf函数详解 头文件&#xff1a;stdio.h 作用&#xff1a;将格式化的数据写入字符串里&#xff0c;也就是将格式化的数据转变为字符串。 演示&#xff1a; #include <stdio.h> struct S {char name[10];int height;…

无人机探测技术,无人机侦测频谱仪技术实现详解

频谱仪&#xff0c;又称为频谱分析仪&#xff0c;是一种用于测量电信号频谱特性的仪器。其基本原理是通过将时域信号转换为频域信号&#xff0c;进而分析信号的频率成分、功率分布、谐波失真等参数。频谱仪利用快速傅里叶变换&#xff08;FFT&#xff09;算法&#xff0c;将采集…

Github 2024-04-24 C开源项目日报 Top9

根据Github Trendings的统计,今日(2024-04-24统计)共有9个项目上榜。根据开发语言中项目的数量,汇总情况如下: 开发语言项目数量C项目9C++项目1我的电视 - 安卓电视直播软件 创建周期:40 天开发语言:CStar数量:649 个Fork数量:124 次关注人数:649 人贡献人数:1 人Open…

STM32 USB HID报告描述符没有报告长度

STM32 USB HID设置(STM32CubeMX)_我也想成大侠的博客-CSDN博客 不影响鼠标功能

面向对象设计模式

设计模式通常被分为三种类型&#xff1a;创建型模式、结构型模式和行为型模式。 创建型模式 创建型模式主要关注对象的创建机制&#xff0c;它们提供了一种将对象创建和实例化的机制&#xff0c;使得系统在不直接依赖于具体类的情况下能够灵活地创建对象。 创建型模式的典型…

力扣---填充每个节点的下一个右侧节点指针 II

给定一个二叉树&#xff1a; struct Node {int val;Node *left;Node *right;Node *next; } 填充它的每个 next 指针&#xff0c;让这个指针指向其下一个右侧节点。如果找不到下一个右侧节点&#xff0c;则将 next 指针设置为 NULL 。 初始状态下&#xff0c;所有 next 指针都…

Prompt Engineering,提示工程

什么是提示工程&#xff1f; 提示工程也叫【指令工程】。 Prompt发送给大模型的指令。比如[讲个笑话]、[用Python编个贪吃蛇游戏]、[给男/女朋友写情书]等看起来简单&#xff0c;但上手简单精通难 [Propmpt]是AGI时代的[编程语言][Propmpt]是AGI时代的[软件工程][提示工程]是…

Spring5深入浅出篇:JDK代理与CGLIB代理区别

Spring5深入浅出篇:JDK代理与CGLIB代理区别 很多粉丝私信我这个Spring5的课程在哪看,这边是在B站免费观看欢迎大家投币支持一下. https://www.bilibili.com/video/BV1hK411Y7zf JDK动态代理与CGLIB的区别 在Java的世界里&#xff0c;动态代理主要有两种实现方式&#xff1a;JDK…

广州大学《虚拟现实与游戏开发》实验报告一HTC-VR环境搭建与开发

广州大学学生实验报告 开课实验室&#xff1a; 学院 年级、专业、班 姓名 学号 实验课程名称 虚拟现实与游戏开发 成绩 实验项目名称 1. HTC-VR环境搭建与开发 指导老师 实验目的 HTC VIVE硬件安装虚拟现实开发环境搭建 3.熟悉虚拟现实硬件系统和…

springboot-异步、定时、邮件任务

目录 一&#xff0c;前言 二&#xff0c;异步 2.1&#xff0c;案例&#xff1a; 1&#xff0c;首先创建一个service&#xff1a; 2&#xff0c;Controller: ① 想办法告诉spring我们的异步方法是异步的&#xff0c;所以要在方法上添加注解 Async ②去springboot主程序中开…

深入Spring Boot配置机制:如何高效管理应用配置

一、属性的优先级和配置文件的位置 在Spring Boot应用中&#xff0c;我们可以在多个地方定义配置属性&#xff0c;并且Spring Boot提供了一套优先级排序&#xff0c;来决定同一属性多处定义时的覆盖关系。理解这个机制对于配置管理至关重要。 1. 配置文件查找的顺序 Spring …

【debug记录】有gpu,但是 pytorch仍显示 cpu【原来是新电脑没安装cuda】

原来是新电脑没安装cuda&#xff0c;以为安装了pytorch包就可以了。 检查过程&#xff1a; nvcc 不是内部或外部命令&#xff0c;也不是可运行的程序, 说明没有安装cuda。 查看电脑显卡最高支持cuda版本&#xff1a;nvidia-smi 安装cuda&#xff0c;选择版本&#xff1a;ht…

基于小程序实现的查寝打卡系统

作者主页&#xff1a;Java码库 主营内容&#xff1a;SpringBoot、Vue、SSM、HLMT、Jsp、PHP、Nodejs、Python、爬虫、数据可视化、小程序、安卓app等设计与开发。 收藏点赞不迷路 关注作者有好处 文末获取源码 技术选型 【后端】&#xff1a;Java 【框架】&#xff1a;ssm 【…

flink1.18.0 流转表 表转流 jdk17 attachAsDataStream

目的 流表互转 而且流sink 表sink同时存在且都可以输出. 依赖类 package flink.luca.flinkTableAndSQL.Convert;import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor;@Data @AllArgsConstructor @NoArgsConstructor public class Outer…