【Unity实战】切换场景加载进度和如何在后台异步加载具有庞大世界的游戏场景,实现无缝衔接(附项目源码)

文章目录

  • 最终效果
  • 前言
  • 一、绘制不同的场景
  • 二、切换场景加载进度
    • 1. 简单实现
    • 2. 优化
  • 三、角色移动和跳跃控制
  • 四、添加虚拟摄像机
  • 五、触发器动态加载场景
  • 六、最终效果
  • 参考
  • 源码
  • 完结

最终效果

在这里插入图片描述

前言

观看本文后,我的希望你对unity场景管理有更好的理解,并且能够制作具有巨大世界的游戏并无缝加载游戏的各个部分在后台运行而不中断游戏玩法,这种方法非常灵活,而且很容易实现,因此无论您是制作小型平台游戏还是大型开放世界游戏,它应该都适用于两者,准备好让我们开始吧!

一、绘制不同的场景

创建不同场景
在这里插入图片描述

开始菜单界面场景Menu
在这里插入图片描述
主场景 Main,就简单放置个主角人物
在这里插入图片描述
房间1场景Room1,简单放置个平台,记得去除摄像机
在这里插入图片描述

二、切换场景加载进度

1. 简单实现

新增MainMenuManager 代码,实现加载进度,场景切换和加载

public class MainMenuManager : MonoBehaviour
{[SerializeField, Header("加载进度条的父对象")] private GameObject _loadingBarObject;[SerializeField, Header("加载进度条的图像")] private Image _loadingBar;[SerializeField, Header("需要隐藏的对象")] private GameObject[] _objectsToHide;[Space][SerializeField, Header("主要持久化场景的名称")] private string _persistentGameplay = "Main";[SerializeField, Header("游戏关卡场景的名称")] private string _levelScene = "Room1";private List<AsyncOperation> _scenesToLoad = new List<AsyncOperation>(); // 存储待加载场景的列表private void Awake(){_loadingBarObject.SetActive(false); // 禁用加载进度条}//开始游戏public void StartGame(){HideMenu(); _loadingBarObject.SetActive(true); // 启用加载进度条// 加载持久化场景和游戏关卡场景,并将它们添加到待加载场景列表中_scenesToLoad.Add(SceneManager.LoadSceneAsync(_persistentGameplay));_scenesToLoad.Add(SceneManager.LoadSceneAsync(_levelScene, LoadSceneMode.Additive));StartCoroutine(ProgressLoadingBar()); // 启动异步加载进度条的协程}// 隐藏界面private void HideMenu(){for (int i = 0; i < _objectsToHide.Length; i++){_objectsToHide[i].SetActive(false); // 隐藏需要隐藏的对象}}//异步加载进度条的协程private IEnumerator ProgressLoadingBar(){float loadProgress = 0f; // 总的加载进度for (int i = 0; i < _scenesToLoad.Count; i++){while (!_scenesToLoad[i].isDone){loadProgress += _scenesToLoad[i].progress; // 累加加载进度_loadingBar.fillAmount = loadProgress / _scenesToLoad.Count; // 更新加载进度条的显示yield return null; // 等待下一帧}           }}
}

挂载脚本,配置参数

在这里插入图片描述
配置开始游戏点击事件
在这里插入图片描述
被忘记在项目设置加入新的场景
在这里插入图片描述
效果
在这里插入图片描述

2. 优化

正常我们都是按场景名称或者索引去跟踪我们的场景吗,这里其实有一个更好的方法,之后在所有的项目中我们都可以去使用它

灵感来源于一篇Unity论坛的SceneField代码:
https://discussions.unity.com/t/inspector-field-for-scene-asset/40763
在这里插入图片描述
解释:这是代码通过使用SceneField类和SceneFieldPropertyDrawer属性绘制器,开发者可以在自定义的脚本中方便地引用和管理场景对象,并在Inspector面板中进行编辑和选择操作。这对于需要频繁切换场景或者处理多个场景的情况非常有用。

我已经复制代码下来,并加入了一些注释,如下

using UnityEngine;#if UNITY_EDITOR
using UnityEditor;
#endif[System.Serializable]
public class SceneField
{[SerializeField]private Object m_SceneAsset;[SerializeField]private string m_SceneName = "";public string SceneName{get { return m_SceneName; }}// 使其与现有的Unity方法(LoadLevel / LoadScene)兼容public static implicit operator string(SceneField sceneField){return sceneField.SceneName;}
}#if UNITY_EDITOR
[CustomPropertyDrawer(typeof(SceneField))]
public class SceneFieldPropertyDrawer : PropertyDrawer
{public override void OnGUI(Rect _position, SerializedProperty _property, GUIContent _label){EditorGUI.BeginProperty(_position, GUIContent.none, _property);SerializedProperty sceneAsset = _property.FindPropertyRelative("m_SceneAsset");SerializedProperty sceneName = _property.FindPropertyRelative("m_SceneName");_position = EditorGUI.PrefixLabel(_position, GUIUtility.GetControlID(FocusType.Passive), _label);if (sceneAsset != null){// 显示场景选择器,让用户选择一个场景sceneAsset.objectReferenceValue = EditorGUI.ObjectField(_position, sceneAsset.objectReferenceValue, typeof(SceneAsset), false);// 如果已经选择了场景,则将场景名称保存在场景名称变量中if (sceneAsset.objectReferenceValue != null){sceneName.stringValue = (sceneAsset.objectReferenceValue as SceneAsset).name;}}EditorGUI.EndProperty();}
}
#endif

修改MainMenuManager

[SerializeField, Header("主要持久化场景")] private SceneField _persistentGameplay;
[SerializeField, Header("游戏关卡场景")] private SceneField _levelScene;

配置,场景可以通过拖入绑定了
在这里插入图片描述
效果,还是和前面一样,没什么问题
在这里插入图片描述

三、角色移动和跳跃控制

简单实现一下角色的控制

public class PlayerController : MonoBehaviour
{private Rigidbody2D rb; // 刚体组件public float speed, jumpForce; // 移动速度和跳跃力度public Transform groundCheck; // 地面检测点public LayerMask ground; // 地面图层public bool isGround; // 是否在地面上bool jumpPressed; // 是否按下跳跃键//检测范围public float checkRadius;void Start(){rb = GetComponent<Rigidbody2D>(); // 获取刚体组件}void Update(){if (Input.GetButtonDown("Jump")){jumpPressed = true;}}private void FixedUpdate(){isGround = Physics2D.OverlapCircle(groundCheck.position, checkRadius, ground); // 检测是否在地面上GroundMovement(); // 地面移动Jump(); // 跳跃}//画出的射线可以看见private void OnDrawGizmosSelected(){if (groundCheck != null){Gizmos.color = Color.red;Gizmos.DrawWireSphere(groundCheck.position, checkRadius);}}void GroundMovement(){float horizontal = Input.GetAxisRaw("Horizontal"); // 获取水平方向输入rb.velocity = new Vector2(horizontal * speed, rb.velocity.y); // 设置刚体速度if (horizontal != 0){transform.localScale = new Vector3(horizontal, 1, 1); // 翻转角色}}void Jump(){if (jumpPressed && isGround){rb.velocity = new Vector2(rb.velocity.x, jumpForce); // 设置刚体速度jumpPressed = true;}}
}

挂载脚本,记得配置地面图层为Ground
在这里插入图片描述

效果
在这里插入图片描述

四、添加虚拟摄像机

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

五、触发器动态加载场景

新增其他房间场景
在这里插入图片描述
新增SceneLoadTrigger ,定义触发器检测异步加载和卸载不同场景

public class SceneLoadTrigger : MonoBehaviour
{[SerializeField, Header("需要加载的场景数组")] private SceneField[] _scenesToLoad;[SerializeField, Header("需要卸载的场景数组")] private SceneField[] _scenesToUnload;private GameObject _player; // 玩家对象private void Awake(){_player = GameObject.FindGameObjectWithTag("Player"); // 查找并赋值玩家对象}private void OnTriggerEnter2D(Collider2D collision){if (collision.gameObject == _player) // 碰撞体为玩家时{LoadScenes(); UnloadScenes(); }}// 加载场景private void LoadScenes(){for (int i = 0; i < _scenesToLoad.Length; i++){bool isSceneLoaded = false;for (int j = 0; j < SceneManager.sceneCount; j++){Scene loadedScene = SceneManager.GetSceneAt(j);if (loadedScene.name == _scenesToLoad[i].SceneName){isSceneLoaded = true;break;}}if (!isSceneLoaded){SceneManager.LoadSceneAsync(_scenesToLoad[i].SceneName, LoadSceneMode.Additive); // 异步加载需要加载的场景}}}// 卸载场景private void UnloadScenes(){for (int i = 0; i < _scenesToUnload.Length; i++){for (int j = 0; j < SceneManager.sceneCount; j++){Scene loadedScene = SceneManager.GetSceneAt(j);if (loadedScene.name == _scenesToUnload[i].SceneName){SceneManager.UnloadSceneAsync(_scenesToUnload[i].SceneName); // 异步卸载需要卸载的场景}}}}
}

挂载脚本,配置参数,记得配置角色标签为Player,地面我设置了不同颜色,好做区分
在这里插入图片描述
运行效果
在这里插入图片描述

六、最终效果

可以看到,只要配置得当就可以做到场景地图无缝加载,即使是很大的地图,我们也可以把他分割成几个小场景,不影响游戏运行性能
在这里插入图片描述

参考

【视频】https://www.youtube.com/watch?v=6-0zD9Xyu5c

源码

源码整理好了我会放上来,催更加急

完结

赠人玫瑰,手有余香!如果文章内容对你有所帮助,请不要吝啬你的点赞评论和关注,以便我第一时间收到反馈,你的每一次支持都是我不断创作的最大动力。当然如果你发现了文章中存在错误或者有更好的解决方法,也欢迎评论私信告诉我哦!

好了,我是向宇,https://xiangyu.blog.csdn.net

一位在小公司默默奋斗的开发者,出于兴趣爱好,于是最近才开始自习unity。如果你遇到任何问题,也欢迎你评论私信找我, 虽然有些问题我可能也不一定会,但是我会查阅各方资料,争取给出最好的建议,希望可以帮助更多想学编程的人,共勉~
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/216772.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

力控软件与多台PLC之间ModbusTCP/IP无线通信

Modbus TCP/IP 是对成熟的 Modbus 协议的改编&#xff0c; 因其开放性、简单性和广泛接受性而在工业自动化系统中发挥着举足轻重的作用。它作为连接各种工业设备的通用通信协议&#xff0c;包括可编程逻辑控制器 (PLC)、远程终端单元 (RTU) 和传感器。它提供标准化的 TCP 接口&…

仿 美团 / 饿了么,店铺详情页功能

前言 UI有所不同&#xff0c;但功能差不多&#xff0c;商品添加购物车功能 正在写&#xff0c;写完会提交仓库。 效果图一&#xff1a;左右RecyclerView 联动 效果图二&#xff1a;通过点击 向上偏移至最大值 效果图三&#xff1a;通过点击 或 拖动 展开收缩公告&#xff08;可…

mysql 变量和配置详解

MySQL 中还有一些特殊的全局变量&#xff0c;如 log_bin、tmpdir、version、datadir&#xff0c;在 MySQL 服务实例运行期间它们的值不能动态修改&#xff0c;也就是不能使用 SET 命令进行重新设置&#xff0c;这种变量称为静态变量。数据库管理员可以使用前面提到的修改源代码…

【Ambari】HDFS基于Ambari的常规运维

&#x1f984; 个人主页——&#x1f390;开着拖拉机回家_大数据运维-CSDN博客 &#x1f390;✨&#x1f341; &#x1fa81;&#x1f341;&#x1fa81;&#x1f341;&#x1fa81;&#x1f341;&#x1fa81;&#x1f341; &#x1fa81;&#x1f341;&#x1fa81;&#x1f…

BART - 磁共振重建库 linux系统安装 MATLAB 使用

本文主要介绍如何在linux系统中安装伯克利大学的磁共振重建库BART 和在matlab中的配置使用。 安装必要的库 (linux 命令行) $ sudo apt-get install make gcc libfftw3-dev liblapacke-dev libpng-dev libopenblas-dev 下载编译BART 文件 (官网链接:BART Toolbox) 命令行下…

京东数据采集接口推荐(京东大数据分析工具)

随着京东电商平台的不断发展&#xff0c;平台中店铺数量也越来越多&#xff0c;对于电商卖家而言&#xff0c;在电商运营过程中如何做好数据分析也越来越重要。而电商运营数据往往多而杂&#xff0c;想要高效的完成电商数据分析&#xff0c;品牌需要借助一些电商数据分析软件。…

RPC和HTTP的区别

目录 1、RPC是什么 1.1 概念 1.2 RPC的组成部分 1.3 常见的 RPC 技术和框架 1.4 RPC的工作流程 2、HTTP是什么 2.1 概念 2.2 HTTP的消息格式 2.3 HTTP响应状态码有哪些 3、⭐RPC和HTTP的区别 小结 1、RPC是什么 1.1 概念 RPC&#xff08;Remote Procedure Call&am…

002、ArkTS

之——开发语言 杂谈 基础编程语言ArkTS。引用来自华为开发者课堂。 ArkTS是HarmonyOS优选的主力应用开发语言。它在TypeScript&#xff08;简称TS&#xff09;的基础上&#xff0c;匹配ArkUI框架&#xff0c;扩展了声明式UI、状态管理等相应的能力&#xff0c;让开发者以更简洁…

武汉教育E卡通学生证照片尺寸要求及证件照集中采集方法

”武汉教育E卡通“电子学生证旨在数字化中小学生身份&#xff0c;提供通用的教育卡&#xff0c;实现身份认证的电子化、权威化和集成化。校内一卡通系统包括刷卡考勤、电子班牌、图书借阅等&#xff0c;全面记录学生在校业务。同时&#xff0c;采集社会通行、实践活动等数据&am…

基于C#实现三元组

我们知道矩阵是一个非常强大的数据结构&#xff0c;在动态规划以及各种图论算法上都有广泛的应用&#xff0c;当然矩阵有着不足的地方就是空间和时间复杂度都维持在 N2 上&#xff0c;比如 1w 个数字建立一个矩阵&#xff0c;在内存中会占用 1w*1w1 亿的类型空间&#xff0c;这…

Leetcode—1457.二叉树中的伪回文路径【中等】

2023每日刷题&#xff08;四十&#xff09; Leetcode—1457.二叉树中的伪回文路径 实现代码 /*** Definition for a binary tree node.* struct TreeNode {* int val;* struct TreeNode *left;* struct TreeNode *right;* };*/ int record[10] {0};int accumula…

【Spring集成MyBatis】MyBatis的多表查询

文章目录 1. 一对一什么是一对一User、Order类及Mapper&#xff0c;User、Order表一对一操作的实现一对一操作实现的第二种方式 2. 一对多什么是一对多一对多操作实现 3. 多对多什么是多对多多对多的实现 4. 小结 1. 一对一 什么是一对一 一对一指的是表与表之间通过外键进行…