Unity系统学习笔记

文章目录

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,我们可以分别获取使用;

csharp 复制代码
    //查找持有对象引用.
    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.移动物体位置

csharp 复制代码
[void] m_Transform.Translate(Vector3, Space)

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

参数说明

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

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

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

csharp 复制代码
    //向前.
    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.代码上的操作

csharp 复制代码
[void] gameObject.SetActive(bool)
  • 设置游戏物体是显示还是隐藏,true 是显示,false 是隐藏;
  • 所有的游戏物体都可以进行 SetActive()操作。
csharp 复制代码
[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.单个资源加载

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

1.5.4.多个资源加载

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

例子:资源的加载

csharp 复制代码
   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() 像特效一样播放一次的方法

csharp 复制代码
[s][void]AudioSource.PlayClipAtPoint(AudioClip, Vector3)

参数说明:

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

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

1.7.1.前情提要

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

1.7.2.实例化游戏物体

代码:

csharp 复制代码
[s][T]GameObject.Instantiate<T>(GameObject, Vector3, Quaternion)

参数说明:

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

1.7.3.销毁游戏物体

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

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

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

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

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

csharp 复制代码
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.全局名称查找
c# 复制代码
[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.标签查找 ( 一次性查找多个对象)
c# 复制代码
[GameOjbect[]] GameOjbect.FindGameObjectsWithTag(string)
//在场景中查找被赋予了特定标签的游戏物体,查找结果以数组形式返回

1.9.2.Transform 查找

1.查找子物体
c# 复制代码
[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 时间类

c# 复制代码
[float] Time.time //时间. 

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

c# 复制代码
[float] Time.deltaTime //增量时间. 

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

c# 复制代码
[float] Time.timeScale //时间缩放. 

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

  • 1:时间正常;

  • 0:时间暂停;

  • 0.5:时间 0.5 倍慢放;

1.10.2.Mathf 数学类

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

角度与弧度互相转换

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

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

c# 复制代码
[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的过程;

c# 复制代码
[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.插值运算的几个实际应用

单轴向位移和旋转
csharp 复制代码
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 判断语句,停止继续插值。]

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

优化版:

csharp 复制代码
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()插值运算方法,接下来我们就演示"向量插值"和"四元数插值"。
c# 复制代码
[s][Vector3] Vector3.Lerp(a, b, float t)//向量插值
[s][Quaternion] Quaternion.Lerp(a, b, float t)//四元数插值
示例
csharp 复制代码
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.刚体的移动

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

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

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

这里指的就是剧烈运动。

csharp 复制代码
    //给刚体组件添加力,让刚体组件所在的游戏物体,向某个方向发射出去。
    Vector3 参数指的是目标方向的力度。
    [void] m_Rigidbody.AddForce(Vector3) //世界坐标系方向
    [void] m_Rigidbody.AddRelativeForce(Vector3) //物体坐标系方向
例子
csharp 复制代码
    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移动
csharp 复制代码
    /// <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 构造射线对象:
c# 复制代码
[Ray] Camera.main.ScreenPointToRay(Vector3)
//摄像机组件对象方法,接收"屏幕上的一个点"作为参数,创建并返回一个射线对象,Vector3 参数指的就是"屏幕上的一个点";
[Vector3] Input.mousePosition; 
//只读属性,鼠标在屏幕上的位置. 从摄像机所在的位置,向鼠标在屏幕上的位置(方向),创建一根射线。
  1. 场景中默认存在的"Main Camera"称之为:主摄像机;
  2. 之所以能被称之为"主摄像机",不是因为游戏物体名称的中文翻译,而是因为这个 Main Camera 游戏物体身上默认设置了一个专用的 Tag 标签;
  3. [Camera]Camera.main:只读属性,用于获取设置了 MainCamera 标签的游戏物体身上的 Camera 组件对象;
  4. 该属性在项目工程中任意一个脚本中,都可以直接书写调用。
检测物理射线:
c# 复制代码
[bool] Physics.Raycast(Ray, out RaycastHit)

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

  • Ray:需要被检测的射线对象;
  • RaycastHit :结构体,用于存放射线与碰撞体组件的碰撞信息;
    • 使用 out 关键字返回数据对象;
    • 结构体对象只读属性 collider,可以获取与射线发生碰撞的碰撞体组件;
  • bool:射线碰撞到了游戏物体(碰撞体组件)返回真,没有发生碰撞返回假。
使用例子
csharp 复制代码
    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:控制高度。

实例使用

csharp 复制代码
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.延迟调用

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

参数说明:

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

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

4.1.2.重复调用

c# 复制代码
[void] InvokeRepeating(string, float, float)

参数说明:

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

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

4.1.3.取消调用

c# 复制代码
[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.开启协程
c# 复制代码
[Coroutine] StartCoroutine(string)

//协程方法的名称作为参数,开启协程. 

[Coroutine] StartCoroutine(string, object)

//协程方法的名称作为参数,开启协程,以 object 为类型传递方法参数给协程. 
3.停止协程
c# 复制代码
[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付费加强版,在项目实际开发过程中,按需使用即可。
相关推荐
怀旧6661 小时前
spring boot 项目配置https服务
java·spring boot·后端·学习·个人开发·1024程序员节
yi碗汤园1 小时前
【一文了解】C#基础-集合
开发语言·前端·unity·c#
infiniteWei2 小时前
【Lucene】原理学习路线
学习·搜索引擎·全文检索·lucene
follycat2 小时前
[极客大挑战 2019]PHP 1
开发语言·学习·网络安全·php
weixin_518285053 小时前
深度学习笔记11-神经网络
笔记·深度学习·神经网络
并不会6 小时前
常见 CSS 选择器用法
前端·css·学习·html·前端开发·css选择器
龙鸣丿6 小时前
Linux基础学习笔记
linux·笔记·学习
Nu11PointerException8 小时前
JAVA笔记 | ResponseBodyEmitter等异步流式接口快速学习
笔记·学习
亦枫Leonlew10 小时前
三维测量与建模笔记 - 3.3 张正友标定法
笔记·相机标定·三维重建·张正友标定法
考试宝10 小时前
国家宠物美容师职业技能等级评价(高级)理论考试题
经验分享·笔记·职场和发展·学习方法·业界资讯·宠物