前言
单例模式(Singleton Pattern)是最简单的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。
一、前期准备
Unreal Engine 4.25.4
Visual Studio 2019
新建空白关卡和对应的用户控件。如何新建关卡可以参考以下视频
新关卡一片黑?初学者必知的UE4新建关卡时的一些设置【虚幻引擎】。
控件 UI 的摆放如下图所示,一个蓝色按钮和一个红色按钮,下面对应是蓝色方和红色方的比分。
然后修改关卡蓝图,将 UMG 添加到视口。
最后启动运行关卡,预期能看到以下画面。
二、C++ 代码
新建 C++ 类,注意需要打开 显示所有类
,继承自 Object
父类,并将生成的类命名为 Singleton
,如下图所示。
编辑源码如下,需要注意的是由于反射机制, ue4 中的构造函数没办法转为私有。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83
| #pragma once
#include "CoreMinimal.h" #include "UObject/NoExportTypes.h" #include "HAL/ThreadingBase.h" #include "Singleton.generated.h"
UCLASS(BlueprintType, Blueprintable) class SINGLETONOBJECT_API USingleton : public UObject { GENERATED_BODY()
public: UFUNCTION(BlueprintCallable) static USingleton* GetSingletonObjectIns();
UFUNCTION(BlueprintCallable) void InitializeObject();
UFUNCTION(BlueprintCallable) void AddBlueScore();
UFUNCTION(BlueprintCallable) int GetBlueScore();
UFUNCTION(BlueprintCallable) void AddRedScore();
UFUNCTION(BlueprintCallable) int GetRedScore();
private: static FCriticalSection Mutex; static USingleton* SingletonInstance;
int BlueScore; int RedScore;
};
#include "Singleton.h"
USingleton* USingleton::GetSingletonObjectIns() { FScopeLock ScopeLock(&Mutex); if (SingletonInstance == nullptr) { SingletonInstance = NewObject<USingleton>(); SingletonInstance->InitializeObject(); } return SingletonInstance; }
void USingleton::InitializeObject() { this->BlueScore = 0; this->RedScore = 0; }
void USingleton::AddBlueScore() { ++this->BlueScore; }
int USingleton::GetBlueScore() { return this->BlueScore; }
void USingleton::AddRedScore() { ++this->RedScore; }
int USingleton::GetRedScore() { return this->RedScore; }
|
实现单例类后,我们还需要继承实现 GameInstance,这是必要的一步,因为 GameInstance 的生命周期贯穿游戏始终,否则单例类变量会在关卡切换是被系统销毁回收。以下演示了如何新建一个 GameInstance,并且将单例类作为其的私有属性成员。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
| #pragma once
#include "CoreMinimal.h" #include "Engine/GameInstance.h" #include "Singleton.h" #include "MyGameInstance.generated.h"
UCLASS() class SINGLETONOBJECT_API UMyGameInstance : public UGameInstance { GENERATED_BODY()
public: UMyGameInstance();
UFUNCTION() void StartGameInstance();
private: USingleton* GlobalGameData; };
#include "MyGameInstance.h"
FCriticalSection USingleton::Mutex; USingleton* USingleton::SingletonInstance = nullptr;
UMyGameInstance::UMyGameInstance() { GlobalGameData = USingleton::GetSingletonObjectIns(); }
void UMyGameInstance::StartGameInstance() { Super::StartGameInstance(); }
|
三、蓝图引用单例类
编写完成单例类后,具体该如何调用呢?
有两种方式,一种是 C++ 代码调用,另一种是蓝图调用。
C++ 调用很简单,一行代码即可。
1
| USingleton* SIngletonRef = USingleton::GetSingletonObjectIns();
|
接下来展示如何通过外部蓝图调用,在空间蓝图中编写逻辑如下。
编写完成后,保存并编译,接下来启动关卡,预期效果如下图所示。每点击一次蓝色按钮,蓝色比分加一;每点击一次红色按钮,红色比分加一。
参考文献
- Global Data Access, Data Storage Class Accessible From Any CPP or BP Class During Runtime - Old UE4 Wiki
- UE4[C++]在虚幻引擎实现单例模式 - 知乎
- [UE4]线程锁FScopeLock用法_玄冬Wong的博客-CSDN博客