IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 游戏开发 -> UE4C++基础(二) -> 正文阅读

[游戏开发]UE4C++基础(二)

基础回顾

UE4C++基础(一)_WLSTLA-CSDN博客

1. C++与UMG交互

? 首先我们需要设置玩家的输入模式,可以在PlayerController中的BeginPlay进行设置:

void AUIController::BeginPlay()
{
	Super::BeginPlay();
	FInputModeGameAndUI InputMode;
	InputMode.SetLockMouseToViewportBehavior(EMouseLockMode::LockAlways);
	InputMode.SetHideCursorDuringCapture(false);
	SetShowMouseCursor(true);  //显示鼠标
}

? 我们可以创建一个继承于UUserWidgetUObject,作为我们自己的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()
{
	...
}

2. 生命周期

? 这里的生命周期我们只探讨至Tick,参考于梁迪的全反射零耦合框架UE4全反射零耦合框架开发坦克游戏

  • Actor的生命周期

    • 构造函数
    • 初始化Components
    • PostInitializeComponents()
    • BeginPlay()
    • Tick(float DeltaSeconds)
  • Level

    • 初始化GameInstance
    • 创建世界World
    • 创建LevelLevelScriptActor(关卡蓝图)构造
    • Level中存在的Actor构造
    • GameMode构造
    • 随后执行ActorGameModePostInitializeCompoents()
    • 执行LevelActorGameModeBlueprintsGameplay框架类的BeginPlay()
    • 执行所有初始化完成类的Tick()

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3HX1jDen-1638108670374)(image-20211128181138848.png)]

注意:只有当场景中的ActorGamePlay框架中设置的类(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中进行调用。

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uPaveYuT-1638108670378)(image-20211128193706347.png)]

    这个类中写的方法只能供蓝图中使用,如果你想在C++中使用,则需要使用下面这个方法。

  • GameEngine->GameSingleton

? GameEngine->GameSingletonGEngine下的一个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};
};

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hsTkKRYB-1638108670380)(image-20211128195313303.png)]

// 获取Singleton
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

  • 调用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());
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-w02mEEmo-1638108670383)(image-20211128212208655.png)]

  • 调用UPROPERTY()
// 通过反射获取UPROPERTY
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);
}

? 这里FindFieldChecked的函数原型如下:

template<typename T>
T* FindFieldChecked( const UStruct* Scope, FName FieldName )

? 可知这里第一个参数是UStruct*类型的,但我们传入的却是一个UClass*类型的。原因是在UE4UClass是继承自UStructUStruct继承自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.

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WxIJFa5A-1638108670385)(image-20211128212318688.png)]

  • 调用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);
    
    • 通过FScriptDelegate调用
    //3. FScriptDelegate
    FScriptDelegate scriptDele;
    scriptDele.BindUFunction(SpawnObj,FName("ReflectFunc1"));
    struct
    {
        const FString str{"Cocos"};
        int32 val{2021};
    }ParamStruct2;
    scriptDele.ProcessDelegate<USpawnObject>(&ParamStruct2);
    

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-I0pBNFDO-1638108670387)(image-20211128213822829.png)]

5. UClassUObjectCDO

  • UClassUObject的关系

? 两者都是继承自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是获取类的初值。

码字不易,如果觉得对你有帮助的话,点个免费的赞支持下博主把 😆

  游戏开发 最新文章
6、英飞凌-AURIX-TC3XX: PWM实验之使用 GT
泛型自动装箱
CubeMax添加Rtthread操作系统 组件STM32F10
python多线程编程:如何优雅地关闭线程
数据类型隐式转换导致的阻塞
WebAPi实现多文件上传,并附带参数
from origin ‘null‘ has been blocked by
UE4 蓝图调用C++函数(附带项目工程)
Unity学习笔记(一)结构体的简单理解与应用
【Memory As a Programming Concept in C a
上一篇文章      下一篇文章      查看所有文章
加:2021-11-30 15:56:52  更:2021-11-30 15:57:57 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/29 12:47:01-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码