Skip to main content

Overview

LCK automatically persists user settings between sessions:
  • Camera mode (Selfie, First Person, Third Person)
  • Quality profile (SD, HD, 2K, 4K)
  • Camera settings (FOV, smoothness, distance)
  • Audio configuration

Platform-Specific Paths

Windows Save Path

FString SavePath = FPaths::ProjectSavedDir() / TEXT("LCK") / TEXT("settings.json");
// Example: C:/Users/Player/AppData/Local/MyGame/Saved/LCK/settings.json

Platform Detection

FString GetLCKSettingsPath()
{
#if PLATFORM_ANDROID
    return FPaths::GamePersistentDownloadDir() / TEXT("LCK") / TEXT("settings.json");
#else
    return FPaths::ProjectSavedDir() / TEXT("LCK") / TEXT("settings.json");
#endif
}

Settings Structure

FLCKSavedSettings

USTRUCT(BlueprintType)
struct FLCKSavedSettings
{
    GENERATED_BODY()

    // Camera mode
    UPROPERTY()
    ELCKCameraMode CameraMode = ELCKCameraMode::Selfie;

    // Quality profile
    UPROPERTY()
    ELCKVideoQuality Quality = ELCKVideoQuality::HD;

    // Orientation
    UPROPERTY()
    ELCKScreenOrientation Orientation = ELCKScreenOrientation::Landscape;

    // Selfie mode settings
    UPROPERTY()
    float SelfieFOV = 80.0f;

    UPROPERTY()
    float SelfieSmoothness = 50.0f;

    UPROPERTY()
    bool bSelfieFollowEnabled = false;

    // First person settings
    UPROPERTY()
    float FirstPersonFOV = 90.0f;

    UPROPERTY()
    float FirstPersonSmoothness = 75.0f;

    // Third person settings
    UPROPERTY()
    float ThirdPersonFOV = 90.0f;

    UPROPERTY()
    float ThirdPersonSmoothness = 100.0f;

    UPROPERTY()
    float ThirdPersonDistance = 2.0f;

    // Audio
    UPROPERTY()
    bool bMicrophoneEnabled = true;

    UPROPERTY()
    bool bGameAudioEnabled = true;
};

Saving Settings

Using FJsonObjectConverter

void ULCKTabletDataModel::SaveSettings()
{
    // Create settings struct
    FLCKSavedSettings Settings;
    Settings.CameraMode = CurrentCameraMode;
    Settings.Quality = CurrentQuality;
    Settings.SelfieFOV = SelfieModeSettings.FOV;
    // ... populate other fields

    // Convert to JSON
    FString JsonString;
    if (!FJsonObjectConverter::UStructToJsonObjectString(Settings, JsonString))
    {
        UE_LOG(LogLCK, Error, TEXT("Failed to serialize settings to JSON"));
        return;
    }

    // Ensure directory exists
    FString SavePath = GetLCKSettingsPath();
    FString Directory = FPaths::GetPath(SavePath);
    IPlatformFile& PlatformFile = FPlatformFileManager::Get().GetPlatformFile();

    if (!PlatformFile.DirectoryExists(*Directory))
    {
        PlatformFile.CreateDirectoryTree(*Directory);
    }

    // Write to file
    if (!FFileHelper::SaveStringToFile(JsonString, *SavePath))
    {
        UE_LOG(LogLCK, Error, TEXT("Failed to save settings to: %s"), *SavePath);
    }
}

Save Triggers

Settings are saved automatically when:
  • Camera mode changes
  • Quality profile changes
  • Camera settings (FOV, smoothness) change
  • Audio configuration changes
void ULCKTabletDataModel::SetCameraMode(ELCKCameraMode NewMode)
{
    if (CurrentCameraMode != NewMode)
    {
        CurrentCameraMode = NewMode;
        OnCameraModeChanged.Broadcast(NewMode);

        // Auto-save
        SaveSettings();
    }
}

Loading Settings

On Tablet Spawn

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

    // Try to load saved settings
    LoadSettings();
}

void ALCKTablet::LoadSettings()
{
    FString SavePath = GetLCKSettingsPath();

    // Check if file exists
    if (!FPaths::FileExists(SavePath))
    {
        UE_LOG(LogLCK, Log, TEXT("No saved settings found, using defaults"));
        return;
    }

    // Read file
    FString JsonString;
    if (!FFileHelper::LoadFileToString(JsonString, *SavePath))
    {
        UE_LOG(LogLCK, Warning, TEXT("Failed to read settings file"));
        return;
    }

    // Parse JSON
    FLCKSavedSettings Settings;
    if (!FJsonObjectConverter::JsonObjectStringToUStruct(JsonString, &Settings))
    {
        UE_LOG(LogLCK, Warning, TEXT("Failed to parse settings JSON"));
        return;
    }

    // Apply settings
    ApplySettings(Settings);
}

void ALCKTablet::ApplySettings(const FLCKSavedSettings& Settings)
{
    DataModel->SetCameraMode(Settings.CameraMode);
    DataModel->SetQuality(Settings.Quality);
    DataModel->SetOrientation(Settings.Orientation);

    // Apply camera-specific settings
    DataModel->SetSelfieFOV(Settings.SelfieFOV);
    DataModel->SetSelfieSmoothness(Settings.SelfieSmoothness);
    // ... etc
}

Error Handling

Robust Loading Pattern

bool ULCKSettingsManager::TryLoadSettings(FLCKSavedSettings& OutSettings)
{
    FString SavePath = GetLCKSettingsPath();

    // File existence check
    if (!FPaths::FileExists(SavePath))
    {
        return false;
    }

    // Read with error handling
    FString JsonString;
    if (!FFileHelper::LoadFileToString(JsonString, *SavePath))
    {
        UE_LOG(LogLCK, Warning, TEXT("Could not read settings file"));
        return false;
    }

    // Validate JSON is not empty
    if (JsonString.IsEmpty())
    {
        UE_LOG(LogLCK, Warning, TEXT("Settings file is empty"));
        DeleteCorruptedSettings();
        return false;
    }

    // Parse with validation
    if (!FJsonObjectConverter::JsonObjectStringToUStruct(JsonString, &OutSettings))
    {
        UE_LOG(LogLCK, Warning, TEXT("Settings JSON is invalid, resetting"));
        DeleteCorruptedSettings();
        return false;
    }

    // Validate loaded values
    if (!ValidateSettings(OutSettings))
    {
        UE_LOG(LogLCK, Warning, TEXT("Settings values out of range, resetting"));
        OutSettings = FLCKSavedSettings();  // Reset to defaults
        SaveSettings();
    }

    return true;
}

bool ULCKSettingsManager::ValidateSettings(const FLCKSavedSettings& Settings)
{
    // Validate FOV ranges
    if (Settings.SelfieFOV < 20.0f || Settings.SelfieFOV > 120.0f)
        return false;

    // Validate smoothness
    if (Settings.SelfieSmoothness < 0.0f || Settings.SelfieSmoothness > 100.0f)
        return false;

    // Validate distance
    if (Settings.ThirdPersonDistance < 0.5f || Settings.ThirdPersonDistance > 10.0f)
        return false;

    return true;
}

Async Operations

For better performance, especially on Android:
void ULCKSettingsManager::SaveSettingsAsync()
{
    // Capture settings copy
    FLCKSavedSettings SettingsCopy = CurrentSettings;

    Async(EAsyncExecution::ThreadPool, [SettingsCopy]()
    {
        FString JsonString;
        FJsonObjectConverter::UStructToJsonObjectString(SettingsCopy, JsonString);

        FString SavePath = GetLCKSettingsPath();
        FFileHelper::SaveStringToFile(JsonString, *SavePath);
    });
}

void ULCKSettingsManager::LoadSettingsAsync(TFunction<void(FLCKSavedSettings)> Callback)
{
    Async(EAsyncExecution::ThreadPool, [Callback]()
    {
        FLCKSavedSettings Settings;
        bool bLoaded = TryLoadSettings(Settings);

        // Return to game thread
        AsyncTask(ENamedThreads::GameThread, [Callback, Settings, bLoaded]()
        {
            if (bLoaded)
            {
                Callback(Settings);
            }
            else
            {
                Callback(FLCKSavedSettings());  // Defaults
            }
        });
    });
}

Recording Paths

Video recordings are saved to platform-specific locations:
// Videos saved to user's Videos folder
FString VideoPath = FPaths::VideosDir() / TEXT("LCK") / FileName;
// Example: C:/Users/Player/Videos/LCK/Recording_2024-01-15_14-30-00.mp4

See Also