Unity 18일차 - Unity 강의

한예준·2025년 4월 30일

오늘은 하루종일 강의를 수강하며 TopDownShooting 게임을 만들어보았다. 이전 프로젝트에서 다뤘던 내용도 있었지만, 이전에 사용해보지 못하거나 깊이 다루지 못했던 기능들을 배워볼 수 있었다. 오늘은 이러한 기능들을 정리해보겠다.


TopDownShooting

원거리 공격 구현

이미 '고양이 밥주기 게임'을 따라 만들어보고, 이를 토대로 투사체 발사 시스템을 따로 구현해보았는데, 이 때 배웠던 내용들을 잘 활용할 수 있었다.

  • 무기 위치와 발사 위치의 분리
    • 플레이어 캐릭터가 들고 있는 무기의 위치와 실제 투사체가 발사되는 위치를 다르게 설정했다.
    • 이전에 투사체 시스템을 만들 때에도 플레이어와 발사한 투사체가 충돌하는 경우가 생겼는데, 이를 통해 해결할 수 있었다.
    • RangeWeaponHandler 스크립트에서 projectileSpawnPosition이라는 Transform 변수를 따로 두어 발사 위치를 지정하는 방식으로 쉽게 설계할 수 있었다.
  • 가상 메서드를 이용한 설계
    • 이번 강의에서는 다양한 종류의 무기를 생성해야 했는데, 이를 효율적으로 관리하기 위해 가상 메서드를 활용했다.
      • WeaponHandler 라는 모든 무기가 공통적으로 가져야 할 속성과 기능을 정의하는 기반 클래스를 생성했다.
      • RangeWeaponHandler와 같은 파생 클래스가 이를 상속하였다. 각 무기 타입과 특성에 맞는 공격 방식들을 override 키워드를 사용하여 구현한다.
    • 이렇게 구조를 짠다면 나중에 새로운 종류의 무기를 추가하고 싶으면 단순히 부모 클래스를 상속받으면 쉽게 추가가 가능하다. 이를 통해 확장성과 유지보수성을 늘릴 수 있겠다.
  • 피격 판정 및 효과 구현
    • 투사체가 적에게 명중했을 때 단순히 데미지만 주고 사라지는 것이 아니라 피격 효과를 발생시크는 기능도 구현했다.
    • Particle System을 통해 파티클의 구체적인 효과를 설정해주었다.
    • CreateImpactParticlesAtPostion 메서드를 통해 충돌 지점에 파티클 효과를 생성한다.
    • 이와 같은 방식으로 플레이어 캐릭터가 움직일 때도 파티클 효과가 생성되도록 해주었다.

웨이브 관리

  • 코루틴 활용

    • IEnumeratoryield return을 통해 특정 시간만큼 실행을 잠시 멈췄다가 이어서 실행할 수 있다.
    • 기존에는 Update 내에서 시간을 계산하여 이를 수행하여야 했는데, 이러한 계산 없이 시간 기반 로직을 작성할 수 있었다.
    • 이를 통해 웨이브 시작 전/후 딜레이, 적 스폰 사이의 딜레이 등을 구현하였다.
    //코루틴 예시
      private Coroutine waveRoutine; // 현재 실행 중인 웨이브 코루틴 저장
      [SerializeField] private float timeBetweenSpawns = 0.2f; // 적 생성 간격
      [SerializeField] private float timeBetweenWaves = 1f; // 웨이브 사이 간격
      .
      .
      .
          public void StartWave(int waveCount)
      {
          waveRoutine = StartCoroutine(SpawnWave(waveCount)); // 코루틴 시작
      }
      private IEnumerator SpawnWave(int waveCount)
      {
          enemySpawnComplite = false;
          yield return new WaitForSeconds(timeBetweenWaves); // 웨이브 시작 전 대기
    
          for (int i = 0; i < waveCount; i++)
          {
              yield return new WaitForSeconds(timeBetweenSpawns); // 적 생성 전 잠시 대기
              SpawnRandomEnemy(); // 랜덤 적 생성 로직 호출
          }
    
          enemySpawnComplite = true; // 현재 웨이브 종료
      }
      .
      .
      .

프로젝트 구조

이전에는 모든 스크립트를 Scripts폴더 안에 다 넣어두거나 해봤자 Manager폴더 정도만 생성하는 정도였다. 하지만 본 강의를 따라하며 유니티 에디터 내의 프로젝트 파일 구조를 어떻게 해야할지를 많이 배웠다.

  • Scripts 폴더 구조
    • Entity 폴더
      • 플레이어 PlayerController, 적 EnemyController, 그 외 개체들에 대한 기본적인 로직을 모았다. 또한 이러한 개체들이 공유하는 기반 클래스도 BaseController를 통해 정리했다.
    • Manager 폴더
      • 게임매니저 GameManager, 적들의 생성과 웨이브 관리 EnemyManager, 투사체 관리ProjectileManager 등 게임 시스템을 관리하는 역할을 하는 스크립트를 정리했다.
    • UI 폴더
      • 게임의 UI와 관련된 스크립트를 별도로 관리하였다.
    • Weapon 폴더
      • 무기의 공통 클래스 Weaponhandler 및 무기 종류별 상속 클래스RangeWeaponhandler 등, 투사체 ProjectileController 등 무기 시스템과 관련된 스크립트들을 한 곳에 모아 관리하였다.
  • 이러한 구조의 장점
    • 가독성 증가
      • 내가 찾는 코드가 어디있는지 훨씬 빠르게 찾아낼 수 있었다.
    • 유지보수 용이성 증가
      • 특정 기능에서 발생한 오류를 해결하기 위해 다른 폴더가 아닌 해당 폴더를 우선 찾으며 문제의 원인을 찾고 수정하기가 용이해졌다.
      • 다른 폴더를 건드릴 위험도 낮아졌다.
    • 협업 효율 증대
      • 저번에 C# 팀 프로젝트를 하며 느낀 점인데, 기능을 토대로 작업을 분리하면 깃허브에서 관리하기가 훨씬 용이했다.
      • 이러한 구조도 마찬가지로 기능별로 작업을 분리하였는데, 이를 협업에서 이용한다면 관리가 훨씬 편리해질 것이라 예상한다.

회고 및 느낀점

  • 오늘 하루 동안 진행한 강의를 통해 실제로 게임을 구성하는 다양한 요소들을 더 깊이 있게 이해할 수 있었다.
  • 특히 이전에 내가 직접 구현해보았던 투사체 시스템이나 적 스폰 시스템을 다시 접하면서 이를 발전시킨 설계 방법과 효율적인 구조를 배울 수 있었다.
  • 앞으로 개인 프로젝트나 팀 프로젝트에서 이를 적용하기 위해 오늘 배웠던 내용을 꾸준히 익혀나가겠다.

0개의 댓글