UE--Slate 焦点、捕获,输入处理与玩家控制器的关系
Slate 焦点、捕获,输入处理与玩家控制器的关系
在虚幻引擎开发中我们必不可少的会接触到键盘输入、鼠标输入、ui上各种焦点问题、鼠标被ui捕获等等
以及与玩家控制器之间的关系(也就是玩家控制器 或者 本地玩家(玩家输入 UPlayerInput) 是如何拿到输入事件的)
这个问题一直困扰我 我今天就记录一下自己当前浅薄的知识
1、Slate如何绘制到屏幕上
1.首先我们需要知道Slate中的几个基本类型
1.SCompoundWidget(单子控件容器)
- 定义:只有一个 ChildSlot,可以包裹一个控件(SWidget)
- 用途:自定义复合控件。
- 例子:SButton、SScrollBox、SWindow
2.SPanel(多子控件容器)
- 定义 :可以拥有多个 Slot,每个 Slot 存一个子控件(SWidget)。
- 用途 :用于布局、排列、聚合多个控件。
- 例子 :SVerticalBox、SOverlay、SDPIScaler、SConstraintCanvas(大家熟悉的画布面板)
3.SLeafWidget(叶子控件容器)
- 定义 :没有Slot,也就是没有子控件。
- 用途 :负责绘制或显示具体内容
- 例子 :STextBlock、SImage
4.SWidget (控件)
- Slate控件顶层父类
- 自定义控件( 一般来说不用它)
2.Slate绘制流程
先来一个简易的类图
简易的流程图
从图上看出来了 slate会从树的顶部 一直往下进行遍历
不断的调用
SWidget::Paint
SWidget::OnPaint
也就是说 你想让ue显示你新写的的Slate UI
你需要添加到某个已经在屏幕上显示的Slate 的控件中的Slot里面就可以了
2.Slate响应输入
Slate响应输入的关键函数都在
FSlateApplication类里面
类似ProcessMouseButtonDownEvent这类函数
1.从鼠标左键开始
Slate响应鼠标按键按下时
会调用到 FSlateApplication::ProcessMouseButtonDownEvent 函数
先来个简易的流程图
从上图我们看出最关键的地:
FEventRouter::Route <>
FEventRouter::FToLeafmostPolicy 迭代策略系列
控件路径
捕获路径
键盘焦点路径
下面我们就说说这几个路径
2.控件路径
- 控件路径:主要是用于 在鼠标没有被任何Slate控件捕获的情况下
我们拥有的Slate控件树从根到最后的叶子的迭代路径 - 由来:FSlateApplication::Tick 函数中 调用的 SWidget::Paint中调用的FHittestGrid::AddWidget函数中进行添加的
- 控件需要可视与可交互 才会被收集
- 当然响应时也做出了可交互的判断
3.键盘焦点路径
- 键盘输入响应的控件路径
其实就是FSlateUser::WeakFocusPath - 由来:
这个会在每次调用 FSlateUser::SetFocusPath
进行设置 一般是1直接调用 FSlateApplication::SetUserFocus
这个会将当前要设置的焦点控件 从根到自己为止的控件迭代路径 保留到WeakFocusPath
让键盘输入事件时使用
4.捕获路径
- Slate控件捕获鼠标时 缓存的控件路径
- 鼠标事件响应时会做 SlateUser捕获鼠标与否做 控件迭代路径的不同
- 由来:FSlateApplication::ProcessReply 函数中 FSlateUser::SetPointerCaptor会将捕获的控件为止的路径 保存到FSlateUser::PointerCaptorPathsByIndex
- 返回的 FReply 也是在FSlateApplication::ProcessReply中进行处理的
3.FSlateApplication::ProcessReply函数
- 触发这个函数会在引擎的Tick里触发
- FEventRouter::Route <>中
- 这个函数处理了FReply
- FSlateApplication::SetUserFocus
- FSlateUser::SetPointerCaptor
Tick触发时主要是处理ULocalPlayer::SlateOperations 也是 FReply
好像鼠标移动也会进行处理
4.Slate键盘响应
- FSlateApplication::ProcessKeyDownEvent
在这个函数中我们发现 它直接使用了焦点路径进行迭代的
TSharedRef<FWidgetPath> EventPathRef = SlateUser->GetFocusPath();const FWidgetPath& EventPath = EventPathRef.Get();// Switch worlds for widgets inOnPreviewMouseButtonDown the current pathFScopedSwitchWorldHack SwitchWorld(EventPath);// Tunnel the keyboard eventReply = FEventRouter::RouteAlongFocusPath(this, FEventRouter::FTunnelPolicy(EventPath), InKeyEvent, [] (const FArrangedWidget& CurrentWidget, const FKeyEvent& Event){if (CurrentWidget.Widget->IsEnabled()){const FReply TempReply = CurrentWidget.Widget->OnPreviewKeyDown(CurrentWidget.Geometry, Event);return TempReply;}return FReply::Unhandled();}, ESlateDebuggingInputEvent::PreviewKeyDown);
别看FEventRouter::RouteAlongFocusPath<> 函数不一样 其实也是 FEventRouter::Route <>
5.玩家控制器拿到输入事件
1.玩家控制器的输入事件也是从一个Slate控件传递下来的
只是比我们的UMG创建的SObjectWidget在树中的层级 高了很多
2.玩家控制器只要获取鼠标输入事件他就会进行捕获鼠标 还有焦点
这样的话焦点路径和捕获路径都被设置成当前的Slate控件了 (一般是SViewport类似的)
以后该Slate控件后面的控件都没法拿到输入了
参考:
(6 条消息) UE4-Slate源码学习(二)slate事件触发 - 知乎
虚幻引擎UI组件开发快速入门【UMG/Slate】_umg获取slate-CSDN博客
UE4c++ Slate整套学习流程(源码编译+Slate框架+SlateViewer)_widget reflector-CSDN博客
【UE Slate】 虚幻引擎Slate开发快速入门-CSDN博客
tails/126990479?spm=1001.2014.3001.5506)
【UE Slate】 虚幻引擎Slate开发快速入门-CSDN博客