Skip to content

Commit b060295

Browse files
committed
update ue notes
1 parent c184f94 commit b060295

35 files changed

+1027
-257
lines changed

_posts/UE/2024-06-04 UnrealEditor in UE.md

Lines changed: 3 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,8 @@
1-
[Actor蓝图之CDO与SCS](https://zhuanlan.zhihu.com/p/681252301) 中谈到了若干种不同的蓝图类,包括
1+
### MISC
22

3-
* 蓝图类(UBluePrint 类)
4-
* 蓝图骨骼类(UBlueprintGeneratedClass 类),它保存在 UBluePrintCore(UBluePrint 的基类)的 SkeletonGeneratedClass 字段中
5-
* 蓝图产生类(UBlueprintGeneratedClass 类),它保存在 UBluePrintCore 类的 GeneratedClass 字段中
6-
7-
根据 [UE4蓝图解析(一)](https://zhuanlan.zhihu.com/p/69067129) 中的描述,编辑器里面编辑的蓝图的各项属性,都存在 UBluePrint 类中,因此编辑器中调整蓝图就是在调整这个类
8-
9-
> 其实,蓝图编译过程就是把UBlueprint描述的信息转换为BlueprintGeneratedClass的过程
10-
11-
**值得注意的是,BlueprintGeneratedClass 是 UClass 的子类,因此我认为它是实际执行的蓝图类的元类(如果这个蓝图继承自 Actor,那么实际执行的蓝图类当然是 Actor 的子类)**
12-
13-
经过测试,在蓝图产生类的 CDO 中(这个描述准确吗),不包含在编辑器里面设置的 component(SCS 界面或者在 construction script 中)
14-
15-
[cdo-get-blueprint-components](https://erlite.dev/cdo-get-blueprint-components/) 中记录了如何根据 CDO 来获取蓝图中的 component
16-
17-
[【UE·蓝图底层篇】一文搞懂NativeClass、GeneratedClass、BlueprintClass、ParentClass](https://blog.csdn.net/j756915370/article/details/121556800) 这篇讲得也不错
18-
19-
TODO:蓝图类 vs 蓝图产生类?
20-
21-
Level BluePrint 是什么?
3+
cmd 命令 `ShowFlag.Collision 1` 可以在编辑器和游戏中显示 static mesh 的碰撞体,而 level editor 的 show -> collision 只会在编辑器中显示碰撞体
224

5+
下面这些是以前的东西
236
## Material Editor
247

258
**任何不理解的地方,看看编译出来的 Shader code 多半就知道了**

_posts/UE/2024-10-28 Camera in Unreal.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ spring arm 的核心逻辑在 `UpdateDesiredArmLocation` 函数中,这个函
2020
#### Lagging
2121
有时候人物的位置,或者旋转方向发生迅速的变化,我们不希望摄像机也跟着迅速变,否则画面看着就很晕。一个典型的例子是人物上台阶,上一个台阶的时候,人物在 z 轴的位置会迅速变化,如果摄像机始终对准人物的话,会导致画面抖动。因此 spring arm 的 `bEnableCameraLag``bEnableCameraRotationLag` 参数控制是否启用 location 和 rotation 的 lagging,而 `CameraLagSpeed``CameraRotationLagSpeed` 指定摄像机位置和朝向变化的最大速度
2222

23-
对于第三人称视角,如果使用 spring arm + camera component 的设置,应该将 spring arm 的 `bUsePawnControlRotation` 设置为 true,而 camera component 的 `bUsePawnControlRotation` 值设置为 true/false 都不影响。因为不论 camera component 的此字段是 true 还是 false,只要 spring arm 的此字段为 true,最终相机的旋转方向就会与 controller 的 control rotation 一致。如果把 spring arm 和 camera component 的此字段值都设置为 false,那表现就是上面提到的 camera 跟着 pawn 一块转。但如果是 spring arm 的此字段值设为 false,而 camera component 为 true,最终的表现就会很奇怪。主要的原因在于此时 spring arm 的 rotation 是跟着 pawn 走的,如果移动时改变了朝向,那 spring arm 的朝向也跟着改,使得 camera 的位置发生大幅变化,但 camera 的 rotation 又没变,这操作起来就很奇怪了
23+
对于第三人称视角,如果使用 spring arm + camera component 的设置,应该将 spring arm 的 `bUsePawnControlRotation` 设置为 true(同时记得设置 `bInheritPitch`,`bInheritYaw`,`bInheritRoll` 为 true,否则不会生效),而 camera component 的 `bUsePawnControlRotation` 值设置为 true/false 都不影响。因为不论 camera component 的此字段是 true 还是 false,只要 spring arm 的此字段为 true,最终相机的旋转方向就会与 controller 的 control rotation 一致。如果把 spring arm 和 camera component 的此字段值都设置为 false,那表现就是上面提到的 camera 跟着 pawn 一块转。但如果是 spring arm 的此字段值设为 false,而 camera component 为 true,最终的表现就会很奇怪。主要的原因在于此时 spring arm 的 rotation 是跟着 pawn 走的,如果移动时改变了朝向,那 spring arm 的朝向也跟着改,使得 camera 的位置发生大幅变化,但 camera 的 rotation 又没变,这操作起来就很奇怪了
2424
#### Target Offset vs Socket Offset
2525
从名字上就能看出来,target offset 就是相对于摄像机的对象的偏移,而通常 camera component 会作为 spring arm component 的子节点挂着,因此 socket offset 就是摄像机的位置偏移
2626
因此这俩 offset 实际上指定了 spring arm 的两个端点的偏移,用代码表示为(具体的逻辑在 `UpdateDesiredArmLocation` 函数中)
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
讨论模板中的天空光照是如何实现的
2+
3+
加入 `ADirectionalLight`,并设置其 `UDirectionalLightComponent``bAtmosphereSunLight` 为 true,这使得这个方向光会与 volumetric cloud 等天空中的物体相交互(不然整个天空是黑的,虽然其它物体仍被正常照亮)
4+
5+
我目前的理解是,`ADirectionalLight``ASkyAtmosphere` 与构成了天空的主要部分(光照来源和大气散射)。在材质编辑器中通过 `SkyAtmosphereViewLuminance``SkyAtmosphereLightDiskLuminance` 节点获取到由 `ASkyAtmosphere``ADirectionalLight` 组成的天空的颜色分布,作为材质的颜色输出。然后我们加入天空球,并将这个材质贴到天空球上,这就在屏幕上看到了天空的渲染结果。与 sky atmosphere 相关节点的文档可以在 [Sky Atmosphere Component Properties](https://dev.epicgames.com/documentation/en-us/unreal-engine/sky-atmosphere-component-properties-in-unreal-engine) 中找到
6+
7+
`ASkyLight` 的作用是捕获天空的环境光,然后用这个环境光渲染其它物体。如果不加入 `ASkyLight`,那么在渲染物体时不会考虑天空的环境光。这里天空的环境光不局限于 `ADirectionalLight``ASkyAtmosphere` 带来的环境光,例如我们可以只使用天空球,在材质中设置它的 emissive color 为红色。只有加入 `ASkyLight` 后,这个天空球的颜色才能转化为环境光照亮其它物体
8+
9+
`AVolumetricCloud` 在天空加入体积云,除了影响天空的渲染结果外,还会对 `ASkyLight` 捕获的环境光产生影响
10+
11+
`AExponentialHeightFog` 加入雾气,因为我是否关闭 `ASkyLight` 并不影响它的渲染结果。说明它的渲染不与环境光交互,根据 [Sky Atmosphere Component Properties](https://dev.epicgames.com/documentation/en-us/unreal-engine/sky-atmosphere-component-properties-in-unreal-engine) 中的描述,在项目设置中开启 _Support Sky Atmosphere Affecting Height Fog_ 后,它的渲染会与 `ADirectionalLight` 交互(关闭这个设置后,模板中的雾气就直接黑了)
12+
`ADirectionalLight``ASkyAtmosphere` 的组合只会渲染天空(即上半球),如果没有 `AExponentialHeightFog`,模板场景的下半球就全黑了
13+
14+
TODO:进一步弄清楚这些元素在渲染时是如何交互的
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
TODO:解释 `TObjectPtr` 等指针模板的含义
2+
### Smart Pointers
3+
#### Smart Pointers in C++
4+
首先可以看看 [std::shared_ptr](https://en.cppreference.com/w/cpp/memory/shared_ptr) 在 implements notes 一节的描述。每个被追踪的 pointer 都有个与之对应的,在堆上分配的 _control block_。值得注意的是,_control block_ 中除了包含 shared pointer 的引用计数,还包含了 weak pointer 的引用计数。当 shared pointer 的引用计数归零时,调用 pointer 指向对象的析构函数。当 weak pointer 和 shared pointer 的引用计数都归零后,才能释放 _control block_。明白了这个事情,就能想清楚 weak pointer 的 `expired``lock` 函数是怎么实现的了
5+
6+
另一个有趣的事情是,implements notes 中以及 [std::make_shared](https://en.cppreference.com/w/cpp/memory/shared_ptr/make_shared) 的 notes 中提到 `std::make_shared` 相比于 `std::shared_ptr<T>(new T(args...))` 的写法,会节省一次堆内存分配,因为 `std::make_shared` 会把类型 `T`_control block_ 的内存一块申请了。但这样不好的地方在于,shared pointer 的引用计数归零后,类型 `T` 占用的内存也不会被释放,直到 weak pointer 的引用计数也归零后,类型 `T`_control block_ 占用的内存会一起释放(这要求 `std::make_shared` 不能使用 custom deleter,否则就没办法把对象的析构和内存的释放这两步分离开了)。另外就是 `std::make_shared` 必须使用 public constructor,因为函数内部显然没办法调用 non-public 的类型 `T` 的构造函数。另一方面,这也说明 intrusive reference counting 可能提供更高的性能,因为它不再需要堆内存分配了
7+
8+
继承了 [std::enable_shared_from_this](https://en.cppreference.com/w/cpp/memory/enable_shared_from_this) 的类会额外地有一个 `mutable std::weak_ptr<T> weak_this` 字段,在 `std::shared_ptr` 的构造函数中会进行检测类型 `T` 是否继承了 `std::endable_shared_from_this`,如果是,就会设置 `weak_this` 为指向对应 _control block_ 的 weak pointer,这样就可以从 raw pointer 中增加引用计数,产生新的 shared pointer
9+
#### Smart Pointers in Unreal
10+
文档 [Unreal Smart Pointer Library](https://dev.epicgames.com/documentation/en-us/unreal-engine/smart-pointers-in-unreal-engine) 介绍了 UE 里的 smart pointer,这些结构可以从 C++ 中对应过来。`TSharedPtr` 就是 `std::shared_ptr`,而 `TSharedRef` 表示一个非空的 `TSharedPtr``TWeakPtr` 就是 `std::weak_ptr``MakeShareable` 就类似于 `std::shared_ptr<T>(new T(args...))` 的写法,`MakeShared` 就相当于 `std::make_shared``TSharedFromThis` 就是 `std::enable_shared_from_this`
11+
### Object Reference
12+
右击 Asset,打开 reference viewer,可以看到 asset 的引用与被引用情况,可以过滤掉 hard reference 或者 soft reference。在蓝图变量中,object reference 和 class reference 是 hard reference,它引用的资产需要一起加载进来,而 soft object reference 以及 soft class reference 是 soft reference,需要手动加载资产
13+
```c++
14+
TObjectPtr<MyType> MyName; // object reference
15+
TSubclassOf<MyType> MyName; // class reference
16+
TSoftObjectPtr<MyType> MyName; // soft object reference
17+
TSoftClassPtr<MyType> MyName; // soft class reference
18+
```
19+
`TObjectPtr<T>` 的说明见 [Unreal Engine 5 Migration Guide](https://dev.epicgames.com/documentation/en-us/unreal-engine/unreal-engine-5-migration-guide?application_version=5.0) ,据说这样可以实现 editor 下的 dynamic resolution and access tracking,但我没看出来这是怎么实现的,[Why should I replace raw pointers with TObjectPtr?](https://forums.unrealengine.com/t/why-should-i-replace-raw-pointers-with-tobjectptr/232781) 中的讨论也感觉含糊其辞,TODO:解释 `TObjectPtr<T>` 与 incremental GC 的关系。`TSubclass<T>``TObjectPtr<UClass>` 的封装,来保证赋值的 uclass 对应的类是模板参数 `T` 的子类
20+
21+
`TSoftObjectPtr<T>` 则不再是 8 字节,而是 48 字节,因为它除了存储一个弱引用指针 `FWeakObjectPtr` 外,还需要存储这个 object 的资产路径 `FSoftObjectPath`,这个路径分为三部分,`PackageName``AssetName``SubPathString`。例如路径 `/Game/MyAsset.MyAsset_C:DefaultSubobject``PackageName` 就是 `/Game/MyAsset``AssetName``MyAsset_C`,而 `SubPathString``DefaultSubobject``TSoftClassPtr<T>` 是类似的
22+
23+
这里什么时候应该用 object reference,什么时候用 class reference 稍微有些微妙
24+
* 对于一些数据类型的资产,我们使用 object reference,这样在编辑器中设置好引用后,我们会得到 `/Game/MyAsset.MyAsset`
25+
* 而对于蓝图类蓝图类这样的类型,我们通常会使用 class reference,对应 object 路径为 `/Game/MyAsset.MyAsset_C`。当然也可以用 object reference,对应的 object 路径为 `/Game/MyAsset.MyAsset`(也就是 `UBlueprint`
26+
27+
注:`FWeakObjectPtr``TWeakObjectPtr` 的内部表示,`TWeakObjectPtr` 的描述见 UObject Management in Unreal 一文
28+
29+
TODO:解释 `TObjectPtr<T>`
30+
* [简析UE5的对象指针FObjectPtr与TObjectPtr](https://zhuanlan.zhihu.com/p/504115127) 讨论了 `TObjectPtr<T>` 在 editor 下 dynamic resolution and access tracking 的功能,但我其实还没理解到底该怎么用这玩意。因为我自己测试的时候添加了 `AddObjectHandleReferenceResolvedCallback` 回调后,这函数一次都没执行过。不知道是不是我的代码太简单了之类的
31+
* 解释 `TObjectPtr<T>` 在 GC 的增量可达性分析中的作用
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
[《InsideUE4》GamePlay架构(十一)Subsystems](https://zhuanlan.zhihu.com/p/158717151) 讲得很好了
2+
* Subsystem 是单例,需要多个 subsystem 就通过继承创建出多个类型
3+
* Subsystem 可以根据 outer 分出 UEditorSubsystem,UEngineSubsystem,UGameInstanceSubsystem,UWorldSubsystem,ULocalPlayerSubsystem
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
### UCLASS
2+
Blueprintable: 表示可以用这个类作为蓝图类的父类
3+
BlueprintType: 表示这个类可以作为变量类型在蓝图变量中使用
4+
5+
### UPROPERTY
6+
EditDefaultsOnly:只能编辑变量的默认值(不能编辑放入关卡中的实例的相应字段)
7+
EditInstanceOnly:只能编辑放入关卡中的实例的相应字段,不能编辑默认值
8+
EditAnywhere:表示 EditDefaults 且 EditInstance
9+
VisibleDefaultsOnly:表示该变量只能在编辑默认值的窗口可见,并且不能编辑,即与 EditXXX property 不兼容
10+
VisibleInstanceOnly:表示该变量只能在编辑实例值的窗口可见,并且不能编辑,即与 EditXXX property 不兼容
11+
VisibleAnywhere:表示 VisibleDefaults 且 VisibleInstance
12+
BlueprintReadOnly:蓝图程序中只读
13+
BlueprintReadWrite:蓝图程序中可读可写
14+
15+
16+
Instanced:TODO,没看明白是啥意思,如果说表示一种 ownership 的话,`UPanelSlot` 中的 Parent 和 Content 都有这个字段,岂不是父节点和子节点循环 owner?
17+
18+
### UFUNCTION
19+
BlueprintImplementableEvent:只能在蓝图中实现的函数
20+
BlueprintNativeEvent:C++ 中可加个 `_Implementation` 后缀进行定义,或者在蓝图中进行重载
21+
22+
### META
23+
AllowPrivateAccess,如果该 uproperty 使用 BlueprintReadOnly 或 BlueprintReadWrite 标记了,并且是私有成员,那么需要使用 AllowPrivateAccess 标记
24+
BlueprintSpawnableComponent,表示可以通过 SCS 添加进 actor
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
最基本的 Movement Component 的实现,然后是 NavMesh 以及 Projectile
2+
NavMesh 感觉 AI Controller 会涉及,然后 Projectile 是在第一人称模板中的子弹中用到了
3+
4+
移动时对 rotation 的控制
5+
`Use Controller Desired Rotation`
6+
`Use Controller Rotation Yaw`
7+
`Orient Rotation to Movement`
8+
这些选项主要是决定哪些操作会影响角色朝向:摄像机朝向移动是否改变角色朝向(如果是,这可以保持角色始终看向摄像机的前方),角色移动是否影响角色朝向(如果不是,那么就可以实现侧着身子走这种)

0 commit comments

Comments
 (0)