BPVM 小食包 #11 - 链接和绑定:最终组装 | ebp BPVM 小食包 #11 - 链接和绑定:最终组装 | ebp BPVM 小食包 #11 - 链接和绑定:最终组装 | ebp
文章

BPVM 小食包 #11 - 链接和绑定:最终组装

创建属性和函数后,它们只是松散的部件。链接和绑定将所有内容连接到一起,形成一个可工作的类。这就是最终的流水线。

BPVM 小食包 #11 - 链接和绑定:最终组装

本文内容基于Unreal Engine 5.6.0

BPVM 小食包 - 蓝图知识快速投喂!是蓝图到字节码系列的一部分。

零散部件问题

编译后,你有:

  • 属性已创建 ✓
  • 函数已生成 ✓
  • 内存已分配 ✓

但它们没有连接。就像拥有所有汽车零件但没有组装!

两步组装

虚幻使用两个操作来连接所有内容:

1
2
3
4
5
// 步骤 1: 找到 C++ 连接
NewClass->Bind();

// 步骤 2: 链接所有属性
NewClass->StaticLink(true);

想象成:

  1. Bind: 连接到引擎(找到方向盘)
  2. StaticLink: 内部连接(连接仪表板)

Bind(): 找到 C++ 函数

Bind() 搜索三个关键事物:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
void UClass::Bind()
{
    // 1. 找到构造函数
    ClassConstructor = FindConstructor();
    // "我如何创建实例?"

    // 2. 找到 VTable 辅助器
    ClassVTableHelperCtorCaller = FindVTableHelper();
    // "我如何设置虚函数?"

    // 3. 找到静态函数
    ClassCppStaticFunctions = FindStaticFunctions();
    // "我可以调用哪些 C++ 函数?"

    // 递归绑定父类
    if (GetSuperClass()) {
        GetSuperClass()->Bind();
    }
}

这就像为你的类找到说明书!

为什么 Bind 重要

没有 Bind(),蓝图无法:

1
2
3
4
5
6
7
8
// 无法创建实例
AMyActor* Actor = NewObject<AMyActor>();  // 没有构造函数!

// 无法调用父函数
Super::BeginPlay();  // 没有 VTable!

// 无法调用静态函数
AMyActor::StaticFunction();  // 找不到!

Bind() 在蓝图和 C++ 之间创建了桥梁!

StaticLink() 创建属性链表:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
void UStruct::StaticLink(bool bRelinkExistingProperties)
{
    // 将所有属性链接成一个链表
    FProperty* Previous = nullptr;
    for (FProperty* Prop : Properties) {
        if (Previous) {
            Previous->Next = Prop;
        }
        Prop->Offset = CalculateOffset(Prop);
        Previous = Prop;
    }

    // 计算总大小
    PropertiesSize = 0;
    for (FProperty* Prop : PropertyLink) {
        PropertiesSize += Prop->ElementSize;
    }
}

之前:属性存在但彼此不了解 之后:属性形成一个链表,具有计算的偏移量!

内存布局计算

StaticLink() 确定所有内容存储的位置:

1
2
3
4
5
6
7
8
9
10
// StaticLink 之前
Property: Health (?)
Property: Armor (?)
Property: Name (?)

// StaticLink 之后
Property: Health  Offset: 0x0000 (4 字节)
Property: Armor   Offset: 0x0004 (4 字节)
Property: Name    Offset: 0x0008 (16 字节)
总大小: 0x0018 (24 字节)

现在引擎知道每个属性在内存中的确切位置!

引用链

属性可以相互引用:

1
2
3
4
// 在 StaticLink 期间
FObjectProperty* MyActorRef;
MyActorRef->PropertyClass = AMyActor::StaticClass();
MyActorRef->LinkInternal();  // 连接到类!

这创建了对象之间的引用网络!

父类递归

两个操作都递归工作:

1
2
3
4
5
6
7
8
9
// Bind() 向上遍历链
BP_MyActor::Bind()
   AActor::Bind()
     UObject::Bind()

// StaticLink() 也是
BP_MyActor::StaticLink()
   AActor::StaticLink()
     UObject::StaticLink()

继承的每一层都得到正确的连接!

对齐魔法

StaticLink() 还处理内存对齐:

1
2
3
4
5
6
7
8
// 为 CPU 缓存优化
if (Property->Size == 1) {
    Alignment = 1;  // 字节可以放在任何地方
} else if (Property->Size <= 4) {
    Alignment = 4;  // 对齐到 4 字节
} else {
    Alignment = 8;  // 对齐到 8 字节
}

这使你的蓝图在运行时更快!

最终连接

两个操作之后:

1
2
3
4
5
6
7
8
// 所有内容都连接了!
Class {
    Constructor:  ( Bind 找到)
    VTable:  ( Bind 找到)
    Properties:  ( StaticLink 链接)
    Size: 0x0018  ( StaticLink 计算)
    Alignment: 8  ( StaticLink 计算)
}

你的类现在是一台完全功能的机器!

真实世界示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 你创建一个蓝图:
float Health = 100;
int32 Armor = 50;
AActor* Target;

// Bind() 和 StaticLink() 之后:
BP_MyClass {
    Constructor  AMyActor::AMyActor()  // 找到了!
    Properties  [
        0x00: Health (float, 4 字节)
        0x04: Armor (int32, 4 字节)
        0x08: Target (AActor*, 8 字节)
    ]
    总大小: 16 字节
    属性链: HealthArmorTargetnullptr
}

快速要点

  • Bind() 找到 C++ 函数(构造函数、VTable、静态函数)
  • StaticLink() 连接属性并计算内存布局
  • 属性变成具有偏移量的链表
  • 内存被对齐以提高性能
  • 两者都通过继承递归工作
  • 它们一起将松散的部件转换为可工作的类!

组装完成

当编译完成 Bind() 和 StaticLink() 后,你的蓝图类不再是部件集合 - 它是一台完全组装的、准备运行的机器,每根线都连接好,每个螺栓都拧紧!

想要更多细节?

完整的链接过程:

下一篇:理解变成字节码的语句!


🍿 BPVM 小食包系列

本文由作者按照 CC BY 4.0 进行授权