미사일 커맨더로 배우는 유니티 C# 프로그래밍 연습 – 내용 소개

유니티를 공부하고 계신가요? 만약 초보자 대상의 유니티 또는 C# 입문서를 다 읽으셨고, 다음 단계로 조금 더 능숙한 게임 프로그래밍을 하고 싶으시다면 동영상 예제가 포함된 전자책, <미사일 커맨더를 통해 배우는 유니티 C# 프로그래밍 연습>을 추천 드립니다.

 

책에서 다루는 내용들

미사일 커맨더(Missile Commander)는 우리에게 잘 알려진 고전 아케이드 게임으로서, 아주 간단한 게임 규칙을 가지고 있습니다. 이 책이 초급자 대상이었다면 아주 쉽고 간단한 방식으로 만들었겠지만, 그 다음 단계를 지향하는 분들을 대상으로 하고 있기 때문에 가급적이면 확장성과 유지 보수성을 염두에 두고 제작해 보았습니다. 예제 코드를 작성하는 동안 제가 주로 중점을 두었던 부분은 다음과 같습니다.

느슨한 커플링

게임을 개발하는 과정에서 우리는 어쩔 수 없이 원래의 기획을 변경해야 하는 경우를 마주치게 됩니다. 기획이 변경되면 이에 따라 새로운 코드를 추가하거나 기존 코드를 변경할 수밖에 없는데, 게임 개발의 속성상 이러한 상황은 피할 수가 없는 숙명과도 같습니다.  따라서 프로그래머는 자신이 지금 작성하는 코드에 대해 반드시 수정 요구가 들어올 것이라는 점을 염두에 두고, 미리 이에 대비한 구조 설계를 해 두어야 합니다. 이러한 대비책 중 하나는 느슨한 커플링(loose coupling – 느슨한 클래스간의 결합) 구조를 만드는 것입니다. 다시 말해서 각각의 클래스들이 서로에 대해 너무 의존하지 않도록 설계해야 합니다. 

클래스들이 너무 강하게 결합되어 있으면, 기획의 변경 등으로 인해 어떤 하나의 클래스를 변경했을 때 연쇄적으로 그 영향을 받는 클래스들이 생기게 됩니다. 심할 경우, 수정한 클래스와 직접 관련이 없어 보이는 클래스까지도 변경해야 하는 경우가 있습니다. 이런 일이 많아지면 코드의 유지 보수 기간과 비용이 급증하게 되어 전체적인 생산성이 크게 떨어질 수 밖에 없습니다. 여러분이 게임 프로그래밍을 할 때, 느슨한 커플링(결합)을 항상 염두에 두어야 하는 이유가 바로 여기에 있습니다. (클래스간의 상호 의존도가 높은 경우를 ‘강하게 커플링되었다’고 하고, 그 반대의 경우를 ‘느슨하게 커플링되었다’고 표현합니다.)

객체 지향적 프로그래밍

유니티의 차기 버전이 공개되면서, 유니티 C# 프로그래밍 방식이 기존의 객체 지향적 프로그래밍(Object-Oriented Programming)에서 데이터 지향적 프로그래밍(Data-Oriented Programming)으로 바뀌어 가는 추세입니다. (최근 코펜하겐에서 열린 Unite 2019 행사에서는 이 데이터 지향적 프로그래밍 방식인 DOTS 에 대한 강연이 주를 이루었습니다. 따라서 유니티를 공부하시는 분들은 이제 이 방법론에 대해서도 관심을 가지셔야 합니다. DOTS은 아직 완성 단계가 아니라 시중에 관련된 책은 찾아 보기 어려우며 유니티 홈페이지에서 영문 자료로 정보를 습득하실 수 있습니다.)

그럼에도 불구하고 객체 지향적 프로그래밍은 아직까지 게임 프로그래머라면 반드시 깊이 있게 이해해야 할 분야입니다. 따라서 예제 게임인 미사일 커맨더(Missile Commander)를 만드는 과정에서 저는 최대한 객체 지향의 원칙에 충실하려고 애를 썼습니다. 간단한 코드들이지만 캡슐화와 추상화, 다형성, 그리고 상속성이라는 ‘객체 지향 프로그래밍의 원리’들을 모두 사용해 보았습니다. 이 중에서 상속은 자칫하면 강한 커플링의 원인이 되기 때문에 상속을 부득이하게 사용할 경우 1단계 이상의 상속은 사용하지 않았으며, 캡슐화와 추상화, 다형성은 느슨한 커플링을 위해 매우 중요한 원칙이므로 최대한 이에 충실한 코딩을 하기 위해 노력하였습니다. 각각의 코드를 설명하는 가운데에서 이들 개념을 직접적으로 사용하지는 않았지만, 이 책의 예제를 통해 충분한 실습을 하신 뒤 여러분이 가지고 계신 C# 입문서의 OOP(객체 지향적 프로그래밍) 항목을 다시 읽어 보시면 어떤 부분에서 객체 지향적 방법을 사용했는지 쉽게 이해하실 수 있을 것입니다. 

이벤트 주도형 프로그래밍

느슨한 커플링을 위해 제가 많이 사용한 방법은 이벤트(Event)를 이용하는 것입니다. 클래스의 인스턴스들(예를 들어 총알, 빌딩, 폭발 효과 등)이 서로 커뮤니케이션할 수 있게 하는 방법은 다양하지만, 확장성을 고려할 때 가장 이상적인 방법은 각각의 인스턴스들이 서로의 존재를 모르는 가운데에서도 필요한 정보를 주고 받을 수 있도록 하는 것입니다. 이벤트(Event)를 사용하면 이런 식의 게임 프로그래밍이 가능합니다. 

이 책에서 제가 보여 드리는 예제를 따라 가시다 보면, 때로는 지나쳐 보일 정도로 이벤트 중심의 코딩을 하는 것을 경험하실 수가 있을 것입니다. 물론 실제 현업에서 코딩을 할 때 이렇게 모든 것을 이벤트 중심으로만 작성하지는 않지만, 이런 연습을 통해 이 방식의 장점과 단점을 파악할 기회를 가져 보시는 것은 매우 중요합니다. 특히 기획의 변경이 잦은 게임 개발 프로젝트를 할 경우, 이벤트 중심으로 코딩을 하게 되면 코드의 추가나 수정으로 인해 야기되는 ‘도미노 효과’를 큰 어려움 없이 피할 수가 있습니다.  

디펜던시 인젝션(Dependency Injection) 

흔히 ‘의존성 주입’이라고 번역되는 ‘디펜던시 인젝션’은 느슨한 커플링을 위해 많이 사용되는 방식입니다. 클래스 A가 어떤 일을 하기 위해 클래스 B의 인스턴스에 의존해야 하는 경우, B를 A의 디펜던시(의존성)이라고 합니다. 그리고 디펜던시 인젝션이란 클래스 A내부에서 클래스 B의 인스턴스를 직접 만들어 참조하는 방법 대신, 클래스 A 외부에서 만들어진 인스턴스를 (외부에서 내부로) 주입받아 참조하는 방식을 가리킵니다. 이에 대해서는 제가 나중에 예제를 통해 자세히 설명 드릴 예정이므로 여기에서는 생소한 용어 때문에 너무 고민하실 필요는 없습니다. 책을 읽어 나가시는 과정에서 이 개념에 자연스럽게 친숙해지실 것입니다. (참고로 ‘포켓몬 고’ 개발자들도 이 개념에 근거하여 게임을 개발했다고 유나이트 2016에서 발표한 적이 있습니다. 관심 있는 분들은 유튜브에서 해당 영상 https://youtu.be/8hru629dkRY 을 찾아 보시기 바랍니다.) 

싱글톤으로 대표되는 글로벌 참조의 최소화

초급자 대상으로 쓰여진 유니티 입문서나 기타 동영상 강좌들 중에는 싱글톤(Singleton) 패턴을 지나치게 선호하는 경우가 있습니다. 싱글톤을 일단 만들어 놓으면 사용하기가 매우 편합니다. 하지만 이 방식은 프로그래머들에게 금기라고 할 수 있는 글로벌 참조(언제 어디서나 바로 참조할 수 있는 클래스 인스턴스)를 기반으로 하고 있기 때문에 남용할 경우 위험합니다. 또한 이 방식은 클래스 들이 강하게 결합하는 구조를 만들게 하므로 아무 생각 없이 싱글톤을 남발하게 되면 나중에 그 대가를 톡톡히 치르게 되는 날이 오게 됩니다. 따라서 이 책에서는 싱글톤 사용을 최대한 자제하였고, 맨 마지막의 오디오 매니저(AudioManager)를 만들 때만 이 방식을 제한적으로 사용하였습니다.

오브젝트 풀링이 적용된 팩토리(Factory)

오브젝트 풀링(Object Pooling)은 클래스의 인스턴스들을 재활용해서 메모리를 절약하고, 동시에 가비지 컬렉션을 수행하는 횟수를 최소화해서 게임 도중에 퍼포먼스가 일시적으로 저하되지 않도록 하기 위한 방법입니다. 미사일 커맨더 게임 예제에서는 싱글톤 방식으로 글로벌하게 접근할 수 있는 오브젝트 풀링 대신, 디펜던시 인젝션(의존성 주입) 방식으로 사용할 수 있는 오브젝트 풀링 클래스를 구현하고 여기에 팩토리(Factory) 개념을 적용하여 범용적으로 사용할 수 있게 해 보았습니다.

컴포지션 루트(Composition Root) 개념의 사용

게임에 사용되는 각각의 클래스들(정확하게는 클래스의 인스턴스들)이 서로의 존재를 모르는 상태에서도 커뮤니케이션할 수 있게 하기 위해서는 게임의 시작 포인트(entry point)에 해당하는 클래스(여기에서는 GameManager) 한 곳에서 이들을 생성하고 서로 연동해 주는 작업을 전담하는 것이 좋습니다. 이런 방식을 컴포지션 루트(Composition Root)라고 합니다. 유니티에서는 컴포지션 루트의 역할을 하는 시작포인트가 엔진 차원에서 제공되고 있지는 않습니다. 따라서 원래는 이 방식을 ‘이론 그대로’ 사용할 수 없습니다. 하지만 그 원리를 부분적으로 나마 구현하는 방법이 없는 것은 아니며, 잘만 사용하면 상당히 편리합니다. 미사일 커맨더 게임 예제에서는 게임 매니저(GameManager) 클래스를 마치 컴포지션 루트처럼 사용하여, 중요한 클래스들이 서로의 존재를 모른 상태에서도 문제 없이 소통할 수 있게 만들어 보았습니다.

 

이상으로 간단하게 이 책에서 다루는 내용에 대해 설명해 보았습니다. 만약 실제로 게임 로직을 구현하는 가운데 이런 기법들을 어떻게 사용하는지 자세히 알고 싶으시다면, 다음 공식 페이지를 방문해서 책을 구매해 보시기 바랍니다. 이 전자책은 완전한 1인 독립 출판 형식으로 발간되었으므로 오직 아래의 공식 페이지에서만 구입하실 수 있습니다. 그럼 지금 바로 공식 페이지를 방문해 보세요!

공식 페이지에서 지금 구매하기