Init
This commit is contained in:
@@ -0,0 +1,113 @@
|
||||
# ScriptConstant
|
||||
|
||||
- **功能描述:** 把一个静态函数的返回值包装成为一个常量值。
|
||||
- **使用位置:** UFUNCTION
|
||||
- **引擎模块:** Script
|
||||
- **元数据类型:** string="abc"
|
||||
- **关联项:** [ScriptConstantHost](ScriptConstantHost.md)
|
||||
- **常用程度:** ★★★
|
||||
|
||||
把一个静态函数的返回值包装成为一个常量值。
|
||||
|
||||
- 函数的名字即为常量的默认名称,但ScriptConstant也可以额外提供一个自定义名称。
|
||||
- 常量作用域默认存在于该静态函数的外部类中,但也可以通过ScriptConstantHost来指定到另外一个类型中。
|
||||
|
||||
## 测试代码:
|
||||
|
||||
```cpp
|
||||
USTRUCT(BlueprintType)
|
||||
struct INSIDER_API FMyPythonConstantStruct
|
||||
{
|
||||
GENERATED_BODY()
|
||||
public:
|
||||
UPROPERTY(BlueprintReadWrite, EditAnywhere)
|
||||
FString MyString;
|
||||
};
|
||||
|
||||
UCLASS(Blueprintable, BlueprintType)
|
||||
class INSIDER_API UMyPython_ConstantOwner :public UObject
|
||||
{
|
||||
GENERATED_BODY()
|
||||
public:
|
||||
};
|
||||
|
||||
UCLASS(Blueprintable, BlueprintType)
|
||||
class INSIDER_API UMyPython_Constant_Test :public UObject
|
||||
{
|
||||
GENERATED_BODY()
|
||||
public:
|
||||
UFUNCTION(BlueprintPure, meta = (ScriptConstant))
|
||||
static int32 MyIntConst() { return 123; }
|
||||
|
||||
UFUNCTION(BlueprintPure, meta = (ScriptConstant = "MyOtherIntConst"))
|
||||
static int32 MyIntConst2() { return 456; }
|
||||
|
||||
UFUNCTION(BlueprintPure, meta = (ScriptConstant))
|
||||
static FMyPythonConstantStruct MyStructConst() { return FMyPythonConstantStruct{ TEXT("Hello") }; }
|
||||
|
||||
UFUNCTION(BlueprintPure, meta = (ScriptConstant = "MyOtherStructConst"))
|
||||
static FMyPythonConstantStruct MyStructConst2() { return FMyPythonConstantStruct{ TEXT("World") }; }
|
||||
|
||||
public:
|
||||
UFUNCTION(BlueprintPure, meta = (ScriptConstant="FirstString", ScriptConstantHost = "/Script/Insider.MyPython_ConstantOwner"))
|
||||
static FString MyStringConst() { return TEXT("First"); }
|
||||
****};
|
||||
|
||||
```
|
||||
|
||||
## 生成的Py代码:
|
||||
|
||||
```cpp
|
||||
class MyPython_Constant_Test(Object):
|
||||
r"""
|
||||
My Python Constant Test
|
||||
|
||||
**C++ Source:**
|
||||
|
||||
- **Module**: Insider
|
||||
- **File**: MyPython_ScriptConstant.h
|
||||
|
||||
"""
|
||||
MY_OTHER_STRUCT_CONST: MyPythonConstantStruct #: (MyPythonConstantStruct): My Struct Const 2
|
||||
MY_STRUCT_CONST: MyPythonConstantStruct #: (MyPythonConstantStruct): My Struct Const
|
||||
MY_OTHER_INT_CONST: int #: (int32): My Int Const 2
|
||||
MY_INT_CONST: int #: (int32): My Int Const
|
||||
|
||||
class MyPython_ConstantOwner(Object):
|
||||
r"""
|
||||
**My Python Constant Owner
|
||||
|
||||
**C++ Source:**
|
||||
|
||||
- **Module**: Insider
|
||||
- **File**: MyPython_ScriptConstant.h
|
||||
|
||||
"""
|
||||
FIRST_STRING: str #: (str): My String Const
|
||||
```
|
||||
|
||||
## 运行的结果:
|
||||
|
||||
可见在类中生成了相应的常量。而MyStringConst因为指定了ScriptConstantHost 而生成在别的类中。
|
||||
|
||||
```cpp
|
||||
LogPython: print(unreal.MyPython_Constant_Test.MY_INT_CONST)
|
||||
LogPython: 123
|
||||
LogPython: print(unreal.MyPython_Constant_Test.MY_OTHER_INT_CONST)
|
||||
LogPython: 456
|
||||
LogPython: print(unreal.MyPython_Constant_Test.MY_OTHER_STRUCT_CONST)
|
||||
LogPython: <Struct 'MyPythonConstantStruct' (0x00000A0FC4051F00) {my_string: "World"}>
|
||||
LogPython: print(unreal.MyPython_Constant_Test.MY_STRUCT_CONST)
|
||||
LogPython: <Struct 'MyPythonConstantStruct' (0x00000A0FC4051EA0) {my_string: "Hello"}>
|
||||
LogPython: print(unreal.MyPython_ConstantOwner.FIRST_STRING)
|
||||
LogPython: First
|
||||
```
|
||||
|
||||
## 原理:
|
||||
|
||||
生成的逻辑在这个GenerateWrappedConstant 函数里。
|
||||
|
||||
```cpp
|
||||
auto GenerateWrappedConstant = [this, &GeneratedWrappedType, &OutGeneratedWrappedTypeReferences, &OutDirtyModules](const UFunction* InFunc)
|
||||
{}
|
||||
```
|
@@ -0,0 +1,17 @@
|
||||
# ScriptConstantHost
|
||||
|
||||
- **功能描述:** 在ScriptConstant的基础上,指定常量生成的所在类型。
|
||||
- **使用位置:** UFUNCTION
|
||||
- **引擎模块:** Script
|
||||
- **元数据类型:** string="abc"
|
||||
- **关联项:** [ScriptConstant](ScriptConstant.md)
|
||||
- **常用程度:** ★
|
||||
|
||||
在ScriptConstant的基础上,指定常量生成的所在类型。
|
||||
|
||||
测试代码见ScriptConstant。ScriptConstantHost指定的字符串应该是个对象路径。
|
||||
|
||||
```cpp
|
||||
UFUNCTION(BlueprintPure, meta = (ScriptConstant="FirstString", ScriptConstantHost = "/Script/Insider.MyPython_ConstantOwner"))
|
||||
static FString MyStringConst() { return TEXT("First"); }
|
||||
```
|
@@ -0,0 +1,9 @@
|
||||
# ScriptDefaultBreak
|
||||
|
||||
- **使用位置:** USTRUCT
|
||||
- **引擎模块:** Script
|
||||
- **元数据类型:** bool
|
||||
- **关联项:** [ScriptDefaultMake](ScriptDefaultMake.md)
|
||||
- **常用程度:** ★
|
||||
|
||||
见ScriptDefaultMake的原理和测试代码。
|
@@ -0,0 +1,173 @@
|
||||
# ScriptDefaultMake
|
||||
|
||||
- **功能描述:** 禁用结构上的HasNativeMake,在脚本里构造的时候不调用C++里的NativeMake函数,而采用脚本内建的默认初始化方式。
|
||||
- **使用位置:** USTRUCT
|
||||
- **引擎模块:** Script
|
||||
- **元数据类型:** bool
|
||||
- **关联项:** [ScriptDefaultBreak](ScriptDefaultBreak.md)
|
||||
- **常用程度:** ★
|
||||
|
||||
禁用结构上的HasNativeMake,在脚本里构造的时候不调用C++里的NativeMake函数,而采用脚本内建的默认初始化方式。
|
||||
|
||||
ScriptDefaultBreak也是同理。
|
||||
|
||||
## 测试代码:
|
||||
|
||||
```cpp
|
||||
|
||||
USTRUCT(BlueprintType, meta = (ScriptDefaultMake, ScriptDefaultBreak,HasNativeMake = "/Script/Insider.MyPython_MakeBreak_Test.MyNativeMake", HasNativeBreak = "/Script/Insider.MyPython_MakeBreak_Test.MyNativeBreak"))
|
||||
struct INSIDER_API FMyPythonMBStructNative
|
||||
{
|
||||
GENERATED_BODY()
|
||||
public:
|
||||
UPROPERTY(BlueprintReadWrite, EditAnywhere)
|
||||
int32 MyInt = 0;
|
||||
|
||||
UPROPERTY(BlueprintReadWrite, EditAnywhere)
|
||||
FString MyString;
|
||||
};
|
||||
|
||||
UCLASS(Blueprintable, BlueprintType)
|
||||
class INSIDER_API UMyPython_MakeBreak_Test :public UObject
|
||||
{
|
||||
GENERATED_BODY()
|
||||
public:
|
||||
UFUNCTION(BlueprintPure, meta = ())
|
||||
static FMyPythonMBStructNative MyNativeMake(int32 InInt) { return FMyPythonMBStructNative{ InInt,TEXT("Hello") }; }
|
||||
|
||||
UFUNCTION(BlueprintPure, meta = ())
|
||||
static void MyNativeBreak(const FMyPythonMBStructNative& InStruct, int& outInt) { outInt = InStruct.MyInt + 123; }
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
## 生成的py代码:
|
||||
|
||||
无论有没有加ScriptDefaultMake, ScriptDefaultBreak,MyPythonMBStructNative生成的py代码其实是一样的。不同点在于构成和to_tuple时候的结果不同。
|
||||
|
||||
```cpp
|
||||
class MyPythonMBStructNative(StructBase):
|
||||
r"""
|
||||
My Python MBStruct Native
|
||||
|
||||
**C++ Source:**
|
||||
|
||||
- **Module**: Insider
|
||||
- **File**: MyPython_ScriptMakeBreak.h
|
||||
|
||||
**Editor Properties:** (see get_editor_property/set_editor_property)
|
||||
|
||||
- ``my_int`` (int32): [Read-Write]
|
||||
- ``my_string`` (str): [Read-Write]
|
||||
"""
|
||||
def __init__(self, int: int = 0) -> None:
|
||||
...
|
||||
@property
|
||||
def my_int(self) -> int:
|
||||
r"""
|
||||
(int32): [Read-Write]
|
||||
"""
|
||||
...
|
||||
@my_int.setter
|
||||
def my_int(self, value: int) -> None:
|
||||
...
|
||||
@property
|
||||
def my_string(self) -> str:
|
||||
r"""
|
||||
(str): [Read-Write]
|
||||
"""
|
||||
...
|
||||
@my_string.setter
|
||||
def my_string(self, value: str) -> None:
|
||||
...
|
||||
```
|
||||
|
||||
## 运行的结果:
|
||||
|
||||
- 第二段是加了ScriptDefaultMake, ScriptDefaultBreak后的效果。我故意在C++的Make和Break函数里做了一些不一样,可以观察到调用到C++里的函数。
|
||||
- 第一段是在代码里加上ScriptDefaultMake, ScriptDefaultBreak后(保留HasNativeMake,HasNativeBreak)调用的结果,可见C++里的Make/Break函数就没有再被调用到了。
|
||||
|
||||
```cpp
|
||||
LogPython: b=unreal.MyPythonMBStructNative()
|
||||
LogPython: print(b)
|
||||
LogPython: <Struct 'MyPythonMBStructNative' (0x0000085F2EE9E680) {my_int: 0, my_string: "Hello"}>
|
||||
LogPython: print(b.to_tuple())
|
||||
LogPython: (123,)
|
||||
|
||||
LogPython: b=unreal.MyPythonMBStructNative()
|
||||
LogPython: print(b)
|
||||
LogPython: <Struct 'MyPythonMBStructNative' (0x000005E6C3AAFDC0) {my_int: 0, my_string: ""}>
|
||||
LogPython: print(b.to_tuple())
|
||||
LogPython: (0, '')
|
||||
```
|
||||
|
||||
## 原理:
|
||||
|
||||
在FindMakeBreakFunction函数里,如果发现有ScriptDefaultMake或ScriptDefaultBreak标记,就不去使用C++里由HasNativeMake,HasNativeBreak指定的函数。
|
||||
|
||||
另外py里的结构初始化会调用到默认的init或者结构的make函数,而to_tuple就相当于break的作用,会调用到默认的每个属性to_tuple或者是结构的自定义break函数。
|
||||
|
||||
```cpp
|
||||
const FName ScriptDefaultMakeMetaDataKey = TEXT("ScriptDefaultMake");
|
||||
const FName ScriptDefaultBreakMetaDataKey = TEXT("ScriptDefaultBreak");
|
||||
|
||||
namespace UE::Python
|
||||
{
|
||||
|
||||
/**
|
||||
* Finds the UFunction corresponding to the name specified by 'HasNativeMake' or 'HasNativeBreak' meta data key.
|
||||
* @param The structure to inspect for the 'HasNativeMake' or 'HasNativeBreak' meta data keys.
|
||||
* @param InNativeMetaDataKey The native meta data key name. Can only be 'HasNativeMake' or 'HasNativeBreak'.
|
||||
* @param InScriptDefaultMetaDataKey The script default meta data key name. Can only be 'ScriptDefaultMake' or 'ScriptDefaultBreak'.
|
||||
* @param NotFoundFn Function invoked if the structure specifies as Make or Break function, but the function couldn't be found.
|
||||
* @return The function, if the struct has the meta key and if the function was found. Null otherwise.
|
||||
*/
|
||||
template<typename NotFoundFuncT>
|
||||
UFunction* FindMakeBreakFunction(const UScriptStruct* InStruct, const FName& InNativeMetaDataKey, const FName& InScriptDefaultMetaDataKey, const NotFoundFuncT& NotFoundFn)
|
||||
{
|
||||
check(InNativeMetaDataKey == PyGenUtil::HasNativeMakeMetaDataKey || InNativeMetaDataKey == PyGenUtil::HasNativeBreakMetaDataKey);
|
||||
check(InScriptDefaultMetaDataKey == PyGenUtil::ScriptDefaultMakeMetaDataKey || InScriptDefaultMetaDataKey == PyGenUtil::ScriptDefaultBreakMetaDataKey);
|
||||
|
||||
UFunction* MakeBreakFunc = nullptr;
|
||||
if (!InStruct->HasMetaData(InScriptDefaultMetaDataKey)) // <--- 有了default, 会直接返回null
|
||||
{
|
||||
const FString MakeBreakFunctionName = InStruct->GetMetaData(InNativeMetaDataKey);
|
||||
if (!MakeBreakFunctionName.IsEmpty())
|
||||
{
|
||||
// Find the function.
|
||||
MakeBreakFunc = FindObject<UFunction>(/*Outer*/nullptr, *MakeBreakFunctionName, /*ExactClass*/true);
|
||||
if (!MakeBreakFunc)
|
||||
{
|
||||
NotFoundFn(MakeBreakFunctionName);
|
||||
}
|
||||
}
|
||||
}
|
||||
return MakeBreakFunc;
|
||||
}
|
||||
}
|
||||
|
||||
struct FFuncs
|
||||
{
|
||||
static int Init(FPyWrapperStruct* InSelf, PyObject* InArgs, PyObject* InKwds)
|
||||
{
|
||||
const int SuperResult = PyWrapperStructType.tp_init((PyObject*)InSelf, InArgs, InKwds);
|
||||
if (SuperResult != 0)
|
||||
{
|
||||
return SuperResult;
|
||||
}
|
||||
|
||||
return FPyWrapperStruct::MakeStruct(InSelf, InArgs, InKwds);
|
||||
}
|
||||
};
|
||||
|
||||
GeneratedWrappedType->PyType.tp_init = (initproc)&FFuncs::Init;
|
||||
|
||||
// python wrapper 给每个类型都映射了 to_tuple 函数,会调用类型的 break 函数转换为 tuple
|
||||
static PyObject* ToTuple(FPyWrapperStruct* InSelf)
|
||||
{
|
||||
return FPyWrapperStruct::BreakStruct(InSelf);
|
||||
}
|
||||
|
||||
....
|
||||
{ "to_tuple", PyCFunctionCast(&FMethods::ToTuple), METH_NOARGS, "to_tuple(self) -> Tuple[object, ...] -- break this Unreal struct into a tuple of its properties" },
|
||||
```
|
@@ -0,0 +1,170 @@
|
||||
# ScriptMethod
|
||||
|
||||
- **功能描述:** 把静态函数导出变成第一个参数的成员函数。
|
||||
- **使用位置:** UFUNCTION
|
||||
- **引擎模块:** Script
|
||||
- **元数据类型:** string="a;b;c"
|
||||
- **限制类型:** static function
|
||||
- **关联项:** [ScriptMethodMutable](ScriptMethodMutable.md), [ScriptMethodSelfReturn](ScriptMethodSelfReturn.md)
|
||||
- **常用程度:** ★★★
|
||||
|
||||
把静态函数导出变成第一个参数的成员函数。
|
||||
|
||||
- 把func(A,B)变成A.func(B),这样就可以给A对象添加成员函数方法。有点像C#里的扩展方法。
|
||||
- 也可以直接再提供一个名字来改变包装后的成员函数的名称。注意与ScriptName区分,ScriptName改变的是本身导出到脚本的名字,而ScriptMethod改变的是结果成员函数的名字。把func(A,B)改成A.OtherFunc(B)。
|
||||
|
||||
## 测试代码:
|
||||
|
||||
```cpp
|
||||
UCLASS(Blueprintable, BlueprintType)
|
||||
class INSIDER_API UMyPython_ScriptMethod :public UObject
|
||||
{
|
||||
GENERATED_BODY()
|
||||
public:
|
||||
};
|
||||
|
||||
USTRUCT(BlueprintType)
|
||||
struct INSIDER_API FMyPythonStruct_ScriptMethod
|
||||
{
|
||||
GENERATED_BODY()
|
||||
};
|
||||
|
||||
UCLASS(Blueprintable, BlueprintType)
|
||||
class INSIDER_API UMyPython_ScriptMethod_Test :public UObject
|
||||
{
|
||||
GENERATED_BODY()
|
||||
public:
|
||||
UFUNCTION(BlueprintCallable, meta = (ScriptMethod))
|
||||
static void MyFuncOnObject(UMyPython_ScriptMethod* obj, FString val);
|
||||
|
||||
UFUNCTION(BlueprintCallable, meta = (ScriptMethod = "MySuperFuncOnObject;MyOtherFuncOnObject"))
|
||||
static void MyFuncOnObject2(UMyPython_ScriptMethod* obj, FString val);
|
||||
|
||||
public:
|
||||
UFUNCTION(BlueprintCallable, meta = (ScriptMethod))
|
||||
static void MyFuncOnStruct(const FMyPythonStruct_ScriptMethod& myStruct, FString val);;
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
## 测试效果:
|
||||
|
||||
可见在MyPythonStruct_ScriptMethod里增加了my_func_on_struct的方法,而MyPython_ScriptMethod里增加了my_func_on_object的方法。因此如果在py里你就可以把这两个函数当作成员函数一样调用。
|
||||
|
||||
另外MyFuncOnObject2上面设置了两个ScriptMethod 别称,也可以在MyPython_ScriptMethod里见到。
|
||||
|
||||
```cpp
|
||||
class MyPythonStruct_ScriptMethod(StructBase):
|
||||
r"""
|
||||
My Python Struct Script Method
|
||||
|
||||
**C++ Source:**
|
||||
|
||||
- **Module**: Insider
|
||||
- **File**: MyPython_ScriptMethod.h
|
||||
|
||||
"""
|
||||
def __init__(self) -> None:
|
||||
...
|
||||
def my_func_on_struct(self, val: str) -> None:
|
||||
r"""
|
||||
x.my_func_on_struct(val) -> None
|
||||
My Func on Struct
|
||||
|
||||
Args:
|
||||
val (str):
|
||||
"""
|
||||
...
|
||||
|
||||
class MyPython_ScriptMethod(Object):
|
||||
r"""
|
||||
My Python Script Method
|
||||
|
||||
**C++ Source:**
|
||||
|
||||
- **Module**: Insider
|
||||
- **File**: MyPython_ScriptMethod.h
|
||||
|
||||
"""
|
||||
def my_super_func_on_object(self, val: str) -> None:
|
||||
r"""
|
||||
x.my_super_func_on_object(val) -> None
|
||||
My Func on Object 2
|
||||
|
||||
Args:
|
||||
val (str):
|
||||
"""
|
||||
...
|
||||
def my_other_func_on_object(self, val: str) -> None:
|
||||
r"""
|
||||
deprecated: 'my_other_func_on_object' was renamed to 'my_super_func_on_object'.
|
||||
"""
|
||||
...
|
||||
def my_func_on_object(self, val: str) -> None:
|
||||
r"""
|
||||
x.my_func_on_object(val) -> None
|
||||
My Func on Object
|
||||
|
||||
Args:
|
||||
val (str):
|
||||
"""
|
||||
...
|
||||
class MyPython_ScriptMethod_Test(Object):
|
||||
r"""
|
||||
My Python Script Method Test
|
||||
|
||||
**C++ Source:**
|
||||
|
||||
- **Module**: Insider
|
||||
- **File**: MyPython_ScriptMethod.h
|
||||
|
||||
"""
|
||||
@classmethod
|
||||
def my_func_on_struct(cls, my_struct: MyPythonStruct_ScriptMethod, val: str) -> None:
|
||||
r"""
|
||||
X.my_func_on_struct(my_struct, val) -> None
|
||||
My Func on Struct
|
||||
|
||||
Args:
|
||||
my_struct (MyPythonStruct_ScriptMethod):
|
||||
val (str):
|
||||
"""
|
||||
...
|
||||
@classmethod
|
||||
def my_func_on_object2(cls, obj: MyPython_ScriptMethod, val: str) -> None:
|
||||
r"""
|
||||
X.my_func_on_object2(obj, val) -> None
|
||||
My Func on Object 2
|
||||
|
||||
Args:
|
||||
obj (MyPython_ScriptMethod):
|
||||
val (str):
|
||||
"""
|
||||
...
|
||||
@classmethod
|
||||
def my_func_on_object(cls, obj: MyPython_ScriptMethod, val: str) -> None:
|
||||
r"""
|
||||
X.my_func_on_object(obj, val) -> None
|
||||
My Func on Object
|
||||
|
||||
Args:
|
||||
obj (MyPython_ScriptMethod):
|
||||
val (str):
|
||||
"""
|
||||
...
|
||||
```
|
||||
|
||||
## 原理:
|
||||
|
||||
在GenerateWrappedDynamicMethod中有详细的如何把静态函数包装成成员函数的过程。感兴趣的可以去细看。
|
||||
|
||||
```cpp
|
||||
PyTypeObject* FPyWrapperTypeRegistry::GenerateWrappedClassType(const UClass* InClass, FGeneratedWrappedTypeReferences& OutGeneratedWrappedTypeReferences, TSet<FName>& OutDirtyModules, const EPyTypeGenerationFlags InGenerationFlags)
|
||||
{
|
||||
// Should this function also be hoisted as a struct method or operator?
|
||||
if (InFunc->HasMetaData(PyGenUtil::ScriptMethodMetaDataKey))
|
||||
{
|
||||
GenerateWrappedDynamicMethod(InFunc, GeneratedWrappedMethodCopy);
|
||||
}
|
||||
}
|
||||
```
|
@@ -0,0 +1,181 @@
|
||||
# ScriptMethodMutable
|
||||
|
||||
- **功能描述:** 把ScriptMethod的第一个const结构参数在调用上改成引用参数,函数内修改的值会保存下来。
|
||||
- **使用位置:** UFUNCTION
|
||||
- **引擎模块:** Script
|
||||
- **元数据类型:** bool
|
||||
- **限制类型:** 第一个参数是结构类型
|
||||
- **关联项:** [ScriptMethod](ScriptMethod.md)
|
||||
- **常用程度:** ★★
|
||||
|
||||
把ScriptMethod的第一个const结构参数在调用上改成引用参数,函数内修改的值会保存下来。
|
||||
|
||||
- 在const参数上如果想改变值,依然要标记c++里的mutable。
|
||||
- 虽然py生成的代码一模一样,但实际调用上ScriptMethodMutable会真正改变参数的值,而没有ScriptMethodMutable的函数并不会改变参数的原始值。
|
||||
- ScriptMethodMutable和UPARAM(ref) 在调用效果上,都可以改变参数的值。但区别是UPARAM(ref) 生成的py代码会返回第一个参数作为返回值。
|
||||
|
||||
```cpp
|
||||
USTRUCT(BlueprintType)
|
||||
struct INSIDER_API FMyPythonStruct_ScriptMethod
|
||||
{
|
||||
GENERATED_BODY()
|
||||
public:
|
||||
UPROPERTY(BlueprintReadWrite, EditAnywhere)
|
||||
mutable FString MyString;
|
||||
|
||||
};
|
||||
|
||||
UCLASS(Blueprintable, BlueprintType)
|
||||
class INSIDER_API UMyPython_ScriptMethod_Test :public UObject
|
||||
{
|
||||
GENERATED_BODY()
|
||||
public:
|
||||
UFUNCTION(BlueprintCallable, meta = (ScriptMethod))
|
||||
static void SetStringOnStruct(const FMyPythonStruct_ScriptMethod& myStruct, FString val);
|
||||
|
||||
UFUNCTION(BlueprintCallable, meta = (ScriptMethod, ScriptMethodMutable))
|
||||
static void SetStringOnStructMutable(const FMyPythonStruct_ScriptMethod& myStruct, FString val);
|
||||
|
||||
UFUNCTION(BlueprintCallable, meta = (ScriptMethod, ScriptMethodMutable))
|
||||
static void SetStringOnStructViaRef(UPARAM(ref) FMyPythonStruct_ScriptMethod& myStruct, FString val);
|
||||
};
|
||||
```
|
||||
|
||||
## 测试效果:
|
||||
|
||||
看py里生成的代码是一致的,如果用UPARAM(ref),则在MyPython_ScriptMethod_Test里面生成的my_func_on_struct_via_ref会返回结构MyPythonStruct_ScriptMethod来达成引用的效果。
|
||||
|
||||
然而my_func_on_struct_mutable返回的是None,同不加ScriptMethodMutable的my_func_on_struct并没有区别。但是实际上在真正调用的时候会真正有区别。
|
||||
|
||||
```cpp
|
||||
class MyPythonStruct_ScriptMethod(StructBase):
|
||||
r"""
|
||||
My Python Struct Script Method
|
||||
|
||||
**C++ Source:**
|
||||
|
||||
- **Module**: Insider
|
||||
- **File**: MyPython_ScriptMethod.h
|
||||
|
||||
"""
|
||||
def __init__(self) -> None:
|
||||
...
|
||||
def my_func_on_struct_via_ref(self, val: str) -> None:
|
||||
r"""
|
||||
x.my_func_on_struct_via_ref(val) -> None
|
||||
My Func on Struct Via Ref
|
||||
|
||||
Args:
|
||||
val (str):
|
||||
"""
|
||||
...
|
||||
def my_func_on_struct_mutable(self, val: str) -> None:
|
||||
r"""
|
||||
x.my_func_on_struct_mutable(val) -> None
|
||||
My Func on Struct Mutable
|
||||
|
||||
Args:
|
||||
val (str):
|
||||
"""
|
||||
...
|
||||
def my_func_on_struct(self, val: str) -> None:
|
||||
r"""
|
||||
x.my_func_on_struct(val) -> None
|
||||
My Func on Struct
|
||||
|
||||
Args:
|
||||
val (str):
|
||||
"""
|
||||
...
|
||||
|
||||
|
||||
class MyPython_ScriptMethod_Test(Object):
|
||||
r"""
|
||||
My Python Script Method Test
|
||||
|
||||
**C++ Source:**
|
||||
|
||||
- **Module**: Insider
|
||||
- **File**: MyPython_ScriptMethod.h
|
||||
|
||||
"""
|
||||
@classmethod
|
||||
def my_func_on_struct_via_ref(cls, my_struct: MyPythonStruct_ScriptMethod, val: str) -> MyPythonStruct_ScriptMethod:
|
||||
r"""
|
||||
X.my_func_on_struct_via_ref(my_struct, val) -> MyPythonStruct_ScriptMethod
|
||||
My Func on Struct Via Ref
|
||||
|
||||
Args:
|
||||
my_struct (MyPythonStruct_ScriptMethod):
|
||||
val (str):
|
||||
|
||||
Returns:
|
||||
MyPythonStruct_ScriptMethod:
|
||||
|
||||
my_struct (MyPythonStruct_ScriptMethod):
|
||||
"""
|
||||
...
|
||||
@classmethod
|
||||
def my_func_on_struct_mutable(cls, my_struct: MyPythonStruct_ScriptMethod, val: str) -> None:
|
||||
r"""
|
||||
X.my_func_on_struct_mutable(my_struct, val) -> None
|
||||
My Func on Struct Mutable
|
||||
|
||||
Args:
|
||||
my_struct (MyPythonStruct_ScriptMethod):
|
||||
val (str):
|
||||
"""
|
||||
...
|
||||
@classmethod
|
||||
def my_func_on_struct(cls, my_struct: MyPythonStruct_ScriptMethod, val: str) -> None:
|
||||
r"""
|
||||
X.my_func_on_struct(my_struct, val) -> None
|
||||
My Func on Struct
|
||||
|
||||
Args:
|
||||
my_struct (MyPythonStruct_ScriptMethod):
|
||||
val (str):
|
||||
"""
|
||||
...
|
||||
```
|
||||
|
||||
在UE Python控制台里调用的记录,分析调用顺序:
|
||||
|
||||
- 一开始调用set_string_on_struct_mutable,再print(a),可以打印出Hello,说明值真正的设置到了a结构里。
|
||||
- 再尝试set_string_on_struct,再print(a),无法打印出FFF,说明值并没有设置到a结构里。说明py在调用的时候很可能构造了一个临时值来当作调对象,调用完成的新值并没有设置到a对象上。
|
||||
- 再尝试set_string_on_struct_via_ref,再print(a),可以打印出First,说明用UPARAM(ref)可以也达成改变参数的效果。
|
||||
|
||||
```cpp
|
||||
LogPython: a=unreal.MyPythonStruct_ScriptMethod()
|
||||
LogPython: print(a)
|
||||
LogPython: <Struct 'MyPythonStruct_ScriptMethod' (0x0000092D08CD6ED0) {my_string: ""}>
|
||||
LogPython: a.set_string_on_struct_mutable("Hello")
|
||||
LogBlueprintUserMessages: [None] UMyPython_ScriptMethod_Test::SetStringOnStructMutable
|
||||
LogPython: print(a)
|
||||
LogPython: <Struct 'MyPythonStruct_ScriptMethod' (0x0000092D08CD6ED0) {my_string: "Hello"}>
|
||||
LogPython: a.set_string_on_struct("FFF")
|
||||
LogBlueprintUserMessages: [None] UMyPython_ScriptMethod_Test::SetStringOnStruct
|
||||
LogPython: print(a)
|
||||
LogPython: <Struct 'MyPythonStruct_ScriptMethod' (0x0000092D08CD6ED0) {my_string: "Hello"}>
|
||||
LogPython: a.set_string_on_struct_via_ref("First")
|
||||
LogBlueprintUserMessages: [None] UMyPython_ScriptMethod_Test::SetStringOnStructViaRef
|
||||
LogPython: print(a)
|
||||
LogPython: <Struct 'MyPythonStruct_ScriptMethod' (0x0000092D08CD6ED0) {my_string: "First"}>
|
||||
```
|
||||
|
||||
## 原理:
|
||||
|
||||
判断如果有ScriptMethodMutable,会设置SelfReturn,从而再最后把函数调用中的临时值复制给原本的参数值,达成可变引用调用的效果。
|
||||
|
||||
```cpp
|
||||
// The function may have been flagged as mutable, in which case we always consider it to need a 'self' return
|
||||
if (!GeneratedWrappedDynamicMethod.SelfReturn.ParamProp && InFunc->HasMetaData(PyGenUtil::ScriptMethodMutableMetaDataKey))
|
||||
{
|
||||
if (!SelfParam.ParamProp->IsA<FStructProperty>())
|
||||
{
|
||||
REPORT_PYTHON_GENERATION_ISSUE(Error, TEXT("Function '%s.%s' is marked as 'ScriptMethodMutable' but the 'self' argument is not a struct."), *InFunc->GetOwnerClass()->GetName(), *InFunc->GetName());
|
||||
return;
|
||||
}
|
||||
GeneratedWrappedDynamicMethod.SelfReturn = SelfParam;
|
||||
}
|
||||
```
|
@@ -0,0 +1,169 @@
|
||||
# ScriptMethodSelfReturn
|
||||
|
||||
- **功能描述:** 在ScriptMethod的情况下,指定把这个函数的返回值要去覆盖该函数的第一个参数。
|
||||
- **使用位置:** UFUNCTION
|
||||
- **引擎模块:** Script
|
||||
- **元数据类型:** bool
|
||||
- **关联项:** [ScriptMethod](ScriptMethod.md)
|
||||
- **常用程度:** ★★
|
||||
|
||||
在ScriptMethod的情况下,指定把这个函数的返回值要去覆盖该函数的第一个参数。
|
||||
|
||||
这种情况下,原本的函数就没有返回值返回了。效果上形如:
|
||||
|
||||
```cpp
|
||||
C Func(A,B) -> void A::Func2(B)
|
||||
调用的时候:
|
||||
从 C=A.Func(B) ->
|
||||
void A::Func2(B)
|
||||
{
|
||||
A=A.Func(B)
|
||||
}
|
||||
```
|
||||
|
||||
## 测试代码:
|
||||
|
||||
注意因为AppendStringOnStructViaRef参数是引用参数,所以为了结果应用到myStruct,在函数体内就不需要创建临时值,可以直接在myStruct上面修改。如果也用临时值的话,myStruct就无法得到修改,也就失去了ref参数的意义。
|
||||
|
||||
```cpp
|
||||
public:
|
||||
UFUNCTION(BlueprintCallable, meta = (ScriptMethod))
|
||||
static FMyPythonStruct_ScriptMethod AppendStringOnStruct(const FMyPythonStruct_ScriptMethod& myStruct, FString val)
|
||||
{
|
||||
FMyPythonStruct_ScriptMethod Result = myStruct;
|
||||
Result.MyString += val;
|
||||
return Result;
|
||||
}
|
||||
|
||||
UFUNCTION(BlueprintCallable, meta = (ScriptMethod,ScriptMethodSelfReturn))
|
||||
static FMyPythonStruct_ScriptMethod AppendStringOnStructReturn(const FMyPythonStruct_ScriptMethod& myStruct, FString val)
|
||||
{
|
||||
FMyPythonStruct_ScriptMethod Result = myStruct;
|
||||
Result.MyString += val;
|
||||
return Result;
|
||||
}
|
||||
UFUNCTION(BlueprintCallable, meta = (ScriptMethod, ScriptMethodMutable))
|
||||
static FMyPythonStruct_ScriptMethod AppendStringOnStructViaRef(UPARAM(ref) FMyPythonStruct_ScriptMethod& myStruct, FString val)
|
||||
{
|
||||
myStruct.MyString += val;
|
||||
return myStruct;
|
||||
}
|
||||
|
||||
//LogPython: Error: Function 'MyPython_ScriptMethod_Test.AppendStringOnStructViaRefReturn' is marked as 'ScriptMethodSelfReturn' but the 'self' argument is also marked as UPARAM(ref). This is not allowed.
|
||||
//UFUNCTION(BlueprintCallable, meta = (ScriptMethod, ScriptMethodMutable,ScriptMethodSelfReturn))
|
||||
//static FMyPythonStruct_ScriptMethod AppendStringOnStructViaRefReturn(UPARAM(ref) FMyPythonStruct_ScriptMethod& myStruct, FString val);
|
||||
```
|
||||
|
||||
## 生成的py代码:
|
||||
|
||||
可见append_string_on_struct_return是没有返回值了。而append_string_on_struct有返回值。append_string_on_struct_via_ref也有返回值。
|
||||
|
||||
```cpp
|
||||
class MyPythonStruct_ScriptMethod(StructBase):
|
||||
def append_string_on_struct_return(self, val: str) -> None:
|
||||
r"""
|
||||
x.append_string_on_struct_return(val) -> None
|
||||
Append String on Struct Return
|
||||
|
||||
Args:
|
||||
val (str):
|
||||
|
||||
Returns:
|
||||
MyPythonStruct_ScriptMethod:
|
||||
"""
|
||||
...
|
||||
def append_string_on_struct(self, val: str) -> MyPythonStruct_ScriptMethod:
|
||||
r"""
|
||||
x.append_string_on_struct(val) -> MyPythonStruct_ScriptMethod
|
||||
Append String on Struct
|
||||
|
||||
Args:
|
||||
val (str):
|
||||
|
||||
Returns:
|
||||
MyPythonStruct_ScriptMethod:
|
||||
"""
|
||||
...
|
||||
def append_string_on_struct_via_ref(self, val: str) -> MyPythonStruct_ScriptMethod:
|
||||
r"""
|
||||
x.append_string_on_struct_via_ref(val) -> MyPythonStruct_ScriptMethod
|
||||
Append String on Struct Via Ref
|
||||
|
||||
Args:
|
||||
val (str):
|
||||
|
||||
Returns:
|
||||
MyPythonStruct_ScriptMethod:
|
||||
"""
|
||||
...
|
||||
```
|
||||
|
||||
测试代码:观察运行的结果以及对象的内存地址。
|
||||
|
||||
- 可以看出append_string_on_struct是有返回值的,但是改变的结果没有应用到参数a上。
|
||||
- append_string_on_struct_return可以应用到参数a上,但是没有返回值。
|
||||
- append_string_on_struct_via_ref可以应用到参数a上,同时也有返回值。但是注意返回值和a其实并不是同一个对象,因为内存地址不同。
|
||||
- 但是注意 ScriptMethodSelfReturn和UPARAM(ref)不能混用,否则会报错: LogPython: Error: Function 'MyPython_ScriptMethod_Test.AppendStringOnStructViaRefReturn' is marked as 'ScriptMethodSelfReturn' but the 'self' argument is also marked as UPARAM(ref). This is not allowed.
|
||||
|
||||
```cpp
|
||||
LogPython: a=unreal.MyPythonStruct_ScriptMethod()
|
||||
LogPython: print(a)
|
||||
LogPython: <Struct 'MyPythonStruct_ScriptMethod' (0x000008DEBAB08ED0) {my_string: ""}>
|
||||
LogPython: b=a.append_string_on_struct("Hello")
|
||||
LogPython: print(b)
|
||||
LogPython: <Struct 'MyPythonStruct_ScriptMethod' (0x000008DEBAB04010) {my_string: "Hello"}>
|
||||
LogPython: print(a)
|
||||
LogPython: <Struct 'MyPythonStruct_ScriptMethod' (0x000008DEBAB08ED0) {my_string: ""}>
|
||||
LogPython: c=a.append_string_on_struct_return("Hello")
|
||||
LogPython: print(c)
|
||||
LogPython: None
|
||||
LogPython: print(a)
|
||||
LogPython: <Struct 'MyPythonStruct_ScriptMethod' (0x000008DEBAB08ED0) {my_string: "Hello"}>
|
||||
LogPython: d=a.append_string_on_struct_via_ref("World")
|
||||
LogPython: print(d)
|
||||
LogPython: <Struct 'MyPythonStruct_ScriptMethod' (0x000008DEBAB06110) {my_string: "HelloWorld"}>
|
||||
LogPython: print(a)
|
||||
LogPython: <Struct 'MyPythonStruct_ScriptMethod' (0x000008DEBAB08ED0) {my_string: "HelloWorld"}>
|
||||
```
|
||||
|
||||
## 原理:
|
||||
|
||||
把输出参数的第一个当作返回参数。输出参数其实就是函数里的返回值。SelfReturn的意思是这个值之后要去覆盖掉调用对象的值,也就是发生调用的对象。
|
||||
|
||||
```cpp
|
||||
// The function may also have been flagged as having a 'self' return
|
||||
if (InFunc->HasMetaData(PyGenUtil::ScriptMethodSelfReturnMetaDataKey))
|
||||
{
|
||||
if (GeneratedWrappedDynamicMethod.SelfReturn.ParamProp)
|
||||
{
|
||||
REPORT_PYTHON_GENERATION_ISSUE(Error, TEXT("Function '%s.%s' is marked as 'ScriptMethodSelfReturn' but the 'self' argument is also marked as UPARAM(ref). This is not allowed."), *InFunc->GetOwnerClass()->GetName(), *InFunc->GetName());
|
||||
return;
|
||||
}
|
||||
else if (GeneratedWrappedDynamicMethod.MethodFunc.OutputParams.Num() == 0 || !GeneratedWrappedDynamicMethod.MethodFunc.OutputParams[0].ParamProp->HasAnyPropertyFlags(CPF_ReturnParm))
|
||||
{
|
||||
REPORT_PYTHON_GENERATION_ISSUE(Error, TEXT("Function '%s.%s' is marked as 'ScriptMethodSelfReturn' but has no return value."), *InFunc->GetOwnerClass()->GetName(), *InFunc->GetName());
|
||||
return;
|
||||
}
|
||||
else if (!SelfParam.ParamProp->IsA<FStructProperty>())
|
||||
{
|
||||
REPORT_PYTHON_GENERATION_ISSUE(Error, TEXT("Function '%s.%s' is marked as 'ScriptMethodSelfReturn' but the 'self' argument is not a struct."), *InFunc->GetOwnerClass()->GetName(), *InFunc->GetName());
|
||||
return;
|
||||
}
|
||||
else if (!GeneratedWrappedDynamicMethod.MethodFunc.OutputParams[0].ParamProp->IsA<FStructProperty>())
|
||||
{
|
||||
REPORT_PYTHON_GENERATION_ISSUE(Error, TEXT("Function '%s.%s' is marked as 'ScriptMethodSelfReturn' but the return value is not a struct."), *InFunc->GetOwnerClass()->GetName(), *InFunc->GetName());
|
||||
return;
|
||||
}
|
||||
else if (CastFieldChecked<const FStructProperty>(GeneratedWrappedDynamicMethod.MethodFunc.OutputParams[0].ParamProp)->Struct != CastFieldChecked<const FStructProperty>(SelfParam.ParamProp)->Struct)
|
||||
{
|
||||
REPORT_PYTHON_GENERATION_ISSUE(Error, TEXT("Function '%s.%s' is marked as 'ScriptMethodSelfReturn' but the return value is not the same type as the 'self' argument."), *InFunc->GetOwnerClass()->GetName(), *InFunc->GetName());
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
GeneratedWrappedDynamicMethod.SelfReturn = MoveTemp(GeneratedWrappedDynamicMethod.MethodFunc.OutputParams[0]);
|
||||
GeneratedWrappedDynamicMethod.MethodFunc.OutputParams.RemoveAt(0, 1, EAllowShrinking::No);
|
||||
}
|
||||
}
|
||||
|
||||
```
|
@@ -0,0 +1,149 @@
|
||||
# ScriptName
|
||||
|
||||
- **功能描述:** 在导出到脚本里时使用的名字
|
||||
- **使用位置:** Any
|
||||
- **引擎模块:** Script
|
||||
- **元数据类型:** string="abc"
|
||||
- **常用程度:** ★★★
|
||||
|
||||
指定导出到脚本中的名字。
|
||||
|
||||
- 可以使用在UCLASS,USTRUCT,UENUM,UFUNCTION,UPROPERTY上使用,改变其导出到脚本的名字。
|
||||
- 如果没有使用ScriptName自定义名字,则导出的名字未默认的python化的名字。如MyFunc()变成my_func()。
|
||||
|
||||
在测试Python的时候,记得打开python插件。
|
||||
|
||||
可在\UnrealEngine\Engine\Plugins\Experimental\PythonScriptPlugin\Source\PythonScriptPlugin\Private\PyTest.h里见到大量写好的测试用例。
|
||||
|
||||
## 测试代码:
|
||||
|
||||
```cpp
|
||||
UCLASS(Blueprintable, BlueprintType)
|
||||
class INSIDER_API UMyPythonTestLibary2 :public UBlueprintFunctionLibrary
|
||||
{
|
||||
GENERATED_BODY()
|
||||
};
|
||||
|
||||
UCLASS(Blueprintable, BlueprintType,meta=(ScriptName="MyPythonLib"))
|
||||
class INSIDER_API UMyPythonTestLibary :public UBlueprintFunctionLibrary
|
||||
{
|
||||
GENERATED_BODY()
|
||||
public:
|
||||
//unreal.MyPythonLib.my_script_func_default()
|
||||
UFUNCTION(BlueprintCallable,meta=())
|
||||
static void MyScriptFuncDefault()
|
||||
{
|
||||
UInsiderSubsystem::Get().PrintStringEx(nullptr, TEXT("MyScriptFuncDefault"));
|
||||
}
|
||||
|
||||
//unreal.MyPythonLib.my_script_func()
|
||||
UFUNCTION(BlueprintCallable,meta=(ScriptName="MyScriptFunc"))
|
||||
static void MyScriptFunc_ScriptName()
|
||||
{
|
||||
UInsiderSubsystem::Get().PrintStringEx(nullptr, TEXT("MyScriptFunc_ScriptName"));
|
||||
}
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
## 测试效果:
|
||||
|
||||
开启编辑器后,引擎会自动根据类型数据信息反射生成向相应的导出到py的胶水代码,我们在C++中定义的类就可以在\Intermediate\PythonStub\[unreal.py](http://unreal.py/)里查看其导出的脚本代码。
|
||||
|
||||
如上的类,在unreal.py生成的py代码如下:
|
||||
|
||||
- 可见UMyPythonTestLibary2 没有加ScriptName就是默认的名字,而UMyPythonTestLibary 的名字变成了MyPythonLib。
|
||||
- MyScriptFuncDefault的导出脚本名字是my_script_func_default,而MyScriptFunc_ScriptName因为写了ScriptName变成了MyScriptFunc
|
||||
|
||||
```cpp
|
||||
|
||||
class MyPythonTestLibary2(BlueprintFunctionLibrary):
|
||||
r"""
|
||||
My Python Test Libary 2
|
||||
|
||||
**C++ Source:**
|
||||
|
||||
- **Module**: Insider
|
||||
- **File**: MyPythonTest.h
|
||||
|
||||
"""
|
||||
...
|
||||
|
||||
class MyPythonLib(BlueprintFunctionLibrary):
|
||||
r"""
|
||||
My Python Test Libary
|
||||
|
||||
**C++ Source:**
|
||||
|
||||
- **Module**: Insider
|
||||
- **File**: MyPython_Test.h
|
||||
|
||||
"""
|
||||
@classmethod
|
||||
def my_script_func_default(cls) -> None:
|
||||
r"""
|
||||
X.my_script_func_default() -> None
|
||||
My Script Func Default
|
||||
"""
|
||||
...
|
||||
@classmethod
|
||||
def my_script_func(cls) -> None:
|
||||
r"""
|
||||
X.my_script_func() -> None
|
||||
My Script Func Script Name
|
||||
"""
|
||||
...
|
||||
|
||||
```
|
||||
|
||||
## 原理:
|
||||
|
||||
在获取各个类型名字的时候,会先判断ScriptName,如果获得,就使用该名字。否则在GetFieldPythonNameImpl里会对名字进行python化处理。
|
||||
|
||||
```cpp
|
||||
\Engine\Plugins\Experimental\PythonScriptPlugin\Source\PythonScriptPlugin\Private\PyGenUtil.cpp
|
||||
const FName ScriptNameMetaDataKey = TEXT("ScriptName");
|
||||
|
||||
FString GetClassPythonName(const UClass* InClass)
|
||||
{
|
||||
return GetFieldPythonNameImpl(InClass, ScriptNameMetaDataKey);
|
||||
}
|
||||
|
||||
TArray<FString> GetDeprecatedClassPythonNames(const UClass* InClass)
|
||||
{
|
||||
return GetDeprecatedFieldPythonNamesImpl(InClass, ScriptNameMetaDataKey);
|
||||
}
|
||||
|
||||
FString GetStructPythonName(const UScriptStruct* InStruct)
|
||||
{
|
||||
return GetFieldPythonNameImpl(InStruct, ScriptNameMetaDataKey);
|
||||
}
|
||||
|
||||
TArray<FString> GetDeprecatedStructPythonNames(const UScriptStruct* InStruct)
|
||||
{
|
||||
return GetDeprecatedFieldPythonNamesImpl(InStruct, ScriptNameMetaDataKey);
|
||||
}
|
||||
|
||||
FString GetEnumPythonName(const UEnum* InEnum)
|
||||
{
|
||||
return GetFieldPythonNameImpl(InEnum, ScriptNameMetaDataKey);
|
||||
}
|
||||
|
||||
TArray<FString> GetDeprecatedEnumPythonNames(const UEnum* InEnum)
|
||||
{
|
||||
return GetDeprecatedFieldPythonNamesImpl(InEnum, ScriptNameMetaDataKey);
|
||||
}
|
||||
|
||||
FString GetFieldPythonNameImpl(const FFieldVariant& InField, const FName InMetaDataKey)
|
||||
{
|
||||
FString FieldName;
|
||||
|
||||
// First see if we have a name override in the meta-data
|
||||
if (GetFieldPythonNameFromMetaDataImpl(InField, InMetaDataKey, FieldName))
|
||||
{
|
||||
return FieldName;
|
||||
}
|
||||
|
||||
//。。。
|
||||
}
|
||||
```
|
@@ -0,0 +1,88 @@
|
||||
# ScriptNoExport
|
||||
|
||||
- **功能描述:** 不导出该函数或属性到脚本。
|
||||
- **使用位置:** UFUNCTION, UPROPERTY
|
||||
- **引擎模块:** Script
|
||||
- **元数据类型:** bool
|
||||
- **常用程度:** ★★★
|
||||
|
||||
不导出该函数或属性到脚本。
|
||||
|
||||
## 测试代码:
|
||||
|
||||
```cpp
|
||||
UCLASS(Blueprintable, BlueprintType, meta = (ScriptName = "MyPythonLib"))
|
||||
class INSIDER_API UMyPythonTestLibary :public UBlueprintFunctionLibrary
|
||||
{
|
||||
GENERATED_BODY()
|
||||
public:
|
||||
UFUNCTION(BlueprintCallable)
|
||||
static void MyScriptFunc_None();
|
||||
|
||||
UFUNCTION(BlueprintCallable, meta = (ScriptNoExport))
|
||||
static void MyScriptFunc_NoExport();
|
||||
public:
|
||||
UPROPERTY(BlueprintReadWrite, EditAnywhere)
|
||||
float MyFloat = 123.f;
|
||||
|
||||
UPROPERTY(BlueprintReadWrite, EditAnywhere, meta = (ScriptNoExport))
|
||||
float MyFloat_NoExport = 123.f;
|
||||
};
|
||||
```
|
||||
|
||||
## 测试效果py代码:
|
||||
|
||||
可见默认的函数和属性都会导出到脚本里。而MyScriptFunc_NoExport和MyFloat_NoExport在py里并没有。
|
||||
|
||||
```cpp
|
||||
class MyPythonLib(BlueprintFunctionLibrary):
|
||||
r"""
|
||||
My Python Test Libary
|
||||
|
||||
**C++ Source:**
|
||||
|
||||
- **Module**: Insider
|
||||
- **File**: MyPythonTest.h
|
||||
|
||||
"""
|
||||
@property
|
||||
def my_float(self) -> float:
|
||||
r"""
|
||||
(float): [Read-Write]
|
||||
"""
|
||||
...
|
||||
@my_float.setter
|
||||
def my_float(self, value: float) -> None:
|
||||
...
|
||||
@classmethod
|
||||
def my_script_func_none(cls) -> None:
|
||||
r"""
|
||||
X.my_script_func_none() -> None
|
||||
My Script Func None
|
||||
"""
|
||||
...
|
||||
```
|
||||
|
||||
## 原理:
|
||||
|
||||
根据这个ScriptNoExport来判断一个属性或函数是否导出。
|
||||
|
||||
```cpp
|
||||
bool IsScriptExposedProperty(const FProperty* InProp)
|
||||
{
|
||||
return !InProp->HasMetaData(ScriptNoExportMetaDataKey)
|
||||
&& InProp->HasAnyPropertyFlags(CPF_BlueprintVisible | CPF_BlueprintAssignable);
|
||||
}
|
||||
|
||||
bool IsScriptExposedFunction(const UFunction* InFunc)
|
||||
{
|
||||
return !InFunc->HasMetaData(ScriptNoExportMetaDataKey)
|
||||
&& InFunc->HasAnyFunctionFlags(FUNC_BlueprintCallable | FUNC_BlueprintEvent)
|
||||
&& !InFunc->HasMetaData(BlueprintGetterMetaDataKey)
|
||||
&& !InFunc->HasMetaData(BlueprintSetterMetaDataKey)
|
||||
&& !InFunc->HasMetaData(BlueprintInternalUseOnlyMetaDataKey)
|
||||
&& !InFunc->HasMetaData(CustomThunkMetaDataKey)
|
||||
&& !InFunc->HasMetaData(NativeBreakFuncMetaDataKey)
|
||||
&& !InFunc->HasMetaData(NativeMakeFuncMetaDataKey);
|
||||
}
|
||||
```
|
@@ -0,0 +1,206 @@
|
||||
# ScriptOperator
|
||||
|
||||
- **功能描述:** 把第一个参数为结构的静态函数包装成结构的运算符。
|
||||
- **使用位置:** UFUNCTION
|
||||
- **引擎模块:** Script
|
||||
- **元数据类型:** string="a;b;c"
|
||||
- **常用程度:** ★★★
|
||||
|
||||
把第一个参数为结构的静态函数包装成结构的运算符。
|
||||
|
||||
- 可以包含多个运算符。
|
||||
|
||||
不同的运算符需要匹配不同的函数签名。规则见如下:
|
||||
|
||||
- bool运算符:bool
|
||||
- bool FuncName(const FMyStruct& Value); //Value的类型可以是const FMyStruct&或者直接FMyStruct
|
||||
- 一元运算符:neg (取负)
|
||||
- FMyStruct FuncName(const FMyStruct&);
|
||||
- 比较运算符: (==, !=, <, <=, >, >=)
|
||||
- bool FuncName(const FMyStruct, OtherType); //OtherType可以是其他任何类型
|
||||
- 数学运算符: (+, -, *, /, %, &, |, ^, >>, <<)
|
||||
- ReturnType FuncName(const FMyStruct&, OtherType); //ReturnType 和 OtherType可以是其他任何类型
|
||||
- 数学赋值运算符:(+=, -=, *=, /=, %=, &=, |=, ^=, >>=, <<=)
|
||||
- FMyStruct FuncName(const FMyStruct&, OtherType); //OtherType可以是其他任何类型
|
||||
|
||||
可见,如果想一个函数同时支持普通数学运算符和赋值运算符,函数签名可以是:
|
||||
|
||||
FMyStruct FuncName(const FMyStruct&, OtherType); //这里OtherType可以是任何类型,也可以是FMyStruct
|
||||
|
||||
这个也常常一起配合ScriptMethod使用,这样就可以在结构中一起提供一个运算成员函数,这个函数的名字还可以通过ScriptMethod来自定义。
|
||||
|
||||
源码里也常见到和ScriptMethodSelfReturn使用的例子,如+=运算符上。但其实ScriptMethodSelfReturn不是必须的,在+=的时候,自然会把返回值应用到第一个参数上。
|
||||
|
||||
## 测试代码:
|
||||
|
||||
```cpp
|
||||
USTRUCT(BlueprintType)
|
||||
struct INSIDER_API FMyPythonMathStruct
|
||||
{
|
||||
GENERATED_BODY()
|
||||
public:
|
||||
UPROPERTY(BlueprintReadWrite, EditAnywhere)
|
||||
int32 Value = 0;
|
||||
};
|
||||
|
||||
UCLASS(Blueprintable, BlueprintType)
|
||||
class INSIDER_API UMyPython_Operator_Test :public UObject
|
||||
{
|
||||
GENERATED_BODY()
|
||||
public:
|
||||
UFUNCTION(BlueprintCallable, meta = (ScriptMethod=HasValue,ScriptOperator = "bool"))
|
||||
static bool IsValid(const FMyPythonMathStruct& InStruct) { return InStruct.Value != 0; }
|
||||
|
||||
UFUNCTION(BlueprintCallable, meta = (ScriptOperator = "neg"))
|
||||
static FMyPythonMathStruct Neg(const FMyPythonMathStruct& InStruct) { return { -InStruct.Value }; }
|
||||
|
||||
UFUNCTION(BlueprintCallable, meta = (ScriptOperator = "=="))
|
||||
static bool IsEqual(const FMyPythonMathStruct& A, const FMyPythonMathStruct& B) { return A.Value == B.Value; }
|
||||
|
||||
UFUNCTION(BlueprintCallable, meta = (ScriptOperator = "+;+="))
|
||||
static FMyPythonMathStruct AddInt(FMyPythonMathStruct InStruct, const int32 InValue) { InStruct.Value += InValue; return InStruct; }
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
## 生成的py代码:
|
||||
|
||||
可见,在py里生成了__bool__,__eq__,__add__,__iadd__,__neg__的函数。同时IsValid加上了ScriptMethod,就有了另一个has_value函数。
|
||||
|
||||
```cpp
|
||||
class MyPythonMathStruct(StructBase):
|
||||
r"""
|
||||
My Python Math Struct
|
||||
|
||||
**C++ Source:**
|
||||
|
||||
- **Module**: Insider
|
||||
- **File**: MyPython_ScriptOperator.h
|
||||
|
||||
**Editor Properties:** (see get_editor_property/set_editor_property)
|
||||
|
||||
- ``value`` (int32): [Read-Write]
|
||||
"""
|
||||
def __init__(self, value: int = 0) -> None:
|
||||
...
|
||||
@property
|
||||
def value(self) -> int:
|
||||
r"""
|
||||
(int32): [Read-Write]
|
||||
"""
|
||||
...
|
||||
@value.setter
|
||||
def value(self, value: int) -> None:
|
||||
...
|
||||
def has_value(self) -> bool:
|
||||
r"""
|
||||
x.has_value() -> bool
|
||||
Is Valid
|
||||
|
||||
Returns:
|
||||
bool:
|
||||
"""
|
||||
...
|
||||
def __bool__(self) -> bool:
|
||||
r"""
|
||||
Is Valid
|
||||
"""
|
||||
...
|
||||
def __eq__(self, other: object) -> bool:
|
||||
r"""
|
||||
**Overloads:**
|
||||
|
||||
- ``MyPythonMathStruct`` Is Equal
|
||||
"""
|
||||
...
|
||||
def __add__(self, other: MyPythonMathStruct) -> None:
|
||||
r"""
|
||||
**Overloads:**
|
||||
|
||||
- ``int32`` Add Int
|
||||
"""
|
||||
...
|
||||
def __iadd__(self, other: MyPythonMathStruct) -> None:
|
||||
r"""
|
||||
**Overloads:**
|
||||
|
||||
- ``int32`` Add Int
|
||||
"""
|
||||
...
|
||||
def __neg__(self) -> None:
|
||||
r"""
|
||||
Neg
|
||||
"""
|
||||
...
|
||||
```
|
||||
|
||||
## 进行运行的测试:
|
||||
|
||||
可见确实支持了数学+=运算符和bool的比较。
|
||||
|
||||
```cpp
|
||||
LogPython: a=unreal.MyPythonMathStruct(3)
|
||||
LogPython: print(a)
|
||||
LogPython: <Struct 'MyPythonMathStruct' (0x0000074C90D5DCF0) {value: 3}>
|
||||
LogPython: print(not a)
|
||||
LogPython: False
|
||||
LogPython: a+=3
|
||||
LogPython: print(a)
|
||||
LogPython: <Struct 'MyPythonMathStruct' (0x0000074C90D5DCF0) {value: 6}>
|
||||
LogPython: print(-a)
|
||||
LogPython: <Struct 'MyPythonMathStruct' (0x0000074C90D5DCF0) {value: -6}>
|
||||
```
|
||||
|
||||
## 原理:
|
||||
|
||||
具体的包装函数都在GenerateWrappedOperator 里,具体想了解的可细看这里。
|
||||
|
||||
```cpp
|
||||
auto GenerateWrappedOperator = [this, &OutGeneratedWrappedTypeReferences, &OutDirtyModules](const UFunction* InFunc, const PyGenUtil::FGeneratedWrappedMethod& InTypeMethod)
|
||||
{
|
||||
// Only static functions can be hoisted onto other types
|
||||
if (!InFunc->HasAnyFunctionFlags(FUNC_Static))
|
||||
{
|
||||
REPORT_PYTHON_GENERATION_ISSUE(Error, TEXT("Non-static function '%s.%s' is marked as 'ScriptOperator' but only static functions can be hoisted."), *InFunc->GetOwnerClass()->GetName(), *InFunc->GetName());
|
||||
return;
|
||||
}
|
||||
|
||||
// Get the list of operators to apply this function to
|
||||
TArray<FString> ScriptOperators;
|
||||
{
|
||||
const FString& ScriptOperatorsStr = InFunc->GetMetaData(PyGenUtil::ScriptOperatorMetaDataKey);
|
||||
ScriptOperatorsStr.ParseIntoArray(ScriptOperators, TEXT(";"));
|
||||
}
|
||||
|
||||
// Go through and try and create a function for each operator, validating that the signature matches what the operator expects
|
||||
for (const FString& ScriptOperator : ScriptOperators)
|
||||
{
|
||||
PyGenUtil::FGeneratedWrappedOperatorSignature OpSignature;
|
||||
if (!PyGenUtil::FGeneratedWrappedOperatorSignature::StringToSignature(*ScriptOperator, OpSignature))
|
||||
{
|
||||
REPORT_PYTHON_GENERATION_ISSUE(Error, TEXT("Function '%s.%s' is marked as 'ScriptOperator' but uses an unknown operator type '%s'."), *InFunc->GetOwnerClass()->GetName(), *InFunc->GetName(), *ScriptOperator);
|
||||
continue;
|
||||
}
|
||||
|
||||
PyGenUtil::FGeneratedWrappedOperatorFunction OpFunc;
|
||||
{
|
||||
FString SignatureError;
|
||||
if (!OpFunc.SetFunction(InTypeMethod.MethodFunc, OpSignature, &SignatureError))
|
||||
{
|
||||
REPORT_PYTHON_GENERATION_ISSUE(Error, TEXT("Function '%s.%s' is marked as 'ScriptOperator' but has an invalid signature for the '%s' operator: %s."), *InFunc->GetOwnerClass()->GetName(), *InFunc->GetName(), *ScriptOperator, *SignatureError);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure that we've generated a finalized Python type for this struct since we'll be adding this function as a operator on that type
|
||||
const UScriptStruct* HostedStruct = CastFieldChecked<const FStructProperty>(OpFunc.SelfParam.ParamProp)->Struct;
|
||||
if (GenerateWrappedStructType(HostedStruct, OutGeneratedWrappedTypeReferences, OutDirtyModules, EPyTypeGenerationFlags::ForceShouldExport))
|
||||
{
|
||||
// Find the wrapped type for the struct as that's what we'll actually add the operator to (via its meta-data)
|
||||
TSharedPtr<PyGenUtil::FGeneratedWrappedStructType> HostedStructGeneratedWrappedType = StaticCastSharedPtr<PyGenUtil::FGeneratedWrappedStructType>(GeneratedWrappedTypes.FindRef(PyGenUtil::GetTypeRegistryName(HostedStruct)));
|
||||
check(HostedStructGeneratedWrappedType.IsValid());
|
||||
StaticCastSharedPtr<FPyWrapperStructMetaData>(HostedStructGeneratedWrappedType->MetaData)->OpStacks[(int32)OpSignature.OpType].Funcs.Add(MoveTemp(OpFunc));
|
||||
}
|
||||
}
|
||||
};
|
||||
```
|
Reference in New Issue
Block a user