코딩/Unity

Unity : Unity Gaming Services (UGS)

americanoallday 2025. 9. 27. 15:25

‘클라우드에 플레이어 계정을 만들고, 디바이스에 토큰을 캐시해 자동 로그인’ 하는 구조. 즉, 사용자에게 UI를 강요하지 않고도(=무자각/사일런트) 인증을 끝낼 수 있음

 

UGS Authentication 한 장 요약

항목 설명 리뷰/검수 관점
무자각(Anonymous) 로그인 SignInAnonymouslyAsync() 호출 → 서버가 Player ID 발급(예: player-xxxxxxxx). 디바이스엔 세션 토큰만 캐시. UI 없이 실시간 처리 가능 → “강제 로그인”으로 보지 않음. 다만 네트워크 실패로 게임이 멈추면 그때는 반려 리스크 ↑
플랫폼/외부 계정 링크 원하면 나중에 Game Center/PGS/Sign in with Apple/Google 등으로 Link → 재설치·다른 기기에서도 같은 Player ID 유지 링크 UI는 선택적(설정 화면 등). 링크가 실패/취소돼도 게임 진행 가능해야 안전
재설치 시 익명만 쓰면 앱 삭제/데이터 삭제 후 다른 Player ID가 생성될 수 있음 랭킹/클라우드 세이브를 유지하려면 나중에 플랫폼 계정과 링크 권장
개인정보 표기 익명이라도 “식별자(플레이어 ID)”는 수집/전송 Play ‘Data safety’ / App Store ‘App Privacy’ 폼에 정확히 표기 필요
UGS vs ‘강제 로그인’ UGS는 사일런트 인증 가능 → 게임 시작을 막지 않으면 OK 앱 시작 시 로그인 완료를 기다리며 로딩 고정은 지양 (오프라인 허용, 재시도·건너뛰기)

 

추천 플로우(검수 친화)

  1. 앱 실행 → 즉시 게임 가능(타이틀/로비 진입).
  2. 백그라운드에서 UGS 익명 로그인 시도(최대 n초 타임아웃).
  3. 실패해도 무시하고 진행(랭킹/클라우드 기능만 잠시 제한, 나중에 자동 재시도).
  4. 설정/프로필 화면에 “계정 연결(선택)” 버튼 제공 → Game Center/PGS/Apple/Google 등 원할 때 링크.
  5. 랭킹 제출은 온라인일 때만 송신, 실패 시 로컬 큐잉 후 재시도
요지는 로그인 성공을 게임 시작의 “조건”으로 만들지 않기

 

최소 코드 예시 (사일런트 + 실패해도 통과)

생성할 파일

  • Scripts/UGS/UgsAuthBootstrapper.cs — 첫 씬에 빈 GameObject 하나 만들고 붙여두면 됨.
// UgsAuthBootstrapper.cs
using UnityEngine;
using System;
using System.Threading;
using System.Threading.Tasks;
using Unity.Services.Core;
using Unity.Services.Authentication;

public class UgsAuthBootstrapper : MonoBehaviour
{
    public static bool IsReady { get; private set; }
    public static bool IsSignedIn => AuthenticationService.Instance?.IsSignedIn ?? false;
    public float timeoutSeconds = 2.0f; // 이 시간 넘기면 그냥 게임 진행

    async void Awake()
    {
        DontDestroyOnLoad(gameObject);

        try
        {
            await UnityServices.InitializeAsync();

            using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(timeoutSeconds));

            if (!AuthenticationService.Instance.IsSignedIn)
            {
                var signInTask = AuthenticationService.Instance.SignInAnonymouslyAsync();
                await Task.WhenAny(signInTask, Task.Delay(-1, cts.Token)); // 타임아웃 도달 시 계속 진행
            }
        }
        catch (Exception)
        {
            // 네트워크/초기화 실패 → 조용히 무시하고 게임 계속
        }
        finally
        {
            IsReady = true; // 준비 여부 플래그만 올림 (게임은 어차피 계속 진행)
        }
    }

    // 선택: 나중에 설정 화면에서 호출
    public static async Task<bool> LinkWithGameCenterAsync()
    {
        try { await AuthenticationService.Instance.LinkWithAppleGameCenterAsync(); return true; }
        catch { return false; }
    }
    // 선택: Android/Google Play Games, 토큰은 PGS SDK로 받아와야 함
    public static async Task<bool> LinkWithGooglePlayGamesAsync(string serverAuthCodeOrIdToken)
    {
        try { await AuthenticationService.Instance.LinkWithGooglePlayGamesAsync(serverAuthCodeOrIdToken); return true; }
        catch { return false; }
    }
}

어디에도 ‘기다리기’ 강제 없음 → UGS가 안 붙어도 플레이 가능.

리더보드 제출 시에는 이렇게 “안전 래퍼”로 부르면 끝:

// 예) 점수 제출 (실패해도 게임 계속)
public static class SafeLeaderboard
{
    const string LB_ID = "daily_highscore";
    const string PENDING_KEY = "PENDING_SCORE";

    public static async System.Threading.Tasks.Task SubmitAsync(int score)
    {
        if (!UgsAuthBootstrapper.IsReady || !UgsAuthBootstrapper.IsSignedIn)
        {
            Buffer(score); return;
        }

        try
        {
            await Unity.Services.Leaderboards.LeaderboardsService.Instance.AddPlayerScoreAsync(LB_ID, score);
            PlayerPrefs.DeleteKey(PENDING_KEY);
        }
        catch { Buffer(score); }
    }

    static void Buffer(int score)
    {
        int pending = PlayerPrefs.GetInt(PENDING_KEY, 0);
        if (score > pending) { PlayerPrefs.SetInt(PENDING_KEY, score); PlayerPrefs.Save(); }
    }

    public static async System.Threading.Tasks.Task FlushAsync()
    {
        if (!UgsAuthBootstrapper.IsSignedIn) return;
        if (!PlayerPrefs.HasKey(PENDING_KEY)) return;
        await SubmitAsync(PlayerPrefs.GetInt(PENDING_KEY));
    }
}
파일 안내: 위 두 파일만 추가하면 되고, 기존 GameManager는 변경 없이 게임 종료 후 점수 제출 지점에서 await SafeLeaderboard.SubmitAsync(bestScore); 같은 한 줄만 덧붙이면 돼.

포인트 정리

  • “UGS=랜덤 ID”?
    → 실질적으로 서버가 발급한 플레이어 계정(Player ID)을 디바이스에서 조용히 사용하는 구조. 유저 경험상 “로그인” 느낌이 없을 뿐, 인증은 맞음.
  • 강제 로그인과의 관계?
    로그인 완료를 기다리느라 플레이를 막지 않으면 강제 로그인으로 보지 않음. 즉, 비동기·무시·재시도로 설계하면 심사에 유리.
  • 재설치/기기 변경?
    익명만이면 같은 사람이라도 새 Player ID가 될 수 있음 → 플랫폼 계정 링크(Game Center/PGS/Apple/Google)를 선택적으로 제공해 두면 안전.
  • 개인정보 표기?
    → 익명이어도 식별자(플레이어 ID/표시명) 수집·전송에 해당 → 스토어 폼(Play Data safety / App Privacy 라벨)에 정확히 표기.