Article directory
final effect
Preface
After watching this article, I hope you have a better understanding of unity scene management and can make games with 巨大世界
and 无缝加载游戏
Partially running in the background, this approach is very flexible and easy to implement, so whether you're making a small platformer or a large open world game, it should work for both, so be prepared let's start! 不中断游戏玩法
1. Draw different scenes
Create different scenarios
Start menu interface scene Menu
Main scene Main, simply place a protagonist character
Room 1 scene Room1, simply place a platform, remember to remove it Camera
2. Switch scene loading progress
1. Simple implementation
Added MainMenuManager code to implement loading progress, scene switching and loading
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; // 等待下一帧
}
}
}
}
Mount script, configuration parameters
Configure start game click event
Forgot to add new scene in project settings
Effect
2. Optimization
Normally we track our scenes by scene name or index? There is actually a better method here, and we can use it in all projects later.
Inspired by a Unity forum SceneField code:
https://discussions.unity.com/t/inspector-field-for-scene-asset/40763 a>
Explanation: This is the code. By using the SceneField class and SceneFieldPropertyDrawer property painter, developers can easily reference and manage scene objects in custom scripts, and edit and select in the Inspector panel. This is very useful for situations where you need to switch scenes frequently or deal with multiple scenes.
I have copied the code and added some comments as follows
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
Modify MainMenuManager
[SerializeField, Header("主要持久化场景")] private SceneField _persistentGameplay;
[SerializeField, Header("游戏关卡场景")] private SceneField _levelScene;
Configuration, the scene can be bound by dragging in
The effect is still the same as before, no problem
3. Character Movement and Jump Control
Simply implement character control
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;
}
}
}
Mount the script, remember to configure the ground layer as Ground
Effect
4. Add virtual camera
Effect
5. Trigger dynamic loading scene
Add other room scenes
Add SceneLoadTrigger to define triggers to detect asynchronous loading and unloading of different scenes
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); // 异步卸载需要卸载的场景
}
}
}
}
}
Mount the script and configure parameters. Remember to configure the role label as Player. I have set different colors for the ground to make it easier to distinguish
Running effect
6. Final effect
It can be seen that as long as the configuration is appropriate, the scene map can be loaded seamlessly. Even if it is a large map, we can divide it into several small scenes without affecting the game running performance.
reference
【Video】https://www.youtube.com/watch?v=6-0zD9Xyu5c
Source code
https://gitcode.net/unity1/unity-bigscene
end
Giving roses to others will leave a lingering fragrance in your hands! If the content of the article is helpful to you, please don't be stingy with your 点赞评论和关注
so that I can receive feedback as soon as possible. Every time you 支持
The greatest motivation for creation. Of course, if you find 存在错误
or 更好的解决方法
in the article, you are welcome to comment and send me a private message!
Good, I am向宇
,https://xiangyu.blog.csdn.net
A developer who has been working quietly in a small company recently started studying Unity by himself out of interest. If you encounter any problems, you are also welcome to comment and send me a private message. Although I may not necessarily know some of the questions, I will check the information from all parties and try to give the best suggestions. I hope to help more people who want to learn programming. People, encourage each other~