Unity 자습서/GamingServices

Gamingservices - ABTestSample #5 재화 표시

개양반 2023. 2. 26.

1. 오늘 알아볼 내용

유저가 보유한 재화를 GamingServices에서 로드한 뒤에 UI에 표시하는 내용에 대해 다룹니다. 

  • 유저가 보유한 재화를 Economy 에서 로드하는 방법
  • 로드 중 발생한 에러를 처리하는 방법
  • Economy 에서 데이터를 로드한 후 UI에 표시하는 방법

 

 

2. 유저가 보유한 재화 데이터 로드

2-1 선행학습

2-1-1 유저의 재화 데이터 로드 요청

var options = new GetBalancesOptions { ItemsPerFetch = 100 };
return EconomyService.Instance.PlayerBalances.GetBalancesAsync(options);
GetBalancesOptions 한번에 로드할 목록의 수이다. 위 예제는 데이터를 로드할 때 100개씩 로드한다.
기본 값은 20이며, 최대 100까지 입력할 수 있다.
PlayerBalances 플레이어의 통화 잔액을 가져오고 업데이트하기 위한 모든 메서드가 포함되어 있다
PlayerBalances.GetBalancesAsync(options) 유저가 보유한 재화들을 로드한다. 

 

await getBalancesResult.GetNextAsync(options.ItemsPerFetch); 로 데이터를 추가로 더 로드할 수 있다.

GetBalancesOptions options = new GetBalancesOptions
{
    ItemsPerFetch = 5
};

GetBalancesResult getBalancesResult = await EconomyService.Instance.PlayerBalances.GetBalancesAsync(options);
List<PlayerBalance> firstFiveBalances = getBalancesResult.Balances;


if (getBalancesResult.HasNext) {
    // 추가로 로드하는 코드이다. GetNextAsync
    getBalancesResult = await getBalancesResult.GetNextAsync(options.ItemsPerFetch);
    List<PlayerBalance> nextFiveBalances = getBalancesResult.Balances;
}

 

2-2 스크립트 작업

2-2-1 EconomyManager.cs

Economy에게 유저가 보유한 재화를 로드하라고 요청하는 코드를 작성하겠습니다. EconomyManager 클래스에 아래의 전역변수와 함수를 추가합니다.

        // EconomyManager.cs
       
        public async Task RefreshCurrencyBalances()
        {
            GetBalancesResult balanceResult = null;
            balanceResult = await GetEconomyBalances();

            if (this == null) return;
        }
    
        static Task<GetBalancesResult> GetEconomyBalances()
        {
            var options = new GetBalancesOptions { ItemsPerFetch = 100 };
            return EconomyService.Instance.PlayerBalances.GetBalancesAsync(options);
        }

 

2-2-2 ABTestLevelDifficultySceneManager.cs

유저가 로그인을 완료하면 GamingServices에게 데이터 로드를 요청하는 코드를 작성한다.

        // ABTestLevelDifficultySceneManager.cs
        
        async Task LoadDataFromServices()
        {
            // -- 경제 구성요소 새로고침 관련 코드 생략

            await LoadServicesData();
            if (this == null) return;

        }
        
        // GamingServices로 부터 데이터 로드를 요청한다.
        async Task LoadServicesData()
        {
            EconomyManager.instance.RefreshCurrencyBalances();
        }

 

 

3. 로드한 데이터를 UI에 표시하기

재화의 데이터를 표시하는 UI는 CurrencyItemView 클래스에서 관리하고 있다.

각각의 CurrencyItemView 는 CurrencyHudView 클래스에서 관리하고 있다.

 

3-1 스크립트 작성

3-1-1 EconomyManager.cs

EconomyManager 클래스에서 유저의 재화 데이터를 로드하면 CurrencyHudView 클래스에게 로드한 데이터를 UI에 표시하라고 요청하는 코드를 작성한다.

        // EconomyManager.cs
        
        // 전역 변수 추가
        public CurrencyHudView currencyHudView;
        
        public async Task RefreshCurrencyBalances()
        {
            // -- 재화 데이터 로드 관련 코드 생략

            // 데이터 로드가 완료되면 데이터를 UI에 표시한다.
            currencyHudView.SetBalances(balanceResult);
        }

 

3-2 테스트

유니티로 돌아가 재생 버튼을 누르면 화면 상단에 Coin 개수가 표시되는 걸 확인할 수 있다.

 

 

4. 데이터 로드 에러 처리

서버로 부터 데이터를 로드하므로 클라이언트 외부의 문제로 에러가 발생할 수 있다. 예를 들면 네트워크 환경이 좋지 않거나 데이터 로드 횟수 초과 등을 꼽을 수 있다. 이러한 에러를 처리하는 방법에 대해 알아보자.

참고로 에러 관련 코드는 깊게 분석하지 않았다. 프로젝트 만들때 그냥 복붙할 생각을 하고 있다. 유저가 어떤 이유로 에러가 생겼는지 알 수 있게 디버그로그 처리된 부분을 팝업창으로 변경할 생각하고 있다.

 

4-1 스크립트 작성

4-1-1 CancellationTokenHelper.cs

Assets\Common\Scripts 폴더에 CancellationTokenHelper.cs를 만들고 아래의 코드를 작성한다.

using System;
using System.Threading;

#if UNITY_EDITOR
using UnityEditor;
#endif

namespace Unity.Services.Samples
{
    // See Microsoft's CancellationTokenSource docs
    // (https://learn.microsoft.com/en-us/dotnet/api/system.threading.cancellationtokensource?view=net-6.0)
    // for more information about its purpose and functioning.
    public class CancellationTokenHelper : IDisposable
    {
        CancellationTokenSource m_CancellationTokenSource;
        bool m_Disposed;

        public CancellationToken cancellationToken => m_CancellationTokenSource.Token;

        public CancellationTokenHelper()
        {
            m_CancellationTokenSource = new CancellationTokenSource();
#if UNITY_EDITOR
            EditorApplication.playModeStateChanged += OnPlayModeStateChanged;
#endif
        }

#if UNITY_EDITOR
        void OnPlayModeStateChanged(PlayModeStateChange playModeStateChange)
        {
            if (playModeStateChange == PlayModeStateChange.ExitingPlayMode)
            {
                m_CancellationTokenSource?.Cancel();
            }
        }
#endif

        // IDisposable related implementation modeled after
        // example code at https://learn.microsoft.com/en-us/dotnet/api/system.idisposable?view=net-6.0
        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        void Dispose(bool triggeredByUserCode)
        {
            if (m_Disposed)
            {
                return;
            }

            // If triggeredByUserCode equals true, dispose both managed and unmanaged resources.
            if (triggeredByUserCode)
            {
                // Dispose managed resources.
                m_CancellationTokenSource.Dispose();
                m_CancellationTokenSource = null;
            }

#if UNITY_EDITOR
            // Clean up unmanaged resources.
            EditorApplication.playModeStateChanged -= OnPlayModeStateChanged;
#endif

            m_Disposed = true;
        }

        ~CancellationTokenHelper()
        {
            Dispose(false);
        }
    }
}

 

4-1-2 Utils.cs 

Assets\Common\Scripts 폴더에 Utils.cs를 만들고 아래의 코드를 작성한다.

using System;
using System.Collections;
using System.Collections.Generic;
using System.Threading.Tasks;
using UnityEngine;


namespace Unity.Services.Samples
{
    public static class Utils
    {
        public static async Task<T> RetryEconomyFunction<T>(Func<Task<T>> functionToRetry, int retryAfterSeconds)
        {
            if (retryAfterSeconds > 60)
            {
                Debug.Log($"Economy returned a rate limit exception with an extended Retry After time " +
                          $"of {retryAfterSeconds} seconds. Suggest manually retrying at a later time.");
                return default;
            }

            Debug.Log($"Economy returned a rate limit exception. Retrying after {retryAfterSeconds} seconds");

            try
            {
                // CancellationToken을 사용하면 지연 시간을 기다리는 동안 재생 모드를 종료하면
                // Task.Delay가 취소되도록 할 수 있습니다.
                // 그렇지 않으면 재생 모드 밖에서도 이 코드의 나머지 부분을 계속 실행하려고 시도할 것입니다.
                using (var cancellationTokenHelper = new CancellationTokenHelper())
                {
                    var cancellationToken = cancellationTokenHelper.cancellationToken;

                    await Task.Delay(retryAfterSeconds * 1000, cancellationToken);

                    // Call the function that we passed in to this method after the retry after time period has passed.
                    var result = await functionToRetry();

                    if (cancellationToken.IsCancellationRequested)
                    {
                        return default;
                    }

                    Debug.Log("Economy retry successfully completed");

                    return result;
                }
            }
            catch (OperationCanceledException)
            {
                return default;
            }
            catch (Exception e)
            {
                Debug.LogException(e);
            }

            return default;
        }
    }
}

 

4-1-3 EconomyManager.cs 수정

재화 데이터 로드를 요청하는 RefreshCurrencyBalances() 를 수정한다.

        // EconomyManager.cs
        
        public async Task RefreshCurrencyBalances()
        {
            GetBalancesResult balanceResult = null;

            try
            {
                balanceResult = await GetEconomyBalances();
            }
            catch (EconomyRateLimitedException e)
            {
                balanceResult = await Utils.RetryEconomyFunction(GetEconomyBalances, e.RetryAfter);
            }
            catch (Exception e)
            {
                // 아래의 코드를 팝업창에 띄워 유저가 어떤 에러가 발생했는지 알 수 있게 한다.
                // 팝업창에 RefreshCurrencyBalances()를 요청하는 버튼을 만들어두는 것도 좋다.
                Debug.Log("Problem getting Economy currency balances:");
                Debug.LogException(e);
            }

            if (this == null) return;

            // -- UI 표시 관련 코드 생략
        }

 

5. 로그아웃과 함께 유저 재화 UI 정보 갱신

로그아웃하면 현재 계정의 재화 금액이 UI에 표시되지 않드록 UI를 갱신해줘야 합니다.

5-1 스크립트 작업

5-1-1 EconomyManager.cs 수정

아래의 함수를 EconomyManager.cs 에 추가합니다.

        // EconomyManager.cs
        
        public void ClearCurrencyBalances()
        {
            currencyHudView.ClearBalances();
        }

 

5-1-2 ABTestLevelDifficultySceneManager.cs 수정

ABTestLevelDifficultySceneManager.SignOut 에서 로그아웃하면 위의 ClearCurrencyBalances를 호출하도록 함수를 수정합니다.

        // ABTestLevelDifficultySceneManager.cs
        
        void SignOut()
        {
            if (AuthenticationService.Instance.IsSignedIn)
            {               
                EconomyManager.instance.ClearCurrencyBalances();
                
                // -- 계정 로그아웃 코드 관련 생략
                // UpdateSceneViewAfterSignOut();
            }
        }

# 제가 깜빡하고 경제 데이터를 클리어한 뒤에 UI를 갱신하는 코드를 빼 먹었습니다. 관련 코드는 다음 게시글에 올리겠습니다. 

 

오늘은 여기까지!

제 블로그가 만족스럽다면 커피 한잔 값으로 후원을 해주실 수 있어요!

 

댓글

💲 추천 글