虚幻C++入门个人笔记(2)——标记宏、结构体枚举、资源加载、代理
标记宏
类UCLASS、成员属性UPROPERTY、成员函数UFUNCTION
蓝图和C++的交互
UObject
UE4对于C++进行调整后,靠拢了其他虚拟机语言,为了方便对象内存管理和映射数据操作,UE4对UECPP进行了更加严谨的语法定义,所有UE中使用的对象大部分继承自UObject,这样的系统级的管理更加轻松,不必涉及更多不定性内容,UObject具备如下优点
1.垃圾回收(new出来的对象不用去释放了)
2.引用更新(更新受限于GC回收机制,需要使用UPROPERTY宏标记)(苹果消亡,对象里指向它的指针指向为空)
3.序列化(场景中的Actor被保存时发生)(保存时保存到本地)
4.默认属性变化自动更新(对象在蓝图中暴露了属性,在C++中改了属性,蓝图中会自动修改,蓝图里改c++也会改)
5.源码调整了对象属性时,编译后会自动更新到资源实例上,前提场景实例没有修改过属性值,修改过后将使用场景中的修改内容做填充参考
6.自动属性初始化(不赋值指向默认值)
7.自动编辑器整合(可以将C++属性暴露到蓝图上)
8.运行时类型信息可用
9.运行时使用Cast可以进行类型信息投射检查(指针转换关系,尝试性地将一个指针转换为另一个指针,转换的过程是安全的,失败返回空成功返回真,两个类没有关系的时候就会失败)
10.网络复制
UCLASS宏
用于标记从UObject派生的类,使得UObject处理系统识别到它们
该宏为UObject提供一个对UCLASS的引用,描述是基于虚幻引擎的类型。对于标记UCLASS的类,引擎可进行识别
继承关系中的头文件描述
#include "MyObject.generated.h"
必须是最后一个被引入的头文件,默认由UE模板生成,此头文件需要了解其他类,所以需要放到最后
UCLASS()
UCLASS宏使虚幻引擎4能识别UMyObject
class MYPROJECT_API UMyObject:public UObject
如MyProject希望将UMyObject类公开到其他模板,则需要指定MYPROJECT_API
GENERATED_BODY()
GENERATED_BODY宏不获取参数,但会对类进行设置,以支持引擎要求的基础结构,GENERATED_BODY宏将成员访问等级设为“public”,而非设置“private”的缺省语言
创建UObject类对象指针
动态构建对象的时候使用到这个函数
NewObject()是最为简单的UObject工厂模式,他需要可选的外部对象和类,并会创建拥有自动生成的名称的新实例。
UCLASS宏标记
语法结构
UCLASS(描述指令,描述指令,...)
常用标记
BlueprintType
此类可以作为蓝图中的一种变量类型使用,类默认均可被蓝图访问,一般我们用此标记描述结构体,提供给蓝图访问
Blueprintable或NotBlueprintable
用来标明当前类是否可以被蓝图继承,默认可被继承,标记关系向子类传递,子类可覆盖描述关系
Abstract
Abstract类修饰符将类声明为“抽象基类”,这样会阻止用户在虚幻编辑器中向这个世界中添加这个类的Actor,或者在游戏过程中创建这个类的实例
Const
本类中的所有属性及函数均为常量,并应作为常量导出,该标记由子类继承
Config
将类内的成员变量数据信息保存到本地配置文件中,需要显式调用函数SaveConfig使用,并配合UPROPERTY宏操作
例:UCLASS(Config=Game)
ClassGroup
用来配置组件在添加时分组情况
例:UCLASS(ClassGroup=(ZQSELF)) //分组名字不要写中文
meta
额外操作宏
例:meta=(BlueprintSpawnableComponent) //当前组件可以在蓝图中再被延展
UFUNCTION宏标记
用途:将成员函数暴露到蓝图中去,在蓝图中享有操作权限
语法结构(标注在函数顶端,禁止在尾端加分号)
UFUNCTION(指令,指令...,meta(key=value))
注意:在UFUNCTION修饰的函数中,如果参数类型是引用型参数,则在蓝图中将当作返回参数使用,无法查到输入阵脚。如果参数类型是const修饰的引用型参数,则参数被当作输入阵脚使用
常用标记
BlueprintCallable
表明此函数可在蓝图中被调用(当类被蓝图继承后才有效果)
注意:如果函数参数是引用类型,则在蓝图中调用被当作输入阵脚,如果从传入参数是const修饰的引用类型,则在蓝图中被当作输入针脚
例1:UFUNCTION(BlueprintCallable)
int32 CTime(const int32& num);
例2:UFUNCTION(BlueprintCallable)
int32 CTime(int32& num); //被当作函数输出阵脚使用
Category
标明此函数在蓝图中的分类
用法Category="UE4TEST|MyActor" // |符号用来划分分类级别
BlueprintImplementableEvent
标记的函数只能在C++中调用,在蓝图中无法调用——在CPP中无需定义即可调用,调用的是蓝图中的一个节点
用此标记可以在C++中构建函数声明,但定义由蓝图完成,从而打到C++向蓝图进行调用的目的,在CPP中无需定义
UFUNCTION(BlueprintImplementableEvent,Category="UECPP|ACPPActory")
类似纯虚函数,但是继承关系中的蓝图并不用必须重写此函数
注意:
如果函数没有返回类型,则在蓝图中当作事件Event使用
如果函数存在返回类型,则在蓝图中当作函数使用(需要在函数的overlap中寻找)
如果带有参数
没有返回值,参数是基本数据类型,则当作普通事件输入参数使用
没有返回值,参数是基本数据类型引用,则当作函数看待,在蓝图函数重载表中寻找
没有返回值,参数是自定义数据类型(如FString),编译不过
没有返回式,参数是自定义数据类型引用,则当作普通函数看待,在蓝图函数重载表中寻找
如果有返回值,则当作函数使用
传递引用操作如BlueprintCallable
BlueprintNativeEvent
标记的函数只能在C++中调用,在蓝图中无法调用
此标记可以标注函数可以在蓝图中被重写,并且具备在C++中有另一个标记函数声明格式为:
函数名_Implementation(参数列表) //必须要在C++中声明且定义
达到效果,如果蓝图重写此函数,则函数逻辑实现在蓝图,如果蓝图没有重写此函数,则函数实现在(函数名_Implementation)上
蓝图中实现后,可以右键函数节点,选择add call to parent function可以调用父类的函数逻辑(类似类中的虚函数,在继承关系中子类可以调用父类的虚函数)
注意:
如果函数没有返回类型,则在蓝图中当作事件Event使用
如果函数存在返回类型,则在蓝图中当作函数使用(需要在函数的overlap中寻找)
基本效果同上
传递引用操作如BlueprintCallable
BlueprintPure
特殊标记,构建一个蓝图中的纯函数,用来获取对象数据。所以此标记的函数必须有有效返回值(无返回值编译报错),并且在蓝图中无法加入到执行队列,只能以输出值的操作方式被使用,定义实现均放在CPP中
例:UFUNCTION(BlueprintCallable,BlueprintPure)
UPROPERTY宏标记
语法结构
注意:用于将对象属性暴露到蓝图中操作,整型中只有int32能暴露到蓝图中
UPROPERTY(标记,标记,...,meta(key=value,key=value,...))
类型 参数名称
注意:就算没有标记,该加UPROPERTY的地方也要加,否则没有智能指针的操作特性,例如UClass* ActorClass //用来拾取类资产
常用标记
BlueprintReadOnly
标记属性可以在蓝图中获取,但是只能读取无法操作
BlueprintReadWrite
在蓝图中即可读取也可以操作设置
Category
标明此变量在蓝图中的分类
用法Category="UE4Test|MyActor" // |符号用来划分分类级别
Config
标记此属性可被存储到指定的配置文件中,启动时属性内容将从配置文件中获取,UPROPERTY下方应跟上配置文件存的数据内容
//相应的UClass也应,如CLASS(Config=UE4CPP)
对应的两个函数:
SaveConfig()
LoadConfig()
EditAnywhere
表示此属性可以在编辑器窗口中进行编辑也可以在场景细节面板中编辑(都可以)
EditDefaultsOnly
可以在蓝图编辑器中编辑原型数据,但无法在场景细节中编辑场景中的具体对象(蓝图里可以改)
EditInstanceOnly
表明该属性的修改权限在实例,不能在蓝图编辑器原型中修改(实例中可以改)
VisibleAnywhere
表明属性可以在属性窗口可见(原型实例中均可以看到),但无法编辑(都可以看到但不能修改)
注意如果标记组件指针,则表示组件内容在细节面板中显示所有编辑项
VisibleDefaultsOnly
属性仅能在原型蓝图编辑属性窗口中可见,无法编辑(蓝图右侧可以看到但不能修改)
注意如果标记组件指针,则表示组件内容在细节面板中显示所有编辑项
VisibleInstanceOnly
属性仅能在实例属性窗口中可见,无法编辑(实例可以看到不能修改)
注意如果标记组件指针,则表示组件内容在细节面板中显示所有编辑项
EditFixedSize
限定动态数组长度禁止在蓝图属性面板中修改(单一添加无法显示,需要配合使用上面两个标记)
在类里的构造函数调用Array.Init(0,10)可以在类的构造的时候为这个数组以代码方式进行初始化
meta描述
别名标记指令(也可以用在函数上)
meta=(DisplayName="别名")
使用此标记可以帮助我们对函数或是属性进行别名操作,可以使用在UF和UP中,可以在蓝图中或是细节面板中显示别名
成员属性值域约束(只能用来约束整型)
meta=(UIMin="10",UIMax="20") //UI上约束,只是面板约束,如直接填入数据则不约束
meta=(ClampMin="0",ClampMax="10") //UI上约束,填入数据也被约束
成员属性修改约束
EditCondition
可以借助一个布尔变量来控制另一个变量是否可以在面板中被修改
UPROPERTY(EditAnywhere,EditFixedSize)
bool bShow;
UPROPERTY(EditAnywhere,AdvancedDisplay,meta=(ClampMin="0",ClampMax="10",EditCondition="bShow"))
int32 wock;
结构体,枚举
结构体(后面接BREAK)
在虚幻C++中结构体和普通C++结构体构建方式相同,但是如果希望构建于蓝图交互的结构体则需要额外的处理
UE支持结构体的构建和使用,但是由于蓝图特殊,普通的结构体定义无法被蓝图访问,我们需要借助USTRUCT宏进行构建UE中的结构体
注意:结构体名称必须使用F开头,不以F开头编译不过,必须带两个操作宏,如需要在蓝图中使用,需要加入BlueprintType标记
例:
USTRUCT(BlueprintType)
struct FBox
{
GENERATED_USTRUCT_BODY();
UPROPERTY(EditAnywhere,BlueprintReadWrite) //不加这个蓝图没法break和在右侧面板调整
int32 x;
UPROPERTY(EditAnywhere,BlueprintReadWrite)
int32 y;
};
类中声明结构体:
UPROPERTY(EditDefaultsOnly)
FBox box;
枚举(后面接SWITCH)
列出了有穷数列的集合
语法与C++相同,总的来说可以使用以下两种方式进行构建
第一种 空间构建方式
例:
namespace EState
{
enum Type
{EGS_Blue, //EGS是标题头
EGS_Red};
}
使用
EState::Type type;
特点:使用空间名称作为访问依据,可以更清晰的标明意图,方便使用
第二种 约束枚举大小
enum class ZColo:uint8
{
EGS_ERed,
EGS_EBlue
}
构建与蓝图交互的枚举
借助标记宏UENUM可以将C++中的枚举暴露到蓝图中使用,需要使用标记BlueprintType(对于枚举定义的位置并没有严格要求)
j建议使用:方法一:空间构建枚举暴露蓝图
声明枚举
UENUM(BlueprintType)
namespace EState
{
enum Type
{EGS_Blue,
EGS_Red};
}
定义时枚举对象需要注意格式:(必须使用此方法才可以暴露到蓝图)
UPROPERTY(EditAnywhere,BlueprintReadWrite)
TEnumAsByte<EState::Type> Color; //Color是另外随便起的一个名字,这是一个包裹,把枚举类型进行了一个包裹,这个Color可以理解成就是枚举,其实是具有枚举特性的一个TEnumAsByte的对象类型
方法二:
声明枚举
UENUM(BlueprintType)
enum class ENColor:uint8
{
ENC_Red,
ENC_Blue
};
定义枚举
UPROPERTY(EditAnywhere,BlueprintReadWrite)
ENColor Color;
UMETA宏
可以帮助枚举名进行蓝图别名创建,方便在蓝图中寻找操作(空间声明枚举的方式不适用)
语法
UENUM(BlueprintType)
enum class ENColor:unit8
{
ENC_Red UMETA(DisplayName="R"),
ENC_Blue UMETA(DisplayName="B"),
};
资源加载
在虚幻中我们需要注意处理的资源分为类资源和非类资源(粒子,音频,材质,地图等等)。虚幻中提供了多种机制用来将资产进行加载。资产加载到内存中,我们操作需要通过引用来完成。
引用(对于资产在内存中存储的一种操作方式)总的来说分两种:
硬性引用:对象A引用对象B并导致对象B在对象A加载时加载(缺点:加载了但不一定会用到B)
软性引用:对象A通过间接机制(例如字符串形式的对象路径)来引用对象B
直接属性引用(在编辑器面板中拿资产,硬性引用)
编辑器直接加载(当对象实例化的时候就会直接加载那些资产)
通过使用属性标记宏UPROPERTY(Edit三个都可以)来将资产对象指针暴露到编辑器面板,从而直接从编辑器面板拾取资产
注意:UClass类指针,专门用来拾取类模板资产
UPROPERTY(EditDefaultsOnly)
UClass* ActorClass; //用来拾取任意类资产
UPROPERTY(EditDefaultsOnly) //禁止在这里写注释,语法错误
class USoundCue* SoundSrc; //用来获取音频资产
//UTexture,UMaterial,UStaicMesh,USkeletalMesh
TSubClassOf
TSubClassOf是提供UClass的安全模板类,通过此模板类我们可以快速在编辑器中进行类型选择,帮助我们快速构建某种类型对象数据
TSubClassOf对类型有约束,只能选取模板类型或是继承自模板类型的类或蓝图
语法
TSubClassOf<T> type;
type.Get(); //获取UClass数据对象
构建指定类的对象
构造函数的加载
在构造函数中可以借助构造函数资产加载类进行资源引用,更加方便便捷
静态资源引用类ConstructorHelpers可以进行类引用,源资源引用,ConstructorHelpers只能在构造函数中使用
注意:
1.操作路径前加入/Game/前缀
2.ConstructorHelpers类将尝试在内存中查找该资产,如果找不到则进行加载
3.ConstructorHelpers只能在构造函数中使用
4.如果加载失败或是未找到资源,对象内的资产属性为null
5.如果加载蓝图类模板对象时,需要加注"_C"
常用资源加入分类
FClassFinder 常用来加载创建后的蓝图对象
FObjectFinder 用来加载各种资源,如音频,图片,材质,静态网格
FClassFinder语法
ConstructHelpers::FClassFinder<APawn> (TEXT("/Game/Flappybird/Blueprints/BP_Bird")).Class
//返回数据类型是TSubClassOf
也可以使用如下路径拾取蓝图对象
ConstructHelpers::FClassFinder<AActor> UnitSelector(TEXT("Blueprint'/Game/Flappybird/Blueprints/MyBlueprint.MyBlueprint_C'"));
//下划线C必须要加
FObjectFinder语法
ConstructHelpers::FObjectFinder<UTexture2D> BarFillObj(TEXT("/Game/UI/HUD/BarFill"));
BarFillTexture=BarFillObj.Object; //将获取的数据内容指针保存
查找加载(软性引用)
在只知道目标资源路径的基础上,进行运行时态的资源加载,UE提供了LoadObject用来加载资产,LoadClass用来加载类,通过模板约束对象类型,增加操作安全,但是注意,资源加载可能会失败或无效,需要对操作的结果进行判定
LoadObject可直接返回资源有效对象指针
LoadClass返回操作类,非对象返回UClass类型指针
原型如下
//Load an object
template<class T>
inline T* LoadObject(UObject* Outer,const TCHAR* Name,const TCHAR* Filename=nullptr,uint32 LoadFlags=LOAD_None,UPackageMap* Sandbox=nullptr)
{
return (T*)StaticLoadObject(T::StaticClass(),Outer,Name,Filename,LoadFlags,Sandbox);
}
//Load a class object
template<class T>
inline UClass* LoadClass(UObject* Outer,const TCHAR* Name,const TCHAR* Filename=nullptr,uint32 LoadFlags=LOAD_None,UPackageMap* Sandbox=nullptr)
{
return (T*)StaticLoadClass(T::StaticClass(),Outer,Name,Filename,LoadFlags,Sandbox);
};
Outer帮助我们进行搜索范围锁定,可以填入同目录资源,如不存在填入空
Name资源文件名,可在编辑器中通过选择资源右键获取引用名,注意蓝图加载需要加入后缀"_C"
例子:
Sound=LoadObject<USoundCue>(nullptr,TEXT("路径"));
SubActorClass=LoadClass<AActor>(nullptr,TEXT("路径_C"));
注意:
1.查找加载想在什么地方加载就可以在什么地方加载
2.同级别的资产可以加进Outer
3.蓝图加载需要加入后缀"_C"
4.使用前必须使用If做安全检查,因为可能加载失败
间接属性引用
间接属性引用的设计目的是解决硬性引用加载方式影响启动效率。间接属性引用可以很好的帮助我们解决问题。在使用时它和直接属性引用相同,但是操作时需要提前进行加载。间接属性引用以字符串形式与模板代码存储在一起以便安全地检查资产是否已经加载,而不是进行直接指针引用,使用IsPending()方法可检查资产是否已准备好可供访问。
简介属性的目的,将资源加载动作进行拆分,以降低在启动时内存消耗问题与启动耗时问题
FSoftObjectPath
FSoftObjectPath是一个简单的结构体,使用一个字符串包含资源的完整名称。可以在编辑器中拾取资源(这与直接属性引用相同),但是并不加载资源。资源的加载需要通过额外的代码编写完成
FSoftObjectPath被暴露到面板中对于资源的拾取并没有特定的要求,所有能够被序列化的资源均能被拾取(类资源,非类资源)
例子:
UPROPERTY(EditAnywhere)
FSoftObjectPath SourcePath;
资源加载
FSoftObjectPath只是存储了资源的路径,使用前必须通过加载方式方可获得资源。加载方式分为同步加载(如果资源过大会导致游戏程序卡顿)和异步加载
同步加载
在加载运行线程中,阻塞线程的流程执行,将线程停止在当前加载逻辑中,加载完成后继续线程的执行逻辑操作,对于加载小资源可以保证资源的不为空,但加载大资源将导致调用线程卡顿
例子:
1. 非类资源
//直接设置资产路径替代细节面板引用,如果细节面板引用则可忽略
SourcePath.SetPath(TEXT("路径"));
UObject* Source=SourcePath.TryLoad(); //尝试加载 同步加载
USoundBase* Sound=Cast<USoundBase>(Source);
if(Sound)
{UGameplayStatics::PlaySound2D(GetWorld(),Sound);}
2. 类资源
UObject* Source=SourcePath.TryLoad(); //加载类资产
UClass* ObjClass=Cast<UClass>(Source);
if(ObjClass)
{GetWorld->SpawnActor<AActor>(ObjClass,FVector(200,200,200),FRotator::ZeroRotator);}
异步加载
在加载线程,不阻塞当前线程逻辑加载资源,加载器本身具备线程进行资源加载。较之同步加载更加的灵活,但是相对维护成本较高,资源加载成功后需要进行回调通知,以完成整个加载流程
异步加载
FStreamableManager
FStreamableManager可以帮助我们构建异步处理逻辑,用来将加载逻辑与游戏主逻辑进行,以达到高效加载资源的目的。建议FStreamableManager创建在全局游戏的单例对象中,结合FSoftObjectPath进行加载。FStreamableManager支持异步加载的同时也支持同步加载
FStreamableManager m_Streamable; //构建为栈对象,需要引入头文件,不要构建为堆对象
FSoftObjectPath同步加载
同步加载与之前使用的TryLoad基本一致
例子:
头文件中
UPROPERTY(EditAnywhere)
FSoftObjectPath SourcePath;
FStreamableManager m_Streamable; //最好构建为一个全局对象,即在全局域构建
源文件中
//借助fstreamablemanager配合fsoftobjectpath同步加载资源
UObject* Source=m_Streamable.LoadSynchronous(SourcePath); //此步骤会阻塞进程
USoundBase* Sound=Cast<USoundBase>(Source);
if(Sound)
{UGameplayStatics::PlaySound2D(GetWorld(),Sound);}
FSoftObjectPath异步加载
异步加载需要设置回调通知对象与通知函数
void LoadSourceCallBack(); //写一个异步加载回调通知函数,无返回类型,无参,无标记宏
在BeginPlay里
//借助fstreamablemanager配合fsoftobjectpath同步加载资源
m_Streamable.RequestAsyncload(SourcePath,FStreamableDelegate::CreateUObject(this,&AMyAcotr::LoadSourceCallBack)); //此步骤不会阻塞进程
异步加载通知函数的定义
//此函数调用,则表明异步加载完成
//加载完成后转换资产
void AMyAcotr::LoadSourceCallBack()
{USoundBase* Sound=Cast<USoundBase>(SourcePath.ResolveObject()); //ResolveObject函数用来探查资源是否存在内存中
if(Sound)
{UGameplayStatics::PlaySound2D(GetWorld(),Sound);}}
模板资源拾取类
TSoftObjectPtr和TSoftClassPtr
模板类帮助我们在进行资源操作时增加了类型安全检查,我们可以在细节面板中根据给定的模板类型拾取对应的资源,以获得更加高效的操作
同样的,TSoftObjectPtr和TSoftClassPtr也分为同步和异步加载,针对资源拾取类别不同,使用需要注意
UPROPERTY(EditAnywhere)
TSoftObjectPtr<class USoundBase> SoftSound;
UPROPERTY(EditAnywhere)
TSoftClassPtr<class ACharacter> SoftCharacter;
TSoftObjectPtr同步加载例子
头文件中
FStreamableManager m_Streamable;
UPROPERTY(EditAnywhere)
TSoftObjectPtr<class USoundBase> SoftSound;
源文件中
SoftSound.ToSoftObjectPath(); //可以转换为FSoftObjectPath对象
//同步加载
UObject* Source=m_Streamable.LoadSynchronous(SoftSound); //此步骤会阻塞进程
USoundBase* Sound=Cast<USoundBase>(Source);
if(Sound)
{UGameplayStatics::PlaySound2D(GetWorld(),Sound);}
TSoftObjectPtr异步加载例子
m_Streamable.RequestAsyncload(SoftSound.ToSoftObjectPath(),FStreamableDelegate::CreateUObject(this,&AMyAcotr::LoadSourceCallBack)); //此步骤不会阻塞进程
异步加载通知函数的定义
//此函数调用,则表明异步加载完成
//加载完成后转换资产
void AMyAcotr::LoadSourceCallBack()
{USoundBase* Sound=Cast<USoundBase>(SoftSound.Get()); //Get用来加载资源
if(Sound)
{UGameplayStatics::PlaySound2D(GetWorld(),Sound);}}
TSoftClassPtr同步加载
TSubclassOf<ACharacter> CharacterClass=m_Streamable.LoadSynchronous(SoftCharacter); //此步骤会阻塞进程
if(SoftCharacter)
{GetWorld()->SpawnActor<ACharacter>(CharacterClass);}
TSoftClassPtr异步加载
m_Streamable.RequestAsyncload(SoftCharacter.ToSoftObjectPath(),FStreamableDelegate::CreateUObject(this,&AMyAcotr::LoadSourceCallBack)); //此步骤不会阻塞进程
void AMyAcotr::LoadSourceCallBack()
{UClass* CharacterClass=CharacterClass.Get(); //Get用来加载资源
if(CharacterClass)
{GetWorld()->SpawnActor<ACharacter>(CharacterClass);}}
代理
代理可以帮助我们解决一对一或是一对多的任务分配工作。主要可以帮助我们解决通知问题。我们可以通过代理完成调用某一给对象的一个函数,而不直接持有该对象的任何指针
想去调用某个函数,但不是直接去调用,而是通过另一个入口去调用(代理)
单播代理——只能进行通知一个对象
多播代理——可以进行多人通知
动态代理——可以被序列化(这体现在于蓝图进行交互,C++中可以将通知事件进行蓝图广播)
单播代理
通过宏进行构建,单播代理只能绑定一个通知对象,无法进行多个对象通知
构建宏分为两种
一种是有返回类型的,一种是没有返回类型的
语法
DECLARE_DELEGATE(GMDelegateOne)
//构建宏
GMDelegateOne GmDel; //声明代理
常用绑定函数
BindUObject 绑定UObject类型对象成员函数的代理
BindSP 绑定基于共享引用的成员函数代理
BindRaw 绑定原始自定义对象成员函数的代理,操作调用需要注意执行需要检查IsBound
BindStatic 绑定全局函数成为代理
UnBind 解除绑定代理关系
绑定需要注意,绑定中传递的对象类型必须和函数指针所属类的类型相同否则绑定会报错
GmDel.BindUObject(act,&AMyActor::Say);
调用执行
为了保证调用的安全性,执行Execute函数之前需要检查是否存在有效绑定使用函数IsBound
Execute 调用代理通知,不安全,需要注意
ExecuteIfBound 调用代理通知,安全,但是有返回类型的回调函数无法使用此函数执行回调
IsBound 检查当前是否存在有效代理绑定
GmDel.ExecuteIfBound();
构建步骤
1.通过宏进行声明代理对象类型(根据回调函数选择不同的宏)
2.使用代理类型进行构建代理对象
3.绑定回调对象,和操作函数
4.执行代理对象回调
DECLARE_DELEGATE(CallTest);
DECLARE_DELEGATE_RetVal_OneParam(int32,CallTestRe,int32)
cb.BindUObject(am,&AMyActor::CallBackTest);
cb.ExecuteIfBound();
cbr.BindUObject(am,&AMyActor::CallBackRe);
if(cbr.IsBound())
{int32 num=0;
num=cbr.Excute(100);}
多播代理
无法构建具有返回值的多播代理
构建宏
DECLARE_MULTICAST_DELEGATE[_Const,_RetVal,etc.](DelegateName)
绑定函数
AddUObject()
广播
调用函数Broadcast,但是调用不保证执行顺序的正确性
构建步骤
1.使用宏构建代理类型
2.使用代理类型构建多播代理对象
3.添加绑定代理
4.执行调用
动态代理
允许被序列化的数据结构,这将使得代理可以被数据化提供给蓝图进行使用,达到在CPP中调用代理广播,将事件通知到蓝图中
动态代理和普通代理基本相同,分为单向和多向,动态代理无法使用带有返回值的函数进行构建(动态单播除外,并且单播无法再蓝图中绑定无法使用宏BlueprintAssignable修饰)
UE中的大部分通知时间均使用动态代理(方便蓝图操作),如碰撞通知
构建一个动态代理
DECLARE_DYNAMIC_DELEGATE[...Const,..._RetVal,etc.](DelegateName)
创建一个动态的多播代理
DECLARE_DYNAMIC_MULTICAST_DELEGATE[...Const,...RetVal,etc.](DelegateName)
操作函数
BindDynamic(UserObject,FuncName) 在动态代理上调用BindDynamic()的辅助宏
AddDynamic(UserObject,FuncName) 在动态多播代理上调用AddDynamic()的辅助宏
RemoveDynamic(UserObject,FuncName) 在动态多播代理上调用RemoveDynamic()的辅助宏
动态代理与单播代理和多播代理的区别
1.动态代理构建类型名称需要用F开头(动态代理实现机制构建了类)
2.动态代理对象类型可以使用UPROPERTY标记,其他代理均无法使用(不加编译可过,调用会出错)
3.动态代理绑定对象的函数需要使用UFUNCTION进行描述(因为需要跟随代理被序列化)
构建
动态单播构建
DECLARE_DYNAMIC_DELEGATE(FGMDynDelegate); //分号不要漏了
FGMDynDelegate GmDyDele;
GmDyDele.BindDynamic(ActorName,&AMyActor::Say);
GmDyDele.ExcuteIfBound();
动态多播构建操作如上
动态代理用于蓝图
需要注意的是,在构建动态代理提供蓝图使用时,需要在代理上增加标记宏UPROPERTY(BlueprintAssignable)
构建宏
DECLARE_DYNAMIC_MULTICAST_DELEGATE(FGMDynMulDele);
对象声明
UPROPERTY(BlueprintAssignable,BlueprintReadWrite)
FGMDynMulDele OnGmDynMulDele;
在C++中调用
if(OnGmDynMulDele.IsBound())
{
OnGmDynMulDele.Broadcast();
}
事件
事件本身和多播代理一样,为了操作的安全性,事件提供了额外的操作限定,即禁止在声明事件对象的外部调用事件传播,清理,检查等函数。通过操作隔离,最大程度的增加了事件的安全性。派生类允许调用事件的广播
在虚幻C++中事件和多播几乎相同。只是构建方式略不同
构建宏
事件类型构建宏由于需要限定事件对象调用约束关系,需要提供声明所在类型,并且需要在类内部进行声明。事件没有返回值
如果想要去构建一个代理,这个代理在类外部的地方都不能调用就用事件(虽然说并没有实际上约束到的效果)
OwningType即当前声明事件的类
一般构建语法如下:将事件声明在类的内部,通过函数构建返回事件对象操作
public:
DECLARE_EVENT(AMyActor,FChangeEvent) //声明事件类型
FChangeEvent& OnChanged(){return ChangeEvent;}
private:
FChangeEvent ChangeEvent; //构建事件对象
绑定函数与广播
AddUObject();
调用函数Broadcast,但是调用不保证执行顺序的正确性。事件广播无需检查是否存在有效的绑定,事件广播应发生在事件类型的类内部
移除
FDelegateHandle handle=NotifyEvent.AddUObject(my,&AMyActor::Call); //使用一个代理句柄变量等出来
NotifyEvent.Remove(handle); //移除这个代理句柄
标记宏总结:
UE中大部分的对象都是UObject,UObject通过标记宏可以使得UObject处理系统识别到它们,从而实现蓝图和C++之间的交互
标记宏有UCLASS(类), UPROPERTY(成员属性), UFUNCTION(成员函数),在标记宏的后面括号里可以添加标记,从而实现蓝图与C++中的交互
UCLASS(BlueprintType) //允许蓝图使用下一行的类的类型
UPROPERTY(BlueprintReadWrite) //允许蓝图的事件图表里进行读写
UPROPERTY(EditAnywhere) //允许蓝图的编辑器面板和实例中可以编辑参数
UFUNCTION(BlueprintCallable) //允许蓝图在事件图表中调用该函数节点
UFUNCTION中也可以实现蓝图和C++交互的两个较特殊的标记
1.BlueprintImplementableEvent
2.BlueprintNativeEvent
资源加载总结:
有三种加载方式:
1.通过使用属性标记宏UPROPERTY(Edit三个都可以)来将资产对象指针暴露到编辑器面板,从而直接从编辑器面板拾取资产
UPROPERTY(EditDefaultsOnly)
UClass* ActorClass; //用来拾取任意类资产
UPROPERTY(EditDefaultsOnly) //禁止在这里写注释,语法错误
class USoundCue* SoundSrc; //用来获取音频资产
//UTexture,UMaterial,UStaicMesh,USkeletalMesh
2.在构造函数中可以借助构造函数资产加载类进行资源引用,静态资源引用类ConstructorHelpers可以进行类引用,源资源引用,ConstructorHelpers只能在构造函数中使用
ConstructHelpers::FClassFinder<APawn> (TEXT("/Game/Flappybird/Blueprints/BP_Bird")).Class
//返回数据类型是TSubClassOf
拾取蓝图对象
ConstructHelpers::FClassFinder<AActor> UnitSelector(TEXT("Blueprint'/Game/Flappybird/Blueprints/MyBlueprint.MyBlueprint_C'"));
//下划线C必须要加
FObjectFinder语法
ConstructHelpers::FObjectFinder<UTexture2D> BarFillObj(TEXT("/Game/UI/HUD/BarFill"));
BarFillTexture=BarFillObj.Object; //将获取的数据内容指针保存
3.LoadClass用来加载类,通过模板约束对象类型,增加操作安全,但是注意,资源加载可能会失败或无效,需要对操作的结果进行判定
Sound=LoadObject<USoundCue>(nullptr,TEXT("路径"));
SubActorClass=LoadClass<AActor>(nullptr,TEXT("路径_C"));
代理总结:
1.单播代理
2.多播代理
3.动态代理
4.事件
虚幻C++入门个人笔记(2)——标记宏、结构体枚举、资源加载、代理相关推荐
- Python爬虫入门 | 7 分类爬取豆瓣电影,解决动态加载问题
比如我们今天的案例,豆瓣电影分类页面.根本没有什么翻页,需要点击"加载更多"新的电影信息,前面的黑科技瞬间被秒-- 又比如知乎关注的人列表页面: 我复制了其中两个人昵称 ...
- Unity资源加载入门
写在前面 本文转载自:https://gameinstitute.qq.com/community/detail/123460,供自己学习用,如有疑问,请移步原创. 引言 Unity的资源加载及管理, ...
- 【读书笔记】【WebKit技术内 幕(二)】Chromium Webkit资源加载与网络栈、DOM树、HTML解释器、影子DOM、CSS解释器和样式布局、网页层次与渲染、绘图上下文、
文章目录 前言 Something great 第4章 资源加载和网络栈 Webkit 资源加载 Chromium多进程资源加载 Chromium 网络栈 第5章 HTML解释器和DOM模型 DOM模 ...
- unity 通过resouce加载图片_Unity游戏开发笔记-资源管理之资源加载
资源加载是游戏中非常重要也非常繁琐的的一部分,不合理的资源管理,必定回给游戏的内存带来非常大的压力,尤其是一些重度游戏,不但资源特别多,引用关系特别复杂.维护一个不会内存泄漏而且加载效率高的资源加载框 ...
- 虚幻C++入门个人笔记(3)——接口、智能指针、动画蓝图、行为树、EQS系统
接口 接口的词义广泛,用来陈述功能.选项,与其他程序结构进行沟通的方式.接口抽象出了交互结构,提供了两个未知逻辑交互的便捷性.对于编程中,如何更好地设计低耦合程序起到了至关重要的作用.设计者可以在互不 ...
- 虚幻C++入门个人笔记(1)——一些基础、UE_LOG、TArray、翻译转换
工程目录文件 .vs VS环境配置文件(不用管不要删) Binaries 存放编译生成的结果二进制文件.该目录可以gitignore(可以忽略),每次都会生成 ...
- 【OS学习笔记】十 实模式:实现一个程序加载器-程序加载器如何将用户程序加载到内存并执行
上一篇文章学习了以下内容: 用一种不同的分段方法,从另一个不同的的角度理解处理器的分段内存访问机制 使用循环和条件转移指令来优化主引导扇区代码 点击链接查看上一篇文章:点击链接查看 对于主引导扇区部分 ...
- 《零基础看得懂的C++入门教程 》——(9)结构体原来如此
一.学习目标 了解C++语言的结构体的使用方法 了解C++语言结构体的结构的赋值 了解多种C++语言结构体变量的赋值方法和取值方法 目录 预备第一篇,使用软件介绍在这一篇,C++与C使用的软件是一样的 ...
- Android开发笔记(一百七十一)使用Glide加载网络图片
如何方便而又快速地显示网络图片,一直是安卓网络编程的热门课题,前些年图片缓存框架Picasso.Fresco等等大行其道,以至于谷歌按捺不住也开发了自己的Glide开源库.由于Android本身就是谷 ...
- 【ESP32S3学习笔记】LVGL相关结构体学习——lv_disp_drv_t
LVGL相关结构体学习--lv_disp_drv_t 提示:这里可以添加系列文章的所有文章的目录,目录需要自己手动添加 例如:第一章 Python 机器学习入门之pandas的使用 提示:写完文章后, ...
最新文章
- python 购物车代码
- 问题 | CondaHTTPError: HTTP 404 NOT FOUND for url
- java jar log4j_使用Log4j
- python lambda函数加法_python lambda的使用详解
- 微信浏览器不支持下载文件或应用解决方案
- 【原】webpack--loaders,主要解释为什么需要loaders和注意事项
- Selenium私房菜系列8 -- 玩转Selenium Server
- 赛尔笔记 | 通用领域条件性知识图谱数据集
- 数据结构编程题及解析c语言版,数据结构习题集答案(C语言版).pdf_c语言数据结构题目,c语言数据结构答案-C/C++文档类资源...
- WebBrowser填充表单值的问题.
- linux vim常用快捷键
- 计算机网络 复习提纲(完整版)
- 小米青春版路由器解锁root与SSH方法
- 朗文3000词汇表带音标_朗文少儿英语2A-Unit3知识归纳(单词含音标版
- PT100铂热电阻三种测温方法介绍
- C语言/C++基础之五彩炫酷珠
- 年终思路梳理(三)——工业互联网
- 高什么发,什么并发,高并什么? ? ?
- mysql表新增添加一列
- JS 位数不够自动左补0