눈팅하는 게임개발자 블로그

개발 기록 - Title Scene 본문

Project/Tactical Architect Tower Defense

개발 기록 - Title Scene

Palamore 2020. 9. 13. 15:22

Title(Lobby) Scene

게임을 처음 시작하면 나타나는 본 게임 전의 게임 타이틀 씬이다.

관련 코드는 GoogleServiceManger, UpgradeShopManager

github.com/Palamore/TATD_Codes/blob/master/GoogleServiceManager.cs

github.com/Palamore/TATD_Codes/blob/master/UpgradeShopManager.cs

 

해당 씬의 플로우는 대충 이렇다.

구글, 게스트 로그인 모두 Game Start, Tutorial, Gallery, Credit 4개의 버튼이 활성화되며

구글 로그인의 경우 추가로 Upgrade, Achievement, Ranking 3개의 버튼이 추가로 활성화된다.

대충 간단한 것들로는

Tutorial – 튜토리얼.

Start – 게임 시작.

Gallery – 게임 오브젝트 정보 열람.

Credit – 개발자 크레딧.

Achieve – 구글플레이 업적.

Ranking – 구글플레이 랭킹.

 

이 외로는 Upgrade.

이 씬에서 가장 귀찮았던 Upgrade를 다뤄보자.

Upgrade에서는 타워들의 공격력, 공격속도를 강화할 수 있으며.

각 판을 플레이 할 때마다 Point를 보상으로 타워를 강화할 수 있게 하고

이 정보를 구글 계정마다 따로 보관하여 게임에 적용되도록 하고 싶었다.

 

Point를 주고 타워를 강화하는 것까지는 클라이언트 안에서 쉽게 해결할 수 있지만

이 정보를 각 계정마다 따로 보관하는 방법을 몰랐는데,

이것 저것 방법을 찾아보다가

감사하게도 Save, Load 모두 가능한 코드를 전체 다 블로그에 올려주신 분이 있어서 가져다가 적용해보았다.

ijemin.com/blog/%EC%9C%A0%EB%8B%88%ED%8B%B0-%EA%B2%8C%EC%9E%84-%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%A5%BC-%EA%B5%AC%EA%B8%80-%ED%94%8C%EB%A0%88%EC%9D%B4-%ED%81%B4%EB%9D%BC%EC%9A%B0%EB%93%9C-%EC%97%90-%EC%A0%80%EC%9E%A5/

 

저장할 정보들의 규칙을 대강 정하고, 코드를 조금 고쳤다.

#region Save
    public void SaveToCloud(string _data, int _dataType)
    {
        StartCoroutine(Save(_data, _dataType));
    }

    IEnumerator Save(string _data, int _dataType)
    {
        while (CheckLogin() == false)
        {
            Login();
            yield return new WaitForSeconds(2.0f);
        }
        bIsSaving = true;
        string id = Social.localUser.id;
        string fileName = string.Empty;
        if (_dataType == 0)
            fileName = string.Format("LevelPoint", id);
        else if (_dataType == 1)
            fileName = string.Format("DmgPoint1", id);
        else if (_dataType == 2)
            fileName = string.Format("DmgPoint2", id);
        else if (_dataType == 3)
            fileName = string.Format("AtkSpdPoint1", id);
        else if (_dataType == 4)
            fileName = string.Format("AtkSpdPoint2", id);

        saveData = _data;

        OpenSavedGame(fileName, true);
    }
    void OpenSavedGame(string _fileName, bool _saved)
    {
        ISavedGameClient savedClient = PlayGamesPlatform.Instance.SavedGame;
        if (_saved == true) // Save 목적
        {
            savedClient.OpenWithAutomaticConflictResolution(_fileName, DataSource.ReadCacheOrNetwork, ConflictResolutionStrategy.UseLongestPlaytime, OnSavedGameOpenedToSave);
        }
        else                // Load 목적
        {
            savedClient.OpenWithAutomaticConflictResolution(_fileName, DataSource.ReadCacheOrNetwork, ConflictResolutionStrategy.UseLongestPlaytime, OnSavedGameOpenedToRead);
        }
    }

    void OnSavedGameOpenedToSave(SavedGameRequestStatus _status, ISavedGameMetadata _data)
    {
        if (_status == SavedGameRequestStatus.Success)
        {
            byte[] b = Encoding.UTF8.GetBytes(string.Format(saveData));
            SaveGame(_data, b, DateTime.Now.TimeOfDay);
        }
        else
        {
            MakeDialogue("Game Save Failed");
        }
    }

    void SaveGame(ISavedGameMetadata _data, byte[] _byte, TimeSpan _playTime)
    {
        ISavedGameClient savedClient = PlayGamesPlatform.Instance.SavedGame;
        SavedGameMetadataUpdate.Builder builder = new SavedGameMetadataUpdate.Builder();

        builder = builder.WithUpdatedPlayedTime(_playTime).WithUpdatedDescription("Saved at " + DateTime.Now);

        SavedGameMetadataUpdate updatedData = builder.Build();
        savedClient.CommitUpdate(_data, updatedData, _byte, OnSavedGameWritten);
    }

    void OnSavedGameWritten(SavedGameRequestStatus _status, ISavedGameMetadata _data)
    {
        bIsSaving = false;
        if (_status == SavedGameRequestStatus.Success)
        {

        }
        else
        {
            MakeDialogue("Save Fail");
        }
    }



    #endregion
    

게임 세이브 부분 코드.

받아온 데이터를 클래스 내부 변수(saveData)에 저장하고 인덱스(_dataType)에 따라서 저장할 fileName을 결정해주고

fileName에 해당하는 파일을 ISavedGameMetadata _data에 저장하는 듯 하다.

_data를 받아오는데 성공했으면 saveData를 byte[] 형식의 데이터로 바꿔주고 이를 클라우드 파일에 업데이트 해준다.

로드는 반대로. 클라우드에서 데이터를 가져와서 해당 byte[]형식의 데이터를 string으로 바꿔준다.

 

가져온 데이터의 정보는

해당 계정이 소지한 Point와 강화된 모든 터렛(8종류)의 공격력, 공격속도 값인데.

강화 수치는 최대 수치가 50%이므로 2자리의 정수값으로 표현할 수 있다.(00~50)

즉 타워 1개당 4자리의 string 값이 필요하다.

그래서 총 string은 Point string + 32자리의 string.

32자리의 string을 4개로 나눠서 4개의 8자리 string 타입 데이터가 된다.

예를 들면 데이터가 '12345678'이라면

12 = 1번째 터렛의 공격력 값.

34 = 2번째 터렛의 공격력 값.

56 = 3번째 터렛의 공격력 값.

78 = 4번째 터렛의 공격력 값이 된다.

    void DataProcessing(int _data, int _dataType)
    {
        int temp;
        int temp2;
        temp = _data / 1000000; // 12
        temp2 = _data % 1000000; // 345678
        switch (_dataType)
        {
            case 1:
                dmgRateData[0] = temp;
                break;
            case 2:
                dmgRateData[4] = temp;
                break;
            case 3:
                atkspdRateData[0] = temp;
                break;
            case 4:
                atkspdRateData[4] = temp;
                break;
        }
        temp = temp2 / 10000; // 34
        temp2 = temp2 % 10000; // 5678
        switch (_dataType)
        {
            case 1:
                dmgRateData[1] = temp;
                break;
            case 2:
                dmgRateData[5] = temp;
                break;
            case 3:
                atkspdRateData[1] = temp;
                break;
            case 4:
                atkspdRateData[5] = temp;
                break;
        }
        temp = temp2 / 100; // 56
        temp2 = temp2 % 100; // 78
        switch (_dataType)
        {
            case 1:
                dmgRateData[2] = temp;
                break;
            case 2:
                dmgRateData[6] = temp;
                break;
            case 3:
                atkspdRateData[2] = temp;
                break;
            case 4:
                atkspdRateData[6] = temp;
                break;
        }
        temp = temp2; // 78
        switch (_dataType)
        {
            case 1:
                dmgRateData[3] = temp;
                break;
            case 2:
                dmgRateData[7] = temp;
                break;
            case 3:
                atkspdRateData[3] = temp;
                break;
            case 4:
                atkspdRateData[7] = temp;
                
                BuffConfirm();
                Destroy(CurLoadCover);
                GSM.MakeDialogue("데이터 로드완료.");
                bIsLoading = false;
                SoundManager.Instance().Acknowledge.Play();
                break;
        }
    }

가져온 데이터를 가공하는 함수 DataProcessing.

 

기본적인 타워 디펜스 게임은 각 게임마다 독립적이지만 (이번 게임이 다음 게임에 영향을 주지 않지만)

이 Upgrade 시스템을 넣어서 각 게임이 어느정도 차이가 있고(강화수치 차이)

유저가 유의미한 보상을 얻을 수 있게끔 하고 싶었다.

 

다만 문제가 되는 부분이 게임 앱에서 계정을 통해 클라우드로 액세스 하는 데 시간이 오래 걸리는지

아니면 그냥 내가 코드를 이상하게 짰는지 로딩 하는 데 시간이 더럽게 많이 걸린다. (로드하는데만 5초)

한번 고쳐보자.

 

클라우드로 액세스 하는 데 시간이 오래걸린다고 치고

위의 코드는 로드 한번에 클라우드 액세스를 총 5번을 하게 된다. 이것이 오래 걸리는 원인일 수 있다.

지금은 각각의 fileName에 따로 접근하기 위해 클라우드에 액세스하는 모든 과정을 5번 반복하고 있는데.

이걸 1번으로 줄여보자.

방법은 2가지 정도 생각난다.

1. 클라우드 파일에 액세스 하기 직전의 Instance 상태를 따로 클래스 내부에 저장해서 각각의 fileName에 접근하거나.

2. 5개의 fileName을 하나의 string 변수로 합쳐서 한번만 액세스하거나.

 

첫 번째 방법의 Instance는 아마         

ISavedGameClient savedClient = PlayGamesPlatform.Instance.SavedGame;

이놈(savedClient)일 텐데 API를 뜯어보니 인스턴스를 받아오자마자 fileName 데이터를 인자로 받는다.

fileName을 인자로 받아서 ISavedGameMetaData으로 변환하는 부분의 로직 때문에

이놈이 처음 Instance를 받아온 상태를 클래스 내부 변수로 따로 저장한다고 해도

fileName이 다르다면 결국 모든 과정을 다시 반복할 수 밖에 없는 것.

filename 변수가 사용되는 부분

첫 번째 방법은 기각.

 

두 번째 방법.

5개를 무식하게 합쳐보자.

0번째~31번째 string 값까지는 강화정보, 나머지 값을 Point 정보로 하나의 string 값을 만들어보자.

    void DataProcessing(string _data)
    {
        int k = 0;
        string tmp;
        for (int i = 0; i < 8; i++)
        {
            tmp = "" + _data[k] + _data[k + 1];
            DmgRateInt[i] = int.Parse(tmp);
            k += 2;
        }
        for (int i = 0; i < 8; i++)
        {
            tmp = "" + _data[k] + _data[k + 1];
            AtkSpdRateInt[i] = int.Parse(tmp);
            k += 2;
        }
        tmp = _data.Remove(0, 32);

        LevelPoint = int.Parse(tmp);

        BuffConfirm();
        Destroy(Load_Cover_Now);
        GSM.MakeDialogue("데이터 로드완료.");
        bIsLoading = false;
        SoundManager.Instance().Acknowledge.Play();
    }

새로 바꾼 DataProcessing 함수.

이게 훨씬 더 짧고 깔끔해보이네. 처음부터 이렇게 할걸.

 

테스트 빌드에서는 잘 돌아간다.

로딩 속도도 5초에서 1초로 빨라졌다.

대충 클라우드 파일 하나에 액세스 하는 데 1초 정도 소모되는 모양이다.

'Project > Tactical Architect Tower Defense' 카테고리의 다른 글

개발 기록 - 상점 시스템  (0) 2020.09.18
개발 기록 - Enemy  (0) 2020.09.18
개발 기록 - Turret  (0) 2020.09.14
개발 기록 - Tutorial Scene  (0) 2020.09.13
개인정보 처리 방침  (0) 2019.11.30