본문 바로가기
다이어리/내일배움 개발일지

게임개발캠프 - 팀과제(C) 3일차

by E.Clone 2024. 1. 27.

과정명 : 내일배움캠프 Unity 게임개발 3기

전체진행도 : 25일차

부분진행도 : Chapter3.2 - 3일차

작성일자 : 2024.01.26(금)

개발일지 목록 : 클릭


1. 진행중인 과정에 대해

팀 과제 '탕후루 만들기' -> (개명)'탕후루를 부탁해!' 의 대부분 기능을 구현하였다. 사운드쪽만 월요일까지 작업하는 것으로 하고, 나머지 시간은 QA에 할애 할 것으로 보인다.

팀 과제에 할애하느라 알고리즘 풀이를 거의 일주일만에 했는데 간단한 것도 생각이 안 나 찾아보며 진행했다.

2. 오늘 학습에 대해

오늘의 팀 프로젝트 커밋

한 게 정말 많지만, 기록하기에 할애할 여유가 없어 매일 아쉽다.

지금까지 진행하며 가장 아쉬운 점은 GameManager.cs에 너무 많은 량의 로직 코드를 작성했다는 것이다.

보기에도 지저분하고, GameManager에 대한 의존도가 심각하게 높다.

게임 전체를 총괄하는 역할은 맞지만, 세세한 공정까지 모두 직접 하는 애매한 포지션이 되어버렸다.

이러한 생각을 하며 대화형 인공지능에게 코드리뷰를 맡겨 본 결과는 아래와 같다.


유니티에서 GameManager와 같은 중앙 관리 클래스를 사용하는 것은 일반적인 패턴입니다. 이는 게임의 전반적인 상태 관리, 싱글톤 구현, 씬 전환, 점수 관리 등을 위해 사용될 수 있습니다. 하지만, 제공하신 코드를 보면 몇 가지 개선할 수 있는 부분이 보입니다:

  1. 책임 분리(Separation of Concerns): 현재 GameManager 클래스에는 여러 책임이 혼재되어 있습니다. 예를 들어, UI 관리, 게임 상태 관리, 씬 로딩, 점수 계산 및 고점수 관리, 사운드 관리 등이 모두 한 클래스 내에 구현되어 있습니다. 이러한 다양한 책임들을 별도의 클래스로 분리하여 각각의 관심사를 더 명확히 구분하는 것이 좋습니다. 예를 들어, UI 관련 로직은 UIManager, 점수 및 고점수 관리는 ScoreManager, 사운드 관리는 AudioManager와 같이 별도의 클래스로 분리할 수 있습니다.
  2. 긴 메소드 및 클래스: GameManager 클래스와 그 내부의 메소드들이 매우 길고 복잡합니다. 이는 코드를 읽고 이해하는 데 더 많은 시간이 소요되며, 유지 보수가 어려워질 수 있습니다. 각 메소드가 하나의 명확한 기능만 수행하도록 리팩토링하고, 필요하다면 복잡한 기능을 여러 개의 보조 메소드로 분리하는 것이 좋습니다.
  3. 정적 접근과 싱글톤: GameManager.Instance를 통해 전역적으로 접근 가능한 싱글톤 패턴을 사용하고 있습니다. 이는 편리할 수 있지만, 오버유즈는 전역 상태에 대한 의존성을 높이고 테스트와 유지보수를 어렵게 만듭니다. 싱글톤 사용을 최소화하고, 가능한 컴포넌트 간 의존성을 명시적으로 만드는 것이 좋습니다.
  4. 하드코딩된 값과 마법의 숫자: 코드 내에서 직접적으로 하드코딩된 값(예: "HighScore", "_MainScene" 등)과 마법의 숫자(예: 0.1f, 3 등)를 사용하고 있습니다. 이러한 값들은 상수로 정의하거나 구성 파일에서 불러오는 방식으로 관리하는 것이 좋습니다. 이렇게 하면 나중에 값을 변경할 때 한 곳에서만 수정하면 되므로 유지 보수가 용이해집니다.
  5. 리소스 관리: Resources.Load를 사용하여 리소스를 불러오는 방식은 유연하지만, 사용하지 않는 리소스가 메모리에 남아있을 수 있으므로 효율적인 리소스 관리를 위해 주의가 필요합니다. 가능하다면, 유니티의 Addressable Asset System 같은 더 효율적인 리소스 관리 시스템을 고려하는 것이 좋습니다.
  6. 게임 상태 관리: 게임 상태를 enum과 switch 문으로 관리하는 것은 간단한 프로젝트에는 적합할 수 있지만, 상태가 복잡해질수록 관리가 어려워집니다. 상태 패턴(State Pattern)을 적용하여 각 상태를 별도의 클래스로 구현하고, 이를 통해 상태 전환 로직을 더 명확하고 유지보수하기 쉽게 만들 수 있습니다.

코드를 개선하려면, 위의 지점들을 고려하여 리팩토링을 진행하는 것이 좋습니다. 이는 코드의 가독성, 확장성, 유지 보수성을 향상시키고, 장기적으로 프로젝트의 품질을 높이는 데 도움이 됩니다.


모든 씬에서 독립적인 클래스 상태를 가지는 싱글톤에 대해 배우며 적극적으로 활용하자고 생각했지만, 실제 프로젝트를 진행 해 보면 싱글톤이 마냥 좋은 것만은 아니었다. 특히 씬이 갱신 될 때마다 초기화를 해줘야 하는 번거로움을 싱글톤 스크립트에서 해결하려고 하면 코드가 무척 길어지고, 씬이 로드 되는 도중에 실행되지 않도록 별도의 작업도 하나하나 해 주어야 하는데 개발 과정에서 많은 버그를 일으키고 시간이 소비된다.

프로젝트의 전체 스크립트 파일

위와 같은 문제점들은 다른 스크립트에 책임분리를 함으로써 해결되는 부분이기 때문에, 스크립트 파일이 많아지는 것에 거부감을 갖지 않고 활용하면 좋겠다는 생각을 하였다.

내가 맡은 파트에서 GameManager.csUIManager.cs 에서 대부분을 해결했는데, DisplayMainPanel.cs 스크립트를 하나 추가하여 사용한 것 만으로도 숨통이 트이는 듯한 기분을 느꼈다.

알고리즘

오늘은 가볍게 한 문제만 풀이하였다. 별도의 메모를 해 가며 풀이하는 건 너무 많은 에너지가 소모되어, 코드를 작성하며 생각하고 참고한 내용들을 주석에 적는 방식을 채택하였다.

코딩테스트 연습 > 2022 KAKAO TECH INTERNSHIP > 성격 유형 검사하기

using System;
using System.Linq;
using System.Collections.Generic;

public class Solution {
    public string solution(string[] survey, int[] choices) {
        string answer = "";

        // 일주일 쉬어서 감 다 죽음

        // 찾아보기-1. choices의 각 원소를 3 빼서 새로운 배열에 담는 법(Linq)
        // int[] newChoices = choices.Select(choice => choice - 4).ToArray();
            // choices.Select(choice => choice - 4)를 사용했을 때의 결과 자료형은 IEnumerable<int>
            // LINQ의 Select 메서드는 컬렉션의 각 요소에 대해 지정된 변환 함수를 적용하고
            // 변환된 요소들을 포함하는 새로운 IEnumerable<T> 시퀀스를 반환
            // 변환 함수 choice => choice - 4는 정수를 반환하기 때문에, 결과 시퀀스의 타입은 IEnumerable<int>

        // 생각해보니, 위 방식은 설문결과의 분산(?)을 알 수 없게 만드는 요인이 되기 떄문에 사용하지 않도록 했다.

        // 찾아보기-2. 사전형식 자료형 초기화
        Dictionary<char, int> scores = new Dictionary<char, int>{
            {'R',0},{'T',0},{'C',0},{'F',0},
            {'J',0},{'M',0},{'A',0},{'N',0},
        };
            // var 사용과 Dictionary<char, int> 사용 중 자유롭게 선택. 장단점은 특별한 건 없고 상황이나 취향에 맞게.

        // 각 문항별 점수 집계
        int threshold = 4;
        for(int i = 0; i < choices.Length; i++){
            int selectedNum = choices[i];
            if(selectedNum<threshold)
                scores[survey[i][0]] += (threshold - selectedNum);
            if(selectedNum>threshold)
                scores[survey[i][1]] += (selectedNum - threshold);
        }

        // 성격유형 진단
        if(scores['R']>=scores['T'])    answer += 'R'.ToString();
        else                            answer += 'T'.ToString();
        if(scores['C']>=scores['F'])    answer += 'C'.ToString();
        else                            answer += 'F'.ToString();
        if(scores['J']>=scores['M'])    answer += 'J'.ToString();
        else                            answer += 'M'.ToString();
        if(scores['A']>=scores['N'])    answer += 'A'.ToString();
        else                            answer += 'N'.ToString();

        return answer;
    }
}
  • 코드를 간결하게 하는 Linq, 사전형의 사용을 다시 찾아보았다.
  • 탭을 활용하여 반복되는 코드를 깔끔하게 정리했다.

탭을 활용하는 건 처음 시도 한 방법인데, 이전 팀원 중 한명의 코드 작성 방식이 떠오른 겸 적용 해 보았는데 깔끔하게 작성된 것을 보고 흡족했다.

3. 과제에 대해

  • 팀 과제 지속 리팩토링

4. 참고자료

  • 없음 / ChatGPT-GPT4 는 잊어버렸던 내용이나 코드리뷰 등 적재적소에 활용중
반응형