[toc]

# Pilot 引擎 Github 与课程地址

课程: https://www.bilibili.com/video/BV14r4y1p7tt?spm_id_from=333.337.search-card.all.click

Pilot 引擎 Github: https://github.com/BoomingTech/Pilot

# 大体逻辑

先通过 reflection 和 serializer 中定义的宏在对应需要反射的类中声明反射的 Operator

REFLECTION_TYPE(Quaternion)
CLASS(Quaternion, Fields)
{
REFLECTION_BODY(Quaternion);
}

上面三个宏分别是:

#define REFLECTION_BODY(class_name) \
friend class Reflection::TypeFieldReflectionOparator::Type##class_name##Operator; \
friend class PSerializer;
// public: virtual std::string getTypeName() override {return #class_name;}
#define REFLECTION_TYPE(class_name) \
namespace Reflection \
{ \
namespace TypeFieldReflectionOparator \
{ \
class Type##class_name##Operator; \
} \
};
#define CLASS(class_name, …) class class_name

可以看,声明了 Type##class_name##Operator 对象,并且设置为友联,但是没有实现

实现是放在了预编译阶段生成的代码中了

比如  Quaternion.h  就会生成  quaternion.reflection.gen.h , 在生成的 all_reflection.h

那么这些代码在哪生成的呢?

在项目的 Pilot/engine/bin 目录下有三个子目录,Linux/macOS/Windows, 以 Windows 为例,Windos 目录下有一个文件 meta_parser.exe
这个文件就是用来生成 quaternion.reflection.gen.h 的,目录中还有一个文件, precompile.json , 记录的是需要检查的文件列表

我们知道 C 程序需要使用 cmake 来生成 VS 工程方便后续的代码,命令都放在 cmakeLists.txt 中,其中最外层的 CmakeLists.txt 最后几段有这样一段命令

set(CODEGEN_TARGET “PilotPreCompile”)
include(source/precompile/precompile.cmake)

可以看到引用到了 source/precompile/precompile.cmake
在实际的 precompile.cmake 文件中的最后几段有实际的生成反射需要的文件的命令

COMMAND
${CMAKE_COMMAND} -E echo "************************************************************* "
COMMAND
${CMAKE_COMMAND} -E echo "**** [Precompile] BEGIN "
COMMAND
${CMAKE_COMMAND} -E echo "************************************************************* "

COMMAND
PRECOMPILE_PARSER"{PRECOMPILE\_PARSER} "{PILOT_PRECOMPILE_PARAMS_PATH}" “PARSER_INPUT""{PARSER\_INPUT}" "{ENGINE_ROOT_DIR}/source” ${sys_include} “Pilot” S 0 0 0

# BUILDING ====================================================================================

COMMAND
${CMAKE_COMMAND} -E echo “+++ Precompile finished +++”

实际有用的是倒数第二条指令,其中这条指令中的每个参数都是在上文中得到的,都是绝对路径,在我的电脑环境中分别代表:

  • PRECOMPILE_PARSER : C:\G\GithubRepo\PilotTest\engine\bin\Windows\x64\meta_parser.exe
  • PILOT_PRECOMPILE_PARAMS_PATH : “C:\G\GithubRepo\PilotTest\engine\bin\precompile.json”
  • PARSER_INPUT : “parserHeader.h”
  • ${ENGINE_ROOT_DIR}/source : “C:\G\GithubRepo\PilotTest\engine\source”
  • sys_include : “*”

其中 PARSER_INPUT 代表的是根据之前的 PILOT_PRECOMPILE_PARAMS_PATH json 文件中所有需要引用到的文件的合并 #include
这些命令执行完成后,就会生成所有的反射和序列化代码,这些代码放在 source/_generated/ 目录下

然后后续对这些类的反射实际上都已经在编译期完成了.

# 反射的初始化

pilot 引擎会进入 PilotEditor 项目中的 main, 通过 main 函数会调用引擎内核 PilotEngine 类中的 startEngine 方法
startEngine 方法中的第一行就是每个类型的反射操作类 Type##className##Oprator 的注册

void PilotEngine::startEngine(const EngineInitParams& param)
{
Reflection::TypeMetaRegister::Register();
}

这个 Register 方法进入的是之前生成的文件中的一个整合的 .h 文件,即 all_reflection.h
这个类中的一部分如下:

void TypeMetaRegister::Register(){
TypeWrappersRegister::Quaternion();
TypeWrappersRegister::AnimNodeMap();
TypeWrappersRegister::AnimationAsset();
TypeWrappersRegister::AnimationChannel();
TypeWrappersRegister::AnimationClip();
TypeWrappersRegister::Vector3();
TypeWrappersRegister::RawBone();
}

至此,Pilot 的反射注册完毕,可以正常使用,但是实际上对反射类型的操作还是需要继续学习框架内的内容