开放参数至蓝图
C++中常常使用UE4中的一些宏来设置想要暴露于蓝图的类、属性、方法等。
暴露类
使用UCLASS([specifiers])
暴露类至蓝图,其中的specifiers在暴露类的时候并不常用,并在第一行使用GENERATED_BODY()
,添加头文件#include "XXX.generated.h"
,如:
#include "GameFramework/Actor.h" #include "MyActor.generated.h" UCLASS() class AMyActor : public AActor { GENERATED_BODY() public: // 设置该Actor属性的默认值 AMyActor(); // 每一帧都调用 virtual void Tick( float DeltaSeconds ) override; protected: // 游戏开始或产生时调用 virtual void BeginPlay() override; };
往往通过在编辑器中“文件 -> 添加C++类 -> 使用类引导选择父类创建类”的方式直接生成对应的模板。
暴露属性
使用UPROPERTY([specifiers])
宏暴露属性至蓝图,如:
UPROPERTY([specifier, specifier, ...], [meta(key=value, key=value, ...)]) Type VariableName;
其中常用的specifier如下:
- EditAnywhere:可通过“属性”窗口在原型和实例上进行编辑;
- VisibleAnywhere:该属性在“属性”窗口中可见,但无法编辑,与EditAnywhere不兼容;
- BlueprintReadWrite:可从蓝图读取或写入此属性;
- BlueprintReadOnly:此属性在蓝图中只读,表示希望该属性作为常量出现在蓝图中;
- Category=“XXX”:给该属性分类以便在虚幻编辑器中查询;
此处注意EditAnyWhere和BlueprintReadWrite的区别,前者表示在虚幻编辑器中可以在“属性”窗口中对该属性值进行编辑。然而若需要在蓝图脚本编辑器中设置该属性,则需要使用BlueprintReadWrite,相当于为该属性自动添加了get和set方法。
暴露函数
使用UPROPERTY([specifiers])
宏暴露属性至蓝图,如:
UFUNCTION([specifier, specifier, ...], [meta(key=value, key=value, ...)]) ReturnType FunctionName([Parameter, Parameter, ...])
其中常用的specifier如下:
- BlueprintCallable:表示此函数可以直接在蓝图中执行,函数的实现只能在C++中进行;
- BlueprintImplementableEvent:该函数的具体实现只能在蓝图中进行。对于没有返回值的函数,可以当做一种事件来处理,不必有具体的实现。而对于有返回值的函数,则需要在蓝图编辑器中的左边栏查找该函数并进行覆写。其调用还只能在C++原生代码中进行;
- BlueprintNativeEvent:蓝图可以调用该函数,该函数的默认实现在C++中已经完成了,但是蓝图可以对该函数进行覆盖重写。这个参数可以实现最灵活的函数调用;
注意:对于BlueprintNativeEvent函数,需要一些特殊处理:
- 首先,要声明一个新的虚函数,函数名为 FunctionName_Implementation;
- 其次,对该函数的C++实现要转而对该虚函数进行;
- 最终,无论C++或者蓝图调用该函数时,都是直接使用函数的原名。
Example:
// header file UFUNCTION(BlueprintNativeEvent) void CountdownHasFinished(); virtual void CountdownHasFinished_Implementation(); // cpp source file void ACountdown::CountdownHasFinished_Implementation() { CountdownText->SetText(TEXT(“Go!”)); } void ACountdown::BeginPlay() { Super::BeginPlay(); CountdownHasFinished(); }
暴露结构体
游戏性类中的UStruct可包含变量,包括UProperty变量、函数和运算符。结构体的声明发生在类的声明之前。使用UStruct,不必继承自任何特定类,只需用USTRUCT(specifiers)
标记该结构体,如:
USTRUCT([Specifier, Specifier, ...]) struct StructName { GENERATED_USTRUCT_BODY() UPROPERTY([specifier, specifier, ...], [meta(key=value, key=value, ...)]) Type VariableName; UFUNCTION([specifier, specifier, ...], [meta(key=value, key=value, ...)]) ReturnType FunctionName([Parameter, Parameter, ...]) };
其中Specifier往往也就只用BlueprintType
,表示结构体可以在蓝图中使用。
与UObject不同的是,UStruct不会被垃圾回收,必须自行管理其生命周期。UStruct应该是纯传统数据类型,包含UObject反射支持,可以在虚幻编辑器、蓝图操控、序列化、联网等中编辑。
代理
使用代理类似函数指针,可以以通用的但类型安全的方式调用成员函数。通过使用代理,并将其动态地绑定到任何对象的成员函数上,然后在该对象上调用函数,即使调用者不知道该对象的类型也没关系。
与标记为BlueprintImplementableEvent的UFUNCTION类似,但是UFUNCTION仅仅能在蓝图中覆写,却无法在蓝图中调用。即无法通过蓝图中的某些事件去触发。而代理则可以绑定至不同类型的事件/流程,并实现不同的流程。
单播代理和多播代理区别
单播代理仅仅能绑定一个函数,而多播代理却可以绑定多个函数。单播代理可以代理有返回值的函数,而多播代理不可以代理有返回值的函数。
绑定时单播代理使用BindXXX()
等方法。根据要绑定的函数类型的不同使用不同的方法,如绑定全局函数使用BindRaw()
,绑定静态函数使用BindStatic()
等等。由于是单播代理,故仅能绑定一个函数,以最后绑定的函数为准。详见官方文档。常用绑定UObject方法,如:
this->StringDelegateWithoutPar.BindUObject(this, &AActorPluginDemo::FuncForDelegateNoPar);
多播代理进行绑定时则采用AddXXX()
等方法。与单播代理类似。
单播代理最终使用Execute()
或ExecuteIfBound()
执行,其中后者更安全。但要注意若有返回值,则只能使用Execute()
来获取返回值。最好之前使用IsBound()
确认是否已经绑定有函数。多播代理则使用Broadcast()
广播所有的参数。
动态代理和非动态代理的区别
动态代理可以用于Blueprint中,而非动态代理仅能在C++中进行绑定。注意声明方式的区别!
// 声明非动态代理不需要添加参数名称 DECLARE_DELEGATE_TwoParams(DelegateName, Param1Type, Param2Type); // 声明动态代理则需要添加参数名称 DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(DynamicDelegateName, Param1Type, Param1Name, Param1Type, Param2Name);
为了动态代理能够在Blueprint中使用,则自然需要在声明对应的变量的时候添加UPROPERTY([specifiers])
。其中可用于多播代理的Specifier有BlueprintAssignable、BlueprintAuthorityOnly、BlueprintAuthorityOnly、
BlueprintCallable几种,详见官方文档。
之后就可以在蓝图中对该动态代理变量进行绑定,并对其实现不同的业务流程。
使用代理多播TArray
若想使用代理多播TArray到蓝图中,则参数类型必须为TArray的引用,而且要加上const
关键字,否则蓝图中会报错,如:
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(DynamicDelegateName, const TArray<FSelfDefinedStruct>&, StructArray);
同样地,蓝图可调用函数中的参数若为TArray,也需要使用TArray的const引用,如:
UFUNCTION(BlueprintCallable) void function(int32 par1, const TArray<FString>& strArr);
参考资料
- https://docs.unrealengine.com/en-us/Programming/UnrealArchitecture/Reference/Classes
- http://api.unrealengine.com/CHN/Programming/UnrealArchitecture/Reference/Properties/index.html
- http://api.unrealengine.com/CHN/Programming/UnrealArchitecture/Reference/Functions/index.html
- http://api.unrealengine.com/CHN/Programming/UnrealArchitecture/Reference/Structs/index.html
- http://api.unrealengine.com/CHN/Programming/UnrealArchitecture/Delegates/