Cloud Save란?
Cloud Save는 플레이어의 게임 진행 상태, 게임 설정, 인벤토리 등의 작은 데이터를 클라우드에 저장하고 불러올 수 있는 기능입니다. 이를 통해 플레이어가 여러 디바이스에서 동일한 게임 데이터를 사용할 수 있어 데이터를 안전하게 보존할 수 있습니다.
주요 기능
- 크로스 디바이스 동기화: 플레이어는 다른 기기에서도 같은 데이터로 게임을 이어서 할 수 있습니다.
- 데이터 보안: 로컬 저장소에 의존하지 않고 클라우드에 데이터를 백업함으로써 데이터 손실을 방지합니다.
Cloud Save 대시보드의 주요 탭들
1. 인덱스 설정 (Index Configuration)
- 설명: Cloud Save에서 인덱스는 데이터를 빠르게 검색하고 접근할 수 있도록 만들어줍니다. 인덱스를 설정하면 대량의 데이터 중에서도 필요한 데이터를 효과적으로 찾을 수 있기 때문에 성능 최적화에 중요한 역할을 합니다.
- 활용 예시: 플레이어가 수집한 게임 아이템 목록을 검색하거나, 특정 업적을 달성한 플레이어 데이터를 검색할 때 인덱스가 빠르게 결과를 반환하도록 도와줍니다.
2. 게임 데이터 (Game Data)
- 설명: 모든 플레이어에게 동일하게 적용되는 게임 데이터는 글로벌 설정에 해당하는 정보들을 저장합니다. 예를 들어 게임 업데이트나 패치 후에도 일관된 데이터가 유지되도록 할 수 있습니다.
- 활용 예시: 게임 내에서 사용하는 기본 설정(예: 그래픽 옵션, 게임 난이도)이나 이벤트 정보 등을 관리할 때 이 기능을 사용합니다.
3. 플레이어 데이터 (Player Data)
- 설명: 각각의 플레이어별로 저장되는 개인 데이터를 관리합니다. 이는 플레이어의 진행 상황, 획득한 아이템, 경험치, 설정 등을 포함하며, 각 플레이어마다 고유한 정보를 동기화합니다.
- 활용 예시: 플레이어가 다른 기기에서 로그인을 해도 동일한 게임 진행 상태를 유지하게 할 수 있습니다. 예를 들어, 플레이어가 모바일에서 진행한 데이터를 PC에서도 불러와 이어서 게임을 진행할 수 있습니다.
4. 플레이어 파일 (Player Files)
- 설명: 대용량 파일을 클라우드에 저장할 수 있습니다. 예를 들어 플레이어의 저장 파일, 리플레이 영상, 사용자 생성 콘텐츠 등을 저장합니다.
- 활용 예시: 게임 리플레이 파일을 저장해 언제든 다시 재생할 수 있거나, 플레이어가 직접 만든 캐릭터나 게임 맵 같은 사용자 생성 콘텐츠를 저장하고 불러오는 용도로 사용합니다.
Cloud Save 사용하기
1. Unity 서비스 초기화 및 로그인
먼저 Unity Services를 초기화하고, 플레이어가 익명 또는 정식 로그인 방식을 사용해 인증되어야 합니다.
using Unity.Services.CloudSave;
using Unity.Services.Authentication;
using Unity.Services.Core;
using UnityEngine;
using System.Threading.Tasks;
public class CloudSaveExample : MonoBehaviour
{
public string username = "your_username"; // 사용자 이름
public string password = "your_password"; // 비밀번호
async void Start()
{
// Unity Services 초기화
await UnityServices.InitializeAsync();
Debug.Log("Unity Services Initialized");
// 익명 로그인
await SignInAnonymouslyAsync();
// 사용자 이름과 비밀번호로 로그인
await SignInWithUsernameAndPasswordAsync(username, password);
}
// 익명 로그인 메서드
async Task SignInAnonymouslyAsync()
{
if (!AuthenticationService.Instance.IsSignedIn)
{
try
{
await AuthenticationService.Instance.SignInAnonymouslyAsync();
Debug.Log("플레이어가 익명으로 로그인되었습니다.");
}
catch (AuthenticationException ex)
{
Debug.LogError("익명 로그인 실패: " + ex.Message);
}
catch (RequestFailedException ex)
{
Debug.LogError("요청 실패: " + ex.Message);
}
}
}
// 사용자 이름과 비밀번호로 로그인하는 메서드
async Task SignInWithUsernameAndPasswordAsync(string username, string password)
{
try
{
await AuthenticationService.Instance.SignInWithUsernameAndPasswordAsync(username, password);
Debug.Log("로그인 성공!");
Debug.Log($"PlayerID: {AuthenticationService.Instance.PlayerId}");
}
catch (AuthenticationException ex)
{
Debug.LogError("인증 실패: " + ex.Message);
}
catch (RequestFailedException ex)
{
Debug.LogError("요청 실패: " + ex.Message);
}
}
}
2. Player Data
데이터를 키-값 형태로 저장할 수 있습니다.
using Unity.Services.CloudSave;
using Unity.Services.Authentication;
using Unity.Services.Core;
using UnityEngine;
using System.Collections.Generic;
using System.Threading.Tasks;
using Unity.Services.CloudSave.Models.Data.Player;
public class CloudSaveManager : MonoBehaviour
{
async void Start()
{
// Unity Services 초기화
await UnityServices.InitializeAsync();
Debug.Log("Unity Services Initialized");
// 플레이어를 익명으로 로그인
if (!AuthenticationService.Instance.IsSignedIn)
{
await AuthenticationService.Instance.SignInAnonymouslyAsync();
Debug.Log($"PlayerID: {AuthenticationService.Instance.PlayerId}");
}
}
// 1-1. Save an item: 플레이어 데이터 저장 (default)
async void SavePlayerData()
{
var data = new Dictionary<string, object> { { "testKey_default", "testValue" } };
await CloudSaveService.Instance.Data.Player.SaveAsync(data);
Debug.Log("플레이어 데이터 저장 완료: testKey = testValue");
}
// 1-2. Save an item: 플레이어 데이터 저장 (public)
async void SavepublicPlayerData()
{
var data = new Dictionary<string, object> { { "testKey_public", 2 } };
await CloudSaveService.Instance.Data.Player.SaveAsync(data, new Unity.Services.CloudSave.Models.Data.Player.SaveOptions(new PublicWriteAccessClassOptions()));
}
// 2-1. Fetch data: 데이터 가져오기
async void FetchPlayerData()
{
var playerData = await CloudSaveService.Instance.Data.Player.LoadAsync(new HashSet<string> { "keyName" });
if (playerData.TryGetValue("keyName", out var keyName))
{
Debug.Log($"keyName: {keyName.Value.GetAs<string>()}");
}
}
// 2-2. Fetch data: 데이터 가져오기 (Public)
async void FetchPublicPlayerData()
{
var playerData = await CloudSaveService.Instance.Data.Player.LoadAsync(new HashSet<string> { "keyName" },
new LoadOptions(new PublicReadAccessClassOptions()));
if (playerData.TryGetValue("keyName", out var keyName))
{
Debug.Log($"keyName: {keyName.Value.GetAs<string>()}");
}
}
// 2-3. Fetch data: 데이터 가져오기 (Protected)
async void FetchProtectedPlayerData()
{
var playerData = await CloudSaveService.Instance.Data.Player.LoadAsync(new HashSet<string> { "keyName" },
new LoadOptions(new ProtectedReadAccessClassOptions()));
if (playerData.TryGetValue("keyName", out var keyName))
{
Debug.Log($"keyName: {keyName.Value.GetAs<string>()}");
}
}
// 2-4. Fetch data: 데이터 가져오기 (지정 플레이어 ID의 퍼블릭 데이터)
async void LoadPublicDataByPlayerId()
{
var playerId = "JE1unrWOOzzbIwy3Nl60fRefuiVE";
var playerData = await CloudSaveService.Instance.Data.Player.LoadAsync(new HashSet<string> { "keyName" },
new LoadOptions(new PublicReadAccessClassOptions(playerId)));
if (playerData.TryGetValue("keyName", out var keyName))
{
Debug.Log($"keyName: {keyName.Value.GetAs<string>()}");
}
}
// 3. Delete an item: 특정 키 데이터 삭제
async void DeletePlayerData()
{
// 지정값만 삭제
await CloudSaveService.Instance.Data.Player.DeleteAsync("testKey");
// 모든 값 삭제
await CloudSaveService.Instance.Data.Player.DeleteAllAsync();
}
// 4-1. Get a list of keys: 저장된 모든 데이터 키 목록 확인
async void ListPlayerKeys()
{
var keys = await CloudSaveService.Instance.Data.Player.ListAllKeysAsync();
for (int i = 0; i < keys.Count; i++)
{
Debug.Log("플레이어 데이터 키 목록: " + keys[i].Key);
}
}
// 4-2. Get a list of keys: 저장된 모든 데이터 키 목록 확인 (Public)
async void PublicListPlayerKeys()
{
var keys = await CloudSaveService.Instance.Data.Player.ListAllKeysAsync(
new ListAllKeysOptions(new PublicReadAccessClassOptions())
);
for (int i = 0; i < keys.Count; i++)
{
Debug.Log(keys[i].Key);
}
}
// 4-3. Get a list of keys: 저장된 모든 데이터 키 목록 확인 (Protected)
async void ProtectedListPlayerKeys()
{
var keys = await CloudSaveService.Instance.Data.Player.ListAllKeysAsync(
new ListAllKeysOptions(new ProtectedReadAccessClassOptions())
);
for (int i = 0; i < keys.Count; i++)
{
Debug.Log(keys[i].Key);
}
}
}
3. Player Files
// 1. Save Player File: 파일 저장
async Task SavePlayerFile()
{
byte[] fileData = System.Text.Encoding.UTF8.GetBytes("This is a test file.");
await CloudSaveService.Instance.Files.Player.SaveAsync("testfile.txt", fileData);
}
// 2. Delete Player File: 파일 삭제
async Task DeletePlayerFile()
{
await CloudSaveService.Instance.Files.Player.DeleteAsync("fileName");
}
// 3. List Player Files: 저장된 파일 목록 조회
async Task ListPlayerFiles()
{
List<FileItem> files = await CloudSaveService.Instance.Files.Player.ListAllAsync();
for (int i = 0; i < files.Count; i++)
{
// 파일 제목 출력
Debug.Log($"파일 제목: {files[i].Key}");
}
}
// 4. Get Player File as a byte array: 파일을 바이트 배열로 가져오기
async Task GetPlayerFileAsByteArray()
{
byte[] file = await CloudSaveService.Instance.Files.Player.LoadBytesAsync("testfile.txt");
// 파일 내용을 문자열로 변환하여 출력 (텍스트 파일일 경우)
string fileContent = System.Text.Encoding.UTF8.GetString(file);
Debug.Log($"파일 내용: {fileContent}");
}
// 5. Get Player File as a stream: 파일을 스트림으로 가져오기
async Task GetPlayerFileAsStream()
{
using (var stream = await CloudSaveService.Instance.Files.Player.LoadStreamAsync("testfile.txt"))
{
using (StreamReader reader = new StreamReader(stream))
{
string content = reader.ReadToEnd();
Debug.Log("플레이어 파일 스트림으로 가져옴: " + content);
}
}
}
// 6. Get Player File Metadata: 파일 메타데이터 가져오기
async Task GetPlayerFileMetadata()
{
var metadata = await CloudSaveService.Instance.Files.Player.GetMetadataAsync("testfile.txt");
Debug.Log(metadata.Key);
Debug.Log(metadata.Size);
Debug.Log(metadata.ContentType);
Debug.Log(metadata.Created);
Debug.Log(metadata.WriteLock);
}
4. Game Data
// 데이터 가져오기
public async void LoadCustomItemData()
{
// 커스텀 아이템의 ID 설정
var customItemId = "my-custom-item";
// Cloud Save 서비스에서 해당 커스텀 아이템 데이터 로드
var customItemData = await CloudSaveService.Instance.Data.Custom.LoadAllAsync(customItemId);
// 가져온 각 커스텀 아이템의 키와 값을 출력
foreach (var customItem in customItemData)
{
Debug.Log($"키: {customItem.Key}, 값: {customItem.Value.Value}");
}
}
// Query Custom Items
public async void QueryCustomItems()
{
var query = new Query(
// 첫 번째 인자는 필터 리스트로, 모든 필터가 true로 평가되어야 결과에 포함됨
new List<FieldFilter>
{
// "indexedKeyName"이 "value"와 같은지 확인하는 필터
new FieldFilter("indexedKeyName", "value", FieldFilter.OpOptions.EQ, true),
// "anotherIndexedKeyName"이 "otherValue"와 같지 않은지 확인하는 필터
new FieldFilter("anotherIndexedKeyName", "otherValue", FieldFilter.OpOptions.NE, true)
},
// 두 번째 인자는 응답에 포함할 키들의 리스트 (옵션)
// 인덱스에 없는 키도 포함 가능하며, 아무것도 지정하지 않으면 플레이어 ID만 반환됨
new HashSet<string>
{
// 응답에서 "indexedKeyName"의 값을 포함하도록 지정
"indexedKeyName"
}
);
// 쿼리 실행, 결과를 받음
var results = await CloudSaveService.Instance.Data.Custom.QueryAsync(query);
// 쿼리 결과의 개수를 로그로 출력
Debug.Log($"쿼리 결과 항목 수: {results.Count}");
// 각 결과 항목에 대해 ID와 데이터를 출력
results.ForEach(r =>
{
// 결과 항목의 ID 출력
Debug.Log($"Custom Item ID: {r.Id}");
// 결과 항목의 데이터에서 키와 값 출력
r.Data.ForEach(d => Debug.Log($"키: {d.Key}, 값: {d.Value.GetAsString()}"));
});
}
마무리
Unity Cloud Save는 여러 디바이스에서 동일한 저장데이터, 게임데이터를 제공하는 데 필수적인 도구입니다. 이를 통해 플레이어 데이터를 클라우드에 저장해, 데이터 손실 없이 진행 상황을 유지할 수 있습니다.
'Unity > Unity Cloud(UGS)' 카테고리의 다른 글
[Unity] UGS - Asset Manager (1) | 2024.10.26 |
---|---|
[Unity] UGS - Cloud Code (0) | 2024.10.25 |
[Unity] UGS - Remote Config (0) | 2024.10.23 |
[Unity] UGS - Player Authentication (0) | 2024.10.15 |
[Unity] 유니티 클라우드 - 충돌 보고서(Cloud Diagnostics) (0) | 2024.05.09 |