本文档介绍的是是普通的C++ class/struct,或者是UCLASS,USTRUCT中未标记UPPROPERTY,UFUNCTION的属性、方法的调用。
本文档也适用于非UE环境下(比如服务器,Unity等),C++ class/struct的访问。
支持特性:
- 构造函数
- 静态函数
- 成员变量
- 成员函数
- 构造函数/静态函数/成员函数,均支持重载
- 支持继承
- 可生成typescript声明
- UE类型未标记UPPROPERTY,UFUNCTION成员声明后,会无缝出现于原类声明中
- 支持Js对象映射到C++的JsObject,JsObject可以获取/设置属性,调用Js函数。
- 支持Js函数映射到std::function
- 支持自定义转换器
如果希望在JsEnv之外的地方使用该特性,比如游戏模块,需要:
-
使用动态库版本的v8库,切换方法:
-
到puerts官网下载和puerts配套的v8库,解压于:“Plugins/Puerts/ThirdParty/”目录下
-
找到JsEnv.Build.cs文件,将UseNewV8变量改为true
-
-
在该模块的“.Build.cs”文件中加入对JsEnv模块的依赖
-
该模块的“.Build.cs”文件中,将bEnableUndefinedIdentifierWarnings设置为false
以一个最简单的普通c++ class为例
//Calc.h
class Calc
{
public:
static int32_t Add(int32_t a, int32_t b)
{
return a + b;
}
};
我们按如下方式声明
#include "Calc.h"
#include "Binding.hpp"
UsingCppType(Calc);
struct AutoRegisterForCPP
{
AutoRegisterForCPP()
{
puerts::DefineClass<Calc>()
.Function("Add", MakeFunction(&Calc::Add))
.Register();
}
};
AutoRegisterForCPP _AutoRegisterForCPP__;
(编译,进入UE界面,点击生成按钮)即可在TypeScript中调用
import * as cpp from 'cpp'
let Calc = cpp.Calc;
//static function
console.log(Calc.Add(12, 34));
说明:
-
使用到的C++类用UsingCppType前置声明
-
注册类的成员信息,基本模式是:
puerts::DefineClass<YouClass>().Function/Method/Property().Register();
-
静态AutoRegisterForCPP类型变量,只是为了利用C++的机制完成自动注册,实际上注册语句可放在脚本调用前执行的任意合法C++代码中。
上章节演示的就是静态函数,用.Function(名字,函数引用)注册静态函数,函数引用有几种方式:
-
函数没有重载:MakeFunction(&Calc::Add)
-
函数有重载,只希望选择其中一个:SelectFunction(float (*)(float, float), &Calc::Add)
-
函数有重载,希望都选择:
CombineOverloads(
MakeOverload(void(*)(), &TestClass::Overload),
MakeOverload(void(*)(int32_t), &TestClass::Overload),
MakeOverload(void(*)(int32_t, int32_t), &TestClass::Overload),
MakeOverload(void(*)(std::string, int32_t), &TestClass::Overload)
)
- 函数没有重载,但希望校验参数:MakeCheckFunction(&Calc::Add)
class TestClass
{
public:
int32_t X;
int32_t Y;
};
声明:
puerts::DefineClass<TestClass>()
.Property("X", MakeProperty(&TestClass::X))
.Property("Y", MakeProperty(&TestClass::Y))
.Register();
class TestClass
{
public:
TestClass();
TestClass(int32_t InX, int32_t InY);
};
声明:
puerts::DefineClass<TestClass>()
.Constructor(CombineConstructors(
MakeConstructor(TestClass, int32_t, int32_t),
MakeConstructor(TestClass)
))
.Register();
如果只有一个构造函数,可以简化
puerts::DefineClass<AdvanceTestClass>()
.Constructor<int>() //if only one Constructor
.Register();
class TestClass
{
public:
int32_t OverloadMethod();
int32_t OverloadMethod(int32_t a);
uint32_t OverloadMethod(uint32_t a);
int64_t OverloadMethod(int64_t a);
TestClass *GetSelf();
};
声明
puerts::DefineClass<TestClass>()
.Method("OverloadMethod", CombineOverloads(
MakeOverload(int32_t(TestClass::*)(), &TestClass::OverloadMethod),
MakeOverload(int32_t(TestClass::*)(int32_t), &TestClass::OverloadMethod),
MakeOverload(uint32_t(TestClass::*)(uint32_t), &TestClass::OverloadMethod),
MakeOverload(int64_t(TestClass::*)(int64_t), &TestClass::OverloadMethod)
))
.Method("GetSelf", MakeFunction(&TestClass::GetSelf))
.Register();
class BaseClass
{
public:
void Foo(int p);
};
class TestClass : public BaseClass
{
public:
};
声明
puerts::DefineClass<BaseClass>()
.Method("Foo", MakeFunction(&BaseClass::Foo))
.Register();
puerts::DefineClass<TestClass>()
.Extends<BaseClass>()
.Register();
直接在MakeFunction、SelectFunction的函数参数后面加默认值即可。
比如:
void SetNextWindowPos(const ImVec2& pos, ImGuiCond cond = 0, const ImVec2& pivot = ImVec2(0, 0));
声明:
Function("SetNextWindowPos", MakeFunction(&ImGui::SetNextWindowPos, 0, ImVec2(0,0)))
比如:
bool Selectable(const char* label, bool selected = false, ImGuiSelectableFlags flags = 0, const ImVec2& size = ImVec2(0, 0));
bool Selectable(const char* label, bool* p_selected, ImGuiSelectableFlags flags = 0, const ImVec2& size = ImVec2(0, 0));
得分两步
1、前置声明(和UsingCppType放置的位置一样)
DeclOverloads(ImGui_Selectable);
DeclOverload(ImGui_Selectable, bool (*)(const char*, bool, ImGuiSelectableFlags, const ImVec2&), &ImGui::Selectable, false, 0, ImVec2(0,0));
DeclOverload(ImGui_Selectable, bool (*)(const char*, bool*, ImGuiSelectableFlags, const ImVec2&), &ImGui::Selectable, 0, ImVec2(0,0));
2、注册
Function("Selectable", CombineOverloads(
SelectOverload(ImGui_Selectable, bool (*)(const char*, bool, ImGuiSelectableFlags, const ImVec2&)),
SelectOverload(ImGui_Selectable, bool (*)(const char*, bool*, ImGuiSelectableFlags, const ImVec2&))
))
类似C# extension method,用一个外部静态函数来模拟成员函数。
和C# extension method也类似,要求该外部静态函数的第一个参数必须是被扩展类型的引用。
以为TSharedPtr添加Equals方法为例
template <class T>
struct TSharedPtrExtension
{
static bool Equals(const TSharedPtr<T> Lhs, const TSharedPtr<T> Rhs)
{
return Lhs == Rhs;
}
};
#define RegisterTSharedPtr(ITEMCLS) \
puerts::DefineClass<TSharedPtr<ITEMCLS>>().Method("Equals", MakeExtension(&TSharedPtrExtension<ITEMCLS>::Equals)).Register();
用MakeExtension声明,参数是一个合格的外部静态函数。
#include "JsObject.h"
class AdvanceTestClass
{
public:
AdvanceTestClass(int A);
void JsObjectTest(FJsObject Object);
};
void AdvanceTestClass::JsObjectTest(FJsObject Object)
{
auto P = Object.Get<int>("p");
UE_LOG(LogTemp, Warning, TEXT("AdvanceTestClass::JsObjectTest({p:%d})"), P);
Object.Set<std::string>("q", "john");
}
在typescript中使用
import * as cpp from 'cpp'
//js object
let obj = new cpp.AdvanceTestClass(100);
let j:any = {p:100};
obj.JsObjectTest(j);
console.log(j.q);
//class decl ...
void AdvanceTestClass::CallJsObjectTest(FJsObject Object)
{
auto Ret = Object.Func<float>(1024, "che");
UE_LOG(LogTemp, Warning, TEXT("AdvanceTestClass::CallJsObjectTest Callback Ret %f"), Ret);
}
在typescript中使用
let obj = new cpp.AdvanceTestClass(100);
obj.CallJsObjectTest((i, str) => {
console.log(i, str);
return 1.01;
})
//class decl ...
void AdvanceTestClass::StdFunctionTest(std::function<int(int, int)> Func)
{
int Ret = Func(88, 99);
UE_LOG(LogTemp, Warning, TEXT("AdvanceTestClass::StdFunctionTest Callback Ret %d"), Ret);
}
在typescript中使用
let obj = new cpp.AdvanceTestClass(100);
obj.StdFunctionTest((x:number, y:number) => {
console.log('x=' + x + ",y=" + y);
return x + y;
})
目前仅支持TArray以及TSharedPtr
- 声明:
UsingContainer(TArray<FVector>);
- 注册:
RegisterTArray(FVector);
- 声明:
UsingTSharedPtr(FVector);
- 注册:
RegisterTSharedPtr(FVector);
比如UObject的CreateDefaultSubobject、GetName、GetOuter、GetClass、GetWorld方法,均不是UFunction
添加如下声明:
#include "CoreMinimal.h"
#include "Binding.hpp"
#include "UEDataBinding.hpp"
UsingUClass(UObject)
UsingUClass(UWorld) // for return type
UsingUClass(UClass)
UsingUClass(USceneComponent)
puerts::DefineClass<UObject>()
#if ENGINE_MAJOR_VERSION >= 4 && ENGINE_MINOR_VERSION >= 23
.Method("CreateDefaultSubobject", SelectFunction(UObject* (UObject::*)(FName, UClass*, UClass*, bool , bool), &UObject::CreateDefaultSubobject))
#else
.Method("CreateDefaultSubobject", SelectFunction(UObject* (UObject::*)(FName, UClass*, UClass*, bool, bool, bool), &UObject::CreateDefaultSubobject))
#endif
.Method("GetName", SelectFunction(FString (UObjectBaseUtility::*)() const, &UObjectBaseUtility::GetName))
.Method("GetOuter", MakeFunction(&UObject::GetOuter))
.Method("GetClass", MakeFunction(&UObject::GetClass))
.Method("GetWorld", MakeFunction(&UObject::GetWorld))
.Register();
注意:和普通c++类不一样,如果是一个UClass,需要使用UsingUClass前置声明,类似的如果是UStruct,需要用UsingUStruct
重新生成ue.d.ts,可以看到上述方法已经添加到UE.Object的声明
class Object {
constructor(Outer?: Object, Name?: string, ObjectFlags?: number);
ExecuteUbergraph(EntryPoint: number): void;
CreateDefaultSubobject(p0: string, p1: $Nullable<Class>, p2: $Nullable<Class>, p3: boolean, p4: boolean) : Object;
GetName() : string;
GetOuter() : Object;
GetClass() : Class;
GetWorld() : World;
static StaticClass(): Class;
static Find(OrigInName: string, Outer?: Object): Object;
static Load(InName: string): Object;
}
后续可以直接在Object对象上使用上述方法。