(UE4 4.20)UE4的全局单例模式Singleton(变量在切换关卡时不被GC)

UE4开发中,很多对象在切换关卡中,伴随定义在Actor的各种对象伴随着Actor的消亡也被GC掉了。比如在我们AMyCharacter里定义各种UObject变量,等切换关卡时,我们的Character销毁了,于是里面的各种UObject也跟着消亡被GC了。那么我们如果需要一些数据在游戏中全局存在,得怎么办呢?

一般两种方式:

(1)在MyGameInstance定义的对象, 切换关卡的时候是不被GC的,是全局存在的。

(2)用SaveGame来保存相应的数据,但是SaveGame应该更多用于游戏关闭后保存数据在本地,在开始时在读取数据的情况。(也就是存储和读取游戏档案),当然你可以巧妙的在上一个关卡结束时保存数据到本地,下一个关卡时在读取本地的数据。

我们经常性的需要一些跨越整个游戏的单例类,并且这些单例类的属性变量,哪怕切换关卡,相应的变量都不会被GC掉。

这时候我们可以定义全局单例类Singleton

怎么实现全局单例模式Singleton呢?

 

定义最先存在的单例类GameSingleton, 与GEngine->GameSingleton关联

GameSingleton为游戏刚开始的单例类对象,起源于GEngine->GameSingleton,负责创建和查询各种继承于SingleAttribute的单例属性类,并且NewObject的对象为 “GameInstance”,GameInstance游戏全局都不会被GC,相应的被NewObject出来的SingleAttribute也不会被GC。

#pragma once

#include "CoreMinimal.h"
#include "UObject/NoExportTypes.h"
#include "GameSingleton.generated.h"

class USingletonAttribute;
class UGameInstance;
/**
 * 
 */
UCLASS()
class SINGLELOBJECT_API UGameSingleton : public UObject
{
	GENERATED_BODY()
	
	
public:
	UGameSingleton();

	static UGameSingleton* Get();

	UPROPERTY()
	TArray<USingletonAttribute*> arraySingletonAttribute;


	template<typename T>
	void AddSingletonAttribute(UGameInstance* gameInstance)
	{
		for (int index = 0; index < arraySingletonAttribute.Num(); ++index)
		{
			if (arraySingletonAttribute[index]->GetClass() == T::StaticClass())
				return;
		}
		
		const FName singletonAttributeName = "singletonAttribute";
		T* singletonAttribute = NewObject<T>(gameInstance, singletonAttributeName);

		if (nullptr == singletonAttribute)
			return;

		arraySingletonAttribute.Add(singletonAttribute);
	}

	template<typename T>
	T* GetSingletonAttribute()
	{
		T* singletonAttribute = nullptr;

		for (int index = 0; index < arraySingletonAttribute.Num(); ++index)
		{
			if (arraySingletonAttribute[index]->GetClass() == T::StaticClass())
			{
				singletonAttribute = Cast<T>(arraySingletonAttribute[index]);
				break;
			}
		}

		return singletonAttribute;
	}
};

#define GGameSingleton UGameSingleton::Get()

 

// Fill out your copyright notice in the Description page of Project Settings.

#include "GameSingleton.h"
#include "Engine/Engine.h"


UGameSingleton::UGameSingleton()
{

}

UGameSingleton* UGameSingleton::Get()
{
	#if WITH_EDITOR

    return Cast<UGameSingleton>(GEngine->GameSingleton);
    #else

    static UGameSingleton* singletonObject = Cast<UGameSingleton>(GEngine->GameSingleton);
    return singletonObject;

    #endif
	
}



UGameSingleton* UGameSingleton::singletonObject = nullptr;



 

定义我们的单例属性类SingleAttribute

SingleAttribute是各种单例属性的基础类,如GameWeatherAttribute(管理全局游戏天气属性)等等会继承于SingleAttribute

#include "CoreMinimal.h"
#include "UObject/NoExportTypes.h"
#include "SingletonAttribute.generated.h"

/**
 * 
 */
UCLASS()
class SINGLELOBJECT_API USingletonAttribute : public UObject
{
	GENERATED_BODY()

public:
	USingletonAttribute();
	
};
#include "SingletonAttribute.h"


USingletonAttribute::USingletonAttribute()
{

}

 

游戏天气属性单例类:GameWeatherAttribute

作为一个管理游戏天气的单例类,全局不会被GC,这里定义一些变量


#pragma once

#include "CoreMinimal.h"
#include "SingletonAttribute.h"
#include "GameWeatherAttribute.generated.h"

/**
 * 
 */
UCLASS()
class SINGLELOBJECT_API UGameWeatherAttribute : public USingletonAttribute
{
	GENERATED_BODY()
	
	
public:
	float a;
	float b;
	float c;
	
public:
	static UGameWeatherAttribute* Get();
};
#include "GameWeatherAttribute.h"
#include "GameSingleton.h"


UGameWeatherAttribute* UGameWeatherAttribute::Get()
{
	return GGameSingleton->GetSingletonAttribute<UGameWeatherAttribute>();
}

 

游戏入口:MyGameInstance


#pragma once

#include "CoreMinimal.h"
#include "Engine/GameInstance.h"
#include "MyGameInstance.generated.h"

/**
 * 
 */
UCLASS()
class SINGLELOBJECT_API UMyGameInstance : public UGameInstance
{
	GENERATED_BODY()

public:
	UMyGameInstance();
	
protected:
	virtual void StartGameInstance() override;
	virtual FGameInstancePIEResult StartPlayInEditorGameInstance(ULocalPlayer* LocalPlayer, 
		const FGameInstancePIEParameters& Params) override;
	
};

 

#include "MyGameInstance.h"
#include "GameSingleton.h"
#include "GameWeatherAttribute.h"




UMyGameInstance::UMyGameInstance()
{

}

void UMyGameInstance::StartGameInstance()
{
	FGameInstancePIEResult InitResult = FGameInstancePIEResult::Failure(FText());

	UGameInstance* gameInstance = GetWorld()->GetGameInstance();
	if (nullptr == gameInstance)
		return;


	GGameSingleton->AddSingletonAttribute<UGameWeatherAttribute>(gameInstance);

	UGameWeatherAttribute* gameWeatherAttribute = UGameWeatherAttribute::Get();
	if (nullptr == gameWeatherAttribute)
	{
		UE_LOG(LogTemp, Error, TEXT("can not get UGameWeatherAttribute ptr"));
		return;
	}

	gameWeatherAttribute->a = 1;
	gameWeatherAttribute->b = 2;
	gameWeatherAttribute->c = 4;


	Super::StartGameInstance();


}

FGameInstancePIEResult UMyGameInstance::StartPlayInEditorGameInstance(ULocalPlayer* LocalPlayer,
	const FGameInstancePIEParameters& Params)
{
	FGameInstancePIEResult InitResult = FGameInstancePIEResult::Failure(FText());

	UGameInstance* gameInstance = GetWorld()->GetGameInstance();
	if (nullptr == gameInstance)
		return InitResult;


	GGameSingleton->AddSingletonAttribute<UGameWeatherAttribute>(gameInstance);

	UGameWeatherAttribute* gameWeatherAttribute = UGameWeatherAttribute::Get();
	if (nullptr == gameWeatherAttribute)
	{
		UE_LOG(LogTemp, Error, TEXT("can not get UGameWeatherAttribute ptr"));
		return InitResult;
	}

	gameWeatherAttribute->a = 1;
	gameWeatherAttribute->b = 2;
	gameWeatherAttribute->c = 4;


	FGameInstancePIEResult StartResult = Super::StartPlayInEditorGameInstance(LocalPlayer,Params);
	

	return StartResult;
}

 

编辑器选择游戏开始的单例类为GameSingleton

 

编辑器选择游戏开始的游戏实例类为MyGameInstance

 

最终测试下数据:

void ASinglelObjectCharacter::BeginPlay()
{
	Super::BeginPlay();


	UGameWeatherAttribute* gameWeatherAttribute = UGameWeatherAttribute::Get();
	if (nullptr == gameWeatherAttribute)
	{
		UE_LOG(LogTemp, Error, TEXT("UGameWeatherAttribute is not get in BeginPlay"));
		return;
	}

	UE_LOG(LogTemp, Warning, TEXT("a =  %f"), gameWeatherAttribute->a);
	UE_LOG(LogTemp, Warning, TEXT("b =  %f"), gameWeatherAttribute->b);
	UE_LOG(LogTemp, Warning, TEXT("c =  %f"), gameWeatherAttribute->c);
}

 

 

其中的MyGameInstance仅仅是为创建单例类做个小例子,不属于单例类实现的部分,你可以在任意地方随时创建和访问继承于SingleAttribute的单例对象.当然你直接在MyGameInstance定义的变量都是切换关卡的时候也是不被GC的,然而一个完整的项目需要的单例类可能有点多,像我这种做法可能好点。

 

评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值