Unity实用框架(一)场景管理框架

这篇具有很好参考价值的文章主要介绍了Unity实用框架(一)场景管理框架。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

Unity实用框架(一)场景管理框架

众所周知,Unity引擎本身提供了具有切换场景功能的SceneManager模块,但只包含比较基础的功能,比如简单的切换场景、创建场景等,想要使得我们的场景管理框架能够适用于更加复杂的情形,显然需要一套更加强大的接口。下面,笔者将提供一种撰写灵活好用的SceneManager的可行思路。

框架思路

IScene/ISceneManager

首先,为了践行面向接口编程的思想,我们需要理解我们想要达成怎样的目标,并以接口的形式将其展现出来。定义Iscene与ISceneManager以抽象场景对象和场景管理组件对象。

定义这两个接口后,在之后的实际实现中,就可以分别使新的类型实现IScene接口称为一个可加载的场景,或实现ISceneManager使得类型拥有以某种逻辑完成场景切换功能的工具(如切换UI场景的UISceneManager或切换战斗场景的BattleSceneManager)。

IScene

一个典型的场景能够定义为一组状态机,在这里,为场景定义如下状态:

可以看到,这里将场景分为三条线,五个状态,还是比较简单的。这五个状态基本上能够满足场景管理的需求。对应上面每一个状态的,是一个函数,该函数执行状态切换时应当完成的操作。当然,由于load状态要负责场景资源的加载,我们自然而然地将其返回值定义为IEnumerator以应用协程。

IEnumerator Load(object savedState);

此外,在接口中定义两个属性,一个为name(string)以标识场景,另一个为ISceneManager接口,此成员应当作为当前SceneManager单例的引用,用于完成Scene与SceneManager间的交互。

IScene的完整定义如下:

public interface IScene
{
    string Name { get; }
    ISceneManager SceneManager { get; set; }
    IEnumerator Load(object savedState);
    void Begin();
    void Resume();
    void Pause();
    void Finish();
}
ISceneManager

在这里,我们在SceneManager使用栈结构来储存场景。当然这并非编写场景管理框架的唯一解决方案,但使用栈自然有它的好处所在。进入下一个页面/回到上一个页面是场景切换中经常出现的模式,使用栈结构能很好的适应这种情况。

既然是使用栈,那么ISceneManager自然就应当定义Push和Pop两种状态,当然,为了更加灵活地处理场景切换,添加replace状态,该状态相当于pop和push的组合,但省去了中间状态。

同时,对于场景切换的中间态,我们单独用一个类来定义它:

public class Transition

这个“中间态”类将极大地扩展我们的SceneManager的可用性,它包含了前一个场景、后一个场景,以及场景切换的动态效果,此类并非实际实现,想要实现类似渐入渐出等效果,应当撰写此类的子类。

public enum TransitionType
{
    Push,
    Pop,
    Replace
}
public class Transition
	{
		public TransitionType TransitionType { get; set; } //转换类型
		public IScene NextScene { get; set; } //下一个场景
		public Type NextSceneType { get; set; } //下一个场景的类型
		public object SavedState { get; set; } //保存的场景数据
		public event Action TransitionEnded; // 回调事件
		public event Action TransitionFadeOuted; // 回调事件
		internal void RaiseTransitionEnded()
		{
			TransitionEnded?.Invoke();
		}
		internal void RaiseTransitionFadeOuted()
		{
			TransitionFadeOuted?.Invoke();
		}
	}

最后,在ISceneManager接口中,包含了当前正在运行的场景、当前指定的Transition,切换场景的方法和回调事件。

public interface ISceneManager
{
    IScene CurrentScene { get; }
    Transition CurrentTransition { get; }
    event Action<Transition> TransitionStarted;
    event Action<Transition> TransitionEnded;
    void SetTransition(Transition transition);//可理解为原生SceneManager的loadscene函数。
}

UISceneManager

现在,我们来完成IsceneManager的一个实际实现。不同种类的场景可能需要应用不同的SceneManager(都实现ISceneManager接口),这里以UI为例。

首先,是UI界面需要的一些组件:

private UIRoot uiRoot; //NGUI相关,如果使用UGUI或者其他GUI控件就不需要它
private UICamera uiCamera; //拍摄UI界面的摄像机
private LayerMask eventReceiverMask = -1; //层级遮罩

然后,需要一个栈储存当前加载了的所有场景

private List<IUIOverlay> stackedOverlayList = new List<IUIOverlay>();
private List<KeyValuePair<IScene, object>> stackedScenes = new List<KeyValuePair<IScene, object>>(); 

其中,上面的IUIOverlay接口定义了当前UI界面上的其他UI,或者说画中画,IUIOverlay本身不在本文的讨论之列。列在这里的目的在于,读者可以清楚如果想用SceneManager实现画中画、弹窗等效果,可以让弹窗、画中画的控制脚本实现此接口,以方便在SceneManager中统一管理。

当然,我们的重点在于SetTransition函数实现。

public void SetTransition(Transition transition)
{
    if (CurrentTransition != null)
    {
        return;
    }

    var t = transition as UITransition; //UITransition是Transition的子类
    if (t.Animation == TransitionAnimation.None)
    {
        CoroutineManager.Instance.StartCoroutine(UIRoot, SetTransitionAsync(transition));
    }
    else if (t.Animation == TransitionAnimation.Fade)
    {
        CoroutineManager.Instance.StartCoroutine(UIRoot, SetFadeTransitionAsync(transition));
    }
    else
    {
        CoroutineManager.Instance.StartCoroutine(UIRoot, SetSlideTransitionAsync(transition));
    }
}

不同的场景转换效果(None无效果,Fade渐入渐出,Slide幻灯片式滑出,这里假设只有这三种方式)需要不同的转换函数,因此转交给不同的函数进行调用。注意场景转换这种开销明显较大的操作应当应用协程。这里的CoroutineManager也并非原生的协程管理器,在其它文章中,笔者将会介绍一个优雅的CoroutineManager将如何设计。

场景转换时要做些什么呢?首先,需要判断当前的转换方式时Push还是Pop,以此判断是从stackedScene中取出场景还是压入场景。

Pop

对于pop,所要做的就是弹出当前场景,并从存于栈中的场景中取出一个并加载。

if (transition.TransitionType == TransitionType.Pop)
{
    if (stackedScenes.Count > 0)
    {
        var kv = stackedScenes[stackedScenes.Count - 1];
        transition.NextScene = kv.Key;
        transition.SavedState = kv.Value;
        stackedScenes.RemoveAt(stackedScenes.Count - 1);
    }
}
CurrentTransition = transition;
//场景类和其数据类共同组成了transition
var nextScene = transition.NextScene;
var savedState = transition.SavedState;
//别忘了在Iscene中定义的状态!在取出栈中场景后,销毁当前场景前,先调用pause
currentScene?.Pause();
Push

对于push,要将当前场景和需要保存的场景数据压入栈。

if (transition.TransitionType == TransitionType.Push && currentScene != null)
{
    stackedScenes.Add(new KeyValuePair<IScene, object>(currentScene, nullSavedState));
}
currentScene?.Pause();

压入栈中时,随场景一起压入的是一个空数据,此数据将在场景建立完成后再被填充。由于一般来说,我们需要保存场景在被销毁前的最后一个状态,因此,最合理的方式应该是在IScene.Finish()函数中将数据存入(还记得上面的IScene状态机吗),关于数据的形式不会再本篇文章中阐述。显然,对于每个场景,需要保存的数据种类相差甚远,因此对于每个继承自IScene的场景类,编写一个对于该场景的data类是合理的。比如,对于游戏大厅场景LobbyScene :IScene,写一个LobbySceneData来存储它的数据。这个类就是IScene在加载场景时,load函数所获取的参数!

IEnumerator Load(object savedState);

在导入新场景之后,就可以调用当前场景的finish函数来销毁它,并且加载现在的场景了。编写符合需求的函数来完成场景转换的动画,比如用遮罩来实现渐入渐出、或者canvas上的"loading"字样,不在这里赘述。

//统一场景转换动画?

currentScene?.Finish();

//统一场景转换动画?

currentScene = nextScene;
StartCoroutine(currentScene.Load(currentScene.data));

//...
currentScene?.Begin();
//..
currentScene?.Resume();
//..

综上,我们就实现了一个最简单的场景管理器!以LobbyScene为例,这一套场景管理的关系如下所示

更新时间:2022.7.16文章来源地址https://www.toymoban.com/news/detail-408785.html

到了这里,关于Unity实用框架(一)场景管理框架的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Unity 场景管理

    在Unity游戏开发中,场景管理是一个关键的任务,涉及到场景的序列化、AssetBundle加载、ROI(region of interest) 感兴趣区)和LOD(Level of Detail)管理。本篇博客将讨论这些概念,并探索如何通过它们来实现高效的场景管理。 场景序列化与AssetBundle加载: 在游戏开始阶段,我们可以

    2024年02月08日
    浏览(22)
  • Unity 3D开发--SceneManager场景管理(异步使用同一个过渡场景)

    在U3D开发过程中经常使用到多场景的切换,有同步SceneManager.LoadScene()和异步SceneManager.LoadSceneAsync()两种方法,同步的话一般就会卡住界面直到加载完成,使用异步的话一般都做一个加载的进度条,每次切换的时候都需要一个加载动画,所以需要建一个专门的过渡加载场景来进

    2024年02月14日
    浏览(44)
  • GameFrameWork框架(Unity3D)使用笔记(八) 实现场景加载进度条

            游戏在转换场景的时候,需要花费时间来加载相关的资源。而这个过程往往因为游戏场景的规模和复杂度以及玩家电脑配置的原因花费一小段时间(虽然这个项目里用不到)。         所以,如果这一小段时间,画面就卡在这里,啥也做不了,玩家也不知道啥时候能

    2024年02月02日
    浏览(49)
  • 项目实训--Unity多人游戏开发(十一、PUN框架--游戏场景篇(加载与同步))

    本地加载 有许多方法,举几个例子: 本地加载 本地异步加载(场景内容过多时,防止卡顿异步加载) 多人游戏加载场景 pun2插件加载 这个异步加载起初是觉得场景内容如果过多会引起卡顿。所以把这个应用在了单机模式上。 至于多人游戏加载场景,可能用不到而且也没法

    2024年02月01日
    浏览(63)
  • Unity框架-Mono管理器

    一、前言 在Unity开发中,经常会遇到需要在没有继承 MonoBehaviour 的类中使用协程和Update的情况。为了解决这个问题,我们可以使用Mono管理器,该管理器允许非 MonoBehaviour 类使用一些常用的Mono方法。在本篇文章中,我们将深入探讨Mono管理器的实现以及如何使用它进行协程管理

    2024年01月20日
    浏览(38)
  • Unity框架学习--资源管理器

    1、Inspector窗口拖拽         在脚本中用public声明变量,然后在Inspector窗口把要加载的资源拖拽给该脚本的变量。         不建议在大型项目使用。在公司的项目也不要用。         如果你是独立游戏开发者,则可以用。         不支持热更新。 2、Resources         用Reso

    2024年02月12日
    浏览(47)
  • Unity 3D 开发--UI管理框架

    一、UI基类 一般情况下都是用Panel做容器来放各种控件的,一个Panle相当一个UI小界面,然后做成Prefab进行加载,所有界面都有载入载出功能,有的可能还有等待和恢复的,适合建立一个UI基类,然后各个子界面继承。 二、UI子类 每个UI子界面都继承基类,然后实现各个方法,

    2024年02月13日
    浏览(46)
  • Unity框架学习--4 Mono管理器

    作用 :使不继承MonoBehaviour的类能够开启协程,并且可以使用FixedUpdate、Update、LateUpdate进行每帧更新。 原理: 1、在场景中创建一个继承MonoBehaviour的“执行者”脚本,这个脚本就专门用来开启协程和监听帧更新。 2、Mono管理器访问这个“执行者”脚本,就可以实现所需的效果

    2024年02月13日
    浏览(38)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包