[22] 虚幻引擎知识拓展 智能指针、JSON解析、蓝图解析JSON插件、模块、动态库静态库

大纲

虚幻智能指针

  共享指针

  共享引用

JSON解析 对象型、数组型、解析Json文件、书写Json、读取场景Actor保存到Json

任务 : 封装高德地图天气系统插件给蓝图使用

蓝图解析JSON插件

自定义模块

有限状态机

多线程

GET、Post、Socket链接

UDP

简历

静态库动态库

UE静态库动态库

Salte

垃圾回收机制

内容

虚幻智能指针

创建共享指针

// //创建共享指针
//TSharedPtr<FMyClass> pMyClass = MakeShareable(new FMyClass);
TSharedPtr<FMyClass> pMyClass(new FMyClass);
//检查有效性
if (pMyClass)
{
}
TSharedPtr<FMyClass> p2;

//比较
if (pMyClass == p2)
{
}
//获取引用计数(当前共享指针管理的内存有多少人在用)
UE_LOG(LogTemp, Log, TEXT("%d"), pMyClass.GetSharedReferenceCount());
p2 = pMyClass;
UE_LOG(LogTemp, Log, TEXT(" == %d"), pMyClass.GetSharedReferenceCount());
//释放
pMyClass = nullptr;
UE_LOG(LogTemp, Log, TEXT(" == %d"), p2.GetSharedReferenceCount());

共享引用

// //创建共享指针
//TSharedPtr<FMyClass> pMyClass = MakeShareable(new FMyClass);
TSharedPtr<FMyClass> pMyClass(new FMyClass);
//检查有效性
if (pMyClass)
{
}
TSharedPtr<FMyClass> p2;

//比较
if (pMyClass == p2)
{
}
//获取引用计数(当前共享指针管理的内存有多少人在用)
UE_LOG(LogTemp, Log, TEXT("%d"), pMyClass.GetSharedReferenceCount());
p2 = pMyClass;
UE_LOG(LogTemp, Log, TEXT(" == %d"), pMyClass.GetSharedReferenceCount());
//释放
pMyClass = nullptr;
UE_LOG(LogTemp, Log, TEXT(" == %d"), p2.GetSharedReferenceCount());

弱指针

// //创建共享指针
//TSharedPtr<FMyClass> pMyClass = MakeShareable(new FMyClass);
TSharedPtr<FMyClass> pMyClass(new FMyClass);
//检查有效性
if (pMyClass)
{
}
TSharedPtr<FMyClass> p2;

//比较
if (pMyClass == p2)
{
}
//获取引用计数(当前共享指针管理的内存有多少人在用)
UE_LOG(LogTemp, Log, TEXT("%d"), pMyClass.GetSharedReferenceCount());
p2 = pMyClass;
UE_LOG(LogTemp, Log, TEXT(" == %d"), pMyClass.GetSharedReferenceCount());
//释放
pMyClass = nullptr;
UE_LOG(LogTemp, Log, TEXT(" == %d"), p2.GetSharedReferenceCount());

循环引用 : 智能指针引用计数为0才会释放

弱指针

JSON解析

对象型JSON

//解析对象型JSON
FString JsonStr = TEXT("{\"name\":\"二狗\", \"age\" : 39}");
//创建接收Json对象的数据
TSharedPtr<FJsonObject> JsonObject;
//借助解析工厂,解析Json文本串到阅读器
TSharedRef<TJsonReader<>> JsonReader = TJsonReaderFactory<>::Create(JsonStr);
//借助解析器,完成解析
//反向序列化:从其它格式数据到内存对象格式数据
if (FJsonSerializer::Deserialize(JsonReader, JsonObject))
{
	//读取数据?
	UE_LOG(LogTemp, Log, TEXT("%s"), *JsonObject->GetStringField(TEXT("name")));
	//检查是否存在某个字段
	float age = 0;
	if (JsonObject->TryGetNumberField(TEXT("age"), age))
	{
		UE_LOG(LogTemp, Log, TEXT("== %f"), age);
	}
}

数组型JSON

//数组型JSon
FString JsonStr =TEXT("[\"二狗\", 34, true, {\"name\":\"二狗\", \"age\" : 39}, [49]]");

TArray<TSharedPtr<FJsonValue>> JsonValues;

TSharedRef<TJsonReader<>> JsonReader = TJsonReaderFactory<>::Create(JsonStr);

if (FJsonSerializer::Deserialize(JsonReader, JsonValues))
{
    //读取元素
    UE_LOG(LogTemp, Log, TEXT("%s"), *JsonValues[0]->AsString());
    //
    float Num = 0;
    if (JsonValues[1]->TryGetNumber(Num))
    {
       UE_LOG(LogTemp, Log, TEXT("%f"), Num);
    }
    TSharedPtr<FJsonObject> JsonObject = JsonValues[3]->AsObject();
    UE_LOG(LogTemp, Log, TEXT("%s"), *JsonObject->GetStringField(TEXT("name")));
    //解析数组
    UE_LOG(LogTemp, Log, TEXT("%f"), JsonValues[4]->AsArray()[0]->AsNumber());
}

从磁盘解析JSON

//从磁盘解析json
FString JsonStr;

if (FFileHelper::LoadFileToString(JsonStr, TEXT("d:\\a.json")))
{
	TSharedPtr<FJsonObject> JsonObject;
	TSharedRef<TJsonReader<>> JsonReader = TJsonReaderFactory<>::Create(JsonStr);
	if (FJsonSerializer::Deserialize(JsonReader, JsonObject))
	{
		TArray<TSharedPtr<FJsonValue>> person = JsonObject->GetArrayField(TEXT("person"));
		TArray<TSharedPtr<FJsonValue>> teachers = person[0]->AsObject()->GetArrayField(TEXT("teachers"));
		TSharedPtr<FJsonObject> liu = teachers[1]->AsObject()->GetObjectField(TEXT("liu"));
		TArray<TSharedPtr<FJsonValue>> hobby = liu->GetArrayField(TEXT("hobby"));
		for (auto hb : hobby)
		{
			UE_LOG(LogTemp, Log, TEXT("%s"), *hb->AsString());
		}
	}
}

序列化Json 对象型

//序列化json 对象型
TSharedPtr<FJsonObject> JsonObject(new FJsonObject);
//设置字段
JsonObject->SetStringField(TEXT("name"), TEXT("二狗"));
JsonObject->SetNumberField(TEXT("age"), 40);
//构建书写器
FString JsonStr;
TSharedRef<TJsonWriter<>> JsonWriter = TJsonWriterFactory<>::Create(&JsonStr);
//序列化
if (FJsonSerializer::Serialize(JsonObject.ToSharedRef(), JsonWriter))
{
    UE_LOG(LogTemp, Log, TEXT("%s"), *JsonStr);
}

序列化Json 数组型

//数组型
TArray<TSharedPtr<FJsonValue>> JsonValues;

JsonValues.Add(MakeShareable(new FJsonValueString(TEXT("二狗"))));
JsonValues.Add(MakeShareable(new FJsonValueNumber(49)));
JsonValues.Add(MakeShareable(new FJsonValueBoolean(true)));

FString JsonStr;
TSharedRef<TJsonWriter<>> JsonWriter = TJsonWriterFactory<>::Create(&JsonStr);
if (FJsonSerializer::Serialize(JsonValues, JsonWriter))
{
	UE_LOG(LogTemp, Log, TEXT("%s"), *JsonStr);
}

案例 : 读取场景中的Actor的坐标、缩放、旋转保存到JSON文件

TArray<TSharedPtr<FJsonValue>> JsonValues;

TArray<AActor*> Actors;
//获取场景所有Actor
UGameplayStatics::GetAllActorsOfClass(this, AActor::StaticClass(), Actors);

for (auto ac : Actors)
{
    //新建一个对象型 Json
    TSharedPtr<FJsonObject> JsonObject(new FJsonObject);
    JsonObject->SetStringField(TEXT("Name"), ac->GetName());

    TSharedPtr<FJsonObject> Position(new FJsonObject);
    Position->SetNumberField(TEXT("X"), ac->GetActorLocation().X);
    Position->SetNumberField(TEXT("Y"), ac->GetActorLocation().Y);
    Position->SetNumberField(TEXT("Z"), ac->GetActorLocation().Z);
    JsonObject->SetObjectField(TEXT("Position"), Position);

    TSharedPtr<FJsonObject> Rotation(new FJsonObject);
    Rotation->SetNumberField(TEXT("Pitch"), ac->GetActorRotation().Pitch);
    Rotation->SetNumberField(TEXT("Yaw"), ac->GetActorRotation().Yaw);
    Rotation->SetNumberField(TEXT("Roll"), ac->GetActorRotation().Roll);
    JsonObject->SetObjectField(TEXT("Rotation"), Rotation);

    TSharedPtr<FJsonObject> Scale(new FJsonObject);
    Scale->SetNumberField(TEXT("X"), ac->GetActorScale().X);
    Scale->SetNumberField(TEXT("Y"), ac->GetActorScale().Y);
    Scale->SetNumberField(TEXT("Z"), ac->GetActorScale().Z);
    JsonObject->SetObjectField(TEXT("Scale"), Scale);

    JsonValues.Add(MakeShareable(new FJsonValueObject(JsonObject)));
}

FString JsonStr;
TSharedRef<TJsonWriter<>> JsonWriter = TJsonWriterFactory<>::Create(&JsonStr);

if (FJsonSerializer::Serialize(JsonValues, JsonWriter))
{
    FFileHelper::SaveStringToFile(JsonStr, TEXT("d:\\b.json"));//保存到文件
}

插件

蓝图无法直接操作C++的东西 , 使用C++包裹一层在蓝图里面调用

纯标记 : 每次调用都会执行一次引脚

侵入式指针

蓝图解析JSON插件

 

插件安装

引擎安装 : 运行时插件 plugins下 > Runutime

项目安装 : 项目下的 > plugins 下

Public和Private的区别 

导出标记宏 : 有了他能在外面使用

Private也能通通过增加宏标签导入

有限状态机

工厂的方法 : 统一逻辑统一管理

函数指针 : 单独剥离逻辑

网络通信

GET

void FHttpHelper::RequestHttpGet(FString Url)
{
    //创建请求对象HTTP
    HttpRequest = FHttpModule::Get().CreateRequest();
    //设置请求方式
    HttpRequest->SetVerb(TEXT("Get"));
    //设置请求地址
    HttpRequest->SetURL(Url);
    //设置请求头
    HttpRequest->SetHeader(TEXT("User-Agent"), TEXT("UnrealEngine"));
    HttpRequest->SetHeader(TEXT("Content-Type"), TEXT("application/json"));
    //绑定回调函数(消息请求发送后,用来响应结果)
    HttpRequest->OnProcessRequestComplete().BindRaw(this, &FHttpHelper::OnRequestComplete);
    //执行请求
    HttpRequest->ProcessRequest();
     
}

POST

void FHttpHelper::RequestHttpPost(const FString& Message)
{
    //创建请求对象
    HttpRequest = FHttpModule::Get().CreateRequest();
    //设置请求方式
    HttpRequest->SetVerb(TEXT("Post"));
    //设置请求头
    HttpRequest->SetHeader(TEXT("User-Agent"), TEXT("UnrealEngine"));
    HttpRequest->SetHeader(TEXT("Content-Type"), TEXT("application/json"));
    //设置地址
    HttpRequest->SetURL(TEXT("127.0.0.1:8888"));
    //设置Post信息
    HttpRequest->SetContentAsString(Message);
    //绑定回调函数(消息请求发送后,用来响应结果)
    HttpRequest->OnProcessRequestComplete().BindRaw(this, &FHttpHelper::OnRequestComplete);
    //执行请求
    HttpRequest->ProcessRequest();
}

Socket

Socket
 //-- .h
#pragma once
#include "CoreMinimal.h"

class FSocketHelper : public FRunnable
{
public:
    FSocketHelper();
    virtual ~FSocketHelper() override;
    bool Connect(const FString& IP,int32 Port);

    void SendMessage(const FString& Message);

    void StartReceive();

    void CloseSocket();

protected:

    virtual  uint32 Run() override;

private:
    FSocket* MySocket;

    bool bRunning;
};
//-- .cpp
#include "FSocketHelper.h"

#include "Sockets.h"
#include "SocketSubsystem.h"

FSocketHelper::FSocketHelper() : MySocket(nullptr), bRunning(false)
{
}

FSocketHelper::~FSocketHelper()
{
	if (MySocket)
	{
		ISocketSubsystem::Get(PLATFORM_SOCKETSUBSYSTEM)->DestroySocket(MySocket);
	}
	CloseSocket();
}

bool FSocketHelper::Connect(const FString& IP, int32 Port)
{
	//构建链接地址
	TSharedRef<FInternetAddr> InternetAddr = ISocketSubsystem::Get(PLATFORM_SOCKETSUBSYSTEM)->CreateInternetAddr();
	//解析文本地址信息
	bool bParse = true;
	InternetAddr->SetIp(*IP, bParse);
	if (!bParse)
	{
		return false;
	}
	//设置端口号
	InternetAddr->SetPort(Port);
	//创建套接字
	MySocket = ISocketSubsystem::Get(PLATFORM_SOCKETSUBSYSTEM)->CreateSocket(NAME_Stream, TEXT("MySocket"));
	//启动链接 返回真和假,真则代表链接服务器成功了,假,则代表没有成功
	return MySocket->Connect(*InternetAddr);
}

void FSocketHelper::SendMessage(const FString& Message)
{
	if (MySocket)
	{
		//将文本转换为字节流数据
		FTCHARToUTF8 ctu(*Message, Message.Len());
		int32 SendSize = 0;
		//发送
		MySocket->Send(reinterpret_cast<const uint8*>(ctu.Get()), ctu.Length(), SendSize);
	}
}

void FSocketHelper::StartReceive()
{
	bRunning = true;
	//启动线程
	FRunnableThread::Create(this, TEXT("MySocketThread"));
}

void FSocketHelper::CloseSocket()
{
	if (!bRunning)
	{
		return;
	}
	bRunning = false;
	if (MySocket)
	{
		MySocket->Close();
		ISocketSubsystem::Get(PLATFORM_SOCKETSUBSYSTEM)->DestroySocket(MySocket);
		MySocket = nullptr;
	}
}

uint32 FSocketHelper::Run()
{
	//创建消息缓冲区
	const int32 BufferSize = 1024;
	uint8* Buffer = new uint8[BufferSize];
	//设置接收数据大小变量
	int32 ReceiveSize = 0;
	
	while (bRunning)
	{
		//接收消息
		MySocket->Recv(Buffer, BufferSize, ReceiveSize);
		if (ReceiveSize > 0)//说明你收到了消息
		{
			//解析消息
			FUTF8ToTCHAR utt(reinterpret_cast<const ANSICHAR*>(Buffer), ReceiveSize);
			//转换成FString
			FString Msg(utt.Length(), utt.Get());
			UE_LOG(LogTemp, Log, TEXT("%s"), *Msg);
		}
		else//服务器可能关闭了,或是链接超时了
		{
			CloseSocket();
		}
	}
	return 0;
}

//-- mode.cpp 使用
void AUECppGameModeBase::SocketConnect()
{
	if (!SocketHelper)
	{
		SocketHelper = MakeShareable(new FSocketHelper);
		SocketHelper->Connect(TEXT("127.0.0.1"), 8060);
		if (SocketHelper->Connect(TEXT("127.0.0.1"), 8060))
		{
			SocketHelper->StartReceive();
		}
	}
}

void AUECppGameModeBase::SendSocketMessage(const FString& Message)
{
	if (SocketHelper)
	{
		SocketHelper->SendMessage(Message);
	}
}

多线程

游戏引擎很依赖前后顺序 , 

以前 : 整个场景的地图加载 , 玩家才可以进入游戏。

现在 : 场景和玩家可以同时加载。

析构函数需要写虚函数 , 可以调用父类的

跨进程通信麻烦 , 跨线程通讯容易

进程和线程

进程:是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基
础。
线程:是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。一条线程指的是进程中
一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。 

简单来说,进程是最小的资源分配单位,线程是最小的运算调度单位(CPU调度的最小单位)。进程中可以启动多个线程。

线程函数

创建线程

void AUECppGameModeBase::StartThread()
{
    if (!MyRunnable)
    {
       MyRunnable = MakeShareable(new FMyRunnable);
    }
    //启动线程
    MyThread = FRunnableThread::Create(MyRunnable.Get(), TEXT("MyTestThread"));
    
}

线程休眠

终止线程

MyThread->Kill(false);//true等待线程结束,如果线程不结束,则一直阻塞当前调用线程,直到Mythread线程结束

线程锁 : 互斥锁 效率低 , 休眠结束在让别人使用对象

先先调用 : 谁的代码块先执行完

FScopeLock lock(&m_CriticalSection);

UDP

模块引入

发消息

//声明创建
//-- .h
#pragma once

class FUdpHelper
{
public:

	FUdpHelper();

	void SendMessage(const FString& Message);


private:
	FSocket* MyUdpSocket;
	
};
//--.cpp
#include "FUdpHelper.h"

#include "Common/UdpSocketBuilder.h"

FUdpHelper::FUdpHelper() : MyUdpSocket(nullptr), bRunning(false)
{
}

FUdpHelper::~FUdpHelper()
{
	CloseSocket();
}

void FUdpHelper::SendMessage(const FString& Message)
{
	if (!MyUdpSocket)
	{
		//创建面向非链接的套接字(UDP)
		MyUdpSocket = FUdpSocketBuilder(TEXT("MyUdpSocket")).AsReusable().WithBroadcast().AsBlocking();
		//设置消息缓冲区大小
		int32 BufferSize = 0;
		if (!MyUdpSocket->SetReceiveBufferSize(1024, BufferSize))
		{
			UE_LOG(LogTemp, Log, TEXT("UDP缓冲区设置失败!"));
			ISocketSubsystem::Get(PLATFORM_SOCKETSUBSYSTEM)->DestroySocket(MyUdpSocket);
			MyUdpSocket = nullptr;
			return;
		}
		//创建地址
		TSharedRef<FInternetAddr> InternetAddr = ISocketSubsystem::Get(PLATFORM_SOCKETSUBSYSTEM)->CreateInternetAddr();
		bool bParse = false;
		InternetAddr->SetIp(TEXT("127.0.0.1"), bParse);
		if (!bParse)
		{
			ISocketSubsystem::Get(PLATFORM_SOCKETSUBSYSTEM)->DestroySocket(MyUdpSocket);
			MyUdpSocket = nullptr;
			return;
		}

		//设置端口号
		InternetAddr->SetPort(8050);
		//将文本转成字节流
		FTCHARToUTF8 ctu(*Message, Message.Len());
		int32 SendSize = 0;
		MyUdpSocket->SendTo(reinterpret_cast<const uint8*>(ctu.Get()), ctu.Length(), SendSize, *InternetAddr);
		if (!bRunning)
		{
			bRunning = true;
			FRunnableThread::Create(this, TEXT("MyUDPThread"));
		}
	}
}

uint32 FUdpHelper::Run()
{
	//创建消息缓冲区
	const int32 BufferSize = 1024;
	uint8* Buffer = new uint8[BufferSize];
	//设置接收数据大小变量
	int32 ReceiveSize = 0;

	while (bRunning)
	{
		//接收消息
		MyUdpSocket->Recv(Buffer, BufferSize, ReceiveSize);
		if (ReceiveSize > 0) //说明你收到了消息
		{
			//解析消息
			FUTF8ToTCHAR utt(reinterpret_cast<const ANSICHAR*>(Buffer), ReceiveSize);
			//转换成FString
			FString Msg(utt.Length(), utt.Get());
			UE_LOG(LogTemp, Log, TEXT("%s"), *Msg);
		}
		else //服务器可能关闭了,或是链接超时了
		{
			CloseSocket();
		}
	}
	return 0;
}

void FUdpHelper::CloseSocket()
{
	if (!bRunning)
	{
		return;
	}
	bRunning = false;
	if (MyUdpSocket)
	{
		MyUdpSocket->Close();
		ISocketSubsystem::Get(PLATFORM_SOCKETSUBSYSTEM)->DestroySocket(MyUdpSocket);
		MyUdpSocket = nullptr;
	}
}


//--调用
void AUECppGameModeBase::SendUdpMessage(const FString& Message)
{
	if (!UdpHelper)
	{
		UdpHelper = MakeShareable(new FUdpHelper);
	}
	UdpHelper->SendMessage(Message);
}

收消息

内网穿透

 UPD聊天

禁止在游戏在主线程之外调用游戏UI

读和写同时发生要考虑线程安全

队列加了线程锁

XML

void AUECppGameModeBase::ParseXml()
{
	TSharedPtr<FXmlFile> XmlFile = MakeShareable(new FXmlFile);
	if (XmlFile->LoadFile(TEXT("F:\\UnrealProject\\2024-04-The-Fifth-Stage\\Week17\\Teacher\\1.xml")))
	{
		FXmlNode* RootNode = XmlFile->GetRootNode();
		//获取子节点
		FXmlNode* Node = RootNode->GetChildrenNodes()[0];
		//读取标签名称
		UE_LOG(LogTemp, Log, TEXT("%s"), *Node->GetTag());
		//读取属性值
		UE_LOG(LogTemp, Log, TEXT("%s"), *Node->GetAttribute(TEXT("graduate")));
		//读取标签值
		UE_LOG(LogTemp, Log, TEXT("%s"), *Node->GetContent());
	}
}

void AUECppGameModeBase::WriteXml()
{
	TSharedPtr<FXmlFile> XmlFile = MakeShareable(new FXmlFile(TEXT("<person></person>"), EConstructMethod::ConstructFromBuffer));

	FXmlNode* RootNode = XmlFile->GetRootNode();

	TArray<FXmlAttribute> Attributes;
	FXmlAttribute Age(TEXT("Age"), TEXT("20"));
	Attributes.Add(Age);
	RootNode->AppendChildNode(TEXT("hero"), TEXT("二狗"), Attributes);

	XmlFile->Save(TEXT("d:\\2.xml"));
}

动态库静态库

逻辑的合集

编译阶段 , 静态阶段 , 运行起来是动态阶段

C++静态库

常规项目改成库项目

引入静态库

配置相对路径目录 静态库

不在一个解决方案引入静态库

配置目录

配置输入目录

C++动态库

项目不用编译就可以在新项目里面用 , 只修改动态库内容

函数不可以导出 , 类可以

直接引入dll并使用

显示调用

隐式调用

通过定义默认宏 , 动态确定是到处还是到处_declspec(dllexport) || _declspec(dllimport)

虚幻静态库

Salte

void AUECppGameModeBase::RegisterTab()//注册
{
    //注册
    FGlobalTabmanager::Get()->RegisterNomadTabSpawner(
                               TabName, FOnSpawnTab::CreateUObject(this, &AUECppGameModeBase::OnSpawnTab))
                            .SetDisplayName(NSLOCTEXT("mytab", "kd3", "自定义停留Tab"));
}

void AUECppGameModeBase::ShowTab()//显示
{
    FGlobalTabmanager::Get()->TryInvokeTab(TabName);
}

void AUECppGameModeBase::UnRegisterTab()//卸载
{
    FGlobalTabmanager::Get()->UnregisterNomadTabSpawner(TabName);
}

TSharedRef<SDockTab> AUECppGameModeBase::OnSpawnTab(const FSpawnTabArgs& Args)
{
    //加载UMG页面
    TSubclassOf<UUserWidget> WigetClass = LoadClass<UUserWidget>(
       nullptr, TEXT("/Script/UMGEditor.WidgetBlueprint'/Game/UMG/UMG_ChatUI.UMG_ChatUI_C'"));
    UUserWidget* Widget = CreateWidget<UUserWidget>(UGameplayStatics::GetPlayerController(this, 0), WigetClass);

    //加载Widget
    return SNew(SDockTab).TabRole(ETabRole::NumRoles)
       [
          Widget->TakeWidget()
       ];

    //手写控件
    return SNew(SDockTab).TabRole(ETabRole::NomadTab)
       [
          SNew(SVerticalBox)
          + SVerticalBox::Slot()
          [
             SNew(SButton).HAlign(HAlign_Center).VAlign(VAlign_Center).OnClicked(
                FOnClicked::CreateUObject(this, &AUECppGameModeBase::OnButtonClicked))
             [
                SAssignNew(ButtonTextblock, STextBlock).Text(NSLOCTEXT("mytab", "adsksd2", "按钮文字"))
             ]
          ]
          + SVerticalBox::Slot()
          [
             SNew(SCheckBox)
             [
                SNew(STextBlock).Text(NSLOCTEXT("Mytab", "asddfdgf", "选项)"))
             ]
          ]
       ];
}

FReply AUECppGameModeBase::OnButtonClicked()
{
    if (ButtonTextblock)
    {
       Number++;
       ButtonTextblock->SetText(FText::AsNumber(Number));
       return FReply::Handled();
    }
    return FReply::Unhandled();
}

//--windows窗口
void AUECppGameModeBase::BuildWindow()
{
	if (!MyWindow)
	{
		SAssignNew(MyWindow, SWindow)
		.Title(NSLOCTEXT("mywindow", "asdk2", "自定义窗口"))
		.ClientSize(FVector2D(800, 600))
		.ScreenPosition(FVector2D(100, 100))
		[
			SNew(SButton)
			[
				SNew(STextBlock).Text(NSLOCTEXT("mywindow", "asdkfd2", "按钮文本"))
			]
		];
	}
}

void AUECppGameModeBase::ShowWindow()
{
	if (MyWindow)
	{
		FSlateApplication::Get().AddWindow(MyWindow.ToSharedRef());
	}
}

垃圾回收机制

GC

addtoRoot慎用

自定义类持有U类指针 , 阻止垃圾回收机制

TIPS

面试 : 循环引用

Json和虚幻数组的区别 , 不是同质类型

多思考这个游戏我来做这个功能怎么做

时间膨胀值 : 局部和全局
时空穿梭 : 

前面const是值不能改 , 后面的const的地址不能改

面试 : 

插件和模块有什么区别 : 插件是可以打包在其他项目里面用 , 模块的话是内部的

模块里面只会放相关功能 , 一个插件可以有多个模块

没有白色执行引脚的函数 , 如果输出引脚被2个地方调用了 , 函数会调用2次

Source

面试

多态应用在哪里 : 有限状态机

进程和线程 : 进程是一个计算机最小的一个调度单位 , 线程是CPU最小的调度单位 , 进行中可以启动多个线程。

5.2和5.3请求有差异会踢除HTTP

API & 节点

 

面试技巧课

彩色简历

提前10~20分钟

信息准备: 

个人职业规划 : 公司有没有管理方面的发展

 

怎么看待加班 : 

在自己工作的时间内完成自己的工作 , 如果工作的项目比较感我也愿意配合加班 。

那咋们公司加班的话是补休还是有加班费的。

 

posted @ 2024-06-03 21:52  啊賢  阅读(101)  评论(0编辑  收藏  举报