PlayFab 사용법

playfab을 유니티에서 코드레벨로 사용할 때 필요한 내용들을 정리한다..!

앞 포스팅에선 PlayFab의 기본 구조에 대해 설명했지만 백엔드를 정말 제대로 사용하고 싶다면 사실 파이어베이스같은 데이터베이스 시스템을 이용하는게 가장 바람직하다.

하지만 전혀 관심이 없고 개발에 필요한 데이터만 사용하고 싶다면..Playfab으로 로그인이나 아이템, 그룹등등 다양한 미리 구현된 시스템을 사용할 수 있다.

유니티에 설치하는 방법은 다른 블로그에도 많이 나와 있으니 실질적인 사용법 그리고 느낀점을 위주로 정리한다.

request..?

가장 쉽게 사용할 수 있는 레벨이 PlayFabClientAPI로 클라이언트 단위에서의 API를 사용해 백엔드에 접근하는 것이다.

사실 게임에 제대로 적용하려면 Sever, 클라우드 스크립트, 그룹, 채팅 등등을 적용해 좀 더 유연한 구조를 가져갈 수 있지만 앞서 말한 것 처럼 다양한 API를 먹어보고 싶은 마음이 강해서 구조정도만 이해하고 넘어간다.

PlayFabClientAPI클래스는 약 2300줄이 넘어가는데.. 이걸 다 공부하고, 외우고 사용할 필요가 전혀 없다.

이런 라이브러리를 공부할 땐 사용되는 기본 구조만 알면 모두 동일하게 만들어 놓기 때문에 너무 많아서 언제 다 공부하지..? 라는 생각을 할 필요가 없다!

public class MainMenuUIController : MonoBehaviour
{
    private UINavigation _uiNavigation;
    private MainMenuUIView _mainMenuView;
    
    private void Start()
    {
        _uiNavigation = new UINavigation();
        _mainMenuView = _uiNavigation.UIViewPush("MainMenuView") as MainMenuUIView;
        
        _mainMenuView.LoginButton.onClick.AddListener(() =>
        {
            PlayerLogin(_mainMenuView.EmailInput.text, _mainMenuView.PasswordInput.text);
        });
        
        _mainMenuView.RegisterButton.onClick.AddListener(() =>
        {
            PlayerRegister(_mainMenuView.EmailInput.text, _mainMenuView.PasswordInput.text);
        });
    }

    private void PlayerRegister(string email, string password)
    {
        var request = new RegisterPlayFabUserRequest
        {
            Email = email,
            Password = password,
            RequireBothUsernameAndEmail = false
        };
        
        PlayFabClientAPI.RegisterPlayFabUser(request,
            result =>
            {
                var popup = _uiNavigation.PopupPush("InputPopup") as InputPopup;
                popup.SetInputPopup("이름을 입력하세요", "이름 등록", PlayerNameRegister);
            },
            error =>
            {
                var popup = _uiNavigation.PopupPush("DefalutPopup") as DefalutPopup;
                popup.SetText("회원가입 실패!");
            });
    }
}

작성한 코드의 일부분인데 PlayerRegister함수를 보면 RegisterPlayFabUserRequest라는 클래스를 생성하고, 그 안에 필요한 정보를 넣어준다.

여기서 중요한 개념이 Request와 Result, Error이다.

Request는 요청을 보내는 것이고, Result는 요청에 대한 결과를 받는 것이다. Error는 요청에 대한 결과가 실패했을 때 받는 것이다.

정리하자면 Request는 작업할 내용의 정보를 담고, Result는 작업한 결과를 담고, Error는 작업한 결과가 실패했을 때의 정보를 담는다.

위 코드를 보기 좋게 정한다면 아래 와 같다.

PlayFabClientAPI.RegisterPlayFabUser(작업 내용, 성공시 작업 결과, 실패시 작업 결과);

이 때 작업 실패와 성공은 사용 메서드마다 약간의 차이가 있지만 Action구조로 이루어져 있다.

image

해당 인자값이 각각 Result와 Error이다.

여기서 Result는 PlayFabResultCommon부터 상속관계로 이루어져 있다. 즉, 바인딩이 가능하다

지금은 액션이라 람다식으로 대충 만들어 놨지만 사실 저런 부분도 함수로 처리하는게 조금 더 좋아보인다.

    public void GetPlayerItems(Action<GetUserInventoryResult> onGetPlayerItem)
    {
        PlayFabClientAPI.GetUserInventory(new GetUserInventoryRequest(), result =>
        {
            Debug.Log("GetUserInventory Success");
            onGetPlayerItem?.Invoke(result);
        }, error =>
        {
            Debug.Log(error.ErrorMessage);
            onGetPlayerItem?.Invoke(null);
        });
    }

가장 흔히 보이는 구조인데 그렇게 가독성이 좋다고는 할 수 없는 구조이다.

메서드의 클래스인자부분에 new를 통해 클래스를 넣어서 사용하는데 오히려 가독성 자체가 떨어지는 느낌이라 따로 빼서 사용한다.

두 번째로 playfab은 asny, await를 지원하지 않는다. 그래서 콜백으로 처리해야 하는데 이 부분도 조금 불편하다.(매우..)

따라서 Action을 사용해서 해당 result나 error를 받아서 처리하는 방식으로 사용한다.

만약 GetPlayerItems이 반환값이 있는 함수라면 내부 PlayFabClientAPI.GetUserInventory에서 값을 저장하고 return한다고 해도 콜백 방식이기 때문에 원하는 반환값을 얻을 수 없다.

ex) var result = GetPlayerItems(); 반환값이 item라면 result는 null이다.

따라서 Action을 걸어두어 실행하고자 하는 함수 자체를 넘겨주어 Invoke하는 방식으로 동작하는 것으로 구현했다.

사실 asny, await을 넣어서 만들어보고 싶었지만 실패.. 더 좋은 구조가 있을 것 같다는 생각이 들지만 너무 시간을 투자하는 느낌이라 패스..

이런 구조를 미리 알았다면 좋았을 텐데.. 했지만 해당 API에 대한 정보도 적고.. 글 내용도 마이너해서 찾기가 힘들었다.

CutSene gif

클라단위에 데이터를 저장하지 않고 서버로만 통신하는 모습..

빠르긴 하지만 어느정도 딜레이가 있기 때문에 실제 게임에선 이런 방식으로 하지 않을 것 같다는 생각..

그리고 이런 방식으로 하면 클라단위에 데이터를 저장하지 않기 때문에 서버가 다운되면 게임이 불가능해진다.

아직 서버를 공부하지 않아서 실제 게임서버의 구조를 제대로 잡지 못했지만 종강 이후에 스터디를 만들어서 공부해보고 싶다..! 게임 백엔드 그리고 서버(데이터베이스..)

댓글남기기