Unity基础

本文详细介绍了Unity中的基础3D数学概念,包括向量操作、插值函数、四元数的使用,以及延迟函数、协程、资源的同步与异步加载、场景切换的异步方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Unity基础

3D数学 – 基础

一些方法
float value01 = Mathf.PI; // Π
int value03 = Mathf.Abs(-2); // 绝对值
int value04 = Mathf.CeilToInt(1.3f); // 向上取整
int value05 = Mathf.FloorToInt(1.6f); // 向下取整
int value06 = Math.Clamp(5, 10, 20); // 钳制函数:在10~20直接返回值,10~20之外返回接近的那个值
int value07 = Mathf.Min(10, 90, 2); // 最小值,参数可变长
int value08 = Mathf.Max(1, 2, 6); // 最大值,参数可变长
float value09 = Mathf.Pow(5, 2); // 幂运算
int value10 = Mathf.RoundToInt(1.5f); // 四舍五入
float value11 = Mathf.Sqrt(4.0f); // 平方根      
插值运算

在这里插入图片描述

三角函数

在这里插入图片描述

在这里插入图片描述

	// 弧度转角度
    float rad = 5;
    float anger = 5 * Mathf.Rad2Deg;

    // 角度转弧度
    anger = 1;
    rad = anger * Mathf.Deg2Rad;

    // 三角函数:参数是弧度值
    anger = 30 * Mathf.Deg2Rad;
    float sin30rad = Mathf.Sin(anger);

    // 反三角函数:返回值是弧度值
    float asin = Mathf.Asin(1);
    print(asin * Mathf.Rad2Deg);
坐标系

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

3D数学 – 向量

理解

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

常用函数
// 点乘
Vector3.Dot(dir1, dir2);

// 叉乘
Vector3.Cross(dir1, dir2);

// 角度
Vector3.Angle(dir1, dir2);

// 两点间距离
Vector3.Distance(pos1, pos2);
线性插值

Mathf.Lerp用法相同,只是参数为Vector3

在这里插入图片描述

3D数学 – 四元数

在这里插入图片描述

// 初始化四元数
    Quaternion q = Quaternion.AngleAxis(60, Vector3.right);
    this.transform.rotation = q;

// 欧拉角转四元数
    Vector3 euler = new Vector3(1, 2, 3);
    Quaternion qua = Quaternion.Euler(euler);

// 四元数转欧拉角
    euler = qua.eulerAngles;

在这里插入图片描述

在这里插入图片描述

看向

四元数计算

在这里插入图片描述

延迟函数

  1. 延迟函数

    参数一:函数名 字符串 参数二:延迟时间 单位秒

    注意:延迟函数无法直接调用有参函数,函数名必须是该脚本申明的函数

    Invoke("function", 5);
    
  2. 延迟重复执行函数

    参数一:函数名 字符串 参数二:第一次执行延迟时间 参数三:之后每次执行的间隔时间

    注意:同上

    InvokeRepeating("function", 5, 5);
    
  3. 取消延迟函数

    CancelInvoke(); // 取消该脚本上所有延迟函数
    CancelInvoke("function"); // 取消指定函数名的延迟函数
    
  4. 判断是否有延迟函数

    bool isInvoke = IsInvoking();
    bool isInvoke2 = IsInvoking("function");
    

影响延迟函数:

脚本依附对象失活、脚本失活,延迟函数继续执行

脚本依附对象销毁、脚本销毁,延迟函数无法执行

协同程序

多线程

注意:新开的线程无法访问Unity相关对象的内容

线程与Unity编辑器共存亡,所以需要关闭线程

	Thread th;

void Start()
{
    th = new Thread(Test); // 创建线程
    th.Start(); // 开启线程
}

// 线程与Unity编辑器共存亡,所以需要关闭线程
private void OnDestroy() 
{
    th.Abort(); // 结束线程
    th = null;    
}

private void Test()
{
    while(true)
    {
        Thread.Sleep(1000);
        print("Thread");
    }
}
协同程序(协程)
  • “假”的多线程,协程不是多线程

  • 将代码分时执行

  • 使用场景:异步加载、下载,场景异步加载,批量创建防止卡顿

  • 与线程的区别:

    ​ 线程:新开一个独立的管道和主线程并行执行

    ​ 协程:新开一个协程在原线程之上开启,进行逻辑的分时分步执行

协程函数的使用
void Start()
{
    // 开启协程函数
    Coroutine cor = StartCoroutine(MyCoroutine(5, "666"));
    // 开启协程函数 或者:
    IEnumerator ie = MyCoroutine(5, "666");
    StartCoroutine(ie);

    // 关闭所有协程
    StopAllCoroutines();
    // 关闭指定协程
    StopCoroutine(cor);

}

// 携程函数声明: 返回值 IEnumerator 或其他继承它的类型
IEnumerator MyCoroutine(int i, string s)
{
    print(i);
    // 必须使用 yield return 进行返回
    yield return new WaitForSeconds(5f); // 等待5秒
    print(s);
}
yeild return
  1. 等待一帧后执行

    yield return 数字

    yield return null

    在update和Lateupdate之间执行

  2. 等待指定秒后执行

    yield return new WaitForseconds(秒)

    在Update和LateUpdate之间执行

  3. 等待下一个固定物理帧更新时执行

    yield return new WaitForFixedUpdate()

    在Fixedupdate和碰撞检测相关函数之后执行

  4. 等待摄像机和GUI渲染完成后执行

    yield return new WaitForEndofFrame()

    在Lateupdate之后的渲染相关处理完毕后之后

  5. 一些特殊类型的对象 比如异步加载相关函数返回的对象

  6. 一些特殊类型对象

    异步加载

  7. 跳出协程

    yield break

    关闭协程函数,返回值会为空

影响协程函数

组件和对象销毁,协程不执行

物体失活协程不执行,组件失活协程执行

协同程序原理

  1. 协程本体
    • 协程可以分为两个部分:协程函数本体 协程调度器
    • 协程本体就是一个能中间暂停返回的函数
    • 协程调度器是Unity内部实现的,会在对应的时机帮助我们继续执行协程函数
    • Unity只实现了协程调度部分,协程本体的本质是一个C#迭代器的方法
  2. 协程调度器
    • 相当于把一个协程函数(迭代器)放入Unity的协程调度器中帮助我们管理进行执行

Resources资源动态加载

一些特殊文件夹
  1. 工程路径获取

    编辑模式下使用,实际游戏发布后路径就不存在了

    print(Application.dataPath);
    
  2. Resources 资源文件夹

    路径获取:一般不获取路径,通过API进行加载

    注意:需要手动创建文件夹

    作用:

    • 通过Resources相关API动态加载的资源需要放在其中
    • 该文件夹下所有文件都会被打包
    • 打包时Unity会对其进行压缩加密
    • 该文件夹打包后只读,只能通过Resources相关API加载
  3. StringmingAssets 流动资源文件夹

    路径获取:Application.streamingAssetsPath

    注意:需要手动创建文件夹

    作用:

    • 打包出去的文件不会被压缩加密
    • 移动平台只读,PC平台可读可写
    • 可以放入一些需要自定义动态加载的初始资源
  4. persisitenDataPath 持久数据文件夹

    路径获取:Application.persistenDataPath

    注意:不需要我们自己创建

    作用:

    • 固定数据文件夹
    • 所有平台可读可写
    • 一般用于放置动态下载或者动态创建的文件,游戏中创建或者获取的文件都放在其中
  5. Plugins 插件文件夹

    路径获取:一般不获取

    注意:需要手动创建文件夹

    作用:

    • 插件文件夹,不同平台的插件相关文件放在其中

    • 比如IOS和Android平台

  6. Editor 编辑器文件夹

    路径获取:一般不获取路径

    注意:需要手动创建文件夹

    作用:

    • 开发Unity编辑器时,编辑器相关脚本放在该文件夹中

    • 该文件夹中的内容不会被打包出去

  7. 默认资源文件夹 Standard Assets

    路径获取:一般不获取

    注意:需要手动创建,

    作用:

    • 一般Unity自带资源都放在这个文件夹下
    • 代码和资源优先被编译
Resources同步加载

同一个资源多次加载不会造成内存浪费,如果资源已经被加载不会重复加入缓存区,但是会有性能浪费,所有不建议多次加载同一资源

  1. Resources资源动态加载的作用

    • 通过代码动态加载Resources文件夹下指定路径资源

    • 避免繁琐的拖曳操作

  2. 常用资源类型

    • 预设体对象 —— GameObject

    • 音效文件 —— Audiolip

    • 文本文件 —— TextAsset

    • 图片文件 —— Texture

    • 其他类型 —— 需要什么用什么类型

      注意:预设体对象加载需要实例化

  3. 资源同步加载(普通方法)

    • 预设体对象

    在这里插入图片描述

    Object obj = Resources.Load("Cube"); // 加载预设体资源文件,配置数据在内存中
    Instantiate(obj); // 实例化
    
    • 音效文件

      在这里插入图片描述

      public AudioSource audioS; // 音效源组件
      
      objMusic = Resources.Load("Music/BKMusic"); // 加载数据
      audioS.clip = objMusic as AudioClip; // 为组件添加音效切片
      AudioS.Play(); // 播放音效
      
      
    • 文本资源

      在这里插入图片描述

      TextAsset ta = Resources.Load("Txt/Test") as TextAsset; // 加载(返回类型是obj)
      print(ta.text); // 获得文本内容
      print(ta.bytes); // 获得二进制字节数组
      
    • 图片

      在这里插入图片描述

      Texture tex = Resources.Load("Tex/TextJPG") as Texture; // 加载
      
    • 其他类型资源

      需要什么资源就用什么类型,比如动画资源等等

    • 同名资源

      Resources.Load加载同名资源时,无法准确加载出你想要的内容

      在这里插入图片描述

      方法一:加载指定类型的资源

      Texture tex = Resources.Load("Tex/TextJPG", typeof(Texture)) as Texture; 
      

      方法二:加载指定类型的所有资源

      Object[] objs = Resources.LoadAll("Tex/TestJPG");
      
  4. 资源同步加载(泛型方法)

    常用这种方法

    TextAsset tex = Resources.Load<TextAsset>("Tex/TestJPG");
    
Resources异步加载
  • 加载过大资源可能造成程序卡顿,因为从硬盘把数据读取到内存中需要进行计算,越大的资源耗时越长,就会造成掉帧卡顿
  • 异步加载是内部新开一个线程进行资源加载,不会造成主线程的卡顿
  • 异步加载不能马上得到加载资源,至少需要等待一帧

方法一:通过异步加载中的完成事件监听,使用加载的资源

// 开启异步加载
ResourceRequest rq = Resources.LoadAsync<Texture>("Tex/TestJPG");
// 资源下载结束的事件函数监听
rq.completed += LoadOver;
private void LoadOver( AsynOperation rq)
{
	print("加载结束");
}

方法二:通过协程使用加载的资源

StartCoroutine(Load());

IEnumerator Load()
{
	ResourceRequest rq = Resources.LoadAsync<Texture>("Tex/TestJPG");
    yeild return rq; // Unity自动判断异步加载是否加载完毕
}

区别:

  • 完成事件监听异步加载:

    优:写法简单

    缺:只能在资源加载结束后进行处理 “线性加载”

  • 协程异步加载

    优:可以在协程中处理复杂逻辑,比如同时加载多个资源,比如进度条更新

    缺:写法稍麻烦

Resources资源卸载

方法一:卸载指定资源

注意:该方法不能释放GameObject对象,一般用于不需要实例化的内容如图片,文本,音效等等

if(Input.GetKeyDown(KeyCode.Alpha1))
{
	Texture tex = Resources.Load<Texture>("Tex/TestJPG"); // 资源加载
}
if(Input.GetKeyDown(KeyCode.Alpha2))
{
	Resources.UnloadAsset(tex); // 资源卸载
    tex = null; //
}

方法二:卸载未使用资源

注意:一般在过场景时和GC一起使用

Resources.UnloadUnsedAssets();  // 卸载所有未使用的资源
GC.Collect();

场景切换

场景同步切换

缺点:在场景切换时会删除当前场景上的所有对象,并且去加载下一个场景的相关信息,如果当前场景对象过多或者下一个场景对象过多,会造成卡顿

SceneManager.LoadScene("SceneTest");
场景异步切换

方法一:通过事件回调函数异步加载

AsyncOperation ao = SceneManager.LoadSceneAsync("SceneTest"); 
ao.completed += (a) =>
{
    // 异步加载结束自动调用事件函数
    print("加载结束");
};
ao.completed += LoadOver;
private void LoadOver(AsyncOperation ao)
{
    print("LoadOver"); // 
}

方法二:通过协程异步加载

StartCoroutine(LoadScene("SceneTest"));

DontDestroyOnLoad(this,gameObject); // 脚本依附的对象过场景时
IEnumerator LoadScene(string name)
{
    AsyncOperation ao = SceneManager.LoadSceneAsync(name);
    yield return ao; // Unity自动判断异步加载是否加载完毕
}

LineRender

LineRenderer lineRenderer = gameObject.AddComponent<LineRenderer>();
 // 首位连接
 lineRenderer.loop = true;
 //  开始与结束的宽度
 lineRenderer.startWidth = 0.02f;
 lineRenderer.endWidth = 0.02f;
 // 开始与结束的颜色
 lineRenderer.startColor = Color.red;
 lineRenderer.endColor = Color.red;
 // 设置材质
 lineRenderer.material = Resources.Load<Material>("TestMaterial");
 // 设置点
 lineRenderer.positionCount = 4; // 需要先定义个数
 lineRenderer.SetPositions(new Vector3[]{
     new Vector3(-1, 0, -1),
     new Vector3(-1, 0, 1),
     new Vector3(1, 0, 1),
     new Vector3(1, 0, -1),
 });
 // 是否使用世界坐标系
 lineRenderer.useWorldSpace = false;

范围检测

  • 检测对象必须具备碰撞器
  • 范围检测相关API在执行瞬间进行一次检测,是瞬时检测
  • 范围检测API不会产生碰撞器,只是判断碰撞计算
指定层级

层级使用二进制

Debug.Log(LayerMask.NameToLayer("UI")); // 返回对应名字的层数
int layer = 1 << LayerMask.NameToLayer("UI");
Debug.Log(Convert.ToString(layer, 2)); // 层级检测时规则是二进制的
// 通过或运算就可以多选检测层级
int layer1 = 1 << LayerMask.NameToLayer("UI") | 1 << LayerMask.NameToLayer("Water");
Debug.Log(Convert.ToString(layer1, 2));
// 通过同或运算可以排除层级
int layer2 = ~ (0 ^ layer);
Debug.Log(Convert.ToString(layer2, 2));
Box
  • 立方体中心,立方体长宽高、角度、指定层级(默认检测所有层)、是否忽略触发器(默认UseGlobal)
  • 是否忽略触发器:UseGlobal使用全局设置与设置情况相同,Collide检测触发器,Ignore忽略触发器

UseGlobal:
在这里插入图片描述

// 立方体:
Collider[] colliders = 
Physics.OverlapBox(Vector3.zero, Vector3.one, Quaternion.identity, layer, QueryTrig	gerInteraction.Collide);
foreach (Collider collider in colliders)
{
    Debug.Log(collider.gameObject.name);
}

// 返回数量,参数传递Collider数组
if (Physics.OverlapBoxNonAlloc(Vector3.zero,  Vector3.one, colliders, Quaternion.identity, layer) != 0)
{
    Debug.Log("yes");
}
Sphere、Capsule

中心点、球半径、层级,是否忽略触发器

// 球体
colliders = Physics.OverlapSphere(Vector3.zero, 2f, layer, QueryTriggerInteraction.Collide);
int num1 = Physics.OverlapSphereNonAlloc(Vector3.zero, 2f, colliders, layer);

point1、point2、球半径、层级、是否忽略触发器

// 胶囊体
colliders = Physics.OverlapCapsule(Vector3.zero, Vector3.up, 2f, layer, QueryTriggerInteraction.Collide);
int num2 = Physics.OverlapCapsuleNonAlloc(Vector3.zero, Vector3.up, 2f, colliders,layer);

射线检测

射线
 // 射线
 Ray r = new Ray(target.transform.position, transform.forward);
 Vector3 point = r.origin; // 起点
 Vector3 dir = r.direction; // 方向

 // 摄像机射线
 // 以屏幕位置为起点,摄像机视口为方向的射线
 Ray r2 =  Camera.main.ScreenPointToRay(Input.mousePosition);
函数
// 重载一
// Ray射线、距离、层级、是否忽略触发器(同范围检测)
 if (Physics.Raycast(new Ray(Vector3.zero, Vector3.forward), 1000, 1 << LayerMask.NameToLayer("Water"), QueryTriggerInteraction.UseGlobal))
 {
     Debug.Log("Raycast1");
 }
 // Ray = point,direction
 if (Physics.Raycast(Vector3.zero, Vector3.forward, 1000))
 {
     Debug.Log("Raycast1_1");
 }

 // 重载二
 // Ray射线、碰撞信息、距离、层级、是否忽略触发器(同范围检测)
  if (Physics.Raycast(new Ray(Vector3.zero, Vector3.forward), out RaycastHit hitInfo , 1000, 1 << LayerMask.NameToLayer("Water"), QueryTriggerInteraction.UseGlobal))
 {
     Debug.Log("Raycast2");
     // 碰撞对象
     GameObject hit = hitInfo.collider.gameObject;
     Transform transform = hitInfo.transform;
     // 碰撞点
     Vector3 hitPoint = hitInfo.point;
     // 碰撞法线
     Vector3 hitNormal = hitInfo.normal;
     // 碰撞距离
     float hitDistance = hitInfo.distance;
 }
 // Ray = point,direction
 if (Physics.Raycast(Vector3.zero, Vector3.forward, out RaycastHit hitinfo ,1000))
 {
     Debug.Log("Raycast2_2");
 }
// 重载三
// 检测碰撞到的多个对象
RaycastHit[] hits   = Physics.RaycastAll(r, 1000, layer, QueryTriggerInteraction.UseGlobal);
foreach (RaycastHit hit in hits)
{
    Debug.Log(hit.transform.gameObject.name);
}
// Ray = point,direction
hits   = Physics.RaycastAll(Vector3.zero, Vector3.forward ,1000, layer, QueryTriggerInteraction.UseGlobal);

// 重载四
// 返回检测个数,传入检测数组
if (Physics.RaycastNonAlloc(r, hits, 1000, layer) > 0)
{
	foreach(RaycastHit hit in hits)
    {
        Debug.Log(hit.transform.gameObject.name);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值