基础回顾
UE4C++基础(一)_WLSTLA-CSDN博客
1. C++与UMG交互
? 首先我们需要设置玩家的输入模式,可以在PlayerController 中的BeginPlay 进行设置:
void AUIController::BeginPlay()
{
Super::BeginPlay();
FInputModeGameAndUI InputMode;
InputMode.SetLockMouseToViewportBehavior(EMouseLockMode::LockAlways);
InputMode.SetHideCursorDuringCapture(false);
SetShowMouseCursor(true);
}
? 我们可以创建一个继承于UUserWidget 的UObject ,作为我们自己的UMG 基类,复写Initialize() 函数,随后在蓝图中创建UMG 中继承该UObject 。
UCLASS()
class REFLECTIONFRAME_API UMyUIWidget : public UUserWidget
{
GENERATED_BODY()
public:
UMyUIWidget(const FObjectInitializer& ObjectInitializer);
virtual bool Initialize()override;
...
};
创建Widget 的方法:
TSubclassOf<UUserWidget> NewWidget;
UUserWidget* CurrentWidget;
CurrentWidget = CreateWidget(GetWorld(),NewWidget);
CurrentWidget->AddToViewport();
#include "Blueprint/WidgetTree.h"
#include "Components/TextBlock.h"
UTextBlock* SpawnText;
SpawnText = WidgetTree->ConstructWidget<UTextBlock>(UTextBlock::StaticClass());
从蓝图获取组件的方法:
UCanvasPanel* Root = Cast<UCanvasPanel>(GetRoot());
if(Root)
{
UTextBlock* Text = Cast<UTextBlock>(Root->GetChildAt(0));
}
- 根据
Widget 名字来获取。(名字与UMG 中命名的相同)
UBorder* BorderPanel = (UBorder*)GetWidgetFromName(TEXT("Panel"));
if(BorderPanel)
{
UE_LOG(LogTemp,Warning,TEXT("Get BorderPannel"));
}
- 使用
UPROPERTY() ,中的meta=(BindWidget) 获取。和根据名字来获取相同,都是基于反射来获取,这里属性的命名需要和UMG中相同。
UPROPERTY(meta=(BindWidget))
class UButton* Button3;
Button 组件绑定代理的方法
UButton* Button = GetWidgetFromName(TEXT("Button2"));
FScriptDelegate ClickDelegate;
ClickDelegate.BindUFunction(this,FName("ButtonClickEvent"));
Button->OnClicked.Add(ClickDelegate);
void UMyUIWidget::ButtonClickEvent()
{
...
}
UPROPERTY(meta=(BindWidget))
class UButton* Button3;
Button3->OnClicked.AddDynamic(this,&UMyUIWidget::Button3ClickEvent);
void UMyUIWidget::Button3ClickEvent()
{
...
}
2. 生命周期
? 这里的生命周期我们只探讨至Tick ,参考于梁迪的全反射零耦合框架UE4全反射零耦合框架开发坦克游戏
-
Actor 的生命周期
- 构造函数
- 初始化
Components PostInitializeComponents() BeginPlay() Tick(float DeltaSeconds) -
Level
- 初始化
GameInstance - 创建世界
World - 创建
Level ,LevelScriptActor (关卡蓝图)构造 Level 中存在的Actor 构造GameMode 构造- 随后执行
Actor 、GameMode 的PostInitializeCompoents() - 执行
Level 中Actor 、GameMode 、Blueprints 、Gameplay 框架类的BeginPlay() - 执行所有初始化完成类的
Tick()
注意:只有当场景中的Actor 和GamePlay 框架中设置的类(PlayerController、PlayerState.. )都执行完成Construct、PostInitializeComponents 后才执行BeginPlay() , GamePlay 中的框架类需要等待GameMode 执行完PostInitializeComponents 才会进行Construct 。
3. 全局类与接口
-
UBlueprintFunctionLibrary 相当于蓝图中的蓝图函数库,在C++ 中继承于UBlueprintFunctionLibrary 。 UCLASS()
class REFLECTIONFRAME_API UMyFunctionLibrary : public UBlueprintFunctionLibrary
{
GENERATED_BODY()
public:
UFUNCTION(BlueprintPure)
static FORCEINLINE int32 GetMaxNum(const int32& val1,const int32& val2)
{
return val1>val2?val1:val2;
}
};
同时其中函数需要是静态函数。之后你便可以在Blueprint 中进行调用。 这个类中写的方法只能供蓝图中使用,如果你想在C++ 中使用,则需要使用下面这个方法。 -
GameEngine->GameSingleton
? GameEngine->GameSingleton 是GEngine 下的一个UObject 指针,作为全局单例提供给C++ 使用,需要在编辑器中指定类型,UE4 的建议这里存放的对象里面的变量最好是不需要修改的。也就是存放一些const 变量,函数也不能修改类变量。
UCLASS(Blueprintable,BlueprintType)
class REFLECTIONFRAME_API UMySingletonObject : public UObject
{
GENERATED_BODY()
public:
FORCEINLINE int32 GetGloableVal()const
{
return _GloableInt;
}
private:
const int32 _GloableInt{10};
};
UMySingletonObject* mySingle = Cast<UMySingletonObject>(GEngine->GameSingleton);
mySingle->GetGloableVal();
其他的全局类:
-
UGameInstance :生命周期伴随整个程序的启动到结束,可以跨关卡交流数据 -
UGamePlayStatics :头文件#include "Kismet/GameplayStatics.h" ,包含一些GamePlay 框架开发中常用的方法(例如:GetAllActorsOfClass() ) …
4. 反射的应用
? 反射是UE4 的一大特性,这里我们可以使用反射来实现获取UEnum、UPROPERTY、UFUNCTION 。
UENUM()
enum class EState:uint8
{
Default,
Run,
Walk
};
UCLASS()
class REFLECTIONFRAME_API USpawnObject : public UObject
{
GENERATED_BODY()
public:
UFUNCTION()
int32 ReflectFunc1(const FString&str,int val);
UPROPERTY()
int32 IntVal{10};
};
int32 USpawnObject::ReflectFunc1(const FString& str, int val)
{
UE_LOG(LogTemp,Warning,TEXT("str:%s,val:%d"),*str,val);
return val+10;
}
? 如上述UObejct 类,我们需要利用反射来调用其中的EState、IntVal、ReflectFunc1 。
UEnum* enumPtr = FindObject<UEnum>(ANY_PACKAGE,TEXT("EState"));
if(enumPtr)
{
UE_LOG(LogTemp,Warning,TEXT("enum index:0 is %s"),*FName(enumPtr->GetNameByIndex(0)).ToString());
}
USpawnObject* SpawnObj = NewObject<USpawnObject>();
FIntProperty* IntPropPtr = FindFieldChecked<FIntProperty>(SpawnObj->StaticClass(),FName("IntVal"));
if(IntPropPtr)
{
int32 intval = IntPropPtr->GetPropertyValue_InContainer(SpawnObj);
UE_LOG(LogTemp,Warning,TEXT("get intval val is %d"),intval);
IntPropPtr->SetPropertyValue_InContainer(SpawnObj,20);
intval = IntPropPtr->GetPropertyValue_InContainer(SpawnObj);
UE_LOG(LogTemp,Warning,TEXT("get intval val is %d(After)"),intval);
}
? 这里FindFieldChecked 的函数原型如下:
template<typename T>
T* FindFieldChecked( const UStruct* Scope, FName FieldName )
? 可知这里第一个参数是UStruct* 类型的,但我们传入的却是一个UClass* 类型的。原因是在UE4 中UClass 是继承自UStruct 。UStruct 继承自UField ,UField 继承自UObject 。这个类中包含了反射、以及序列化相关的数据。
class COREUOBJECT_API UClass : public UStruct
class COREUOBJECT_API UStruct : public UField
class COREUOBJECT_API UField : public UObject
-
调用UFUNCTION()
UFunction* functor = SpawnObj->FindFunction(FName("ReflectFunc1"));
struct
{
const FString str{"unreal"};
int32 val{20};
}ParamStruct;
SpawnObj->ProcessEvent(functor,&ParamStruct);
uint8* RetValPtr = (uint8*)&ParamStruct + functor->ReturnValueOffset;
int32* RetVal = (int32*)(RetValPtr);
if(RetVal)
{
UE_LOG(LogTemp,Warning,TEXT("Get Return Val:%d"),*RetVal);
}
TDelegate<void(FString,int32)> FuncDelegate = TDelegate<void(FString,int32)>::CreateUFunction(SpawnObj,FName("ReflectFunc1"));
FuncDelegate.Execute(TEXT("Unity"),10);
FScriptDelegate scriptDele;
scriptDele.BindUFunction(SpawnObj,FName("ReflectFunc1"));
struct
{
const FString str{"Cocos"};
int32 val{2021};
}ParamStruct2;
scriptDele.ProcessDelegate<USpawnObject>(&ParamStruct2);
5. UClass 、UObject 与CDO
? 两者都是继承自UObejct 的类,同时都拥有自己的实例对象,UClass 保存着有反射数据,两者可以通过转换获取对方的实例:
? 全称为类默认对象(Class Default Object ),每个继承自UObject 的类都有CDO ,其中记录着该类默认的信息(例如属性值Int32 val 一开始为10,后面修改为其他数值,CDO 中记录的便是10这个值。前提是获取类的CDO )。
获取UClass 的方法分为GetClass() 和StaticClass() ,随后可以通过GetDefaultObject() 方法获取CDO ,但这通过这两个方法获取的CDO 会有所不同。关于具体的细节可以查看这篇博文,【UE·底层篇】一文搞懂StaticClass、GetClass和ClassDefaultObject_水鸡的游戏开发学习笔记-CSDN博客,这里直接给出我自己的理解:
UCLASS()
class REFLECTIONFRAME_API ACommonActor : public AActor
{
GENERATED_BODY()
public:
UPROPERTY(EditAnywhere,BlueprintReadWrite,Category="CDOTestArg")
int32 val{0};
}
UE_LOG(LogTemp,Warning,TEXT("GetClass() val:%d"),GetClass()->GetDefaultObject<ACommonActor>()->val);
UE_LOG(LogTemp,Warning,TEXT("StaticClass() val:%d"),ACommonActor::StaticClass()->GetDefaultObject<ACommonActor>()->val);
? 结论:通过GetClass() 获取的CDO 是获取对象实例的初值,通过StaticClass 获取的CDO 是获取类的初值。
码字不易,如果觉得对你有帮助的话,点个免费的赞支持下博主把 😆
|