赞
踩
首先我们需要设置玩家的输入模式,可以在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
的方法:
// 1. 使用CreateWidget TSubclassOf<UUserWidget> NewWidget; UUserWidget* CurrentWidget; CurrentWidget = CreateWidget(GetWorld(),NewWidget); CurrentWidget->AddToViewport(); //显示到窗口 /* 删除 CurrentWidget->RemoveFromParent(); CurrentWidget->nullptr; */ // 2. 使用WidgetTree->ConstructWidget方法 创建子组件 #include "Blueprint/WidgetTree.h" #include "Components/TextBlock.h" UTextBlock* SpawnText; SpawnText = WidgetTree->ConstructWidget<UTextBlock>(UTextBlock::StaticClass());
从蓝图获取组件的方法:
从根组件开始获取
假如我们需要获取根组件(CanvasPanel
)下第一个子组件TextBlock
:
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
组件绑定代理的方法
FScriptDelegate
进行绑定UButton* Button = GetWidgetFromName(TEXT("Button2"));
FScriptDelegate ClickDelegate;
ClickDelegate.BindUFunction(this,FName("ButtonClickEvent"));
Button->OnClicked.Add(ClickDelegate);
// Button2->OnClicked.AddUnique(ClickDelegate);
void UMyUIWidget::ButtonClickEvent()
{
...
}
UPROPERTY(meta=(BindWidget))
class UButton* Button3;
Button3->OnClicked.AddDynamic(this,&UMyUIWidget::Button3ClickEvent);
//Button3->OnClicked.AddUniqueDynamic(this,&UMyUIWidget::Button3ClickEvent);
void UMyUIWidget::Button3ClickEvent()
{
...
}
这里的生命周期我们只探讨至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
。
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};
};
// 获取Singleton
UMySingletonObject* mySingle = Cast<UMySingletonObject>(GEngine->GameSingleton);
mySingle->GetGloableVal();
其他的全局类:
UGameInstance
:生命周期伴随整个程序的启动到结束,可以跨关卡交流数据
UGamePlayStatics
:头文件#include "Kismet/GameplayStatics.h"
,包含一些GamePlay
框架开发中常用的方法(例如:GetAllActorsOfClass()
)
…
反射是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
。
Enum
// 通过反射获取UEnum
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());
}
UPROPERTY()
// 通过反射获取UPROPERTY // 1. 通过FindField 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); //set val intval = IntPropPtr->GetPropertyValue_InContainer(SpawnObj); UE_LOG(LogTemp,Warning,TEXT("get intval val is %d(After)"),intval); } // 2. 通过TFieldIterator进行迭代 for (TFieldIterator<UProperty> Ite(YourObjectClass); Ite; ++Ite) { UProperty* Property = *Ite; if (Property->GetNameCPP() == "YourPropertyName") { UBoolProperty* BoolPro = Cast<UBoolProperty>(Property); if (BoolPro) { void* ValuePtr = Property->ContainerPtrToValuePtr<bool>(this); // 获取值 bool GetValue = BoolPro->GetPropertyValue(ValuePtr); // 设置值 BoolPro->SetPropertyValue(ValuePtr,false); } } }
这里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 //Base class for all UObject types that contain fields.
class COREUOBJECT_API UField : public UObject //Base class of reflection data objects.
调用UFUNCTION()
FindFunction
调用UFunction* functor = SpawnObj->FindFunction(FName("ReflectFunc1"));
struct
{
const FString str{"unreal"};
int32 val{20};
}ParamStruct;
//这里第二个参数是传入的是函数参数,void* 类型的 我们需要传入一个struct* 传入后参数会根据变量的内存地址偏移进行自动赋值。
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
调用TDelegate<void(FString,int32)> FuncDelegate = TDelegate<void(FString,int32)>::CreateUFunction(SpawnObj,FName("ReflectFunc1"));
FuncDelegate.Execute(TEXT("Unity"),10);
// 注意TDelegate如果绑定了一个函数其中形参是引用的,改变引用的值不会生效
FScriptDelegate
调用//3. FScriptDelegate 注意FScripDelegate没有返回值,需要改变传入的值可以传入引用,
// 有返回值的代理请使用TDelegate或者UFunction
FScriptDelegate scriptDele;
scriptDele.BindUFunction(SpawnObj,FName("ReflectFunc1"));
struct
{
const FString str{"Cocos"};
int32 val{2021};
}ParamStruct2;
scriptDele.ProcessDelegate<USpawnObject>(&ParamStruct2);
UClass
、UObject
与CDO
UClass
与UObject
的关系 两者都是继承自UObejct
的类,同时都拥有自己的实例对象,UClass
保存着有反射数据,两者可以通过转换获取对方的实例:
CDO
全称为类默认对象(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};
}
//1.GetClass获取CDO 获取对象初值
UE_LOG(LogTemp,Warning,TEXT("GetClass() val:%d"),GetClass()->GetDefaultObject<ACommonActor>()->val); //实例的值
//2.StaticClass获取CDO 获取类初值
UE_LOG(LogTemp,Warning,TEXT("StaticClass() val:%d"),ACommonActor::StaticClass()->GetDefaultObject<ACommonActor>()->val); //0
结论:通过GetClass()
获取的CDO
是获取对象实例的初值,通过StaticClass
获取的CDO
是获取类的初值。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。