UnityScreenNavigator中文文档
UnityScreenNavigator中文文档
misaka10843此文档内容为 OpenAI GPT 翻译,不保证其准确性
Unity Screen Navigator
Unity Screen Navigator 是一个用于在 Unity 的 uGUI 中进行屏幕过渡、过渡动画、过渡历史堆栈以及屏幕生命周期管理的库。概述
功能
- 您可以轻松灵活地创建页面、模态框、页签及其过渡效果。
- 从加载到销毁,管理屏幕的生命周期和内存。
- 使用动画器实现复杂屏幕过渡动画的分离工作流程。
- 精心分离的库,没有额外的功能(例如 GUI 库、状态机)。
- 还包括历史堆栈和过渡期间的点击防护等标准功能。
演示
您可以按照以下步骤播放演示场景。
- 克隆此存储库。
- 打开并播放以下场景。
请注意,此演示中使用的某些图像来自以下免费内容。有关更多信息,包括版权信息,请参阅以下网站。
设置
系统需求
- Unity 2019.4 或更高版本
- uGUI (不支持 UIElements)
安装
- 从窗口(Window)菜单中打开包管理器(Package Manager)
- 点击”+”按钮 > 从 git URL 添加包
- 输入以下内容进行安装
或者,打开 Packages/manifest.json 文件,并在 dependencies 块中添加以下内容。
{ |
如果您想设置目标版本,请按照以下方式进行指定。
基本屏幕过渡
屏幕和过渡的概念
Unity Screen Navigator 将屏幕分为三种类型:「页面」(Page)、「模态」(Modal)和「表格」(Sheet)。
「页面」是按顺序过渡的屏幕。
例如,当从页面 A 过渡到页面 B 时,页面 A 将被堆叠在历史记录中。
当从页面 B 返回时,页面 A 将以其状态完好的形式重新显示。
「模态」是以窗口形式堆叠的屏幕。
当显示模态屏幕时,除前景模态屏幕外,所有交互都将被阻止。
而「表格」用于类似选项卡的 GUI。
不管理历史记录,只显示一个活动屏幕。
这些屏幕可以嵌套。
而且,可以自由指定每个屏幕的区域(不一定是整个窗口)。
创建页面和过渡
要创建页面过渡,请首先将「页面容器」(Page Container)组件附加到 Canvas 下的一个 GameObject 上。
页面将被显示到适应它的大小,因此请调整大小。
然后,将 Page
组件附加到页面视图的根 GameObject 上。
将此 GameObject 放置在 Resources 文件夹下,使用任意名称。
调用 PageContainer.Push()
并传入资源路径以显示页面。
以下是一个示例,将页面推送到 Assets/Resources/ExamplePage.prefab
中的页面:
PageContainer pageContainer; |
还有,使用 PageContainer.Pop()
来取消当前页面并显示前一个页面。
PageContainer pageContainer; |
如果你想在调用 Pop()
时跳过某个特定页面,你可以通过使用可选参数来禁用页面堆叠历史。
创建模态框和过渡
要创建模态框过渡效果,首先在 Canvas 下的一个游戏对象上附加“Modal Container”组件。
通常,模态框被设计为使用其背景覆盖整个窗口并阻止点击。
因此,游戏对象的 RectTransform 的大小应该基本上设置为与窗口大小相匹配。
接下来,将“Modal”组件附加到模态框视图的根游戏对象上。
这个根游戏对象会调整到适应“Modal Container”的大小。
所以,如果你想要创建带有边距的模态框,可以创建一个较小大小的子游戏对象,并在其中创建内容。
将此游戏对象放在任意命名的资源文件夹下。
并使用资源路径调用ModalContainer.Push()
来显示页面。
以下是将模态框放置在Assets/Resources/ExampleModal.prefab
中进行推送的示例代码。
ModalContainer modalContainer; |
另外,使用ModalContainer.Pop()
来取消当前模态框并显示上一个模态框。
ModalContainer modalContainer; |
请注意,你可以根据需要更改模态框的背景。
创建表和过渡
要创建表过渡效果,首先在 Canvas 下的一个游戏对象上附加“Sheet Container”组件。
表将显示在其中,所以请调整大小。
接下来,将“Sheet”组件附加到表视图的根游戏对象上。
将此游戏对象放在任意命名的资源文件夹下。
使用资源路径调用SheetContainer.Register()
来创建表。
创建后,可以通过调用SheetContainer.Show()
来更改活动的表。
此时,如果已经存在活动的表,它将被停用。
以下是显示放置在Assets/Resources/ExampleSheet.prefab
中的表的示例代码。
SheetContainer sheetContainer; |
请注意,当使用Register()
方法实例化具有相同资源键的多个表时,无法通过资源键来保证表实例的唯一性。
在这种情况下,可以使用表 ID 而不是资源键,如下所示。
SheetContainer sheetContainer; |
另外,如果想隐藏活动的表而不是切换表,请使用Hide()
方法。
SheetContainer sheetContainer; |
如何等待过渡完成
每个过渡方法都会返回AsyncProcessHandle
作为返回值。
使用这个对象,你可以等待过渡过程完成。
你可以使用协程、异步方法和回调来实现。
要在协程中等待,使用yield return
,如下所示。
yield return pageContainer.Push("ExamplePage", true); |
要在异步方法中等待,像下面这样使用await
等待AsyncProcessHandle.Task
。
await pageContainer.Push("ExamplePage", true).Task; |
如果你想使用回调函数,可以使用AsyncProcessHandle.OnTerminate
。
pageContainer.Push("ExamplePage", true).OnTerminate += () => { }; |
使用静态方法获取容器
每个容器(PageContainer
/ModalContainer
/SheetContainer
)都有用于获取实例的静态方法。
使用以下的Container.Of()
方法,你可以获取与给定的 Transform 或 RectTransform 关联的最近父级上的容器。
var pageContainer = PageContainer.Of(transform); |
此外,你还可以在容器的检视器中设置Name
属性,以便通过名称获取容器。
在这种情况下,使用Container.Find()
方法,如下所示。
var pageContainer = PageContainer.Find("SomePageContainer"); |
##屏幕过渡动画
设置通用的过渡动画
默认情况下,每种屏幕类型都设置了一个标准的过渡动画。
你可以创建一个继承自TransitionAnimationObject
的类来创建自定义的过渡动画。
这个类有一个属性和一些方法来定义动画的行为。
// 持续时间(秒)。 |
有关实际实现,请参考SimpleTransitionAnimationObject。
然后,实例化这个 Scriptable Object,并将其分配给UnityScreenNavigatorSettings
。
您可以通过Assets > Create > Screen Navigator Settings
创建UnityScreenNavigatorSettings
。
设置每个屏幕的过渡动画
您还可以为每个屏幕设置不同的动画。
每个页面(Page)、模态(Modal)和面板(Sheet)组件都有Animation Container
属性。
您可以将过渡动画设置给它。
您可以通过将Asset Type
设置为Scriptable Object
并将之前描述的TransitionAnimationObject
分配给Animation Object
,来更改此屏幕的过渡动画。
此外,您也可以使用 MonoBehaviour 代替 ScriptableObject。
在这种情况下,首先创建一个继承自TransitionAnimationBehaviour
的类。
有关实际实现,请参考SimpleTransitionAnimationBehaviour。
然后,附加此组件,并将Asset Type
设置为Mono Behaviour
,并将引用分配给Animation Behaviour
。
根据伙伴屏幕更改过渡动画
例如,当屏幕 A 进入并且屏幕 B 退出时,屏幕 B 被称为屏幕 A 的”伙伴屏幕”。
如果您在下图所示的属性中输入伙伴屏幕的名称,则只有当此名称与伙伴屏幕名称匹配时,过渡动画才会应用。
默认情况下,使用预制件的名称作为屏幕名称。
如果您想要显式命名,请取消选中”Use Prefab Name As Identifier”并在”Identifier”属性中输入一个名称。
此外,可以在”Partner Page Identifier Regex”属性中使用正则表达式。
如果设置了多个动画,则它们将按照从顶部开始的顺序进行评估。
屏幕过渡动画和绘制顺序
在具有伙伴屏幕的屏幕的过渡动画中,绘制顺序可能很重要。
例如,一种屏幕覆盖伙伴屏幕的动画。
如果您想要控制绘制顺序,请使用”Rendering Order”属性。
在屏幕过渡期间,屏幕将按照此值减少的顺序进行绘制。
请注意,模态窗口没有”Rendering Order”属性,因为最新的模态窗口始终会显示在前面。
简单创建过渡动画
您可以使用SimpleTransitionAnimationObject
作为简单的过渡动画实现工具。
可以通过Assets > Create > Screen Navigator > Simple Transition Animation
创建它。
然后,将生成类似下方所示的 ScriptableObject,您可以从检视器中设置动画。
您还可以使用SimpleTransitionAnimationBehaviour
将其作为这种实现的 MonoBehaviour。
通过直接将其附加到游戏对象上来使用它。
以下是各个属性的说明。
属性名 | 描述 |
---|---|
Delay | 动画开始之前的延迟时间(秒)。 |
Duration | 动画持续时间(秒)。 |
Ease Type | 缓动函数的类型。 |
Before Alignment | 过渡之前相对于容器的位置。 |
Before Scale | 过渡之前的缩放比例。 |
Before Alpha | 过渡之前的透明度。 |
After Alignment | 过渡之后相对于容器的位置。 |
After Scale | 过渡之后的缩放比例。 |
After Alpha | 过渡之后的透明度。 |
通过伙伴屏幕实现交互动画
您还可以创建引用伙伴屏幕状态的动画。
在以下示例中,前一个模态窗口的图像在平滑过渡到下一个模态窗口时被放大。
要实现这一点,首先创建一个从TransitionAnimationObject
或TransitionAnimationBehaviour
派生的类。
然后,通过参考PartnerRectTransform
属性来获取伙伴屏幕。
如果伙伴屏幕不存在,PartnerRectTransform
将为 null。
有关实际实现,请参考CharacterImageModalTransitionAnimation中的示例。
使用时间轴创建动画
您可以使用时间轴(Timeline)来创建过渡动画。
对于复杂的过渡动画,建议使用时间轴。
要实现这一点,首先将Timeline Transition Animation Behaviour
附加到一个 GameObject 上。
然后将Playable Director
和Timeline Asset
分配给属性。
Playable Director
的Play On Awake
属性需要取消选中。
最后,将此Timeline Transition Animation Behaviour
分配给Animation Container
。
此外,我推荐使用UnityUIPlayables来创建带有 uGUI 的动画。
## 生命周期事件页面的生命周期事件
通过在派生自 Page
类的子类中重写以下方法,您可以编写与页面生命周期相关的过程。
using System.Collections; |
您也可以通过以下方式在外部注册生命周期事件,使用 Page.AddLifecycleEvents()
。
// IPageLifecycleEvent 是上述生命周期事件的接口。 |
此外,您还可以通过将实现了 IPageContainerCallbackReceiver
接口的对象传递给 PageContainer.AddCallbackReceiver()
,从容器中挂钩过渡事件。
public interface IPageContainerCallbackReceiver |
请注意,如果您将 IPageContainerCallbackReceiver
实现为 MonoBehaviour
并将其附加到页面的 GameObject 上,
它将自动注册到 PageContainer
中,而无需调用 PageContainer.AddCallbackReceiver()
。
模态框的生命周期事件
通过在派生自 Modal
类的子类中重写以下方法,您可以编写与模态框生命周期相关的过程。
using System.Collections; |
您也可以通过以下方式在外部注册生命周期事件,使用 Modal.AddLifecycleEvents()
。
// IModalLifecycleEvent 是上述生命周期事件的接口。 |
此外,您还可以通过将实现了 IModalContainerCallbackReceiver
接口的对象传递给 ModalContainer.AddCallbackReceiver()
,从容器中挂钩过渡事件。
public interface IModalContainerCallbackReceiver |
请注意,如果您将 IModalContainerCallbackReceiver
实现为 MonoBehaviour
并将其附加到页面的 GameObject 上,
它将自动注册到 ModalContainer
中,而无需调用 ModalContainer.AddCallbackReceiver()
。
抽屉的生命周期事件
通过在派生自 Sheet
类的子类中重写以下方法,您可以编写与抽屉生命周期相关的过程。
using System.Collections; |
您也可以通过以下方式在外部注册生命周期事件,使用 Sheet.AddLifecycleEvents()
。
// ISheetLifecycleEvent 是上述生命周期事件的接口。 |
此外,您还可以通过将实现了 ISheetContainerCallbackReceiver
接口的对象传递给 SheetContainer.AddCallbackReceiver()
,从容器中挂钩过渡事件。
public interface ISheetContainerCallbackReceiver |
请注意,如果您将 ISheetContainerCallbackReceiver
实现为 MonoBehaviour
并将其附加到页面的 GameObject 上,
它将在不调用 SheetContainer.AddCallbackReceiver()
的情况下被注册到 SheetContainer
。
使用异步方法而不是协程
您也可以使用异步方法而不是协程来定义生命周期事件,如下所示。
using System.Threading.Tasks; |
要使用异步方法,请按以下步骤添加 Scripting Define Symbols
。
- 玩家设置 > 其他设置
- 将
USN_USE_ASYNC_METHODS
添加到Scripting Define Symbols
。
请注意,Scripting Define Symbols
需要设置为所有平台。
加载屏幕资源
更改屏幕资源的加载方法
如上所述,默认情况下,每个屏幕的资源以 Prefab 形式放置在 Resources 文件夹中。
如果要更改加载方法,请首先创建从 AssetLoaderObject
派生的 Scriptable Object。AssetLoaderObject
是 IAssetLoader
的一种实现,并具有以下方法。
// 加载由键指示的资源。 |
请参考 ResourcesAssetLoader 来实现实际操作。
创建完成后,将其实例分配给 UnityScreenNavigatorSettings
的 AssetLoader
属性。
您可以从 Assets > Create > Screen Navigator Settings
创建 UnityScreenNavigatorSettings
。
您还可以通过设置每个 Container
的 AssetLoader
属性来为每个容器设置 IAssetLoader
。
使用地址资产系统进行加载
默认情况下,为地址资产系统提供了一个 IAssetLoader
实现。如果您想使用地址加载每个屏幕,请按照以下步骤进行设置。
- 选择
Assets > Create > Resource Loader > Addressable Asset Loader
- 将在步骤 1 中创建的 ScriptableObject 分配给
UnityScreenNavigatorSettings
的AssetLoader
属性。
同步加载
您可以将 loadAsync
参数设置为 false,以在每个容器的过渡方法中同步加载屏幕。
例如,PageContainer.Push()
应写为以下形式。
PageContainer container; |
此外,您还可以使用 onLoad
回调将其在与过渡方法调用相同的帧中进行初始化。
PageContainer container; |
请注意,如果您使用 AddressableAssetLoader
并进行同步加载,则需要 Addressables 1.17.4 或更高版本。
此外,由于 Addressable 的规范,性能注意事项也很重要。
预加载
页面和模态框仅在请求屏幕过渡时加载。
当加载大型资源时,可能需要较长的时间来加载,从而阻止平滑的过渡。
在这种情况下,预加载资源会非常有用。
以下是使用 PageContainer
进行预加载的示例。
const string pageName = "FooPage"; |
请参考演示中的 HomePage 来了解实际实现。当初始化 Home
页面时,Shop
页面也会同时加载和销毁。
其他特性
一次性弹出多个屏幕
在 PageContainer
和 ModalContainer
中,您可以一次性弹出多个屏幕。
要做到这一点,请在 PageContainer.Pop()
或 ModalContainer.Pop()
的第二个参数中指定要弹出的屏幕数。
PageContainer pageContainer; |
您还可以指定目标 PageID
或 ModalID
。
可以使用 Push()
的 onLoad
回调来获取 PageID
和 ModalID
,如下所示。
PageContainer pageContainer; |
此外,您可以通过指定 Push()
的 pageId
或 modalId
参数来指定任何 ID。
PageContainer pageContainer; |
此外,在同时弹出多个页面或模态框时,被跳过的页面或模态框的生命周期事件(跳转之前和之后)将不会被调用,仅会调用销毁前的事件。
在 PageContainer
中,被跳过的页面的过渡动画将不会播放。
在 ModalContainer
中,被跳过的模态框关闭时的过渡动画将会同时播放。
不将页面堆叠在历史记录中
有些页面在回退过渡时想要跳过,例如加载屏幕。
在这种情况下,您可以在 PageContainer.Push()
方法中将可选参数 stack
设置为 false
,以防止页面被堆叠在历史记录中。
当过渡到下一个页面时,该页面的实例将被销毁,从而在后退时被跳过。
PageContainer container; |
请参考演示中的 TopPage了解实际实现方式。
在不将其堆叠在历史记录中的情况下过渡到加载页面。
更改模态框的背景
默认情况下,模态框的背景设置为一个黑色半透明的屏幕。
您可以在设置中更改此设置。
首先,将 Modal Backdrop
组件附加到模态框背景视图,并将其制作成预制件。
然后,将此预制件分配为模态框的背景。
要更改整个应用程序的模态框背景,请将其分配给 UnityScreenNavigatorSettings
中的 Modal Backdrop Prefab
。
您可以通过 Assets > Create > Screen Navigator Settings
创建 UnityScreenNavigatorSettings
。
您还可以通过将预制件分配给 Modal Container
的 Override Backdrop Prefab
,为每个 Modal Container
设置特定的背景。
单击背景关闭活动的模态框
默认情况下,背景不可点击。
如果您希望在单击背景时关闭活动的模态框,请首先按照上述步骤更改背景。
然后,勾选 Modal Backdrop 组件的 Close Modal When Clicked 选项。
在过渡期间启用交互
从过渡开始到结束的整个过程中,禁用屏幕上的所有容器的交互,例如点击屏幕。
您可以通过更改 UnityScreenNavigatorSettings
的 Enable Interaction In Transition
和 Control Interactions Of All Containers
属性来更改设置。
默认情况下,Enable Interaction In Transition
为 false
,Control Interactions Of All Containers
为 true
。
要在过渡期间启用交互,请将 Enable Interaction In Transition
设置为 true
。
如果只想为当前正在过渡中的容器禁用交互,请将 Enable Interaction In Transition
保持为 false
,并将 Control Interactions Of All Containers
设置为 false
。
您可以通过 Assets > Create > Screen Navigator Settings
创建 UnityScreenNavigatorSettings
。
但是,在一个容器正在过渡时无法过渡到其他屏幕。
因此,如果启用交互,请适时控制过渡的时机。
禁用容器的遮罩
默认情况下,容器外部的屏幕部分将被遮罩。
如果要显示容器外的屏幕,请取消容器的 GameObject 上附加的 Rect Mask 2D
组件的勾选。
获取动画播放信息
您可以从 Page
、Modal
、Sheet
类的以下属性获取当前播放的过渡动画的信息。
属性名 | 描述 |
---|---|
IsTransitioning | 是否正在过渡中。 |
TransitionAnimationType | 过渡动画的类型。如果不在过渡中,则返回 null。 |
TransitionAnimationProgress | 过渡动画的进度。 |
TransitionAnimationProgressChanged | 当过渡动画的进度发生变化时触发的事件。 |
在加载屏幕时使用预加载的 Prefab 实例
PreloadedAssetLoaderObject
允许您直接加载预加载的 Prefab 实例,而不是在加载屏幕时使用资源或 Addressables。您可以通过从 Assets > Create > Resource Loader > Preloaded Asset Loader 创建可脚本化对象,并在其中输入键和 Prefab 来使用它,如下所示。
我还为运行时提供了 PreloadedAssetLoader
的实现。
常见问题
如何将每个屏幕制作为场景而不是 Prefab
您可以通过实现 AssetLoader
来加载放置在场景中的屏幕。
实现 IAssetLoader
来加载包含所请求屏幕的场景文件,并返回屏幕的 GameObject。
有关详细信息,请参阅 更改屏幕资源的加载方法。
如何分离视图和逻辑
我编写了以下博文,以演示该概念和实现方法。
https://light11.hatenadiary.com/entry/2022/01/11/193925
(抱歉,仅提供日语)
如何向每个屏幕传递数据
首先,以示例为例,在加载完成时向屏幕传递数据,如下所示。
但是,还有许多其他可能的传递数据的方法。
例如,可能有一种情况,您希望使用 DI 容器来设置数据。
因此,本库的策略不是实现和强制执行特定的方法。
如何重用弹出的页面或模态
弹出的页面和模态会立即销毁,无法重用。
对于重用的需求本质上可以分为以下两种类型。
- 不希望每次加载屏幕资源
- 希望保留屏幕的状态
其中,加载时间的问题可以通过 预加载 来解决。
至于状态保留,从可维护性的角度来看,状态和视图应该解耦,以便可以重构。
此外,从可用性的角度来看,应该保留的是 “Tab” 转换。
在本库中,使用 “Sheet” 来实现选项卡时,状态始终保持不变。
有关详细信息,请参阅 创建 Sheet 和转换。
如果可重用,用户需要自己管理生命周期。
换句话说,当不再需要时,用户必须调用 Cleanup 方法来销毁实例和清理内存。
许可证
本软件在 MIT 许可下发布。您可以在许可范围内自由使用它。但使用时必须包含以下版权和许可声明。