3 minute read

개인 과제에 객체 지향적 코드 적용

오늘은 개인 과제를 하는데 객체 지향적으로 확장성이 용이하게 짜보았다. 급하게 하루 만에 만들었지만 그래도 나름 머리를 많이 써서 최종 프로젝트에도 적용할 수 있게 기반을 짜보았다.

1. Character 코드 설계

우선 Character 코드를 만들어서 모든 종류의 유닛의 기반을 만들었다. 여기에 CharacterStatCharacterAction으로 기능을 나눴다.

  • CharacterStat: 체력, 공격력, 이동속도 등의 캐릭터의 속성들을 관리.
    • 체력, 공격력, 이동속도 등의 속성도 따로 클래스를 만들고, 체력과 같이 채워지는 형식은 Max와 current 값이 있는 int값 클래스.
    • 이동과 공격력 같이 얼마든지 변화 가능한 속성은 기본값, 추가값이 있는 float값 클래스로 따로 만듦.
    • 속성의 get, set을 적극 활용하여 UI 업데이트도 용이하게 함.
  • CharacterAction: 이동, 공격, 공격 받음 등의 행동을 관리.
    • 이동, 공격, 피해받기를 인터페이스로 기능을 나누어 추가.
    • 이동할 수 없는 캐릭터(건물 등), 피해를 받을 수 없는 물체, 공격할 수 없는 건물 등을 고려하여 기능을 분할.

2. Enemy와 Player 클래스 설계

  • Enemy 클래스: Character 클래스 상속.
    • 추가로 죽으면 주인공에게 줄 돈을 가지고 있음.
  • Player 클래스: Character 클래스 상속.
    • 경험치와 공격 시 애니메이션 변환 기능 추가.
    • 애니메이션과 상태 변화를 만들 때 FSM 사용.

3. FSM(State Machine) 적용

  • StateMachine 클래스: 상태를 변환해주는 역할.
  • State 클래스: Idle, Moving, Attacking 3가지 클래스를 BaseState 기반으로 만듦.
    • 각각 State 시작 시, 실행 중, 종료 시 함수를 만들어 캐릭터가 자동으로 시작, 공격, 이동, 상태를 전환하며 행동하게 함.
    • 새로운 State를 추가할 때도 편리하게 기반 코드가 완성됨.

4. Item 클래스 설계 고민

  • 체력 회복, 공격력 증가 등의 단순한 기능을 인터페이스로 나누기에는 비효율적.
  • 속성, 값 클래스를 아이템이 가지고 있다가 각 속성에 따라 값을 array로 저장하고 이를 받아오는 방식으로 간략화할 필요성.

5. 결론

이번 과제에서는 이전과 달리 확장성을 고려하여 코드를 작성함. 속도가 느리더라도 최대한 세분화하여 작성했으며, 결과적으로 기능 추가 시 시간이 절약됨. 이를 기반으로 최종 프로젝트에도 적용할 수 있을 것으로 기대됨.


이번 작업을 통해 객체 지향적 설계와 확장성을 고려한 코딩의 중요성을 깨닫게 되었다. 특히 코딩하면서 고민을 많이 했는데, 그 중 캐릭터 스탯은 처음에는 Stat로 진행을 했다가, 생각해보니 스탯이 체력과 같은 형태와 공격력과 같은 형태 2가지로 나눠진다는 것을 알고 간결하고 효율적인 코드인 지금의 2가지 스탯 코드로 변하게 되었다. 앞으로도 이를 바탕으로 더 나은 코드를 작성할 수 있도록 노력할 것이다.

동적 생성

장점

  • 씬 변화가 적어 충돌이 적다: 동적 생성 방식은 씬 간의 변화를 최소화하여 충돌 가능성을 줄입니다.
  • Prefab 작업의 분업이 편리하다: Prefab을 통해 작업을 분리하면 팀원이 각자 맡은 부분을 독립적으로 작업할 수 있습니다.
  • 빌드 용량 감소: 리소스나 UI를 Addressable로 관리하여 빌드 용량을 줄일 수 있습니다.
  • 와이어 프레임 변형의 간단한 적용: 동적 생성 방식은 와이어 프레임상의 변형을 쉽게 적용할 수 있습니다.

단점

  • 실행 전 화면 확인 불가: 동적 생성된 오브젝트는 실행하기 전까지 화면에서 확인할 수 없습니다.
  • 프리팹 배치 변경의 불편함: 배치를 변경하려면 해당 프리팹을 찾아가서 수정해야 합니다.

목표

  • 에셋 번들 및 어드레서블 사용: 에셋 번들과 어드레서블을 사용하여 빌드 용량 감소와 깃 충돌 최소화를 목표로 합니다.

구현 방법

  1. 프리팹 위치 지정
    • 프리팹을 특정 구간에 생성하도록 범위를 지정합니다.
    • 예시 코드:
      GameObject newObject = Instantiate(prefab, new Vector3(Random.Range(minX, maxX), 0, Random.Range(minZ, maxZ)), Quaternion.identity);
      
  2. Additive 모드로 씬 로드
    • SceneManager를 사용하여 LoadScene 할 때 모드를 Additive로 설정하여 현재 씬에 추가적인 요소를 얹습니다.
    • 예시 코드:
      SceneManager.LoadScene("AdditionalScene", LoadSceneMode.Additive);
      
  3. 오브젝트 배치 검사
    • 오브젝트를 Instantiate한 후, Ray로 위치 검사를 수행하여 배치가 가능한지 확인하고, 가능하지 않다면 가능한 자리를 찾아 배치합니다.
    • 예시 코드:
      Ray ray = new Ray(position, Vector3.down);
      RaycastHit hit;
      if (Physics.Raycast(ray, out hit))
      {
          // 위치 조정 로직
      }
      
  4. 맵 로드와 물체 생성
    • 맵은 모든 물체가 존재할 기반이 되므로, 이를 다른 씬에 생성하고 Additive로 불러와 물체를 생성하면 충돌 가능성을 줄일 수 있습니다.
    • LoadSceneAsync를 사용하여 맵 로드가 완료된 후 다음 작업을 진행합니다.
    • 예시 코드:
      StartCoroutine(LoadYourAsyncScene());
      
      IEnumerator LoadYourAsyncScene()
      {
          AsyncOperation asyncLoad = SceneManager.LoadSceneAsync("MapScene", LoadSceneMode.Additive);
      
          while (!asyncLoad.isDone)
          {
              yield return null;
          }
      
          // 다음 작업
      }
      
  5. 데이터 소스
    • 동적 생성 시 필요한 데이터는 CSV 파일이나 서버에서 받아옵니다.

위의 방식으로 동적 생성을 구현하면 씬 충돌을 최소화하고, Prefab 작업의 효율성을 높일 수 있습니다. 또한, 에셋 번들 및 어드레서블을 활용하여 빌드 용량을 줄이는 것도 가능합니다. 이를 통해 더욱 유연하고 확장성 있는 프로젝트를 구성할 수 있습니다.