Unity

퀘스트 로드에서 사용했던 FirstOrDefalut 함수

박도치 2024. 2. 27. 19:47

 

문제 상황

 

서론

게임 시간으로 일일 퀘스트인데, 일차가 지날때마다 랜덤으로 생성되는 퀘스트를 순차가 아닌 원하는대로 퀘스트를 받으면 우측 퀘스트 목록에 받은 퀘스트의 순서가 나타나게 된다.

 

이때 퀘스트의 아이디가 사냥퀘스트는 10001, 10002 / 채집퀘스트는 20001, 20002 었는데 10001, 20002, 10002 를 수락하고 게임을 저장했다고 가정해 보자.

 

 

그렇다면 리스트에서 저장한 순서대로 10001, 20002, 10002 퀘스트를 리스트에 저장하고, 이를 다시 로드했을 때 어떤식으로 로드해야 하는지에 대한 고민이었다.

 

필자가 사용한 로직에서는 수락된 퀘스트 뿐만아니라, 수락되지 않고 일일마다 생성되는 퀘스트도 있어야 하기 때문에 먼저 일일생성퀘스트를 로드해주었고, 퀘스트들은 Quest.cs를 상속받은 사냥퀘스트와 채집퀘스트 둘로 나눠서 각각을 리스트로 담는 식으로 하였다.

 

반을 나눠서 위는 사냥퀘스트, 아래는 채집퀘스트로 하기 위해 for문으로 로드한 퀘스트를 반으로 나누어 먼저 사냥퀘스트를 다 써놓고 아래 채집퀘스트를 쓰게하도록 로직을 짰다.

 

    for (int i = 0; i < _questKey.Count; i++)
    {
        if (i < _halfLength)
        {
            EnemyDailyQuest enemyDailyQuest = new EnemyDailyQuest(QuestSO, _questKey[i]);
            enemyDailyQuest.TargetID = activeQuestKey[i];
            enemyDailyQuest.TargetQuantity = activeQuestValue[i];
            enemyDailyQuest.EnemyTotalQuestReward = loadEnemyQuestRewardDic[_questKey[i]];
            ActiveDailyQuests.Add(enemyDailyQuest);
        }
        else
        {
            NatureDailyQuest natureDailyQuest = new NatureDailyQuest(QuestSO, _questKey[i]);
            natureDailyQuest.TargetID = activeQuestKey[i];
            natureDailyQuest.TargetQuantity = activeQuestValue[i];
            natureDailyQuest.NatureToalQuestReward = loadNautreQuestRewardDic[_questKey[i]];
            ActiveDailyQuests.Add(natureDailyQuest);
        }
    }

 

 

본론

 

그리고 여기서 Active된 퀘스트들 중, 수락된 퀘스트와 아이디 값을 비교하여 같으면 수락 퀘스트에 넣어주는 식으로 하였는데 문제는 ActiveDailayQuests의 리스트가 순차적으로 되어있기때문에 이전에 로드했던 10001, 20002, 10002 퀘스트로 불러오는것이 아닌, 10001, 10002, 20002 이렇게 순차적으로 불러오는 것이었다.

 

 

시도 및 해결

 

그래서 do While문과 LinQ의 List.FirstOrDefault를 이용해 이를 해결했다. 이 구문에 대해서는 아래에 다시 설명하고 결론만 말하자면 do While 문으로 위에서 수락한 3개의 퀘스트의 개수만큼 안에서 반복하여 Active된 전체 퀘스트 리스트와 수락된 퀘스트를 로드한 퀘스트 리스트의 ID를 비교하여 일치하면 Accept에 이를 담아서 로드때 덮어주는 방식으로 하였다.

 

if (_loadAcceptQuest.Count > 0)
{
    int index = 0;
    do
    {
        int questNumber = _loadAcceptQuest[index];
        Quest quest = ActiveDailyQuests.FirstOrDefault(q => q.QuestNumber == questNumber);
        if (quest != null)
        {
            AcceptQuest(quest);
            //_loadAcceptQuest.Remove(questNumber);
        }
        index++;
    } while (index < _loadAcceptQuest.Count);
}

 

_loadAcceptQuest 에는 이전에 담아뒀던 10001, 20002, 10002 퀘스트가 담겨있고, ActiveDaily퀘스트에서 퀘스트의 조건에 부합하는 값을 가져와서 AcceptQuest에 담아주는 식으로 한것이다.

 

위와 같이 하니 순차적으로가 아닌 필자가 원하는 방식의 List에 담은 순서 그대로 가져올 수 있게 되었다.

 

알게된 것( List.FirstOrDefalut() )

 

Linq 구문 중 하나로 특정 조건을 만족하는 첫번째 함수를 가져오는 기능을 해준다. 

 

예를 들어 20001, 20002 의 퀘스트 두개를 받고 저장했는데, 로드할 때 Active된 퀘스트에는 10001, 10002, 20001, 20002 가 들어있을 것이다. 그렇다면 위 코드를 사용하게 되면 Active된 퀘스트 중 조건에 부합하는 것(Acitve된 퀘스트 아이디와 로드한 퀘스트의 아이디가 같다면) 의 퀘스트를 가져와라 이기 때문에 10001과 10002는 거르고 20001을 제일 먼저 가져오게 되는 것이다.

 

만일 아무 조건이 없다면 그대로 가장 앞에있는 수를 가져오게 되며, 조건이 만족하지 않는다면 Defalut인 0을 가져오게 된다.

 

주의할 점은 조건에 부합하지 않는 Default와 아예 데이터 집합이 null로 할당되어있는 것을 같다고 생각하면 안된다. 조건에 부합하지 않으면 Default값을 반환하지만, 데이터가 null이라면 ArgumentNullException 예외가 발생할 것이다.

 

 

번외로 List.First() 함수도 위와 같은 방식으로 작동하지만, 둘의 차이는 First의 경우 Defalut값을 반환하지 않기 때문에 따로 예외처리를 해줘야 한다.

 

Default는 특정 조건 요소를 만족하지 않는 경우 0을 반환했지만, First를 사용한다면 InvalidOperationException 예외가 발생하게 된다.