11

Sweet Snippet 之 PlayerPrefs for UE4

 3 years ago
source link: https://blog.csdn.net/tkokof1/article/details/119764449
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

Sweet Snippet 之 PlayerPrefs for UE4

同时被 3 个专栏收录
67 篇文章 0 订阅
57 篇文章 0 订阅
143 篇文章 0 订阅

在 Unity 中进行本地存储,我们一般会用到 PlayerPrefs,而在 UE4 中,我们一般会使用 USaveGame,不过 USaveGame 在使用上和 PlayerPrefs 相差较大,这里给出一个 UE4 的 PlayerPrefs 实现,原理上仅是对 USaveGame 做了进一步的封装

  • 首先我们继承 USaveGame 创建 UPlayerPrefsSaveGame 类型
#include "CoreMinimal.h"
#include "GameFramework/SaveGame.h"
#include "PlayerPrefsSaveGame.generated.h"

class PlayerPrefs;

UCLASS()
class UPlayerPrefsSaveGame : public USaveGame
{
	GENERATED_BODY()

protected:
	UPROPERTY()
    TMap<FString, int> IntMap;
	
	UPROPERTY()
    TMap<FString, float> FloatMap;

	UPROPERTY()
	TMap<FString, FString> StringMap;

	friend class PlayerPrefs;
};
  • 接着就是 PlayerPrefs 类型了,提供了和 Unity 中 PlayerPrefs 基本一致的接口
#include "CoreMinimal.h"
#include "PlayerPrefsSaveGame.h"

class PlayerPrefs
{
public:
	void Init();
	void Release();
	void Update(float DeltaTime);

	// removes all key values from the preferences
	void DeleteAll();
    // removes key from the preferences, if key does not exist, DeleteKey has no impact
	void DeleteKey(const FString& Key);
	// returns the value corresponding to key in the preference file if it exists
	float GetFloat(const FString& Key, float Default = 0);
	// returns the value corresponding to key in the preference file if it exists
	int GetInt(const FString& Key, int Default = 0);
	// returns the value corresponding to key in the preference file if it exists
	FString GetString(const FString& Key, const FString& Default = TEXT(""));
	// returns the value corresponding to key in the preference file if it exists
	bool GetBool(const FString& Key, bool Default = false);
	// returns true if the given key exists in preference, otherwise returns false
	bool HasKey(const FString& Key);
	// writes all modified preferences to disk
	void Save();
	// sets the float value of the preference identified by the given key
	void SetFloat(const FString& Key, float Value);
	// sets a single integer value for the preference identified by the given key
	void SetInt(const FString& Key, int Value);
	// sets a single string value for the preference identified by the given key
	void SetString(const FString& Key, const FString& Value);
	// sets a single bool value for the preference identified by the given key
	void SetBool(const FString& Key, bool Value);

private:
	UPlayerPrefsSaveGame* mPlayerPrefsSaveGame{ nullptr };
	bool mPlayerPrefsDirty{ false };
	float mPlayerPrefsUpdateTime{ 0 };
	float mPlayerPrefsUpdateInterval{ 5 };
};

值得提到的一点是 PlayerPrefs 主动存储的实现方式,代码中除了释放 PlayerPrefs 时会做一次主动存储以外,另外还使用了一个脏标记(mPlayerPrefsDirty)来定时的检查是否要进行主动存储

#include "PlayerPrefs.h"
#include "Kismet/GameplayStatics.h"

namespace 
{
	FString PlayerPrefsSaveGameSlotName = TEXT("PlayerPrefs");
}

void PlayerPrefs::Init()
{
	check(!mPlayerPrefsSaveGame);

	auto SavedGame = UGameplayStatics::LoadGameFromSlot(PlayerPrefsSaveGameSlotName, 0);
	mPlayerPrefsSaveGame = Cast<UPlayerPrefsSaveGame>(SavedGame);

	if (!mPlayerPrefsSaveGame)
	{
		mPlayerPrefsSaveGame = Cast<UPlayerPrefsSaveGame>(UGameplayStatics::CreateSaveGameObject(UPlayerPrefsSaveGame::StaticClass()));
		bool Saved = UGameplayStatics::SaveGameToSlot(mPlayerPrefsSaveGame, PlayerPrefsSaveGameSlotName, 0);
		if (!Saved)
		{
			UE_LOG(LogTemp, Error, TEXT("[PlayerPrefs]Error to save PlayerPrefs ..."));
			return;
		}
	}

	if (!mPlayerPrefsSaveGame)
	{
		UE_LOG(LogTemp, Error, TEXT("[PlayerPrefs]Error to init PlayerPrefs ..."));
		return;
	}

	// add GC reference
	mPlayerPrefsSaveGame->AddToRoot();
}

void PlayerPrefs::Release()
{
	Save();

	if (mPlayerPrefsSaveGame)
	{
		// remove GC reference
		mPlayerPrefsSaveGame->RemoveFromRoot();
	}

	mPlayerPrefsSaveGame = nullptr;
}

void PlayerPrefs::Update(float DeltaTime)
{
	mPlayerPrefsUpdateTime += DeltaTime;
	if (mPlayerPrefsUpdateTime >= mPlayerPrefsUpdateInterval)
	{
		mPlayerPrefsUpdateTime = 0;
		if (mPlayerPrefsDirty)
		{
			Save();
		}
	}
}

// removes all key values from the preferences
void PlayerPrefs::DeleteAll()
{
	if (mPlayerPrefsSaveGame)
	{
		mPlayerPrefsSaveGame->IntMap.Empty();
		mPlayerPrefsSaveGame->FloatMap.Empty();
		mPlayerPrefsSaveGame->StringMap.Empty();
		mPlayerPrefsDirty = true;
	}
}

// removes key from the preferences, if key does not exist, DeleteKey has no impact
void PlayerPrefs::DeleteKey(const FString& Key)
{
	if (mPlayerPrefsSaveGame)
	{
		bool Ret = mPlayerPrefsSaveGame->IntMap.Remove(Key) > 0;
		Ret |= mPlayerPrefsSaveGame->FloatMap.Remove(Key) > 0;
		Ret |= mPlayerPrefsSaveGame->StringMap.Remove(Key) > 0;
		
		if (Ret)
		{
			mPlayerPrefsDirty = true;
		}
	}
}

// returns the value corresponding to key in the preference file if it exists
float PlayerPrefs::GetFloat(const FString& Key, float Default)
{
	if (mPlayerPrefsSaveGame)
	{
		if (mPlayerPrefsSaveGame->FloatMap.Contains(Key))
		{
			return mPlayerPrefsSaveGame->FloatMap[Key];
		}
	}

	return Default;
}

// returns the value corresponding to key in the preference file if it exists
int PlayerPrefs::GetInt(const FString& Key, int Default)
{
	if (mPlayerPrefsSaveGame)
	{
		if (mPlayerPrefsSaveGame->IntMap.Contains(Key))
		{
			return mPlayerPrefsSaveGame->IntMap[Key];
		}
	}

	return Default;
}

// returns the value corresponding to key in the preference file if it exists
FString PlayerPrefs::GetString(const FString& Key, const FString& Default)
{
	if (mPlayerPrefsSaveGame)
	{
		if (mPlayerPrefsSaveGame->StringMap.Contains(Key))
		{
			return mPlayerPrefsSaveGame->StringMap[Key];
		}
	}

	return Default;
}

// returns the value corresponding to key in the preference file if it exists
bool PlayerPrefs::GetBool(const FString& Key, bool Default)
{
	auto DefaultInt = Default ? 1 : 0;
	auto Value = GetInt(Key, DefaultInt);
	return Value > 0;
}

// returns true if the given key exists in preference, otherwise returns false
bool PlayerPrefs::HasKey(const FString& Key)
{
	if (mPlayerPrefsSaveGame)
	{
		bool Ret = mPlayerPrefsSaveGame->IntMap.Contains(Key) ||
		           mPlayerPrefsSaveGame->FloatMap.Contains(Key) ||
		           mPlayerPrefsSaveGame->StringMap.Contains(Key);
	    
		return Ret;
	}

	return false;
}

// writes all modified preferences to disk
void PlayerPrefs::Save()
{
	if (mPlayerPrefsSaveGame)
	{
		bool Saved = UGameplayStatics::SaveGameToSlot(mPlayerPrefsSaveGame, PlayerPrefsSaveGameSlotName, 0);
		if (!Saved)
		{
			UE_LOG(LogTemp, Error, TEXT("[PlayerPrefs]Error to save PlayerPrefs ..."));
		}
		
		mPlayerPrefsDirty = false;
	}
}

// sets the float value of the preference identified by the given key
void PlayerPrefs::SetFloat(const FString& Key, float Value)
{
	if (mPlayerPrefsSaveGame)
	{
		if (mPlayerPrefsSaveGame->FloatMap.Contains(Key))
		{
			mPlayerPrefsSaveGame->FloatMap[Key] = Value;
		}
		else
		{
			mPlayerPrefsSaveGame->FloatMap.Add(Key, Value);
		}

		mPlayerPrefsDirty = true;
	}
}

// sets a single integer value for the preference identified by the given key
void PlayerPrefs::SetInt(const FString& Key, int Value)
{
	if (mPlayerPrefsSaveGame)
	{
		if (mPlayerPrefsSaveGame->IntMap.Contains(Key))
		{
			mPlayerPrefsSaveGame->IntMap[Key] = Value;
		}
		else
		{
			mPlayerPrefsSaveGame->IntMap.Add(Key, Value);
		}

		mPlayerPrefsDirty = true;
	}
}

// sets a single string value for the preference identified by the given key
void PlayerPrefs::SetString(const FString& Key, const FString& Value)
{
	if (mPlayerPrefsSaveGame)
	{
		if (mPlayerPrefsSaveGame->StringMap.Contains(Key))
		{
			mPlayerPrefsSaveGame->StringMap[Key] = Value;
		}
		else
		{
			mPlayerPrefsSaveGame->StringMap.Add(Key, Value);
		}

		mPlayerPrefsDirty = true;
	}
}

// sets a single bool value for the preference identified by the given key
void PlayerPrefs::SetBool(const FString& Key, bool Value)
{
	if (mPlayerPrefsSaveGame)
	{
		if (mPlayerPrefsSaveGame->IntMap.Contains(Key))
		{
			mPlayerPrefsSaveGame->IntMap[Key] = Value ? 1 : 0;
		}
		else
		{
			mPlayerPrefsSaveGame->IntMap.Add(Key, Value ? 1 : 0);
		}

		mPlayerPrefsDirty = true;
	}
}

值得注意的一点是,虽然实现上使用了不同类型的映射表(TMap)来存储不同类型的数值,但程序概念上表键(Key)是相通的,不同映射表之间不存在重复的表键(Key)


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK