사용자 설정과 환경 저장
이전 절들에서는 SaveGame 시스템과 일반 파일 입출력 및 JSON 처리를 통해 게임 데이터를 저장하고 관리하는 방법을 알아보았습니다. 이제 게임에서 매우 중요한 또 다른 종류의 데이터인 사용자 설정(User Settings) 과 환경 설정(Environment Settings) 을 저장하고 관리하는 방법에 대해 살펴보겠습니다. 여기에는 그래픽 품질, 오디오 볼륨, 키 바인딩, 언어 설정 등 플레이어가 게임 경험을 개인화할 수 있는 모든 옵션이 포함됩니다.
언리얼 엔진은 이를 위해 특별히 고안된 UGameUserSettings
클래스를 제공합니다.
UGameUserSettings
시스템이란?
UGameUserSettings
는 플레이어별 게임 설정을 영구적으로 저장하고 로드하며 적용하는 데 사용되는 언리얼 엔진의 내장 시스템입니다. 이 시스템은 각 플레이어의 디바이스에 독립적인 .ini
파일 형태로 설정 데이터를 저장합니다.
UGameUserSettings
의 주요 특징
- 자동 저장/로드: 대부분의 기본 설정(해상도, 그래픽 품질 등)은 엔진에 의해 자동으로 처리됩니다.
- 플랫폼 독립적: SaveGame 시스템과 마찬가지로, 다양한 플랫폼에서 일관된 방식으로 설정이 관리됩니다.
- 쉬운 접근:
UGameplayStatics::GetGameUserSettings()
함수를 통해 언제든지 현재 플레이어의 설정에 접근할 수 있습니다. - 확장성: 기본 설정을 넘어 게임 고유의 커스텀 설정을 추가하고 관리할 수 있습니다.
- 설정 적용: 변경된 설정을 게임에 즉시 적용하거나, 변경 사항을 임시로 저장했다가 나중에 일괄 적용할 수 있습니다.
커스텀 UGameUserSettings
클래스 생성
대부분의 프로젝트에서는 기본 UGameUserSettings
클래스를 직접 사용하는 대신, 이를 상속받아 게임 고유의 설정 변수를 추가합니다.
// MyGameUserSettings.h
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/GameUserSettings.h"
#include "MyGameUserSettings.generated.h"
UCLASS()
class MYPROJECT_API UMyGameUserSettings : public UGameUserSettings
{
GENERATED_BODY()
public:
UMyGameUserSettings();
// 싱글톤 패턴처럼 어디서든 쉽게 접근할 수 있는 헬퍼 함수
UFUNCTION(BlueprintCallable, Category = "Game Settings")
static UMyGameUserSettings* GetMyGameUserSettings();
//========================================
// 커스텀 게임 설정 변수들
// UPROPERTY로 선언해야 직렬화 대상이 됩니다.
//========================================
UPROPERTY(Config) // .ini 파일에 저장될 수 있도록 Config 지정
float MasterVolume;
UPROPERTY(Config)
bool bShowPlayerHealthBar;
UPROPERTY(Config)
FString PlayerPreferredLanguage;
UPROPERTY(Config)
TArray<FKey> CustomAbilityKeyBinds; // 커스텀 능력 키 바인딩 (예시)
//========================================
// 커스텀 설정 제어 함수들
//========================================
UFUNCTION(BlueprintCallable, Category = "Game Settings")
void SetMasterVolume(float NewVolume);
UFUNCTION(BlueprintPure, Category = "Game Settings")
float GetMasterVolume() const;
UFUNCTION(BlueprintCallable, Category = "Game Settings")
void SetShowPlayerHealthBar(bool bShow);
UFUNCTION(BlueprintPure, Category = "Game Settings")
bool GetShowPlayerHealthBar() const;
UFUNCTION(BlueprintCallable, Category = "Game Settings")
void SetPlayerPreferredLanguage(const FString& NewLanguage);
UFUNCTION(BlueprintPure, Category = "Game Settings")
FString GetPlayerPreferredLanguage() const;
// 변경 사항을 .ini 파일에 저장 (부모 클래스 함수 오버라이드)
virtual void SaveSettings() override;
// 저장된 설정을 로드하거나 기본값을 적용 (부모 클래스 함수 오버라이드)
virtual void LoadSettings(bool bForceReload = false) override;
// 설정을 게임에 적용 (그래픽, 사운드 등)
virtual void ApplySettings(bool bCheckFor===r= ===ssues) override;
};
// MyGameUserSettings.cpp
#include "MyGameUserSettings.h"
#include "Kismet/GameplayStatics.h"
#include "AudioDevice.h" // 오디오 볼륨 제어를 위해 포함 (선택 사항)
#include "GameFramework/PlayerController.h" // 플레이어 컨트롤러 접근을 위해 (선택 사항)
#include "Components/AudioComponent.h" // 사운드 컴포넌트 제어 (선택 사항)
UMyGameUserSettings::UMyGameUserSettings()
{
// 기본값 설정
MasterVolume = 0.75f;
bShowPlayerHealthBar = true;
PlayerPreferredLanguage = TEXT("en"); // 기본 언어 영어
CustomAbilityKeyBinds.Add(EKeys::Q); // 기본 Q 키 바인딩
CustomAbilityKeyBinds.Add(EKeys::E); // 기본 E 키 바인딩
}
UMyGameUserSettings* UMyGameUserSettings::GetMyGameUserSettings()
{
// UGameplayStatics::GetGameUserSettings()는 항상 유효한 UGameUserSettings 인스턴스를 반환
return Cast<UMyGameUserSettings>(UGameplayStatics::GetGameUserSettings());
}
void UMyGameUserSettings::SetMasterVolume(float NewVolume)
{
MasterVolume = FMath::Clamp(NewVolume, 0.0f, 1.0f);
// 변경 사항을 즉시 적용하고 싶다면 ApplySettings() 호출
ApplySettings(false);
}
float UMyGameUserSettings::GetMasterVolume() const
{
return MasterVolume;
}
void UMyGameUserSettings::SetShowPlayerHealthBar(bool bShow)
{
bShowPlayerHealthBar = bShow;
// UI 관련 설정은 주로 UI 위젯에서 직접 이 변수를 참조하여 처리합니다.
// 즉시 UI 업데이트를 강제하려면 UI 관리자에게 알림을 보내야 합니다.
}
bool UMyGameUserSettings::GetShowPlayerHealthBar() const
{
return bShowPlayerHealthBar;
}
void UMyGameUserSettings::SetPlayerPreferredLanguage(const FString& NewLanguage)
{
PlayerPreferredLanguage = NewLanguage;
// 언어 변경은 엔진의 로컬라이제이션 시스템을 통해 적용
// FInternationalization::Get().SetCurrentCulture(NewLanguage); // 언어 변경
// UGameplayStatics::SetCurrentCulture(NewLanguage); // Kismet 버전을 사용하면 더 편리
ApplySettings(false); // 변경 사항 적용
}
FString UMyGameUserSettings::GetPlayerPreferredLanguage() const
{
return PlayerPreferredLanguage;
}
void UMyGameUserSettings::SaveSettings()
{
Super::SaveSettings(); // 부모 클래스의 SaveSettings 호출 (실제 파일 저장)
UE_LOG(LogTemp, Warning, TEXT("MyGameUserSettings: Settings Saved!"));
}
void UMyGameUserSettings::LoadSettings(bool bForceReload)
{
Super::LoadSettings(bForceReload); // 부모 클래스의 LoadSettings 호출
UE_LOG(LogTemp, Warning, TEXT("MyGameUserSettings: Settings Loaded!"));
// 로드된 후 필요에 따라 추가 초기화/유효성 검사 로직
MasterVolume = FMath::Clamp(MasterVolume, 0.0f, 1.0f);
}
void UMyGameUserSettings::ApplySettings(bool bCheckFor=ssues)
{
// 먼저 부모 클래스의 ApplySettings를 호출하여 기본 그래픽 설정 등을 적용
Super::ApplySettings(bCheckFor===r= ===ssues);
UE_LOG(LogTemp, Warning, TEXT("MyGameUserSettings: Settings Applied!"));
//========================================
// 커스텀 설정 적용 로직
//========================================
// 마스터 볼륨 적용 (예시)
// 모든 사운드 클래스에 대한 볼륨 설정은 Audio Mixer 또는 Sound Class Mixer를 통해 관리하는 것이 일반적입니다.
// 여기서는 간단히 Audio Device를 통해 모든 사운드에 영향을 줄 수 있는 방법을 예시로 듭니다.
FAudioDevice* AudioDevice = GEngine->GetMainAudioDevice();
if (AudioDevice)
{
// Audio Device의 Listener Volume 또는 특정 Sound Class의 Volume을 조절
// AudioDevice->SetMasterVolume(MasterVolume); // 이런 직접적인 함수는 일반적으로 사용되지 않음
// 대신, Audio Mixer에 커스텀 마스터 볼륨 Sound Class를 만들고 그 볼륨을 제어하는 방식이 더 일반적
}
// 또는 특정 AudioComponent의 볼륨을 직접 제어
// (이 방법은 모든 사운드에 적용되지 않으므로, 더 높은 수준의 오디오 시스템이 필요)
// 예를 들어, 월드의 모든 Ambient Sound Actor의 볼륨을 조절해야 할 수도 있습니다.
// 언어 설정 적용
FInternationalization::Get().SetCurrentCulture(PlayerPreferredLanguage);
// 키 바인딩 적용 (입력 시스템에 바인딩 로직 필요)
// Project Settings -> Input -> Action Mappings 또는 Axis Mappings을 C++에서 동적으로 변경하는 것은 복잡합니다.
// 보통은 FInputBindings와 같은 커스텀 시스템을 구축합니다.
// 이외의 게임 관련 설정 (예: HUD 체력 바 표시 여부)
// 이 설정은 주로 HUD 위젯에서 GetShowPlayerHealthBar()를 호출하여 조건부로 표시 여부를 결정합니다.
}
프로젝트 설정
프로젝트에서 UMyGameUserSettings
를 사용하도록 엔진에 알려야 합니다. 이는 DefaultGameUserSettings.ini
파일에서 설정합니다.
-
Config/DefaultGameUserSettings.ini
파일을 엽니다. (없다면 프로젝트 루트의Config
폴더에 생성) -
다음 내용을 추가하거나 수정합니다.
[/Script/Engine.GameUserSettings] GameUserSettingsClassName=/Script/MYPROJECT.MyGameUserSettings
MYPROJECT
는 여러분의 프로젝트 모듈 이름입니다.
이렇게 설정하면 엔진은 게임 시작 시 UMyGameUserSettings
의 인스턴스를 자동으로 생성하고 로드/저장 로직을 관리합니다.
사용자 설정 활용 및 적용
C++에서 접근 및 변경
#include "MyGameUserSettings.h"
void AMyPlayerController::SetGameVolume(float NewVolume)
{
UMyGameUserSettings* UserSettings = UMyGameUserSettings::GetMyGameUserSettings();
if (UserSettings)
{
UserSettings->SetMasterVolume(NewVolume);
// SaveSettings()는 SetMasterVolume 내부에서 호출될 수도 있고,
// 옵션 메뉴에서 '적용' 버튼 클릭 시 일괄적으로 호출될 수도 있습니다.
UserSettings->SaveSettings();
}
}
블루프린트에서 접근 및 변경
Get My Game User Settings
노드를 사용하여 인스턴스를 얻은 후, 공개된 함수(BlueprintCallable, BlueprintPure)를 호출하여 설정을 읽거나 변경할 수 있습니다. Save Settings
및 Apply Settings
노드를 사용하여 변경 사항을 저장하고 적용합니다.
이미지 출처: Unreal Engine Documentation (Save Game Settings with UMG)
설정 파일 위치
UGameUserSettings
는 기본적으로 다음 위치에 설정을 저장합니다.
- Windows:
C:\Users\[사용자명]\AppData\Local\[프로젝트명]\Saved\Config\Windows\GameUserSettings.ini
- Android/iOS: 각 플랫폼의 앱 데이터 디렉토리 내에 저장됩니다.
이 파일은 일반 텍스트 .ini
형식으로 되어 있어 직접 열어볼 수 있으며, Config
매크로를 사용한 변수들이 섹션 형태로 저장됩니다.
; GameUserSettings.ini 예시
[GameSettings]
MasterVolume=0.650000
bShowPlayerHealthBar=True
PlayerPreferredLanguage=ko
CustomAbilityKeyBinds=(Keys=((KeyName="Q",IsGamepadKey=False,IsMouseButton=False,IsAction=False,IsAxis=False,IsDeprecated=False,IsModifierKey=False,IsReadyForBind=False,IsBindableInSettings=False,IsCustomInput=False),(KeyName="E",IsGamepadKey=False,IsMouseButton=False,IsAction=False,IsAxis=False,IsDeprecated=False,IsModifierKey=False,IsReadyForBind=False,IsBindableInSettings=False,IsCustomInput=False)))
마치며
UGameUserSettings
시스템은 언리얼 엔진에서 사용자 설정과 환경을 효율적으로 관리하기 위한 표준적이고 강력한 방법입니다. 커스텀 클래스를 정의하고 Config
매크로를 사용하여 변수를 .ini
파일에 직렬화하며, SaveSettings()
및 ApplySettings()
함수를 통해 설정을 저장하고 게임에 적용하는 방법을 익히면 플레이어에게 개인화된 게임 경험을 제공할 수 있습니다. 이는 게임의 접근성을 높이고 플레이어 만족도를 향상시키는 데 필수적인 요소입니다.
이것으로 7장 "저장 및 데이터 관리"를 마칩니다.