• 【Unity每日一记】资源加载相关和检测相关


    在这里插入图片描述


    👨‍💻个人主页@元宇宙-秩沅

    👨‍💻 hallo 欢迎 点赞👍 收藏⭐ 留言📝 加关注✅!

    👨‍💻 本文由 秩沅 原创

    👨‍💻 收录于专栏unity每日一记

    🅰️推荐文章


    【软件设计师高频考点暴击】

    【Unityc#专题篇】之c#系统化大礼包】

    【unity数据持久化】数据管理类_PlayerPrfs

    【unity本站最全系列】unity常用API大全一篇文章足以
    在这里插入图片描述






    🎶(AResources资源动态加载


    在这里插入图片描述

    ###同步加载**———————


    5种资源加载的方式,Resource加载介绍


    • Resources(只能加载Resources目录中的资源)
    • AssetBundle(只能加载AB资源,当前设备允许访问的路径都可以)
    • WWW(可以加载任意处资源,包括项目外资源(如远程服务器))
    • AssetDatabase(只能加载Assets目录下的资源,但只能用于Editor)
    • UnityWebRequest(可以加载任意处资源,是WWW的升级版本)

    资源加载适用于需要加载资源较多的 情况就不用一个一个的拖拽进去



    关键字:@ ,as

    卸载AB包的方法: Resource. UnLoadAsset

    void Start()
        {
    
            Object obj= Resources.Load("sound");
            //AudioClip ac = obj as AudioClip;
            AudioClip ac = (AudioClip)obj;
            AudioSource.PlayClipAtPoint(ac, transform.position);
            
            //Resources.LoadAll("Prefabs");
            AudioClip[] audioClips= Resources.LoadAll<AudioClip>("");
            foreach (var item in audioClips)
            {
                Debug.Log(item);
            }
    
            //Resources.UnloadAsset
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • C#中的回收机制是系统自动回收的,有多种回收机制,不像其他语言需要手动回收

      注意:
      //预设体对象加载需要实例化
      //其它资源加载一般直接用
      #endregion

    Resource资源加载操作


    加载资源前首先需要在project面板中创建Resource名字的文件夹,为固定文件夹用于资源加载

    以加载AudioClip类型的资源为例:

    • 根目录加载 Resource.Load< AudioClip>(“voice”);
    • 子目录加载 Resource.Load ( @ " 子目录名/ voice") ;
    • 另一种形式:
      Object xx = Resource.Load(“voice”);
      AudioClip yy as xx ; // 显性类型转换 ,前提是二者兼容
    • 加载同类型所有资源的方法:
      ①根目录加载: AudioClip [] xx = Resource.AllLoad(" “) ;
      ②子目录加载:AudioClip [] yy = Resource.AllLoad(” 子目录名");

    • 1.预设体对象
            Object obj = Resources.Load("Cube");
            Instantiate(obj);
    
    • 1
    • 2
    • 2.音效资源
            Object obj3 = Resources.Load("Music/BKMusic");
            //我们不需要实例化 音效切片 我们只需要把数据 赋值到正确的脚本上即可
            audioS.clip = obj3 as AudioClip;
            audioS.Play();
    
    • 1
    • 2
    • 3
    • 4
    • 3.文本资源

    文本资源支持的格式——.txt .xml .bytes .json .html .csv …

            TextAsset ta = Resources.Load("Txt/Test") as TextAsset;
            //文本内容
             ta.text
            //字节数据组 
             ta.bytes);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 4.图片
            tex = Resources.Load("Tex/TestJPG") as Texture;
    
    • 1

    ——————— 异步加载———————


    如果我们加载过大的资源可能会造成程序卡顿
    异步加载 就是内部新开一个线程进行资源加载 不会造成主线程卡顿

    • Resources.LoadAsync(“XXX”);
    • 注意:

    异步加载 不能马上得到加载的资源 至少要等待一帧

    直接异步加载——适用于加载单个资源

    //通过事件监听
        ResourceRequest rq = Resources.LoadAsync<Texture>("Tex/TestJPG")
        rq.completed += LoadOver;
        //completed 是 ResourceRequest 中的委托    
        // AsyncOperation 是 ResourceRequest 的父类
        private void LoadOver( AsyncOperation rq)
        {
           //在事件中添加结束标识逻辑,这样我们就知道异步加载完成了
            print("加载结束");
            //加载完成后 会保存在 ResourceRequest类中的 asset Object类型成员里
            //此时实现赋值
            picture = (rq as ResourceRequest).asset as Texture;
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    配套协程异步加载——适用于加载多个资源

    //通过协程的调度器自己判断是否加载结束
       
        StartCoroutine(Load());
        IEnumerator Load()
        {
             ResourceRequest rq = Resources.LoadAsync<Texture>("Tex/TestJPG");
             yield return rq;
       // yield return rq会自己判断 该资源是否加载完毕了,加载完毕过后才继续执行后面的代码 ,因为ResourceRequest 也是YieldInstruction的子类
       
           
            //-------------------------------
             //isDone 和 progress API的应用
            while(!rq.isDone)
            {
                //打印当前的 加载进度 
                print(rq.progress);
                yield return null;
            }
            //--------------------------------
            picture = rq.asset as Texture;
    
        }
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    ———————资源加载器———————


    利用异步直接加载和委托的使用构成简单的资源加载器

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    using UnityEngine.Events;
    //-------------------------------------
    //—————————————————————————————————————
    //___________项目:       ______________
    //___________功能:  简单的资源管理器
    //___________创建者:秩沅_______________
    //_____________________________________
    //-------------------------------------
    public class ResourcesControl 
    {
        static  private ResourcesControl control;
        static  public ResourcesControl Control => control;
        private ResourcesControl()
        {
        }
    
        public void AddResources<T>(string name ,UnityAction<T> source ) where T : Object
        {
            ResourceRequest yb = Resources.LoadAsync<T>(name);
            yb.completed += (reO) =>
            {
                source( (reO as ResourceRequest).asset as T );
            };
        }
    }
    
    //外部调用
    private void Start()
    {
       GameObject shootball; 
            ResourcesControl.Control.AddResources<GameObject>("Profabs/ball",(sphere)=> {
                shootball = sphere;
            } );
     }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37

    ———————卸载资源———————


    • Resources多次重复加载不会浪费内存
      但是 会浪费性能(每次加载都会去查找取出,始终伴随一些性能消耗)

    • 1.卸载指定资源
      Resources.UnloadAsset 方法
      注意:

       它只能用于一些 不需要实例化的内容 比如 图片 和 音效 文本等
       一般情况下很少单独使用它
      
      • 1
      • 2
    • 2.卸载未使用的资源

      一般在过场景时和GC一起使用
      Resources.UnloadUnusedAssets();
      GC.Collect();



    🎶(BSceneManager场景资源动态加载



    👨‍💻👍加载场景的方法


    • SceneManager.LoadScene() ; 场景同步加载
    • SceneManager.LoadSceneSAsyn(); 场景异步加载
    • SceneManage。GetActiveScene().name判断当前场景
    • Application.LoadLevel():同步加载
    • Application.LoadLevelAsync():异步加载
    • Application.LoadLevelAddictive():同步附加式加载
    • Application.LoadLevelAddictiveAsync():异步附加式加载

    👨‍💻👍SceneManasger的操作


    首先添加场景加载的命名空间Using UnityEngine.SceneManagement ;
    而后把游戏场景都保存(拖拽)在 Buid setting 里面,相当于存储场景的目录

    在这里插入图片描述


    👍1.同步加载


    SceneManager.Load( 序列号) ;
    SceneManager.Load( “场景名”) ;

     void Start()
        {
            //SceneManager.LoadScene(1);
            //SceneManager.LoadScene("TriggerTest");
        }
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    缺点:加载时造成画面卡帧,因为,在未加载完成前画面是停止的,所以是卡帧现象


    👍2.异步加载


    如果当前场景 对象过多或者下一个场景对象过多
    这个过程会非常的耗时 会让玩家感受到卡顿
    所以异步切换就是来解决该问题的

    通常我们和协程一起使用:

    • SceneManager.LoadAsync(序列号)

    • SceneManager.LoadAsync(“场景名”)

    • StartCoroutine(协程迭代器方法() ); //调用协程

    • AsyncOperation 该类型翻译为异步操作 ,为下面获得异步场景的返回值
      AsyncOperation ao= SceneManager.LoadSceneAsync(2);

    • ao.allowSceneActivation = true 激活场景

    • ao.progress 场景加载的进度

    //-----1.通过事件回调函数 异步加载------
    
            AsyncOperation SS = SceneManager.LoadSceneAsync("XXXX");
            SS.completed += (a) =>
            {
                print("加载结束");
            };
            SS.completed += LoadOver;
          private void LoadOver(AsyncOperation ao)
          {
            print("LoadOver");
          }
    
    //---------2.通过协程异步加载--------
         
        void Start()
        {   
            //由于场景加载后就不会执行加载后的逻辑了,如果要保存就使用 DontDestroyOnLoad(保留场景加载上个场景的东西)第一个异步方法不会出现该情况
            DontDestroyOnLoad(this.gameObject); 
            StartCoroutine(LoadScene("XXXX"));
        }
        
        IEnumerator LoadScene(string name)
        {     
            AsyncOperation SS= SceneManager.LoadSceneAsync(name);                      
    //根据游戏规则 自定义进度条变化的条件
            yield return SS;
            //1.场景加载结束 更新20%
            //2.动态加载怪物再更新20%
            //3.动态加载场景模型进度条顶满 
            //4.加载结束隐藏进度条
        }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33

    总结

    场景异步加载 和 资源异步加载 一样

        1.通过事件回调函数
        2.协程异步加载
    
    • 1
    • 2
    • 1.事件回调函数
      优点:写法简单,逻辑清晰
      缺点:只能加载完场景做一些事情 不能在加载过程中处理逻辑

    • 2.协程异步加载
      优点:可以在加载过程中处理逻辑,比如进度条更新等
      缺点:写法较为麻烦,要通过协程

    场景管理器

    public class SceneControl
    {
        private static SceneControl instance = new SceneControl();
    
        public static SceneControl Instance => instance;
    
        private SceneControl() { }
    
        public void LoadScene(string name, UnityAction action)
        {
            AsyncOperation SS = SceneControl.LoadSceneAsync(name);
            SS.completed += (Scene) =>
            {
                action();
            };
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    🎶(C线渲染器 LineRenderer


    1.LineRenderer是什么

    线渲染器 (Line Renderer)官方文档

    LineRenderer是Unity提供的一个用于画线的组件来在场景中绘制线段
    一般可以用于

    • 绘制攻击范围
    • 武器红外线
    • 辅助功能其它画线功能

    2.LineRenderer相关API

    • 代码动态添加一个线段

        GameObject line = new GameObject();
        line.name = "Line";
        LineRenderer lineRenderer = line.AddComponent();
      
      • 1
      • 2
      • 3
    • 首尾相连
      lineRenderer.loop = true;

    • 开始结束宽
      lineRenderer.startWidth = 0.02f;
      lineRenderer.endWidth = 0.02f;

    • 开始结束颜色

    •   lineRenderer.startColor = Color.white;
        lineRenderer.endColor = Color.red;
      
      • 1
      • 2
    • 设置材质

            m = Resources.Load<Material>("XXX");
            lineRenderer.material = m;
    
    • 1
    • 2
    • 设置点
      先设置点的个数
      —— lineRenderer.positionCount = 4;
      设置 对应每个点的位置
            lineRenderer.SetPositions(new Vector3[] { new Vector3(0,0,0),
                                                      new Vector3(0,0,5),
                                                      new Vector3(5,0,5)});
            lineRenderer.SetPosition(3, new Vector3(5, 0, 0));
    
    • 1
    • 2
    • 3
    • 4
    • 是否使用世界坐标系

        //决定了 是否随对象移动而移动
        lineRenderer.useWorldSpace = false;
      
      • 1
      • 2
    • 让线段受光影响 会接受光数据 进行着色器计算

        lineRenderer.generateLightingData = true;
      
      • 1

    🎶(D手动范围检测


    在这里插入图片描述

    特点:

    • 1.执行该句代码时 进行一次范围检测 它是瞬时的
    • 2.范围检测相关API 并不会真正产生一个碰撞器 只是碰撞判断计算而已

    共同参数:

    • 参数一:物体中心点
    • 参数二:物体的边长大小
    • 参数三:物体的角度
    • 参数四:检测指定层级(不填检测所有层)
    • 参数五:是否忽略触发器 UseGlobal-使用全局设置 Collide-检测触发器 Ignore-忽略触发器 (不填使用UseGlobal)
    • 返回值:在该范围内的触发器(得到了对象触发器就可以得到对象的所有信息)

    UseGlobal-使用全局设置在该界面中已默认

    在这里插入图片描述

    1.方块状范围检测

    • Physics.OverlapBox ——返回值为数组,存储检测到的碰撞器
     Collider[] colliders = Physics.OverlapBox( Vector3.zero, Vector3.one, 
     Quaternion.AngleAxis(45, Vector3.up), 
            1 << LayerMask.NameToLayer("UI") |
            1 << LayerMask.NameToLayer("Default"), QueryTriggerInteraction.UseGlobal);
    
    • 1
    • 2
    • 3
    • 4
    • Physics.OverlapBoxNonAlloc——返回值为Int 表示检测的数量(最多6个参数)
    if(Physics.OverlapBoxNonAlloc(Vector3.zero, Vector3.one, 自定义数组名) != 0)
    
    
    • 1
    • 2

    2.球形状范围检测

    无角度参数
    参数二为球半径

    • Physics.OverlapSphere
        colliders = Physics.OverlapSphere(Vector3.zero, 5, 1 << LayerMask.NameToLayer("Default"));
    
    • 1
    • Physics.OverlapSphereNonAlloc——同BOX
       if( Physics.OverlapSphereNonAlloc(Vector3.zero, 5, colliders) != 0 )
    
    • 1

    .3.胶囊体范围检测

    参数一:半圆一中心点
    参数二:半圆二中心点
    参数三:半圆半径

    • Physics.OverlapCapsule
            colliders = Physics.OverlapCapsule(Vector3.zero, Vector3.up, 1, 1 << LayerMask.NameToLayer("UI"), QueryTriggerInteraction.UseGlobal);
    
    • 1
    • Physics.OverlapCapsuleNonAlloc
    
    if ( Physics.OverlapCapsuleNonAlloc(Vector3.zero, Vector3.up, 1, colliders ) != 0 )
    
    
    
    • 1
    • 2
    • 3
    • 4

    🎶(GRay射线检测


    • 特点
      只需要判断一条线和物体的碰撞情况
      可以在指定点发射一个指定方向的射线
      判断该射线与哪些碰撞器相交,得到对应对象
      瞬时

    • 应用场景
      1.鼠标选择场景上一物体
      2.FPS射击游戏(无弹道-不产生实际的子弹对象进行移动)等

    API

    • Ray X = new Ray(Vector3.right, Vector3.forward);
    参数一参数二
    起点方向
    X.originX.direction
    • Ray XX = Camera.main.ScreenPointToRay(Input.mousePosition);
      屏幕视口坐标转成射线——鼠标点击的地方变成射线

    • Physics.Raycast 无法检测碰到了谁,只会检测碰到了没有

    最多有16个重载

    Physics.Raycast常用参数作用
    参数射线
    参数检测的最大距离 超出这个距离不检测
    参数检测指定层级(不填检测所有层)
    参数是否忽略触发器 UseGlobal-使用全局设置 Collide-检测触发器 Ignore-忽略触发器 不填使用UseGlobal
    返回bool 当碰撞到对象时 返回 true 没有 返回false
    //第一种写法
    Physics.Raycast(XX, 1000, 
    1 << LayerMask.NameToLayer("层级名字"), 
    QueryTriggerInteraction.UseGlobal   )
    
    //第二种写法
    Physics.Raycast(Vector3.right, Vector3.forward,  
    1 << LayerMask.NameToLayer("层级名字"), 
    QueryTriggerInteraction.UseGlobal   )
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    • RaycastHit 物体信息类——得到相交的单个物体物理信息
      在这里插入图片描述
    RaycastHit 在Physics.Raycast的应用作用
    参数射线
    参数out RaycastHit 为什么是out ?RaycastHit是结构体 是值类型 out加上去就变成了引用类型,而RaycastHit没有复制所以不用ref
    参数检测的最大距离 超出这个距离不检测
    参数检测指定层级(不填检测所有层)
    参数是否忽略触发器 UseGlobal-使用全局设置 Collide-检测触发器 Ignore-忽略触发器 不填使用UseGlobal
    返回bool 当碰撞到对象时 返回 true 没有 返回false
    //写法一
          RaycastHit YY;   
         if( Physics.Raycast(XX, out YY, 1000, 
         
         1<<LayerMask.NameToLayer("层级名字"), 
         
         QueryTriggerInteraction.UseGlobal) )
         
    //写法二
    if( Physics.Raycast(Vector3.right, Vector3.forward, out YY, 1000,
     
         1<<LayerMask.NameToLayer("层级名字"), 
         
         QueryTriggerInteraction.UseGlobal) )
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 碰撞到物体的名字 YY.collider.gameObject.name;
    • 碰撞到的点 YY.point
    • 法线信息 YY.normal
    • 碰撞到对象的位置 YY.transform.position
    • 碰撞到对象 离自己的距离 YY.distance等等

    • RaycastHit[] XX= Physics.RaycastAll——得到相交的多个物体物理信息

    在这里插入图片描述
    特点: 先碰到的在数组的后面

    • Physics.RaycastNonAlloc——返回的碰撞的数量 通过out得到数据
      if((r3, XX, 1000, 1 << LayerMask.NameToLayer("Monster"), 
      QueryTriggerInteraction.UseGlobal) > 0 )
     
            {
            }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    🅰️系统路线学习点击跳转


    【Unityc#专题篇】之c#进阶篇】

    【Unityc#专题篇】之c#核心篇】

    【Unityc#专题篇】之c#基础篇】

    【Unity-c#专题篇】之c#入门篇】

    【Unityc#专题篇】—进阶章题单实践练习

    【Unityc#专题篇】—基础章题单实践练习

    【Unityc#专题篇】—核心章题单实践练习


    你们的点赞👍 收藏⭐ 留言📝 关注✅是我持续创作,输出优质内容的最大动力!


    在这里插入图片描述


  • 相关阅读:
    Debye-Wolf积分计算器的用法
    代码随想录训练营day53, 最长公共子序列, 不相交的线, 最大子序和
    如何查看笔记本电池健康度?(超级详细)
    Java面试题相关
    Increment Selection 插件
    C++修炼之路之多态---多态的原理(虚函数表)
    java反射机制详解
    浅谈 AOP 什么是 AOP ?
    如何用Postman做接口自动化测试
    【1282. 用户分组】
  • 原文地址:https://blog.csdn.net/m0_64128218/article/details/132912302