General purpose level manager
- Support single and additive loading
- Custom level data to be passed to the loaded level
- Custom level selection UI
OnLevelLoadedandOnAllLevelPassedevent- All the related scenes sould be "addressable"
- Defined in the namespace
LevelManagement
- Package "Addressables"
The example project is in the "Example" directory. The entry scene is the "LevelManager". Make sure that the package "Addressables" is installed and the scenes in the "Scenes" directory are all marked as addressable.
The simliar scripts mentioned in the "Setups" section could be found in the example project.
LevelDataManager is the class for providing the data to the LevelManager. It should be derived from the class AbstractLevelDataManager (AbstractLevelDataManager is derived from MonoBehaviour). Here is the simplest LevelDataManager.
using LevelManagement;
using System;
using UnityEngine;
public class LevelDataManager : AbstractLevelDataManager
{
[SerializedField]
private LevelDataItem[] _levels = null;
// The total number of levels
public override int Length => _levels.Length;
// Get the scene asset of the target level id
// The scene asset should be addressable
public override AssetReference GetLevelScene(int id)
{
return _levels[id].levelScene;
}
// Get the level data for the loaded scene
// It could be got from the `LevelManager.Instance.GetCurrentLevelData()` or
// be automatically passed from the event `LevelManager.Instance.OnLevelLoaded`.
public override object GetLevelData(int id)
{
return _levels[id].levelData;
}
// Get the preview data for creating the level selection item
// It will be passed to the `AbstractLevelSelectionItem.SetPreview()`,
// so you can set up the appearance of item according to the data passed here.
//
// The preview data could be stored in a seperated file to represent the user
// score, grade, or lock status. Here is just returning the name of the level.
public override object GetPreviewData(int id)
{
return $"Level {id}";
}
}
[Serializable]
public class LevelDataItem
{
public AssetReference levelScene;
public LevelData levelData;
}
[Serializable]
public class LevelData
{
public Vector3 spawnPoint;
public float timeLimitation;
}- Select the scene used for the level selection
- Mark the scene as addressable
- Create an empty gameobject to the scene and attach
LevelManagerandLevelDataManagerto it - Assign the reference of
LevelDataManagerto the "Level Data Manager" field of theLevelManagerin the inspector - Assign the current scene to the "Home Scene" field of the
LevelManagerin the inspector - Set up the loading mode and whether to loop the levels or not
- If the loading mode is
Additive, assign the scene asset which will keep existing through levels to "Top Scene" field of theLevelManagerin the inspector.
- Create an empty gameobject under the canvas component to the scene where the
LevelManagerat - Create a scroll view as the child of created gameobject and adjust it to fit your needs
- Attach
LevelSelectionUIto the gameobject created in the step 1 - Assign the "Content" gameobject under the scroll view gameobject to the "Content Container" field of
LevelSelectionUIin the inspector
LevelSelectionItem is the class for setting up the level selection item in the level selection UI, such as setting up the appearance of the item and binding the callback to the LevelManager. Here is the simplest LevelSelectionItem:
using LevelManagement;
using UnityEngine;
using UnityEngine.UI;
public class LevelSelectionItem : AbstractLevelSelectionItem
{
[SerializeField]
private Button _button = null;
[SerializeField]
private Text _levelText = null;
// Set up the appearance of the level selection item
// The first argument is the ID of the specified level. This value should
// be used for loading the level.
// The second argument is the preview data provided from the
// `LevelDataManager.GetPreviewData()`. You can set up the appearance of
// the item according to the data you provided.
public override void SetPreview(int levelID, object previewData)
{
_levelText.text = (string) previewData;
_button.onClick.AddListener(
() => LevelManager.Instance.LoadLevel(levelID)
);
}
}- Create a button gameobject under the "Content" gameobject of the scroll view created before
- Attach
LevelSelectionItemto it, assign the necessary fields, and adjust the gameobject to fit your needs - Create a prefab from this gameobject and delete the gameobject
- Assign the created prefab to the "Level Item Prefab" of the
LevelSelectionUIin the inspector - Set up the properties of
LevelSelectionUI- Items A Row: Number of level items in a row
- Border Item Gap: The gap between the position of the edge items and the border
- Item Gap: The distance between each item
The gameobject of LevelManager will keep existing through scenes after first created. You can access the instance of the LevelManager to perform level operations by LevelManager.Instance. Here are the functions:
GetCurLevelData(): Get the level data of the current loaded level. The data is provided from theLevelDataManager.GetLevelData()
using LevelManagement;
public class LevelInitializer
{
[SerializeField]
private Transform _playerTransform = null;
private void Start()
{
var data = (LevelData) LevelManager.Instance.GetCurLevelData();
_playerTransform.position = data.spawnPoint;
}
}LoadLevel(levelID): Load the target levelNextLevel(): Load the next level. If "Loop Level" property ofLevelManageris false, invokingNextLevel()will not load new level when the current level is the last level. Otherwise, the first level will be loaded. No matter the value of "Loop Level", theOnAllLevelsPassedevent will be fired when invoking this function in the last level.ReloadLevel(): Reload the current levelLoadHomeScene(): Load the scene specified in the "Home Scene" field of theLevelManagerin the inspector
OnLevelLoaded(levelID, levelData): This event is fired when a level has been loaded. It is useful in the top scene when the loading mode is additive to know that a level is loaded and then initialize or reset objects.
using LevelManagement;
public class TopSceneInitializer
{
[SerializeField]
private Transform _playerTransform = null;
private void Start()
{
LevelManager.Instance.OnLevelLoaded += OnLevelLoaded;
}
// Don't forget to unregister the callback when the object is destroyed
private void OnDestroy()
{
LevelManager.Instance.OnLevelLoaded -= OnLevelLoaded;
}
private void OnLevelLoaded(int levelID, object levelData)
{
var data = (LevelData) levelData;
_playerTransform.position = data.spawnPoint;
}
}OnAllLevelsPassed(): This event is fired when trying to load the next level in the last level.