2024년 8월 12일 월요일 개발일지 / 몬스터 만들기 + 데이터 저장 및 불러오기

2024. 8. 12. 22:20나의 개발자 기록/본 캠프 개발 일지

2024년 8월 12일 월요일


What I did yesterday : 로그인 창 업데이트 + 몬스터 애니메이션 만들기

 

 

해야 할 작업들이 많이 남았기 때문에 주말에도 작업을 진행했습니다. 일단 기존의 로그인 창(왼쪽 이미지)에서 오른쪽 이미지처럼 인풋 필드버튼 등등 에셋에 있던 디자인을 사용하여 꾸미고, 비밀번호를 볼 수 있는 버튼과 아이디와 비밀번호에 대한 조건 등등 좀 더 업그레이드를 했습니다.

 

 

그다음에는 전에 계획했던 몬스터들의 애니메이션을 직접 만들었습니다. 비슷하게 생긴 애들은 같은 공격 모션을 통해서 시간을 단축하고, 죽는 애니메이션의 경우 캐릭터들처럼 통일하는 방향으로 만들어서 최대한 효율적으로 사용해 주말 안에 애니메이션 작업을 끝냈습니다.

 


What I did today : 유니티에 몬스터 적용해서 만들기 + 데이터 저장 및 불러오기 개선 및 적용하기

 

 

어제 작업했던 애니메이션을 바탕으로 캐릭터를 만들었던 것 처럼 몬스터들의 SO 데이터를 만들고, 구글 시트에 데이터를 적어 놓고, 씬에서 해당 데이터를 불러오고, 기존의 몬스터 프리팹을 복사하여 만들려고 하는 몬스터들의 정보들을 넣고, 스프라이트 시트로 애니메이션을 만들어서 적용해서 총 16개의 몬스터를 추가했습니다.

 

 

그 다음 작업으로는 다른 팀원 분의 작업이 끝나는 대로 저장 및 불러오기를 진행해야 하는 것도 있고, 지금까지 작업하면서 string으로 작업했던 것들을 전에 배웠던 팁 중 하나인 Struct를 이용해 보았습니다. Sound도 등록이나 사용할 때 string을 사용하고, 저장 및 불러오기도 key에 해당하는 타입이 string이기 때문에 미리 만들어 두었습니다.

 

 

팀원분의 작업이 끝나고 나서 플레이어에 필요한 데이터를 정리해서 비동기 저장과 불러오기를 만들었습니다. 그런데 생각했던 대로 저장과 불러오기가 작동하지 않는 문제가 있었습니다. 그래서 알아보았더니 JsonUtility 클래스가 제한적인 기능을 제공하기 때문에, 복잡한 데이터 구조에서는 제대로 작동하지 않는 문제가 있었고, 해결 방법으로 Newtonsoft.Json을 사용해 보았습니다.

 

using Newtonsoft.Json;

public async Task SaveData<T>(string key, T value)
{
    try
    {
        string jsonData = JsonConvert.SerializeObject(value); // 데이터를 JSON 문자열로 변환
        var data = new Dictionary<string, object> { { key, jsonData } };
        await CloudSaveService.Instance.Data.ForceSaveAsync(data);
    }
    catch (Exception e)
    {
        DisplayErrorMessage($"Failed to save data: {e.Message}");
    }
}

public async Task<T> LoadData<T>(string key)
{
    try
    {
        var keys = new HashSet<string> { key };
        var data = await CloudSaveService.Instance.Data.LoadAsync(keys);
        if (data.ContainsKey(key))
        {
            string jsonData = data[key].ToString();
            return JsonConvert.DeserializeObject<T>(jsonData); // JSON 문자열을 T 타입 객체로 변환
        }
        else
        {
            DisplayErrorMessage("Key not found");
            return default;
        }
    }
    catch (Exception e)
    {
        DisplayErrorMessage($"Failed to load data: {e.Message}");
        return default;
    }
}

 

그리고 List<string>을 받아오기 위해 로그인 매니저에 있던 저장과 불러오기 로직을 제네릭제이슨을 활용하여 업그레이드를 시켰고, 테스트를 해본 결과 저장은 잘 되지만, 불러오기에서 "Unexpected character encountered while parsing value: U. Path '', line 1, position 1." 오류가 뜨는 문제가 발생했습니다.

 

https://stackoverflow.com/questions/23259173/unexpected-character-encountered-while-parsing-value

 

Unexpected character encountered while parsing value

Currently, I have some issues. I'm using C# with Json.NET. The issue is that I always get: {"Unexpected character encountered while parsing value: e. Path '', line 0, position 0."} So t...

stackoverflow.com

 

 

인터넷에서 해당 오류에 대해서 검색해서 찾아봤는데, 보통 UTF-8 BOM의 문제라던가, API 오류라던가, 경로가 잘못됐다거나 다양한 의견이 있고, 이 의견으로 해결하신 분들이 있었는데... 저는 해결이 안 됐습니다. 그래서 거의 퇴실 시간 넘겨가면서 3시간가량 이것저것 찾아본 결과...

 

https://discussions.unity.com/t/error-when-try-get-player-data-from-cloud-save-unity-services-cloudsave-internal-http-jsonobject/310416

string jsonData = data[key].Value.GetAsString();

저장 및 불러오기가 잘돼서 골드가 잘뜸!

 

위 사이트의 질문에 있는 답변을 통해서 해결을 할 수 있었습니다. Cloud SaveJSON으로 저장하고 불러오면 "Unity.Services.CloudSave.Models.Item"로 반환하는데, 이때 반환하는 Value의 유형이 IDerializable라서 접근을 하려면 기존 코드의 'ToString();'이 아니라 위의 코드 서식에 있는 'Value.GetAsString();'으로 가져와야 제대로 역직렬화를 할 수 있었고, 이를 통해서 불러오기가 정상적으로 작동되었습니다.


면접 질문 대비 : 가비지 컬렉터의 세대 개념에 대해 설명해 주세요.

 

가비지 컬렉터는 메모리 관리 효율성을 높이기 위해 객체를 세대(Generation)로 나누어 관리합니다.

  • 0세대(Generation 0) : 최근에 할당된 객체가 포함됩니다. 가비지 컬렉션이 가장 자주 발생하는 세대입니다.
  • 1세대(Generation 1) : 0세대에서 살아남은 객체가 포함됩니다. 중간 단계의 수명 주기를 가진 객체가 포함됩니다.
  • 2세대(Generation 2) : 1세대에서 살아남은 객체가 포함됩니다. 가장 오래된 객체가 포함되며, 가장 적게 가비지 컬렉션이 발생합니다.

가비지 컬렉터는 0세대부터 시작하여 필요한 경우 높은 세대로 이동하면서 메모리를 회수합니다.