Lua分层状态机及UI框架(StateUI)

概述

StateUI (Lua)是以StateMachine为基础,建立分层状态机以及StateUI Window的交互规则,在框架层内建State切换时对应StateWindowOpen/Close规则。

StateDriver
|
|-- Initialize <==> InitWindow
|
|-- Login <==> LoginWindow
|
|-- SelectRole <==> SelectRoleWindow CreateRoleWindow
|
|-- Gaming
| |
|-- Camp <==> BottomBar
|
|-- Role <==> BottomBar, TopBar, ...
| |
| |-- ...
|
|-- Fight <==> BottomBar, TopBar, ChatBar, ...
| |
| |-- ...
|
|-- Pet <==> BottomBar, ...
|
|-- ...

开发规范

StateUI试图建立一种开发规范,明确规定State与Window的访问权限。哪些代码应该在State中实现,哪些又需要在Window中实现。

  • Window内应该仅处理本Window的UI控件,不能直接操纵其他Window和场景
  • Window仅能向所属的State发送Event
  • Window仅能读取(Get)所属State的指定数据
  • Window仅能关闭自己
  • State可以Open/Close目标Window
  • State可以发送Message给所有Visible的Window
  • State可以向父State发送Event

StateUI的默认规则是:

进入State后,之前的所有Window都会被默认Close。

快速开始

  • 实现IWindowProvider(详情参看IWindowProvider节)
GameWindowProvider = class("GameWindowProvider", IWindowProvider)
  • 启动StateDriver(详情参看StateDriver节)
local driver = StateDriver.new(GameWindowProvider.new())
  • 启动目标State,例如Initialize
Initialize = class("Initialize", State)
driver:ChangeState(Initialize.new())

StateDriver

StateDriverStateUI的驱动器,是整个模块的入口。StateUI本身并不直接处理Window的行为,而是依靠外部注入IWindowProvider的实体对象来间接应用StateUI的逻辑。

-- 帧更新入口,需要确保刷新调用
self:Update(deltaTime)

-- 销毁
self:Destroy()

IWindowProvider

应用层需要实现IWindowProvider接口,然后注入到StateDriver中。从而StateUI可以真正使用规则影响目标Window

-- Provider 初始化。
function M:OnInit() end

-- Provider 销毁。
function M:OnDestroy() end

-- 创建目标窗口并返回真实Window的对象。
function M:Create(name) end

-- 销毁目标窗口。
function M:Destroy(name) end

-- 显示窗口。StateUI会确保目标窗口已经被创建。
function M:Show(name) end

-- 隐藏目标窗口。
function M:Hide(name) end

-- 打开目标窗口,并传入IWindow对象。
function M:Open(name, iwindow, ...) end

-- 关闭目标窗口,并传入IWindow对象。
function M:Close(name, iwindow) end

State

State只需操作关心的WindowOpenClose即可,不关心的Window会被框架层自动Close

State 扩展

local M = class("MyState", State)

function M:OnEnter(ctx)
end

function M:OnExit()
end

function M:OnUpdate(deltaTime)
end

State 切换

使用ChangeState(state, ctx)切换状态,并将传入的state作为返回值返回。其中ctx为可以携带的上下文参数,可以不填。

进入/切换当前State

self:GetParent():ChangeState(state)

退出当前State

self:GetParent():ChangeState(nil)

进入/切换子State

self:ChangeState(state)

退出子State

self:ChangeState(nil)

State向上抛出Event

使用self:Upcast(event, ...)向父级State抛出Event

self:Upcast("Navigate", 1)
父级State处理下层State抛出的Event

实现OnNotify加上Event名称组成的函数。

function M:OnNotifyNavigate(index)
end

Window

StateUI中,Window分为两种:普通Window固定Window

类型 行为
普通Window Window的状态在进入子State后会默认关闭。也就是,如果父State打开普通Window并保持打开状态,那么进入子State后,该Window会被自动关闭。
固定Window Window的状态在进入子State后会被保留。也就是,如果父State打开固定Window并保持打开状态,那么进入子State后,该Window还是会保持打开状态。

StateUI中,Window的打开方式有两种:普通Open排队Open

类型 行为
普通Open 目标Window被直接打开。
排队Open 目标Window进入打开的排队系统,若处于第一位,则直接打开,否则在队列中等待,直到排在前面的Window被关闭。

State可操作Window的接口

-- 预加载目标窗口
self:PreloadWindow(window)

-- 打开目标窗口
self:OpenWindow(window, ...)

-- 打开固定窗口
self:OpenFixedWindow(window, ...)

-- 使用排队系统打开目标窗口
self:OpenWindowQueued(window, ...)

-- 使用排队系统打开固定窗口
self:OpenFixedWindowQueued(window, ...)

-- 关闭目标窗口
self:CloseWindow(window, ...)

-- 情况窗口排队系统
self:ClearQueuedWindows()

-- 查询目标Window是否处于打开状态
self:IsWindowOpened(window)

-- 激活Window域,State退出后,归属该State的所有Window会被销毁。
self:EnableDomain()

向Window发送Message

通过self:SendWindowMessage(messge, ...)向所有有效(显示中)的Window发送消息。

self:SendWindowMessage("Progress", 0.1)

UICtrl接收State发送的Message

实现OnState加上Message名称组成的函数。

function M:OnStateProgress(progress)
end

UICtrl

UICtrl应只关心自己Window内的逻辑,只能发送Event和读取(Get)目标属性。

初始化/清理

WindowOpen创建/显示)的时候,会调用UICtrlOnOpen方法;反之,被Close隐藏/销毁)的时候会调用UICtrlOnClose方法。Window被重新创建的时候会调用OnCreate方法;而销毁时会调用OnDestroy方法。

注意:由于Open可能只是显示目标Window,其背后的资源并未被释放,因此一些消息绑定操作请确保在OnClose中解绑,防止消息触发重入。

function M:OnCreate()
end

function M:OnDestroy()
end

function M:OnOpen(data)
end

function M:OnClose()
end

Close自己

UICtrl可以直接关闭自己所属的Window

self:CloseSelf()

发送Event

通过self:Notify(event, ...)发送Event

self:Notify("Print", "Hello")

State处理Event

实现OnNotify加上Event名称组成的函数。

function M:OnNotifyPrint(obj)
end

Get属性

UICtrl可以通过self:Get(name, ...)获取State层暴露的数据。

local name = self:Get("ID", ...)

State处理属性读取

实现OnGet加上属性名称组成的函数。

function M:OnGetID()
return 1
end