BPVM 小食包 #16 - 阅读字节码:矩阵揭秘
曾经想知道你编译的蓝图实际是什么样子吗?这里教你如何阅读字节码输出并理解你的节点变成了什么。
BPVM 小食包 #16 - 阅读字节码:矩阵揭秘
本文内容基于Unreal Engine 5.6.0
BPVM 小食包 - 蓝图知识快速投喂!是蓝图到字节码系列的一部分。
启用字节码输出
首先,你需要看到字节码!将这个添加到你的配置:
1
2
[Kismet]
CompileDisplaysBinaryBackend=True
现在当你编译时,输出日志会显示实际的字节码!
字节码格式
你的蓝图变成这样的文本:
1
2
3
4
5
6
7
8
9
10
11
12
LogK2Compiler: [function ExecuteUbergraph_BPA_MyActor]:
Label_0x0:
$4E: Computed Jump, offset specified by expression:
$0: Local variable of type int32 named EntryPoint
Label_0x10:
$44: EX_CallFunction (FFrame::Step)
$8: Function PrintString
$B: EX_Nothing
$4: Return expression
$B: EX_Nothing
Label_0x20:
$53: EX_EndOfScript
它看起来像蓝图的汇编语言!
理解符号
$XX: EExprToken(指令或数据)
1
2
3
4
$44 = EX_CallFunction // 调用函数
$0 = EX_LocalVariable // 局部变量
$4 = EX_Return // 从函数返回
$53 = EX_EndOfScript // 字节码结束
这些是虚拟机操作码!
标签是跳转目标
1
2
3
Label_0x0: // 偏移 0 字节
Label_0x10: // 偏移 16 字节
Label_0x20: // 偏移 32 字节
标签标记跳转去的地方。数字是从函数开始的字节偏移!
阅读函数调用
1
2
3
4
$44: EX_CallFunction (FFrame::Step)
$8: Function PrintString
"Hello World"
$B: EX_Nothing
翻译:
- $44 = “我在调用一个函数”
- $8 = “这是函数指针”
- “Hello World” = “这是参数”
- $B = “参数结束”
Ubergraph 之谜
1
2
3
4
[function ExecuteUbergraph_BPA_MyActor]:
Label_0x0:
$4E: Computed Jump, offset specified by expression:
$0: Local variable of type int32 named EntryPoint
记得 Ubergraph 吗?它从跳转表开始:
- EntryPoint 0 = BeginPlay
- EntryPoint 1 = Tick
- EntryPoint 2 = 你的自定义事件
虚拟机根据触发的事件跳转到正确的入口!
阅读变量
1
2
3
$0: Local variable of type float named Health
$1A: Self
$11: Object variable Property /Script/Engine.Actor:RootComponent
变量显示:
- 类型(float、int、object)
- 名称(Health、RootComponent)
- 作用域(Local、Self、Property)
常见 EExprToken 值
这是一个速查表($ 前缀表示反汇编中显示的十六进制值):
1
2
3
4
5
6
7
8
9
10
11
12
$00 = EX_LocalVariable // 局部变量 (hex: 0x00)
$0B = EX_Nothing // Null/空 (hex: 0x0B)
$04 = EX_Return // 返回 (hex: 0x04)
$06 = EX_Jump // 无条件跳转 (hex: 0x06)
$07 = EX_JumpIfNot // 条件跳转 (hex: 0x07)
$1A = EX_Self // 'this' 指针 (hex: 0x1A)
$1C = EX_IntConst // 整数字面量 (hex: 0x1C)
$1F = EX_StringConst // 字符串字面量 (hex: 0x1F)
$27 = EX_ObjectConst // 对象引用 (hex: 0x27)
$44 = EX_CallFunction // 函数调用 (hex: 0x44)
$4E = EX_ComputedJump // 跳转表 (hex: 0x4E)
$53 = EX_EndOfScript // 结束标记 (hex: 0x53)
完整示例
你的蓝图:
1
BeginPlay → Print("Hello")
字节码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
[function ExecuteUbergraph_BP_MyActor]:
Label_0x0:
$4E: Computed Jump // 入口跳转表
$0: EntryPoint
Label_0x10: // BeginPlay 入口
$44: EX_CallFunction // 调用函数
$8: PrintString // 要调用的函数
$1F: String "Hello" // 参数
$B: EX_Nothing // 参数结束
$4: Return // 返回
$B: EX_Nothing
Label_0x30:
$53: EX_EndOfScript // 全部完成
栈机器
虚拟机是一个栈机器:
1
2
3
4
5
6
7
// 你的代码: A = B + 5
// 字节码:
Push B // 将 B 放到栈上
Push 5 // 将 5 放到栈上
Add // 弹出两个,相加,推送结果
Pop A // 将结果弹出到 A
大多数操作在虚拟栈上工作!
为什么偏移量重要
1
2
3
Label_0x10: CallFunction
Label_0x20: Return
Label_0x22: EX_EndOfScript
虚拟机使用字节偏移量进行跳转:
1
2
// 向前跳转 16 字节
JumpIfFalse 0x10 // 去到 Label_0x10
这全都是底层的指针算术!
阅读复杂逻辑
分支节点:
1
2
3
4
5
6
7
8
9
$7: EX_JumpIfNot // 如果条件为假
$0: Local bool Condition // 检查这个变量
Label_0x30 // 跳到这里
// True 路径
CallFunction(DoSomething)
Label_0x30: // False 路径
CallFunction(DoSomethingElse)
分支变成条件跳转!
快速要点
- 在 DefaultEngine.ini 中启用字节码输出
- $XX = EExprToken(指令/数据)
- Label_0xXX = 字节偏移 XX 处的跳转目标
- Ubergraph 从计算的跳转表开始
- 虚拟机是一个栈机器(推送/弹出操作)
- 函数调用显示函数 + 参数 + 结束标记
- 分支变成条件跳转
看见矩阵
一旦你启用了字节码输出,你就可以准确地看到你的蓝图变成了什么。就像看见矩阵 - 那些漂亮的节点只是底层原始字节码的外表!
想要更多细节?
完整的字节码深入剖析与真实示例:
下一篇:函数调用在字节码中如何工作!
🍿 BPVM 小食包系列
- ← #15: 优化解释
- #16: 阅读字节码 ← 你在这里
- #17: 字节码中的函数调用 →
本文由作者按照
CC BY 4.0
进行授权