跨场景注入数据(Injecting data across scenes)
在某些情况下,我们需要将参数从一个场景传递到另一个场景。 Unity提供的方式相当尴尬。 您可以选择创建持久性的GameObject并调用DontDestroyOnLoad()以在更改场景时保持游戏对象不被销毁,或使用全局静态类临时存储数据。
假设你想给下个场景传递一个关卡名称的字符串,定义一个需要输入的类,如下:
public class LevelHandler : IInitializable
{
readonly string _startLevel;
public LevelHandler(
[InjectOptional]
string startLevel)
{
if (startLevel == null)
{
_startLevel = "default_level";
}
else
{
_startLevel = startLevel;
}
}
public void Initialize()
{
...
[Load level]
...
}
}
你可以加载包含LevelHandler
的场景然后使用下面的语法指定特定的关卡
public class Foo
{
readonly ZenjectSceneLoader _sceneLoader;
public Foo(ZenjectSceneLoader sceneLoader)
{
_sceneLoader = sceneLoader;
}
public void AdvanceScene()
{
_sceneLoader.LoadScene("NameOfSceneToLoad", LoadSceneMode.Single, (container) =>
{
container.BindInstance("custom_level").WhenInjectedInto<LevelHandler>();
});
}
}
我们在lambda表达式中添加绑定和在新场景中的安装器上添加绑定是一样的
注意,您可以直接运行场景,这种情况下它将使用默认的“defult_level”,这不会出错因为我们使用了InjectOptional
标签
另一种可以说是更简洁的方法是自定义安装器而不是LevelHandler类。 在这种情况下,我们可以像这样编写我们的LevelHandler类(没有[InjectOptional]标志)。
public class LevelHandler : IInitializable
{
readonly string _startLevel;
public LevelHandler(string startLevel)
{
_startLevel = startLevel;
}
public void Initialize()
{
...
[Load level]
...
}
}
然后,在场景的安装器上添加以下代码:
public class GameInstaller : Installer
{
[InjectOptional]
public string LevelName = "default_level";
...
public override void InstallBindings()
{
...
Container.BindInstance(LevelName).WhenInjectedInto<LevelHandler>();
...
}
}
然后,我们注入安装器而不是直接注入Levelhandler
类中
public class Foo
{
readonly ZenjectSceneLoader _sceneLoader;
public Foo(ZenjectSceneLoader sceneLoader)
{
_sceneLoader = sceneLoader;
}
public void AdvanceScene()
{
_sceneLoader.LoadScene("NameOfSceneToLoad", (container) =>
{
container.BindInstance("custom_level").WhenInjectedInto<GameInstaller>();
});
}
}
ZenjectSceneLoader类还允许更复杂的场景,例如将新场景加载为当前场景的子场景,这将导致新场景继承当前场景中的所有依赖项。 但是,通常更好的方式是使用场景合同名称。
使用合同名称实现父子场景(Scene Parenting Using Contract Names)
将绑定放在项目上下文中是一种添加跨场景的共享依赖项的简单快捷方式,但是在某些情况下,你可能只想在指定的场景中共享依赖项,这时使用项目上下文并不是好的方式,因为在项目上下文中添加的绑定是全局的,项目中所有的场景都能共享。
假设我们要开发一款太空飞船游戏,想要创建一个场景作为环境(包括行星、小行星、恒星等等),然后创建另一个场景包含代表玩家的飞船,我们还希望飞船场景中的所有类能够引用环境场景中声明的绑定。 此外,我们希望能够定义飞船场景和环境场景的多个不同版本。 为了实现这一切,我们将使用Zenject中的“场景合同名称”的功能。
我们将首先使用Unity对多场景编辑的支持,并将我们的环境场景和飞船场景拖动到场景的Heirarchy面板中。 然后在环境场景中选择SceneContext并添加“Contract Name”(合同名称)。 命名为'Environment'。 然后我们只需要在飞船场景中选中SceneContext并将其“Parent Contract Name”设置为相同的值('Environment')。 现在,如果我们按下播放,则飞船场景中的所有类都可以访问环境场景中声明的绑定。
我们在这里使用名称字段而不是显式使用场景名称的原因是以便支持交换不同实现的各种环境场景。 在这个例子中,我们可以定义几个不同的环境,所有环境都使用相同的合同名称“Environment”,这样我们就可以轻松地将它们与不同的飞船场景进行混合和匹配,我们只需将想要的场景拖到场景面板中,然后再运行游戏。
之所以称为“合同名称”是因为所有环境场景都应该遵循飞船场景的某个“合同”(contract)。 例如,飞船场景可能要求无论加载哪个环境场景,都必须存在“AsteroidManager”的绑定,其中包含船舶必须避开的小行星列表。
请注意,您无需同时加载环境场景和飞船场景。 例如,您可能希望在环境中嵌入菜单以便用户在开始之前选择他们的飞船。 因此,您可以创建一个菜单场景并在环境场景之后加载它。 然后,用户选定了飞船之后,您可以通过调用unity的SceneManager.LoadScene方法(确保使用LoadSceneMode.Additive)来加载相关的飞船场景。
另请注意,Validate命令可用于快速验证不同的多场景设置。 如果您在执行此操作时发现场景已卸载,请参阅此处。
此外,我应该提到Unity目前没有内置的方法来保存和恢复多场景设置。 我们使用一个简单的编辑器脚本,如果感兴趣,可以在这里找到。
网友评论