首页 > 其他分享 >Unity-场景的异步加载

Unity-场景的异步加载

时间:2023-11-14 19:26:25浏览次数:42  
标签:异步 传送 传送门 Unity 场景 public destinationTag 加载

Unity-场景的异步加载

为什么需要异步加载

​ 在诸多大型游戏里,场景渲染精度都是动态的,随着场景与角色距离的增加,渲染精度也在递减,这样极大的减少了硬件性能的消耗。

​ 但如果角色使用了某些传送技能,将自己传送到为渲染的地点,游戏可能就会因为需要瞬间渲染大量的场景而卡顿。此时就需要用到 场景的异步加载 了。

异步加载,指的就是在加载角色之前,事先将角色周围的场景渲染好,防止角色传送后出现严重的卡顿,以提高玩家的游戏体验。

场景加载构想

​ 在提供直接的代码之前,我们需要对基本的传送进行一个简单的构思。首先我们需要一个传送门以确定我们 能够触发传送 的地点;传送门前还需要设置一点以确定 传送地点,并将其作为传送门的子物体。

​ 注意这两者的区别:传送门 只是确定 触发传送的地点,而角色需要前往的地点由 传送门前的点 决定。

​ 编写代码,分别给传送门与传送点设置对应的属性(传送门:设置传送模式、传送场景、传送终点名字,以及编写 判断何时传送的函数)(传送点:设置传送点名字),这样简易的传送门就设置好了。

​ 关键在于场景传送代码的逻辑:

  1. 我们需要创建一个控制场景的代码,因为Unity自带 SceneManager 类,所以我们在这里将场景控制的代码命名为 SceneController ,同样将其写成单例模式。
  2. 就如同 GameManager 一样,SceneController 也一样不需要自己执行代码,需要别的类在外部调用。我们只需要在 SceneController 中写上必要的参数(PlayerPrefab:player的预制体,用于在其他场景生成角色),一个进行 异步加载的协程,一个 查找传送终点 的函数,一个 传送函数 供传送门调用即可。
  3. SceneController 中的传送函数需要用到传送门的参数,所以传送函数需要一个 传送门类 作为参数。传送开始前,首先需要判断传送类型(同场景 or 异场景),然后将 传送终点名、终点场景名 传给协程,同时不论同异场景,都需要在协程中将 传送终点名 传给 查找传送终点位置的函数,以获得传送终点的位置信息,在获得终点信息后才能进行场景的异步加载

实例代码

​ 在上面的简单构思中,已经对基本的传送逻辑进行了简单的梳理,现在就是将逻辑实现的时候了。

传送目标点

​ 传送终点的代码十分简单,只是通过枚举(enum)对传送点进行记录,枚举要在Unity窗口中自行选择

public class TransitionDestination : MonoBehaviour
{
    public enum DestinationTag //设立目标点名枚举
    {
        ENTER, A, B, C
    }

    public DestinationTag destinationTag;//目标点名枚举变量,在Unity窗口中设置
}

传送门(可传送地点)

​ 传送门需要判定角色是否在可传送范围内,如果是,则可以在按下某键时进行传送

public class TransitionPoint : MonoBehaviour
{
    public enum TransitionType //创建传送类型的枚举
    {
        SameScene, DifferentScene
    }

    [Header("Transition Info")]
    public string sceneName;//以字符串形式记录 终点场景名
    
    public TransitionType transitionType;//传送类型枚举变量,需要在Unity窗口中设置
    
    public TransitionDestination.DestinationTag destinationTag;//设置传送终点名
    
    private bool canTrans;	//判断是否可传送
    
    private void Update() 
    {
        if(Input.GetKeyDown(KeyCode.E) && canTrans)
        {
            if(SceneController.Instance != null)
            {
                //调用SceneController中的传送函数
                SceneController.Instance.TransitionToDestination(this);
            }
        }
    }

    //利用碰撞体判断Player是否处在可传送位置,是则将可传送Bool值设置为true,否则设置为false
    private void OnTriggerStay(Collider other) 
    {
        if(other.gameObject.CompareTag("Player"))
        {
            canTrans = true;
        }
    }

    private void OnTriggerExit(Collider other) 
    {
        if(other.gameObject.CompareTag("Player"))
        {
            canTrans = false;
        }    
    }
}

ScenController

​ SceneController 的详细介绍已经在场景转换逻辑写清楚了,在这不赘述

public class SceneController : Singleton<SceneController>
{
    public GameObject playerPrefab;//Player的预制体,在Unity窗口中赋值
    GameObject player;
    NavMeshAgent playerAgent;

    protected override void Awake()
    {
        base.Awake();
        DontDestroyOnLoad(this);
    }

    //由传送门调用,由传送门将自身当作参数传递过来,获得传送门的属性
    public void TransitionToDestination(TransitionPoint transitionPoint)
    {
        //判断传送模式
        switch(transitionPoint.transitionType)
        {
            //场景内传送
            case TransitionPoint.TransitionType.SameScene:
                
                //获得当前场景名后,将场景名与目标点传给协程
                StartCoroutine(Transition(SceneManager.GetActiveScene().name, transitionPoint.destinationTag));
                
                break;
            //场景外传送
            case TransitionPoint.TransitionType.DifferentScene:
                
                //将Unity窗口中设置的场景名与目标点传给协程
                StartCoroutine(Transition(transitionPoint.sceneName, transitionPoint.destinationTag));
                break;
        }
    }

    //场景加载协程
    IEnumerator Transition(string sceneName, TransitionDestination.DestinationTag destinationTag)
    {
		//首先判断是不是异场景传送
        if(SceneManager.GetActiveScene().name != sceneName)
        {
            //异步加载加载场景
            yield return SceneManager.LoadSceneAsync(sceneName);
            //场景加载完毕后,调用获取目标点的函数获得目标点,将Player生成在该点
            yield return Instantiate(playerPrefab, GetDestination(destinationTag).transform.position, GetDestination(destinationTag).transform.rotation);
            yield break;
        }
        else
        {
            //进行初始化,获得 Player 及其 NavMeshAgent
            player = GameManager.Instance.playerStats.gameObject;
            playerAgent = player.GetComponent<NavMeshAgent>();
            playerAgent.isStopped = true;
            //调用函数获得目标点的信息,并传送到该点 
            player.transform.SetPositionAndRotation(GetDestination(destinationTag).transform.position, GetDestination(destinationTag).transform.rotation);
            yield return null;
        }
    }

    //查找传送终点函数
    private TransitionDestination GetDestination(TransitionDestination.DestinationTag destinationTag)
    {
        var entrances = FindObjectsOfType<TransitionDestination>();//获取场景内所有的传送目标点
        //遍历目标点数组
        foreach (var item in entrances)
        {
            if(item.destinationTag == destinationTag)
            {
                return item;//查找到后将其return
            }
        }

        return null;
    }
}

标签:异步,传送,传送门,Unity,场景,public,destinationTag,加载
From: https://www.cnblogs.com/MMMMrD/p/17832319.html

相关文章

  • Unity-Menu&场景切换
    Unity-Menu&场景切换开始界面1.要创建开始界面,首先要新建一个场景,用于添加游戏开始界面的内容2.新建按钮步骤:UI>画板>Button(按钮)>根据需要设置按钮3.给按钮添加代码,使得按下按钮就可以进入下一关/退出游戏(1)代码内容需要用到usingUnityEngine.SceneManagement的头文件......
  • Unity-敌人(Enemy)
    Unity-敌人(Enemy)引言​ 敌人是每个游戏中不可缺少的部分,设计得好的敌人可以给游戏增添很多乐趣,设计得差的则会非常影响我们的游戏体验。​ 经过这段时间的学习,我们已经接触了非常多的敌人代码的写法,但是就是没有系统的归类,导致每次写敌人,都要从头开始。现在是时候将他们进行一......
  • Unity-Light(含Unity2021-2d项目升级Urp渲染管线)
    Unity-Light(含Unity2021-2d项目升级Urp渲染管线)普通渲染管线(比较老旧的光效升级方式,已舍弃)​ 要使场景和角色拥有光效,那就得让他们先暗下来,给他们添加相应的材质场景材质的添加​ 选中需要添加材质的场景,在右侧框内的“材质”菜单中,选中Default-Diffuse材质人物材质的添加​......
  • Unity-单例模式
    Unity-单例模式前言​ 对于某些特殊的类,我们希望在整个程序的生命周期只创建一个该类的对象,或是希望在其他类没有持有该类的引用,就可以调用该类中的函数,我们就需要将这个类写成单例模式单例的简单实现publicclassTest(){ pubicabstractTestInstance;//创建程序中该......
  • Unity-射线
    Unity-射线前言​ 在游戏开发的过程中,许多功能的实现都需要物理检测,而发射射线是Unity中物理检测的通用方法。例如,我们需要检测玩家(Player)脚下是否是地面(图层为Ground),只需要从脚底发射一条射线,检测Player脚下GameObject的图层是否为Ground即可。​ 射线和物理检测何其重要,......
  • Unity-对象池 & 多对象池
    Unity-对象池&多对象池简介​ 在制作游戏的过程中,人物和boss的设计往往会有释放多个子弹的攻击方式。我们可以用直接创造子弹然后销毁的办法来实现这些技能的效果,但当子弹开始变多,游戏就会不断的消耗我们的内存。为了解决这个问题,开发者们就引入了状态机。普通对象池创建思......
  • Unity-协程
    Unity-协程协程的简单实现​ 一般的程序执行都是线性的,也就是必须一行一行的执行代码。​ 使用Unity提供的协程,就可以类似于开辟另一条线程,调整根据你所写的代码,调整下一行代码执行的时间。项目示例​ 下面的例子是一个U3DDemo中的代码,实现最简单的Enemy追击Player的......
  • Unity3D 如何用unity引擎然后用c#语言搭建自己的服务器
    Unity3D是一款强大的游戏开发引擎,可以用于创建各种类型的游戏。在游戏开发过程中,经常需要与服务器进行通信来实现一些功能,比如保存和加载游戏数据、实现多人游戏等。本文将介绍如何使用Unity引擎和C#语言搭建自己的服务器,并给出技术详解以及代码实现。对惹,这里有一个游戏开发交流......
  • WonderTrader 源码解析与改造-通用的dll加载器(未完待续)
    背景笔者学习WonderTrader的源码的一些心得体会,本文基于WonderTrader0.9.8,讲解其中的DLLHelper类先看它的应用1.wondertrader\src\TestTrader\main.cpp2.wondertrader\src\Includes\ITraderApi.h3.wondertrader\src\TraderCTP\TraderCTP.cpp......
  • 大师学SwiftUI第9章Part 1 - 异步并发之Task、Async、Await和错误
    其它相关内容请见虚拟现实(VR)/增强现实(AR)&visionOS开发学习笔记苹果系统借助现代处理器的多核可同步执行多条代码,提升同一时间内程序所能执行的任务。例如,一段代码从网上下载文件,另一段代码可以在屏幕上显示进度。此时,我们不能等待第一个执行完后再执行第二个,而必须要同步执行这......