[패스트캠퍼스 수강 후기] 올인원 패키지 : 유니티 포트폴리오 완성 100% 환급 챌린지 27회차 미션 시작합니다.

03. Firebase 네트워크 - 05-2, 05-3 챕터를 진행합니다.


지난 시간에 이어 사용자 데이터/저장 불러오기 준비단계입니다.
Newtonsoft Json Library에 대한 소개로 시작합니다.




JsonUtility
  - Unity에 포함된 Json Library를 사용하는 것이 일반적입니다.
  - 최소한의 기능만 제공하고 있지만, Unity만을 위한 Vector serialize와 MonoBehaviour serialize를 제공합니다.
  - MonoBehaviour의 Deserialize는 별도의 제공함수인 FromJsonOverwrite()를 사용해야 합니다.

Newtonsoft Json
  - JsonUtility에서 제공되지 않는 추가적인 Json 기능들을 사용할 수 있는 외부 Library
  - BSON 지원 : Binary JSON
  - MonoBehaviour와 Vector에 대한 지원 클래스가 없으므로 별도로 설정하여 사용 필요.
  
추가적으로 JsonUtility는 Property에 대한 Serialize를 지원하지 않는데, Newtonsoft JSON은 Property Serialize가 동작하게 되므로, Property Json 변환을 원하지 않는다면 설정되지 않도록 Attribute를 설정하는 기능이 필요합니다.




Newtonsoft Json 다운로드 및 설정 방법을 정리하였습니다.

1) 다운로드
2) 압축해제하여 폴더 확인
3) Newtonsoft.Json.dll을 드래그하여 Unity Assets 폴더에 드랍하여 설정하면 됩니다.

여기서는 Newtonsoft Json을 사용하여 진행합니다.



Json 함수에 대한 설명입니다.




Json을 사용하는 테스트를 위해 UI를 미리 구현하였고, 나머지는 기존과 동일하며 [Save User Data] [Load User Data]가 추가되었습니다.
이를 클릭하면 User Data를 불러오고 저장하는 기능을 하게 됩니다.

실제 게임에서는 로그인을 하면 바로 Load User Data를 호출하여 사용자 데이터를 가져오면 되고,
사용자 데이터가 변경될 때마다 Save User Data를 호출하여 Firebase database에 데이터를 저장하는 루틴으로 구성하면 되겠습니다.



스크립트 기능 구현을 시작합니다.


public class PlayerDataHandler : MonoBehaviour
{
    private DatabaseReference databaseRef;
    private string UserDataPath => "users"; // 루트 폴더
    private string StatsDataPath => "stats"; // /users/uid/stats
    private string EquipmentDataPath => "equipment";
    private string InventoryDataPath => "inventory";
    
    public StatsObject playerStats;
    public InventoryObject playerEquipment;
    public InventoryObject playerInventory;
    
    void Start() {
        databaseRef = FirebaseDatabase.DefaultInstance.RootReference;
    }
    
    public void OnClickedSave() {
        var userId = FirebaseAuthManager.Instance.UserId;
        if (userId == string.Empty) return;
        
        string statsJson = playerStats.ToJson();
        databaseRef.Child(UserDataPath).Child(userId).Child(StatsDataPath).SetRawJsonValueAsync(statsJson).ContinueWith(task => {
            if (task.IsCanceled) { Debug.LogError("Save user data was canceled"); return; }
            if (task.IsFaulted) { Debug.LogError("Save user data encountered an error: " + task.Exception); return; }
            Debug.LogFormat("Save user data in successfully: {0} {1}", userId, statsJson);
        }); // users/uid/stats
       
        string equipmentJson = playerEquipment.ToJson();
        databaseRef.Child(UserDataPath).Child(userId).Child(EquipmentDataPath).SetRawJsonValueAsync(equipmentJson).ContinueWith(task => {
            if (task.IsCanceled) { Debug.LogError("Save equipment data was canceled"); return; }
            if (task.IsFaulted) { Debug.LogError("Save equipment data encountered an error: " + task.Exception); return; }
            Debug.LogFormat("Save equipment data in successfully: {0} {1}", userId, equipmentJson);
        });
       
        string inventoryJson = playerInventory.ToJson();
        databaseRef.Child(UserDataPath).Child(userId).Child(InventoryDataPath).SetRawJsonValueAsync(inventoryJson).ContinueWith(task => {
            if (task.IsCanceled) { Debug.LogError("Save inventory data was canceled"); return; }
            if (task.IsFaulted) { Debug.LogError("Save inventory data encountered an error: " + task.Exception); return; }
            Debug.LogFormat("Save inventory data in successfully: {0} {1}", userId, inventoryJson);
        });
    }
    
    public void OnClickedLoad() {
        var userId = FirebaseAuthManager.Instance.UserId;
        if (userId == string.Empty) return;
        
        databaseRef.Child(UserDataPath).Child(userId).Child(StatsDataPath).GetValueAsync().ContinueWith(task => {
            if (task.IsCanceled) { Debug.LogError("Load user data was canceled"); return; }
            if (task.IsFaulted) { Debug.LogError("Load user data encountered an error: " + task.Exception); return; }
            
            DataSnapshot snapshot = task.Result;
            // snapshot.Child(userId).Child(StatsDataPath).Child("level").GetValue .... // Json 미사용시 처리.. ㅋㅋ
            playerStats.FromJson(snapshot.GetRawJsonValue());
            Debug.LogFormat("Load user data in successfully: {0} {1}", userId, snapshot.GetRawJsonValue());
        });
        
        databaseRef.Child(UserDataPath).Child(userId).Child(EquipmentDataPath).GetValueAsync().ContinueWith(task => {
            if (task.IsCanceled) { Debug.LogError("Load equipment data was canceled"); return; }
            if (task.IsFaulted) { Debug.LogError("Load equipment data encountered an error: " + task.Exception); return; }
            
            DataSnapshot snapshot = task.Result;
            playerEquipment.FromJson(snapshot.GetRawJsonValue());
            Debug.LogFormat("Load equipment data in successfully: {0} {1}", userId, snapshot.GetRawJsonValue());
        });
        
        databaseRef.Child(UserDataPath).Child(userId).Child(InventoryDataPath).GetValueAsync().ContinueWith(task => {
            if (task.IsCanceled) { Debug.LogError("Load inventory data was canceled"); return; }
            if (task.IsFaulted) { Debug.LogError("Load inventory data encountered an error: " + task.Exception); return; }
            
            DataSnapshot snapshot = task.Result;
            playerInventory.FromJson(snapshot.GetRawJsonValue());
            Debug.LogFormat("Load inventory data in successfully: {0} {1}", userId, snapshot.GetRawJsonValue());
        });
    }
}



// 기존 게임 개발시 클래스로서 추가된 코드만 작성하였습니다.
public class StatsObject : ScriptableObject
{
    // 기존코드들..
    
    public string ToJson() {
        return JsonConvert.SerializeObject(levelData, Formatting.Indented);
    }
    
    public void FromJson(string jsonString) {
        PlayerLevelData newLevelData = JsonConvert.DeserializeObject<PlayerLevelData>(jsonString);
        Level = newLevelData.level;
        Exp = newLevelData.exp;
    }
}

public class InventoryObject : ScriptableObject
{
    // 기존코드들..
    
    public string ToJson() {
        return JsonConvert.SerializeObject(container, Formatting.Indented);
    }
    
    public void FromJson(string jsonString) {
        Inventory newContainer = JsonConvert.DeserializeObject<Inventory>(jsonString);
        Debug.Log("from json: " + newContainer.slots.Length);
        
        for (int i = 0; i < Slots.Length; i++) {
            Slots[i].UpdateSlot(newContainer.slots[i].item, newContainer.slots[i].amount);
        }
    }
}


이렇든 Json을 사용하면 다층화된 데이터 구조를 가지는 구조 데이터를 간결하게 구현할 수가 있습니다.





이제 Unity 플레이를 하여 로그인하고,
[Save User Data]를 진행하여 다음과 같이 Firebase Database에 저장된 데이터를 확인할 수 있습니다.




마찬가지로 Database 내에서 데이터 정보를 강제로 수정을 하고, 유니티에서 [Load User Data]를 진행하면 변경된 정보를 가져와서 업데이트되는 것을 확인할 수 있습니다.


복잡하고 어려운 듯한 강의였지만,

결론적으로 회원 가입 및 사용자 데이터 관리 등의 복잡한 기능을 해주는 Firebase를 잘 활용하자! 이고..
Json이라는 엄청난 Library를 이용하여 복잡하고 다계층화된 데이터의 접근 처리를 쉽게할 수 있다! 라고 결론지으면 되겠습니다.

오래전에 DataManager라는 동일한 기능을 하는 클래스를 만들어서 썼었는데.. Json을 썼으면 되는 거였네요.. ^^;;; 무지로 인한 고생인거였겠죠.. ㅎㅎ




<위의 코드들은 제가 보면서 주요한 함수, 코드를 확인하기 위해 타이핑한 용도로, 전체 소스코드가 아님에 주의해 주세요. 전체 코드는 교육 수강을 하면 완벽하게 받으실 수가 있답니다 ^^>

패스트캠퍼스 - 올인원 패키지 : 유니티 포트폴리오 완성 bit.ly/2R561g0

 

유니티 게임 포트폴리오 완성 올인원 패키지 Online. | 패스트캠퍼스

게임 콘텐츠 프로그래머로 취업하고 싶다면, 포트폴리오 완성은 필수! '디아블로'와 '배틀그라운드' 게임을 따라 만들어 보며, 프로그래머 면접에 나오는 핵심 개념까지 모두 잡아 보세요!

www.fastcampus.co.kr

 

+ Recent posts