联机版实现,先备份
This commit is contained in:
125
Tools/README.md
Normal file
125
Tools/README.md
Normal file
@ -0,0 +1,125 @@
|
||||
# UE头文件解析工具
|
||||
|
||||
这是一个用于扫描UE头文件并生成slua插件的emmy-lua注解的Python工具。
|
||||
|
||||
## 功能特性
|
||||
|
||||
- 自动扫描指定目录下的所有UE头文件(.h)
|
||||
- 解析UCLASS、USTRUCT、UENUM、UFUNCTION、UPROPERTY等宏
|
||||
- 生成符合emmy-lua标准的类型注解文件(.d.lua)
|
||||
- 支持C++类型到Lua类型的自动转换
|
||||
- 支持递归扫描子目录
|
||||
|
||||
## 支持的UE宏
|
||||
|
||||
- **UCLASS**: 解析类定义,包括继承关系
|
||||
- **USTRUCT**: 解析结构体定义和属性
|
||||
- **UENUM**: 解析枚举定义和值
|
||||
- **UFUNCTION**: 解析函数声明,包括参数和返回值
|
||||
- **UPROPERTY**: 解析属性声明
|
||||
- **DECLARE_DYNAMIC_DELEGATE**: 解析委托声明
|
||||
|
||||
## 安装和使用
|
||||
|
||||
### 前置要求
|
||||
|
||||
- Python 3.6+
|
||||
- 无需额外依赖
|
||||
|
||||
### 基本用法
|
||||
|
||||
```bash
|
||||
# 扫描Source目录并生成注解文件到Content/Lua/@types目录
|
||||
python ue_header_parser.py Source/BusyRabbit -o Content/Lua/@types
|
||||
|
||||
# 扫描当前目录并生成注解文件到同目录
|
||||
python ue_header_parser.py .
|
||||
|
||||
# 递归扫描整个项目
|
||||
python ue_header_parser.py Source --recursive -o Content/Lua/@types
|
||||
```
|
||||
|
||||
### 命令行参数
|
||||
|
||||
- `directory`: 要扫描的目录路径(必需)
|
||||
- `-o, --output`: 输出目录路径(可选,默认为源文件同目录)
|
||||
- `--recursive`: 递归扫描子目录(可选)
|
||||
|
||||
## 生成的注解格式
|
||||
|
||||
工具会根据UE头文件生成对应的emmy-lua注解:
|
||||
|
||||
### 类注解示例
|
||||
```lua
|
||||
---@class UInventoryComponent : ULuaActorComponent
|
||||
---@field Capacity integer
|
||||
---@field InventoryList table<FInventoryGrid>
|
||||
|
||||
---@param ItemID integer
|
||||
---@param Count integer
|
||||
---@return boolean
|
||||
function UInventoryComponent:IsCanContain(ItemID, Count) end
|
||||
```
|
||||
|
||||
### 结构体注解示例
|
||||
```lua
|
||||
---@class FInventoryGrid
|
||||
---@field ItemID integer
|
||||
---@field CurrentCount integer
|
||||
---@field MaxCount integer
|
||||
---@field Priority integer
|
||||
local FInventoryGrid = {}
|
||||
```
|
||||
|
||||
### 枚举注解示例
|
||||
```lua
|
||||
---@enum EBusyRoleState
|
||||
local EBusyRoleState = {
|
||||
BonfireIdle = 0,
|
||||
Searching = 1,
|
||||
Picking = 2,
|
||||
PickFinished = 3,
|
||||
BackBonfire = 4
|
||||
}
|
||||
```
|
||||
|
||||
### 委托注解示例
|
||||
```lua
|
||||
---@class FOnInventoryChanged
|
||||
---@field Call fun(ItemID: integer)
|
||||
local FOnInventoryChanged = {}
|
||||
```
|
||||
|
||||
## 类型映射
|
||||
|
||||
工具会自动将C++类型映射到Lua类型:
|
||||
|
||||
| C++类型 | Lua类型 |
|
||||
|---------|---------|
|
||||
| int32, int64 | integer |
|
||||
| float, double | number |
|
||||
| bool | boolean |
|
||||
| FString, FText, FName | string |
|
||||
| void | nil |
|
||||
| TArray, TMap, TSet | table |
|
||||
| 其他类型 | any |
|
||||
|
||||
## 注意事项
|
||||
|
||||
1. 工具会跳过没有UE宏的普通头文件
|
||||
2. 生成的注解文件会保存在`.d.lua`文件中
|
||||
3. 如果输出目录不存在,工具会自动创建
|
||||
4. 工具会处理编码问题,但建议确保头文件使用UTF-8编码
|
||||
5. 对于复杂的模板类型,工具会尝试解析内部类型
|
||||
|
||||
## 故障排除
|
||||
|
||||
如果遇到解析错误,请检查:
|
||||
- 头文件语法是否正确
|
||||
- UE宏格式是否符合标准
|
||||
- 文件编码是否为UTF-8
|
||||
- 是否有语法错误或缺失的分号
|
||||
|
||||
## 贡献
|
||||
|
||||
欢迎提交问题和改进建议!
|
||||
402
Tools/ue_header_parser.py
Normal file
402
Tools/ue_header_parser.py
Normal file
@ -0,0 +1,402 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
UE头文件解析工具
|
||||
用于扫描UE头文件并生成slua插件的emmy-lua注解
|
||||
"""
|
||||
|
||||
import os
|
||||
import re
|
||||
import argparse
|
||||
from pathlib import Path
|
||||
from typing import List, Dict, Set, Optional
|
||||
|
||||
class UEHeaderParser:
|
||||
def __init__(self):
|
||||
self.classes = []
|
||||
self.structs = []
|
||||
self.enums = []
|
||||
self.delegates = []
|
||||
|
||||
def parse_header_file(self, file_path: str) -> Dict:
|
||||
"""解析单个头文件"""
|
||||
with open(file_path, 'r', encoding='utf-8', errors='ignore') as f:
|
||||
content = f.read()
|
||||
|
||||
result = {
|
||||
'classes': [],
|
||||
'structs': [],
|
||||
'enums': [],
|
||||
'delegates': [],
|
||||
'file_path': file_path
|
||||
}
|
||||
|
||||
# 解析UCLASS
|
||||
class_pattern = r'UCLASS\s*\([^)]*\)\s*\n\s*class\s+[A-Z_]+\s+(\w+)\s*:\s*public\s+(\w+)'
|
||||
classes = re.findall(class_pattern, content)
|
||||
for class_name, parent_class in classes:
|
||||
result['classes'].append({
|
||||
'name': class_name,
|
||||
'parent': parent_class,
|
||||
'functions': self._parse_functions(content, class_name),
|
||||
'properties': self._parse_properties(content, class_name)
|
||||
})
|
||||
|
||||
# 解析USTRUCT
|
||||
struct_pattern = r'USTRUCT\s*\([^)]*\)\s*\n\s*struct\s+[A-Z_]+\s+(\w+)'
|
||||
structs = re.findall(struct_pattern, content)
|
||||
for struct_name in structs:
|
||||
result['structs'].append({
|
||||
'name': struct_name,
|
||||
'properties': self._parse_struct_properties(content, struct_name)
|
||||
})
|
||||
|
||||
# 解析USTRUCT (BlueprintType变体)
|
||||
struct_pattern2 = r'USTRUCT\s*\([^)]*\)\s*\n\s*struct\s+F(\w+)'
|
||||
structs2 = re.findall(struct_pattern2, content)
|
||||
for struct_name in structs2:
|
||||
result['structs'].append({
|
||||
'name': f'F{struct_name}',
|
||||
'properties': self._parse_struct_properties(content, f'F{struct_name}')
|
||||
})
|
||||
|
||||
# 解析UENUM
|
||||
enum_pattern = r'UENUM\s*\([^)]*\)\s*\n\s*enum\s+class\s+(\w+)'
|
||||
enums = re.findall(enum_pattern, content)
|
||||
for enum_name in enums:
|
||||
result['enums'].append({
|
||||
'name': enum_name,
|
||||
'values': self._parse_enum_values(content, enum_name)
|
||||
})
|
||||
|
||||
# 解析委托
|
||||
delegate_pattern = r'DECLARE_DYNAMIC_(MULTICAST_)?DELEGATE(?:_\w+)?\s*\(\s*(\w+)\s*'
|
||||
delegates = re.findall(delegate_pattern, content)
|
||||
for is_multicast, delegate_name in delegates:
|
||||
result['delegates'].append({
|
||||
'name': delegate_name,
|
||||
'is_multicast': bool(is_multicast),
|
||||
'params': self._parse_delegate_params(content, delegate_name)
|
||||
})
|
||||
|
||||
return result
|
||||
|
||||
def _parse_functions(self, content: str, class_name: str) -> List[Dict]:
|
||||
"""解析UFUNCTION"""
|
||||
functions = []
|
||||
|
||||
# 匹配UFUNCTION声明
|
||||
func_pattern = r'UFUNCTION\s*\([^)]*\)\s*\n\s*(\w+)\s+(\w+)\s*\(([^)]*)\)'
|
||||
matches = re.findall(func_pattern, content)
|
||||
|
||||
for return_type, func_name, params in matches:
|
||||
# 解析参数
|
||||
param_list = []
|
||||
if params.strip():
|
||||
for param in params.split(','):
|
||||
param = param.strip()
|
||||
if param:
|
||||
# 简单的参数解析
|
||||
parts = param.split()
|
||||
if len(parts) >= 2:
|
||||
param_type = parts[-2] if len(parts) > 2 else parts[0]
|
||||
param_name = parts[-1]
|
||||
param_list.append({
|
||||
'type': param_type,
|
||||
'name': param_name
|
||||
})
|
||||
|
||||
functions.append({
|
||||
'name': func_name,
|
||||
'return_type': return_type,
|
||||
'params': param_list
|
||||
})
|
||||
|
||||
return functions
|
||||
|
||||
def _parse_properties(self, content: str, class_name: str) -> List[Dict]:
|
||||
"""解析UPROPERTY"""
|
||||
properties = []
|
||||
|
||||
# 匹配UPROPERTY声明
|
||||
prop_pattern = r'UPROPERTY\s*\([^)]*\)\s*\n\s*(\w+(?:<[^>]*>)?)\s+(\w+);'
|
||||
matches = re.findall(prop_pattern, content)
|
||||
|
||||
for prop_type, prop_name in matches:
|
||||
properties.append({
|
||||
'name': prop_name,
|
||||
'type': prop_type
|
||||
})
|
||||
|
||||
return properties
|
||||
|
||||
def _parse_struct_properties(self, content: str, struct_name: str) -> List[Dict]:
|
||||
"""解析结构体属性"""
|
||||
properties = []
|
||||
|
||||
# 在结构体定义范围内查找属性
|
||||
struct_start = content.find(f'struct {struct_name}')
|
||||
if struct_start == -1:
|
||||
return properties
|
||||
|
||||
# 找到结构体结束位置
|
||||
brace_count = 0
|
||||
struct_content = ""
|
||||
for i in range(struct_start, len(content)):
|
||||
char = content[i]
|
||||
if char == '{':
|
||||
brace_count += 1
|
||||
elif char == '}':
|
||||
brace_count -= 1
|
||||
if brace_count == 0:
|
||||
struct_content = content[struct_start:i+1]
|
||||
break
|
||||
|
||||
if not struct_content:
|
||||
return properties
|
||||
|
||||
# 在结构体内容中查找UPROPERTY
|
||||
prop_pattern = r'UPROPERTY\s*\([^)]*\)\s*\n\s*(\w+(?:<[^>]*>)?)\s+(\w+);'
|
||||
matches = re.findall(prop_pattern, struct_content)
|
||||
|
||||
for prop_type, prop_name in matches:
|
||||
properties.append({
|
||||
'name': prop_name,
|
||||
'type': prop_type
|
||||
})
|
||||
|
||||
return properties
|
||||
|
||||
def _parse_enum_values(self, content: str, enum_name: str) -> List[Dict]:
|
||||
"""解析枚举值"""
|
||||
values = []
|
||||
|
||||
# 找到枚举定义
|
||||
enum_start = content.find(f'enum class {enum_name}')
|
||||
if enum_start == -1:
|
||||
return values
|
||||
|
||||
# 找到枚举内容
|
||||
brace_start = content.find('{', enum_start)
|
||||
if brace_start == -1:
|
||||
return values
|
||||
|
||||
brace_end = content.find('}', brace_start)
|
||||
if brace_end == -1:
|
||||
return values
|
||||
|
||||
enum_content = content[brace_start+1:brace_end]
|
||||
|
||||
# 解析枚举值
|
||||
value_pattern = r'(\w+)\s*(?:=\s*(\d+))?'
|
||||
matches = re.findall(value_pattern, enum_content)
|
||||
|
||||
current_value = 0
|
||||
for name, explicit_value in matches:
|
||||
if explicit_value:
|
||||
current_value = int(explicit_value)
|
||||
|
||||
values.append({
|
||||
'name': name,
|
||||
'value': current_value
|
||||
})
|
||||
|
||||
current_value += 1
|
||||
|
||||
return values
|
||||
|
||||
def _parse_delegate_params(self, content: str, delegate_name: str) -> List[Dict]:
|
||||
"""解析委托参数"""
|
||||
params = []
|
||||
|
||||
# 找到委托声明
|
||||
delegate_pattern = f'DECLARE_DYNAMIC_(MULTICAST_)?DELEGATE(?:_\\w+)?\\s*\\(\\s*{delegate_name}'
|
||||
match = re.search(delegate_pattern, content)
|
||||
if not match:
|
||||
return params
|
||||
|
||||
# 查找参数列表
|
||||
param_start = content.find('(', match.end())
|
||||
if param_start == -1:
|
||||
return params
|
||||
|
||||
param_end = content.find(')', param_start)
|
||||
if param_end == -1:
|
||||
return params
|
||||
|
||||
param_content = content[param_start+1:param_end]
|
||||
|
||||
# 解析参数
|
||||
param_parts = [p.strip() for p in param_content.split(',') if p.strip()]
|
||||
for i, param in enumerate(param_parts):
|
||||
parts = param.split()
|
||||
if len(parts) >= 2:
|
||||
param_type = ' '.join(parts[:-1])
|
||||
param_name = parts[-1]
|
||||
params.append({
|
||||
'type': param_type,
|
||||
'name': param_name
|
||||
})
|
||||
|
||||
return params
|
||||
|
||||
def generate_emmy_lua_annotations(self, parsed_data: Dict) -> str:
|
||||
"""生成emmy-lua注解"""
|
||||
output = []
|
||||
|
||||
# 文件头注释
|
||||
output.append(f'-- 自动生成的emmy-lua注解文件')
|
||||
output.append(f'-- 源文件: {parsed_data["file_path"]}')
|
||||
output.append('')
|
||||
|
||||
# 生成枚举注解
|
||||
for enum in parsed_data['enums']:
|
||||
output.append(f'---@enum {enum["name"]}')
|
||||
output.append(f'local {enum["name"]} = {{')
|
||||
for value in enum['values']:
|
||||
output.append(f' {value["name"]} = {value["value"]},')
|
||||
output.append('}')
|
||||
output.append('')
|
||||
|
||||
# 生成结构体注解
|
||||
for struct in parsed_data['structs']:
|
||||
output.append(f'---@class {struct["name"]}')
|
||||
for prop in struct['properties']:
|
||||
lua_type = self._cpp_to_lua_type(prop['type'])
|
||||
output.append(f'---@field {prop["name"]} {lua_type}')
|
||||
output.append(f'local {struct["name"]} = {{}}')
|
||||
output.append('')
|
||||
|
||||
# 生成类注解
|
||||
for cls in parsed_data['classes']:
|
||||
output.append(f'---@class {cls["name"]} : {cls["parent"]}')
|
||||
|
||||
# 添加属性
|
||||
for prop in cls['properties']:
|
||||
lua_type = self._cpp_to_lua_type(prop['type'])
|
||||
output.append(f'---@field {prop["name"]} {lua_type}')
|
||||
|
||||
# 添加方法
|
||||
for func in cls['functions']:
|
||||
lua_return_type = self._cpp_to_lua_type(func['return_type'])
|
||||
param_annotations = []
|
||||
for param in func['params']:
|
||||
lua_param_type = self._cpp_to_lua_type(param['type'])
|
||||
param_annotations.append(f'---@param {param["name"]} {lua_param_type}')
|
||||
|
||||
if param_annotations:
|
||||
output.extend(param_annotations)
|
||||
output.append(f'---@return {lua_return_type}')
|
||||
output.append(f'function {cls["name"]}:{func["name"]}({", ".join(p["name"] for p in func["params"])}) end')
|
||||
output.append('')
|
||||
|
||||
output.append('')
|
||||
|
||||
# 生成委托注解
|
||||
for delegate in parsed_data['delegates']:
|
||||
output.append(f'---@class {delegate["name"]}')
|
||||
param_types = []
|
||||
for param in delegate['params']:
|
||||
lua_type = self._cpp_to_lua_type(param['type'])
|
||||
param_types.append(f'{param["name"]}: {lua_type}')
|
||||
|
||||
if param_types:
|
||||
output.append(f'---@field Call fun({", ".join(param_types)})')
|
||||
else:
|
||||
output.append('---@field Call fun()')
|
||||
output.append(f'local {delegate["name"]} = {{}}')
|
||||
output.append('')
|
||||
|
||||
return '\n'.join(output)
|
||||
|
||||
def _cpp_to_lua_type(self, cpp_type: str) -> str:
|
||||
"""将C++类型转换为Lua类型"""
|
||||
type_mapping = {
|
||||
'int32': 'integer',
|
||||
'int64': 'integer',
|
||||
'float': 'number',
|
||||
'double': 'number',
|
||||
'bool': 'boolean',
|
||||
'FString': 'string',
|
||||
'FText': 'string',
|
||||
'FName': 'string',
|
||||
'void': 'nil',
|
||||
'TArray': 'table',
|
||||
'TMap': 'table',
|
||||
'TSet': 'table'
|
||||
}
|
||||
|
||||
# 处理模板类型
|
||||
if '<' in cpp_type and '>' in cpp_type:
|
||||
base_type = cpp_type.split('<')[0]
|
||||
inner_type = cpp_type.split('<')[1].split('>')[0]
|
||||
lua_inner_type = self._cpp_to_lua_type(inner_type)
|
||||
return f'{type_mapping.get(base_type, "any")}<{lua_inner_type}>'
|
||||
|
||||
return type_mapping.get(cpp_type, 'any')
|
||||
|
||||
def scan_directory(self, directory: str, output_dir: str = None):
|
||||
"""扫描目录或单个文件并生成注解文件"""
|
||||
header_files = []
|
||||
|
||||
if os.path.isfile(directory) and directory.endswith('.h'):
|
||||
# 单个文件
|
||||
header_files = [directory]
|
||||
elif os.path.isdir(directory):
|
||||
# 目录
|
||||
for root, dirs, files in os.walk(directory):
|
||||
for file in files:
|
||||
if file.endswith('.h'):
|
||||
header_files.append(os.path.join(root, file))
|
||||
else:
|
||||
print(f'错误: {directory} 不是有效的文件或目录')
|
||||
return
|
||||
|
||||
print(f'找到 {len(header_files)} 个头文件')
|
||||
|
||||
for header_file in header_files:
|
||||
try:
|
||||
print(f'正在解析: {header_file}')
|
||||
parsed_data = self.parse_header_file(header_file)
|
||||
|
||||
if any([parsed_data['classes'], parsed_data['structs'], parsed_data['enums'], parsed_data['delegates']]):
|
||||
annotations = self.generate_emmy_lua_annotations(parsed_data)
|
||||
|
||||
# 确定输出文件路径
|
||||
if output_dir:
|
||||
if os.path.isfile(directory):
|
||||
# 单个文件的情况
|
||||
output_file = os.path.join(output_dir, os.path.basename(header_file).replace('.h', '.d.lua'))
|
||||
else:
|
||||
# 目录的情况
|
||||
relative_path = os.path.relpath(header_file, directory)
|
||||
output_file = os.path.join(output_dir, relative_path.replace('.h', '.d.lua'))
|
||||
os.makedirs(os.path.dirname(output_file), exist_ok=True)
|
||||
else:
|
||||
output_file = header_file.replace('.h', '.d.lua')
|
||||
|
||||
with open(output_file, 'w', encoding='utf-8') as f:
|
||||
f.write(annotations)
|
||||
|
||||
print(f'已生成: {output_file}')
|
||||
|
||||
except Exception as e:
|
||||
print(f'解析文件 {header_file} 时出错: {e}')
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description='UE头文件解析工具 - 生成slua插件的emmy-lua注解')
|
||||
parser.add_argument('directory', help='要扫描的目录路径')
|
||||
parser.add_argument('-o', '--output', help='输出目录路径(默认为源文件同目录)')
|
||||
parser.add_argument('--recursive', action='store_true', help='递归扫描子目录')
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
if not os.path.exists(args.directory):
|
||||
print(f'错误: 目录 {args.directory} 不存在')
|
||||
return
|
||||
|
||||
parser = UEHeaderParser()
|
||||
parser.scan_directory(args.directory, args.output)
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
104
Tools/使用示例.md
Normal file
104
Tools/使用示例.md
Normal file
@ -0,0 +1,104 @@
|
||||
# UE头文件解析工具使用示例
|
||||
|
||||
## 基本用法
|
||||
|
||||
### 扫描整个项目
|
||||
```bash
|
||||
python Tools/ue_header_parser.py Source/BusyRabbit/Public -o Content/Lua/@types
|
||||
```
|
||||
|
||||
### 扫描特定目录
|
||||
```bash
|
||||
# 扫描Components目录
|
||||
python Tools/ue_header_parser.py Source/BusyRabbit/Public/Components -o Content/Lua/@types
|
||||
|
||||
# 扫描Level目录
|
||||
python Tools/ue_header_parser.py Source/BusyRabbit/Public/Level -o Content/Lua/@types
|
||||
```
|
||||
|
||||
### 扫描单个文件
|
||||
```bash
|
||||
# 直接指定文件路径(需要先确保输出目录存在)
|
||||
python Tools/ue_header_parser.py Source/BusyRabbit/Public/Components/InventoryComponent.h -o Content/Lua/@types
|
||||
```
|
||||
|
||||
## 生成结果示例
|
||||
|
||||
### 输入头文件 (InventoryComponent.h)
|
||||
```cpp
|
||||
USTRUCT(BlueprintType)
|
||||
struct FInventoryGrid {
|
||||
GENERATED_BODY()
|
||||
public:
|
||||
UPROPERTY(BlueprintReadOnly, DisplayName = "物品ID")
|
||||
int32 ItemID;
|
||||
|
||||
UPROPERTY(BlueprintReadWrite, DisplayName = "当前的数量")
|
||||
int32 CurrentCount;
|
||||
|
||||
UPROPERTY(BlueprintReadWrite, DisplayName = "最大堆叠限制")
|
||||
int32 MaxCount;
|
||||
|
||||
UPROPERTY(BlueprintReadWrite, DisplayName = "优先级")
|
||||
int32 Priority;
|
||||
};
|
||||
|
||||
UCLASS()
|
||||
class BUSYRABBIT_API UInventoryComponent : public ULuaActorComponent {
|
||||
GENERATED_BODY()
|
||||
public:
|
||||
UFUNCTION(BlueprintCallable)
|
||||
bool IsCanContain(int32 ItemID, int32 Count);
|
||||
|
||||
// ... 其他函数和属性
|
||||
};
|
||||
```
|
||||
|
||||
### 输出注解文件 (InventoryComponent.d.lua)
|
||||
```lua
|
||||
-- 自动生成的emmy-lua注解文件
|
||||
-- 源文件: Source/BusyRabbit/Public/Components\InventoryComponent.h
|
||||
|
||||
---@class FInventoryGrid
|
||||
---@field ItemID integer
|
||||
---@field CurrentCount integer
|
||||
---@field MaxCount integer
|
||||
---@field Priority integer
|
||||
local FInventoryGrid = {}
|
||||
|
||||
---@class UInventoryComponent : ULuaActorComponent
|
||||
---@field Capacity integer
|
||||
---@field InventoryList table<any>
|
||||
---@param ItemID integer
|
||||
---@param Count integer
|
||||
---@return boolean
|
||||
function UInventoryComponent:IsCanContain(ItemID, Count) end
|
||||
|
||||
-- ... 其他函数注解
|
||||
```
|
||||
|
||||
## 集成到开发流程
|
||||
|
||||
### 1. 定期生成注解
|
||||
建议在每次UE头文件更新后运行工具重新生成注解。
|
||||
|
||||
### 2. 版本控制
|
||||
将生成的`.d.lua`文件添加到版本控制中,方便团队共享。
|
||||
|
||||
### 3. IDE配置
|
||||
确保IDE(如VSCode)能够识别`Content/Lua/@types`目录中的注解文件。
|
||||
|
||||
## 注意事项
|
||||
|
||||
1. **类型映射**: 工具会自动将C++类型映射到Lua类型
|
||||
2. **模板类型**: 支持`TArray<FInventoryGrid>`等模板类型的解析
|
||||
3. **委托支持**: 自动生成委托类型的Call函数注解
|
||||
4. **错误处理**: 工具会跳过无法解析的文件并继续处理其他文件
|
||||
|
||||
## 故障排除
|
||||
|
||||
如果遇到问题,请检查:
|
||||
- 头文件语法是否正确
|
||||
- UE宏格式是否符合标准
|
||||
- 输出目录权限是否足够
|
||||
- Python版本是否为3.6+
|
||||
Reference in New Issue
Block a user