안동민 개발노트

본문 시작

사용자 설정과 환경 저장

UGameUserSettings를 확장해 그래픽·오디오·키 바인딩 같은 사용자 옵션을 저장하고 적용합니다.

이전 절들에서는 SaveGame 시스템과 일반 파일 입출력 및 JSON 처리를 통해 게임 데이터를 저장하고 관리하는 방법을 알아보았습니다.

이제 게임에서 매우 중요한 또 다른 종류의 데이터인 사용자 설정(User Settings)과 환경 설정(Environment Settings)을 저장하고 관리하는 방법에 대해 살펴보겠습니다.

여기에는 그래픽 품질, 오디오 볼륨, 키 바인딩, 언어 설정 등 플레이어가 게임 경험을 개인화할 수 있는 모든 옵션이 포함됩니다.

언리얼 엔진은 이를 위해 특별히 고안된 UGameUserSettings 클래스를 제공합니다.


UGameUserSettings 시스템이란?

UGameUserSettings는 현재 로컬 기기의 게임 설정을 저장·로드·적용하는 시스템입니다. 계정별 또는 분할 화면 플레이어별 프로필을 자동으로 나누지는 않습니다.

이 시스템은 각 플레이어의 디바이스에 독립적인 .ini 파일 형태로 설정 데이터를 저장합니다.

UGameUserSettings의 주요 특징

  • 자동 저장/로드: 대부분의 기본 설정(해상도, 그래픽 품질 등)은 엔진에 의해 자동으로 처리됩니다.
  • 플랫폼 독립적: SaveGame 시스템과 마찬가지로, 다양한 플랫폼에서 일관된 방식으로 설정이 관리됩니다.
  • 접근: 정적 함수 UGameUserSettings::GetGameUserSettings()로 로컬 설정 객체를 얻습니다. 커스텀 타입으로 캐스팅한 결과는 검사해야 합니다.
  • 확장성: 기본 설정을 넘어 게임 고유의 커스텀 설정을 추가하고 관리할 수 있습니다.
  • 설정 적용: 변경된 설정을 게임에 즉시 적용하거나, 변경 사항을 임시로 저장했다가 나중에 일괄 적용할 수 있습니다.

커스텀 UGameUserSettings 클래스 생성

대부분의 프로젝트에서는 기본 UGameUserSettings 클래스를 직접 사용하는 대신, 이를 상속받아 게임 고유의 설정 변수를 추가합니다.

MyGameUserSettings.h
#pragma once

#include "CoreMinimal.h"
#include "GameFramework/GameUserSettings.h"
#include "InputCoreTypes.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 bCheckForCommandLineOverrides) override;
};
MyGameUserSettings.cpp
#include "MyGameUserSettings.h"
#include "Internationalization/Internationalization.h"

UMyGameUserSettings::UMyGameUserSettings()
{
    // 기본값 설정
    MasterVolume = 0.75f;
    bShowPlayerHealthBar = true;
    PlayerPreferredLanguage = TEXT("en"); // 기본 언어 영어
    CustomAbilityKeyBinds.Add(EKeys::Q); // 기본 Q 키 바인딩
    CustomAbilityKeyBinds.Add(EKeys::E); // 기본 E 키 바인딩
}

UMyGameUserSettings* UMyGameUserSettings::GetMyGameUserSettings()
{
    // 엔진 설정 클래스 등록과 초기화 상태에 따라 캐스팅은 실패할 수 있습니다.
    return Cast<UMyGameUserSettings>(UGameUserSettings::GetGameUserSettings());
}

void UMyGameUserSettings::SetMasterVolume(float NewVolume)
{
    MasterVolume = FMath::Clamp(NewVolume, 0.0f, 1.0f);
    // 값만 변경합니다. 옵션 UI의 적용 버튼에서 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;
    // 값만 변경합니다. 실제 문화권 변경은 ApplySettings에서 시도합니다.
}

FString UMyGameUserSettings::GetPlayerPreferredLanguage() const
{
    return PlayerPreferredLanguage;
}

void UMyGameUserSettings::SaveSettings()
{
    Super::SaveSettings(); // 부모 클래스의 SaveSettings 호출 (실제 파일 저장)
    UE_LOG(LogTemp, Warning, TEXT("MyGameUserSettings: SaveSettings returned."));
}

void UMyGameUserSettings::LoadSettings(bool bForceReload)
{
    Super::LoadSettings(bForceReload); // 부모 클래스의 LoadSettings 호출
    UE_LOG(LogTemp, Warning, TEXT("MyGameUserSettings: LoadSettings returned."));

    // 로드된 후 필요에 따라 추가 초기화/유효성 검사 로직
    MasterVolume = FMath::Clamp(MasterVolume, 0.0f, 1.0f);
}

void UMyGameUserSettings::ApplySettings(bool bCheckForCommandLineOverrides)
{
    // 부모 구현은 기본 설정을 적용하고 SaveSettings도 호출합니다.
    Super::ApplySettings(bCheckForCommandLineOverrides);

    //========================================
    // 커스텀 설정 적용 로직
    //========================================

    // 마스터 볼륨의 실제 오디오 적용은 미구현입니다.
    // 프로젝트의 Sound Class/Mix 또는 오디오 제어 경로에 MasterVolume을 전달해야 합니다.

    // 언어 설정 적용
    if (!FInternationalization::Get().SetCurrentCulture(PlayerPreferredLanguage))
    {
        UE_LOG(LogTemp, Warning, TEXT("Culture was not applied: %s"), *PlayerPreferredLanguage);
    }

    // 키 바인딩 적용 (입력 시스템에 바인딩 로직 필요)
    // Project Settings -> Input -> Action Mappings 또는 Axis Mappings을 C++에서 동적으로 변경하는 것은 복잡합니다.
    // 보통은 FInputBindings와 같은 커스텀 시스템을 구축합니다.

    // 이외의 게임 관련 설정 (예: HUD 체력 바 표시 여부)
    // 이 설정은 주로 HUD 위젯에서 GetShowPlayerHealthBar()를 호출하여 조건부로 표시 여부를 결정합니다.
}

이 예제의 setter는 메모리 값만 바꿉니다. 적용 버튼은 ApplySettings(false)를 호출하며 부모 구현의 저장도 함께 실행됩니다. 취소 UI를 만들려면 편집 전 값을 별도로 보관·복원해야 합니다. RevertVideoMode는 해상도와 전체 화면 모드용이며 커스텀 음량·언어 취소를 대신하지 않습니다.


프로젝트 설정

프로젝트에서 UMyGameUserSettings를 사용하도록 엔진에 알려야 합니다.

이는 DefaultEngine.ini 파일에서 설정합니다.

Config/DefaultEngine.ini 파일을 엽니다. (없다면 프로젝트 루트의 Config 폴더에 생성)

다음 내용을 추가하거나 수정합니다.

[/Script/Engine.Engine]
GameUserSettingsClassName=/Script/MYPROJECT.MyGameUserSettings
  • MYPROJECT는 여러분의 프로젝트 모듈 이름입니다.

이렇게 설정하면 엔진은 게임 시작 시 UMyGameUserSettings의 인스턴스를 자동으로 생성하고 로드/저장 로직을 관리합니다.


사용자 설정 활용 및 적용

C++에서 접근 및 변경

AMyPlayerController 헤더에 SetGameVolume을 선언한 부분 예제입니다. 버튼의 실제 적용 호출은 UI에서 연결해야 합니다.

MyPlayerController.cpp (일부)
#include "MyPlayerController.h"
#include "MyGameUserSettings.h"

void AMyPlayerController::SetGameVolume(float NewVolume)
{
    UMyGameUserSettings* UserSettings = UMyGameUserSettings::GetMyGameUserSettings();
    if (UserSettings)
    {
        UserSettings->SetMasterVolume(NewVolume);
        // 여기서는 편집 값만 변경합니다.
        // 옵션 메뉴의 적용 버튼: UserSettings->ApplySettings(false);
    }
}

블루프린트에서 접근 및 변경

Get My Game User Settings 노드를 사용하여 인스턴스를 얻은 후, 공개된 함수(BlueprintCallable, BlueprintPure)를 호출하여 설정을 읽거나 변경할 수 있습니다.

Apply Settings는 기본 설정 적용과 저장을 포함합니다. Save Settings만 호출하면 커스텀 런타임 적용을 대신하지 않습니다.

설정 파일 위치

UGameUserSettings는 기본적으로 다음 위치에 설정을 저장합니다.

  • Windows 패키지 예시: C:\Users\[사용자명]\AppData\Local\[프로젝트명]\Saved\Config\Windows\GameUserSettings.ini. 에디터와 빌드 대상에 따라 디렉터리가 달라집니다.
  • Android/iOS: 각 플랫폼의 앱 데이터 디렉토리 내에 저장됩니다.

이 파일은 일반 텍스트 .ini 형식으로 되어 있어 직접 열어볼 수 있으며, Config 매크로를 사용한 변수들이 섹션 형태로 저장됩니다.

; GameUserSettings.ini의 커스텀 속성 일부를 나타낸 형식 예시
[/Script/MYPROJECT.MyGameUserSettings]
MasterVolume=0.650000
bShowPlayerHealthBar=True
PlayerPreferredLanguage=ko

설정 값과 런타임 효과의 연결

커스텀 Config 속성의 저장과 기능 적용은 구별합니다.

설정 값과 런타임 효과의 연결
호출·값현재 구현구별할 점
LoadSettings부모에서 설정을 읽고 MasterVolume 범위를 보정합니다.읽기만으로 이 예제의 커스텀 오디오·언어 적용이 실행되지는 않습니다.
ApplySettings부모의 기본 설정 적용·저장 뒤 문화권 변경을 시도합니다.문화권 적용 실패를 기록하지만 앞선 저장을 되돌리지는 않습니다. 유효한 문화권·리소스 정책은 별도입니다.
음량·체력 바음량 값과 표시 플래그를 보관합니다.오디오 제어와 HUD 갱신은 구현하지 않았습니다. 값 변경만으로 해당 효과가 생기지 않습니다.
키 배열Config 속성에 FKey 배열을 보관합니다.배열 저장은 런타임 입력 재바인딩이 아닙니다. 사용하는 입력 시스템에 별도 적용해야 합니다.
LoadSettings
현재 구현: 부모에서 설정을 읽고 MasterVolume 범위를 보정합니다.
구별할 점: 읽기만으로 이 예제의 커스텀 오디오·언어 적용이 실행되지는 않습니다.
ApplySettings
현재 구현: 부모의 기본 설정 적용·저장 뒤 문화권 변경을 시도합니다.
구별할 점: 문화권 적용 실패를 기록하지만 앞선 저장을 되돌리지는 않습니다. 유효한 문화권·리소스 정책은 별도입니다.
음량·체력 바
현재 구현: 음량 값과 표시 플래그를 보관합니다.
구별할 점: 오디오 제어와 HUD 갱신은 구현하지 않았습니다. 값 변경만으로 해당 효과가 생기지 않습니다.
키 배열
현재 구현: Config 속성에 FKey 배열을 보관합니다.
구별할 점: 배열 저장은 런타임 입력 재바인딩이 아닙니다. 사용하는 입력 시스템에 별도 적용해야 합니다.

SaveSettings와 LoadSettings의 void 반환 뒤 로그는 호출 흐름 표시입니다. 저장 파일의 실제 내용이나 옵션 효과를 실행 검증한 결과가 아닙니다.

설정 파일은 사용자가 수정할 수 있으므로 로드 후 값 검증이 필요합니다. 이 예제는 음량 범위만 보정하며, 실제 오디오·입력 시스템과 UI 갱신 연결은 프로젝트에서 완성해야 합니다.