当前位置: 首页 > news >正文

Unity基础学习(二)

在这里插入图片描述

二、Mono中的重要内容

在这里插入图片描述

1、延迟函数

(1)延迟函数定义

延迟执行的函数,可以设定要延迟执行的函数和具体延迟的时间

(2)延迟函数的使用

		#region 1、延迟函数
        //函数:Invoke(函数名/字符串,延迟时间/秒)
        //注意点
        //1.1 延迟函数第一个参数传入的是函数名字符串
        //1.2 延迟函数没有办法传入参数 
        #endregion

        #region 2、延迟重复执行函数
        //函数:InvokeRepeating(函数名,第一次执行的延迟时间,之后每次执行的间隔时间)
        #endregion

        #region 3、取消延迟函数
        //3.1 取消该脚本上的所有延迟函数
        //CancelInvoke()
        //3.2 指定函数名取消
        //CancelInvoke(函数名)
        #endregion

        #region 4、判断是否有延迟函数
        //IsInvoking()
        #endregion

(3)延迟函数受对象失活销毁影响

脚本依附对象失活 延迟函数可以继续执行
脚本依附对象销毁或脚本删除 延迟对象无法继续执行

2、协同程序

(1)Unity是否支持多线程

Unity中支持多线程,不过新开的线程无法访问Unity场景中对象的内容
Unity中的多线程,即使没有点击运行,它也会一直执行,所以要记得关闭
多线程一般用于一些复杂计算,将输出的结果存在公共变量中,供Unity主程序使用

(2)协同程序

(a)定义

协同程序是假的多线程,不是多线程

(b)作用

将代码分时分步执行,不卡主线程
可以设置什么时候执行哪个步骤,多久时间执行下一步

(c)主要使用场景

异步加载文件
异步下载文件
场景异步加载
批量创建时防止卡顿

(3)协程的使用

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Lesson9 : MonoBehaviour
{
    // Start is called before the first frame update
    void Start()
    {

        //第二步   开启协程函数
        Coroutine c1=StartCoroutine(MyFunc(1,"123"));
        Coroutine c2= StartCoroutine(MyFunc(1, "123"));
        //第三步   关闭协程
        //关闭所有
        StopAllCoroutines();
        //关闭指定协程
        StopCoroutine(c1);

    }

   IEnumerator MyFunc(int i,string str)
    {
        //第一步   声明协程函数
        //1.1   返回值为IEnumerator类型及其子类
        //1.2   函数通过yield return 返回值 进行返回
        //执行第一部分
        print(i);
        yield return new WaitForSeconds(5f);
        //执行第二部分
        print(str);
        yield return new WaitForSeconds(5f);
        //执行第三部分
        print("123456");

        //协程内允许写死循环
        while (true)
        {
            print("死循环,等3秒打印");
            yield return new WaitForSeconds(3f);

        }
    }
}

(4)yield return 不同内容的含义

在这里插入图片描述

(5)协程受对象和组件失活销毁的影响

协程开启后
组件和物体销毁 协程不执行
物体失活协程不执行,组件(脚本)失活协程执行

3、协同程序原理

(1)协程的本质

协程的本质是一个C#的迭代器方法,主要分为 协程函数本体 和 协程调度器 两个部分

(a)协程函数本体

协程函数本体是一个能够中间暂停返回的函数

(b)协程调度器

协程调度器是Unity内部实现的,会在对应的时机帮助我们继续执行协程函数

(2)总结

协程的本质,就是利用C#迭代器函数分布执行的特点,加上协程调度逻辑实现的一套分时执行函数的规则

三、Resources资源动态加载

在这里插入图片描述

1、Resources同步加载

(1)Resources资源动态加载的作用

通过代码动态加载Resources文件夹下指定路径资源
避免繁琐的拖曳操作
一个工程中可以有多个Resources文件夹,通过API加载时,会自动去寻找同名文件

(2)常用资源类型

预设体对象——GameObject 注意:预设体对象加载需要实例化,其他资源可以直接用
音效文件——AudioClip
文本文件——TextAsset 注意:支持的文本资源格式 txt xml bytes json html csv
图片文件——Texture
其他类型——需要什么类型用什么类型

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

Object obj= Resources.Load("资源所在Resources文件夹下的路径");

//加载指定类型的资源
Resources.Load("资源名",typeof(资源类型));
//加载指定名字的所有资源
Object[] objs= Resources.LoadAll("资源名");

(4)资源同步加载 泛型方法

Resources.Load<资源类型>("资源名");

2、Resources异步加载

(1)异步加载介绍

当加载过大的资源会造成程序卡顿,Resources异步加载就是内部新开一个线程进行资源加载,不会造成主线程卡顿

(2)异步加载方法

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Lesson11 : MonoBehaviour
{
    // Start is called before the first frame update
    void Start()
    {
        print("开始加载,当前帧为"+Time.frameCount);
        //异步加载不能马上得到加载的资源   至少需要等一帧
        //1.通过异步加载中的完成时间监听  使用加载的资源
        //这句代码表示 Unity开了一个线程进行资源加载
        ResourceRequest rq = Resources.LoadAsync<GameObject>("Cube");
        //为 资源下载结束时   添加的一个监听事件
        rq.completed += LoadOver;
        //2.通过协程使用加载的资源
        StartCoroutine(Load());
    }
    private void LoadOver(AsyncOperation rq)
    {
        print("结束加载,当前帧为" + Time.frameCount);
        //加载结束  才能对资源进行使用
        Instantiate((rq  as ResourceRequest).asset);
    }

    IEnumerator Load()
    {
        
        ResourceRequest rq = Resources.LoadAsync<GameObject>("Sphere");

        //打印加载进度
        while (!rq.isDone)
        {
            print("当前加载进度:" + rq.progress);
            yield return null;
        }
        //第一部分
        yield return rq;
        //Unity会自己判断 资源是否加载完毕 加载完毕后才会继续执行后面的代码
        //实例化预制体
        Instantiate(rq.asset);

        //yield return null;
        第二部分
        //yield return new WaitForSeconds(3);
        第三部分
    }
}

(3)总结

1.完成事件监听异步加载 ”线性加载“
优点:写法简单
缺点:只能在资源加载结束后进行处理
2.协程异步加载 ”并行加载“
好处:可以在协程中处理复杂逻辑,比如同时加载多个资源,加载进度条更新等
坏处:写法较为复杂

3、Resources卸载资源

(1)Resources重复加载资源会浪费内存吗

Resources加载一次资源后,该资源一直放在内存中作为缓存,二次加载时发现内存中存在该资源就会直接取出使用
所有多次加载资源不会浪费内存,但是会浪费性能

(2)如何手动释放掉缓存中的资源

		 //卸载资源
        //1.卸载指定资源
        Resources.UnloadAsset(资源名称);
        //注意:该方法 不能释放 GameObject对象 因为它会用于实例化对象
        //它只能用于一些 不需要实例化的内容 比如 图片 音效 文本等
        //一般情况很少单独使用


        //2.卸载未使用的资源
        Resources.UnloadUnusedAssets();
        //该方法一般在过场景和GC一起使用

四、场景异步切换

1、场景同步切换

SceneManager.LoadScene(“场景名称”);
缺点:
使用同步切换回删除当前场景上所有对象,并去加载下一个场景的相关信息。如果当前场景或者下一个场景内容过多,就会导致同步切换过程耗时很久

2、场景异步切换

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;

public class Lesson12 : MonoBehaviour
{
    // Start is called before the first frame update
    void Start()
    {
        //同步场景切换
        SceneManager.LoadScene("Lesson12Test");
        //异步场景切换
        //1.通过事件回调函数 异步加载
        //AsyncOperation ao= SceneManager.LoadSceneAsync("Lesson12Test");
        //ao.completed += LoadSceneOver;

        //2.通过协程异步加载
        //注意:加载场景会把当前场景上没有特别处理的对象都删除了,所以协程中的部分逻辑可能是执行不了的
        //解决思路:让处理场景加载的脚本依附的对象,过场景时不被移除
        DontDestroyOnLoad(this.gameObject);
        StartCoroutine(LoadScene("Lesson12Test"));   
    }

    private void LoadSceneOver(AsyncOperation ao)
    {
        print("场景加载结束");
    }

    IEnumerator LoadScene(string name)
    {
        //异步加载
        AsyncOperation ao = SceneManager.LoadSceneAsync(name);
        //等待场景加载完成
        //可以在加载过程中,做别的事情
        print("异步加载过程中,打印的信息");
        yield return ao;
        //场景加载完 再执行下面的逻辑
        print("异步加载结束后,打印的信息");
    }
}

五、辅助功能Linerender

1、Linerender介绍

Linerender是Unity提供的一个用于画线的组件
用途:
绘制攻击范围
武器红外线
辅助功能
其他画线功能

2、Linerender参数相关

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

3、Linerender代码相关

		//动态添加一个线段
        GameObject line = new GameObject();
        line.name = "Line";
        LineRenderer lineRenderer= line.AddComponent<LineRenderer>();

        //首尾相连
        lineRenderer.loop = true;
        //开始结束宽
        lineRenderer.startWidth = 0.02f;
        lineRenderer.endWidth = 1f;
        //开始结束颜色
        lineRenderer.startColor = Color.white;
        lineRenderer.endColor = Color.black;
        //设置材质
        lineRenderer.material = material;
        //设置点
        //要先设置点的个数
        lineRenderer.positionCount = 4;
        //设置对应每个点的位置
        lineRenderer.SetPositions(new Vector3[] {new Vector3(0,0,0),
                                                 new Vector3(0,0,5),
                                                 new Vector3(5,0,5),
                                                 new Vector3(0,5,5)});
        //指定设置某个点
        lineRenderer.SetPosition(3, new Vector3(1, 5, 6));
        //设置是否使用世界坐标系
        //决定了 是否随对象移动而移动 false——会跟随对象移动,true——不会跟随对象移动
        lineRenderer.useWorldSpace = false;
        //让线段受光影响 会接受光数据 进行着色器计算
        lineRenderer.generateLightingData = true;

六、核心系统

1、物理系统之范围检测

(1)物理系统之碰撞检测

碰撞产生必要条件:至少一个物体有刚体,两个物体都必须有碰撞器
碰撞和触发:碰撞会产生实际的物理效果,触发看起来不产生碰撞但是可以通过函数监听触发
碰撞检测主要用于实体物体之间产生物理效果时使用

(2)范围检测介绍

在指定位置 进行 范围判断 我们可以得到处于指定范围内的 对象
目标是对 范围内的对象进行处理 比如,受伤,掉血等
例如植物大战僵尸的食人花

(3)如何进行范围检测

//范围检测
        //必备条件:想要被范围检测到的对象,必须具备碰撞器
        //注意点:
        //1.范围检测相关API 只有当执行该句代码时 进行一次范围检测 它是瞬时的
        //2.范围检测相关API 并不会真正产生一个碰撞器 只是碰撞判断计算而已

        //1.盒装范围检测
        //返回在该范围内的触发器  = Physics.OverlapBox(立方体中心点,立方体三边大小,立方体角度,
        //                          检测指定层级(不填检测所有层),是否忽略触发器(UseGlobal-使用全局设置,可以在Project Setting-PyPhysics中进行设置,
        //                          Collide-检测触发器 Ignore-忽略触发器 不填使用UseGlobal))
        Collider[] colliders  =Physics.OverlapBox(Vector3.zero, Vector3.one, Quaternion.AngleAxis(45, Vector3.up), 
                            1 << LayerMask.NameToLayer("UI"));

        //重点知识
        //关于层级
        //通过名字得到层级编号    LayerMask.NameToLayer
        //我们需要通过编号左移构建二进制数
        //这样每一个编号的层级 都是 对应位为1的2进制数
        //我们通过位运算可以选择想要的检测层级
        //好处 一个int 就可以表示所有想要检测的层级信息

        //另一个API
        //返回值:碰撞到的碰撞器数量
        //参数:传入一个数组进行存储
        Physics.OverlapBoxNonAlloc(Vector3.zero, Vector3.one, colliders);

        //2.球形范围检测
        // Physics.OverlapSphere(中心点,球形半径,层级)
        Collider[] colliders1=Physics.OverlapSphere(Vector3.zero, 5f, 1 << LayerMask.NameToLayer("UI"));

        //另一个API
        Physics.OverlapSphereNonAlloc(Vector3.zero, 5f, colliders1);

        //3.胶囊范围检测
        //Physics.OverlapCapsule(上半圆中心点位置,下半圆中心点位置,圆的半径,层级)
        Collider[] colliders2 = Physics.OverlapCapsule(Vector3.zero, Vector3.up, 1, 1 << LayerMask.NameToLayer("UI"));

        //另一个API
        Physics.OverlapCapsuleNonAlloc(Vector3.zero, Vector3.up, 1, colliders2);

2、物理系统之射线检测

(1)射线检测介绍

射线检测可以在指定点发射一个指定方向的射线,判断该射线与哪一些碰撞器相交,得到对应对象

(2)射线对象

		//1.声明一个射线对象
        //Ray(起点,方向)
        Ray ray = new Ray(Vector3.right, Vector3.forward);
        print(ray.origin);//起点
        print(ray.direction);//方向

        //2.摄像机发射出的射线
        //模拟鼠标点击发出射线
        Ray ray1 = Camera.main.ScreenPointToRay(Input.mousePosition); 

(3)碰撞器检测函数

//碰撞检测函数
        //射线检测也是瞬时的,执行代码时进行一次射线检测

        //1.最原始的射线检测
        Ray ray = new Ray(Vector3.zero, Vector3.forward);
        //返回一个布尔值 = Physics.Raycast(射线对象,最远距离/米,层级,是否忽略发射器)
        bool isColl =Physics.Raycast(ray, 1000, 1 << LayerMask.NameToLayer("UI"));

        //2.获取相交物体的信息
        //物体信息类 RaycastHit
        RaycastHit hitInfo;
        //Physics.Raycast(射线对象,物体信息类,最远距离,层级)
        if (Physics.Raycast(ray,out hitInfo,1000, 1 << LayerMask.NameToLayer("UI")))
        {
            print(hitInfo.collider);//碰撞器信息
            print(hitInfo.point);//碰撞到的点
            print(hitInfo.normal);//法线信息
            print(hitInfo.transform.position);//碰到对象的位置信息
            print(hitInfo.distance);//碰撞对象与发射对象的距离

        }

        //3.获取相交的多个对象
        RaycastHit[] raycastHits = Physics.RaycastAll(ray, 1000, 1 << LayerMask.NameToLayer("UI"));
        int count = Physics.RaycastNonAlloc(ray, raycastHits, 1000, 1 << LayerMask.NameToLayer("UI"));

(4)使用时注意的问题

距离和层级 都是int类型
当我们传入参数时,一定要明确传入的参数代表的是距离还是层数

相关文章:

  • docker 占用系统空间太大了,整体迁移到挂载的其他磁盘|【当前普通用户使用docker时,无法指定镜像、容器安装位置【无法指定】】
  • 从 Spring Boot 2 升级到 Spring Boot 3 的终极指南
  • 02、Hadoop3.x从入门到放弃,第二章:集群环境搭建
  • 从零开始用react + tailwindcs + express + mongodb实现一个聊天程序(三) 实现注册 登录接口
  • Gin框架深度解剖:路由树的实现原理
  • 蓝桥杯单片机基础部分——1.5基础模块代码升级
  • 【软件设计】SOLID原则详解与PHP实战示例
  • PageForge v2025.1.6 发布:支持 KaTeX 数学公式渲染
  • Spring AI + 大模型开发应用
  • 爬楼梯问题
  • 【Alertmanager】Alertmanager告警路由,告警静默,告警抑制,高可用的实现
  • CryptoJS库中WordArray对象支持哪些输出格式?除了toString() 方法还有什么方法可以输出吗?WordArray对象的作用是什么?
  • Python入门教程丨3.8 网络编程
  • 计算机毕业设计 ——jspssm504springboot 职称评审管理系统
  • Redis搭建集群
  • linux--多进程开发(4) 进程退出、孤儿进程、僵尸进程、进程回收wait()
  • 从最小依赖角度谈静态库与动态库的选择及配置策略
  • 【大模型学习】Transformer架构解析
  • IDEA集成DeepSeek,通过离线安装解决无法安装Proxy AI插件问题
  • Linux修改Redis密码
  • 网站怎么做动态图片/自动点击关键词软件
  • 想给学校社团做网站/2019年 2022疫情爆发
  • 只做黑白摄影的网站/提高百度搜索排名
  • 老百姓可以做监督政府的网站吗/三只松鼠软文范例500字
  • 个人做的网站/如何自己建立一个网站
  • 做后台财务系统网站/seo托管