Skip to content

Commit

Permalink
update umg in unreal
Browse files Browse the repository at this point in the history
  • Loading branch information
ckf104 committed Nov 27, 2024
1 parent 1f1e91b commit d13b039
Showing 1 changed file with 69 additions and 3 deletions.
72 changes: 69 additions & 3 deletions _posts/UE/2024-11-12 UMG in Unreal.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,58 @@
### Slot
解释:每个子节点都插在父节点的 slot 中,有没有 anchor 依赖于 父节点 slot 的类型
### Slate Brush
TODO:解释 slate brush中各个字段的含义,以及它如何控制渲染效果
### UPanelWidget and UPanelSlot
`UPanelWidget` 的子类才有子节点,有 1 个还是多个子节点取决于 `bCanHaveMultipleChildren` 字段的值,在 `UPanelWidget` 的构造函数中默认为 true

`UPanelSlot` 类用来组织父子节点关系。`UPanelWidget::GetSlotClass` 虚函数表示这个 widget 使用的 slot 类,默认是 `UPanelSlot`。子类可以重载来表示实际使用的 slot 类。例如 Canvas 实际使用的是 `UCanvasPanelSlot`,当然这些新定义的 slot 类必须是 `UPanelSlot` 的子类
```c++
UCLASS(BlueprintType, MinimalAPI)
class UPanelSlot : public UVisual
{
UPROPERTY(Instanced)
TObjectPtr<class UPanelWidget> Parent;

UPROPERTY(Instanced)
TObjectPtr<class UWidget> Content;
};
```
父子节点的组织方法是,`UWidget` 的 `Slot` 字段表示了这个 widget 在父节点的中的位置。而 panel widget 的 slots 字段则记录了所有的子节点。查看 `UPanelWidget::AddChild` 方法可知,每当添加一个新的子节点,就实例化一个新的 slot 赋值给 child widget 的 slot,同时将这个 slot 加入到父节点的 slots 中
##### Widget Tree
user widget 包含一个 widget tree 字段
```c++
/** The widget tree contained inside this user widget initialized by the blueprint */
UPROPERTY(Transient, DuplicateTransient, TextExportTransient)
TObjectPtr<UWidgetTree> WidgetTree;
```
然后 widget tree 包含一个 root widget 字段
```c++
/** The root widget of the tree */
UPROPERTY(Instanced)
TObjectPtr<UWidget> RootWidget;
```
这个 root widget 就是整个层次节点的根了,然后非叶子节点都是 panel widget 或者 `INamedSlotInterface` 的子类,它可以继续延伸出子节点,由此展开成了一整棵树
### Named Slot
`INamedSlotInterface` 提供了另一种以名称来标记子节点的方式(相比于 panel widget 通过序号来标记)。它最典型的子类就是 user widget。我们在 umg editor 中新定义的 user widget A作为 ui 节点放到另一个 user widget B中时,默认情况下是不能作为父节点的,因为它没有继承 panel widget(因此使用 widget tree 提供的 `ForWidgetAndChildren` 函数对 B 进行遍历时也看不到 A 内部的子节点)。但由于它是 named slot interface 的子类,因此可以使用 named slot 来提供子节点
具体来说,我们在自定义 user widget A 时加入 named slot 作为占位符。这样在将 A 放入正在定义的 user widget B 时就可以将子节点加入到 A 的 named slot 下了。对应到 C++ 中的字段,`UWidgetBlueprintGeneratedClass` 的 `InstanceNamedSlots` 字段记录了它的 user widget 类有哪些 named slot(`UWidgetBlueprintGeneratedClass` 是 UClass 的子类)
```c++
UPROPERTY()
TArray<FName> InstanceNamedSlots;
```
然后每个 user widget 实例的 `NamedSlotBindings` 字段则记录了每个 named slot 上挂的 widget
```c++
/** Stores the widgets being assigned to named slots */
UPROPERTY()
TArray<FNamedSlotBinding> NamedSlotBindings;
```

TODO:理解 widget tree 为啥是 `INamedSlotInterface` 的子类
### HUD
player controller 三剑客 HUD,camera manager 和 input component。我也没咋看明白 `AHUD` 类本身有啥东西,一般可能我们自己继承这个类,然后在这个类里面添加管理 widget 的逻辑吧
### Interaction with Others
要动态修改 UI 的内容,一种是获取到 widget 的引用,然后直接修改。另外一种是使用 [property/function binding](https://dev.epicgames.com/documentation/en-us/unreal-engine/property-binding-for-umg-in-unreal-engine)。function binding 的开销显然是最大的,因为每帧都会调这个蓝图函数。我不太确定 property binding 的开销如何
感觉用得最广泛的还是 event driven update。其实本质上就是直接进行修改,只是说现在绑定在了一个 delegate 上,每次变量值被修改了就只需要广播一下就好了。例如我需要显示 character 的生命值,那就额外地有一个 `OnHealthChaned` 多播代理,然后绑定一个修改 UI 的回调就好了

TODO:了解 property binding/function binding 的实现
### Anchor
因为我们通常设计 UI 时,总需要一个参考的屏幕的屏幕分辨率来将其可视化。anchor 这个概念要解决的问题是当屏幕分辨率变化时,如何确定新的 UI 布局。首先我们定义什么是 UI 布局:我们使用一个矩形来标定 UI 元素的位置。给定长宽的画布,我们清楚了每个 UI 矩形在画布中的位置和大小,那么就清楚了 UI 布局

Expand All @@ -21,4 +74,17 @@ TODO:调用 `APlayerController::SetShowMouseCursor` 后就能在游戏里看

TODO:`AddToViewport``AddToPlayerScreen` 用于将 user widget 显示在屏幕上,但具体的实现就感觉水很深了。

334
### Other Tutorials
[unreal ben ui](https://benui.ca/unreal/#ui) 中有很多 UI 相关的教程,涵盖了下面 TODO 的许多主题,值得一看。比如 [Introduction to C++ UIs in Unreal](https://benui.ca/unreal/ui-cpp-basics/) 讨论了如何使用 C++ 构建 UI
TODO:跳过了官方文档中的 [UMG Best Practices](https://dev.epicgames.com/documentation/en-us/unreal-engine/umg-best-practices-in-unreal-engine)
### Widget Component and Interaction
TODO:了解 widget component 以及 widget interaction component
### SWidget
TODO:什么是 slate widget
### Rendering
TODO:解释这个布局,通过 slot,我们有子节点的 desired size,有父节点希望子节点的 desired size,与编辑器右上角的 desired screen,fill screen 等选项有关,控制到底用子节点的 desired size,还是父节点希望子节点的 desired size。即理清楚布局算法是如何工作的
TODO:解释它与 rendering 子系统如何交互
### Input
TODO:解释它如何接收 input,以及如何与 enhanced input 系统协作
### Animation
TODO:试试 UI 动画

0 comments on commit d13b039

Please sign in to comment.