[C#] 무료 게임 개발 라이브러리(Monogame, 구: XNA)을 설치하는 방법을 설치하는 방법


Development note/C#  2021. 3. 2. 18:33

안녕하세요. 명월입니다.


이 글은 C# 무료 게임 개발 라이브러리(Monogame, 구: XNA)을 설치하는 방법에 대한 글입니다.


요즘 게임 프로그램 라이브러리 또는 엔진이라고 하면 보통 Unity와 언리얼 엔진 등을 사용해서 개발을 많이 합니다.

저는 사실 게임 개발을 주로 하는 개발자는 아닙니다만, 게임 개발이 어떤 형식으로 이루어지겠다 정도는 알고 있습니다. 그래서 Unity와 언리얼 엔진을 사용해 본 적은 없지만 구글링이나 Youtube등을 보면 개발이 굉장히 편하고 쉽게 개발할 수 있게 도움을 주는 툴인 것 같더군요.

근데 이 Unity와 언리얼은 무료 라이브러리가 아니기 때문에 배포할때 라이센스 비용이 발생할 것입니다. 아마도 대부분의 게임 라이브러리는 라이센스가 걸려있어서 비용이 발생할 것입니다.

그러나 이 Monogame 라이브러리는 이런 부분이 완전히 무료라서 Monogame으로 게임을 작성해도 소스를 오픈해야 한다거나 라이센스 비용이 발생하는 것에 대해서 매우 자유스럽습니다.


예전에 마이크로소프트에서 XBox 게임 라이브러리로 XNA를 발표한 적이 있습니다. 예전에는 꽤 이 라이브러리에대해서 MS에서도 꽤 지원과 업데이트가 있었는데 2013년 기준으로 XNA에 대한 지원이 중단되고 업데이트가 없어졌습니다.

아마도 XNA으로 3D타입의 개발이 어렵고 여러가지 자원(Resource)에 대한 접근 제한과 한계가 있기 때문에 더이상 포기한 듯 싶네요.

그러나 그 이후에 Monogame이라는 오픈 소스 프로젝트로 명맥을 이어가서 현재까지 오게 되었습니다.


이 Monogame의 장점으로는 역시 완전 오픈 소스라 무료라는 점이고 다중 플렛폼을 지원이 있습니다. 즉, C#으로 개발하면 Build 툴에 의해 Window 뿐아니라 Android, IOS 등등으로 Build가 된다는 뜻입니다.

그리고 예전에 MS에서 부터 시작한 프로젝트이므로 DirectX 자원에 접근하기가 쉽다는 점이 있습니다.


단점이라고는 역시 Unity와 언리얼 엔진처럼 UI 개발 툴이 없고, 순수 프로그램 언어로만 개발을 해야하는 어려움이 있고.. 역시 그렇다 보니 3D 타입의 개발은 어려움이 많이 있습니다. 물론 제가 게임 개발의 경험이 적다보니 개인이 느끼는 단점일 수도 있습니다.

쉐이더 계산을 잘 하는 사람이라면 크게 문제되지 않겠네요..


먼저 Monogame 라이브러리를 설치하기 위해서는 Visual Studio가 설치되어 있어야 합니다.

링크 - [C#] 01. Visual Studio 설치하는 방법


그리고 Visual Studio를 설치할 때나 혹은 이미 설치되어 있는 경우는 Installer를 통해서 cross-platform에 대해서도 같이 설치합니다.

위 이미지처럼 데스크탑 개발과 크로스 플렛폼 개발 관련 라이브러리를 모두 설치합니다.


설치가 끝나고 일단 Visual Studio를 실행합니다.

실행을 하고 아무 프로젝트를 생성하면 위 메뉴에 확장(Extension) 메뉴가 있습니다. 거기서 확장 관리(Manage Extension)을 누르면 Visual studio 확장 기능 창이 표시됩니다.

거기서 Monogame을 검색하고 설치합니다.

Visual Studio를 종료하면 설치 화면이 나오는데 설치를 합니다.

설치가 완료되면 Visual Studio를 다시 실행합니다.


그리고 프로젝트 생성화면에서 기본 템플릿 설정에서 Monogame에 관한 템플릿이 생성된 것을 확인할 수 있습니다.

Monogame를 사용하기 위해서는 여기가 끝이 아니고 MGCB Editor가 필요합니다.

MGCB Editor는 게임에 사용되는 리소스를 관리하는 툴입니다.


.net Framework path가 등록된 커맨드를 관리자 권한으로 실행합니다.

(죄송합니다. OS가 일본어판이라....ㅠㅠ)

# 설치
dotnet tool install --global dotnet-mgcb-editor
# 등록
mgcb-editor --register

그리고 다시 Visual Studio를 실행합니다.

그리고 그 중 Monogame Cross-Platform Desktop Application 템플릿을 선택하고 생성합니다.

그러면 기본적인 Program 클래스와 Game 클래스를 상속받은 Game1 클래스가 만들어집니다.


우선 Game1에서는 Initialize 함수와 LoadContent, Update, Draw가 재정의 되어있습니다.

이건 게임 개발할 때 자주 사용되는 패턴인 Initialize 함수에서 여러 장치등을 초기화하고 LoadContent 함수에서 리소스(이미지나 음악 파일등등)을 로드하고 update는 순서대로 더블 버퍼링에 그리고나서 Draw 함수에서 화면에 표시하는 것입니다.

일단 템플릿에서 이렇게 기본적으로 생성되고 리소스를 추가할 때는 그냥 예전 Visual studio에서 바로 생성하는 것이 아니고 Content 폴더를 보면 Content.mgcb 파일이 있는데 그것을 통해서 리소스를 추가합니다.


제가 공이 땅에 떨어지는 것으로 구현해 보겠습니다.

먼저 리소스에 공을 추가합니다.

Content.mgcb를 Open with...를 통해서 MGCB Editor를 실행합니다.(Default로 설정했으면 더블 클릭으로 실행됩니다.)

리소스를 추가해서 Build 합니다. 여기서 Build는 리소스를 프로그램 상에서 사용할 수 있게 xnb형식으로 변환합니다.

그리고 솔루션 탐색기를 확인하면 xnb파일이 추가된 것을 확인할 수 있는데 Build 후에 파일이 같이 복사가 되도록 Copy always 옵션으로 바꾸어 줍니다.


그리고 Game1.cs 소스를 작성합니다.

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using System.Diagnostics;

namespace ExampleGame
{
  public class Game1 : Game
  {
    // 화면 Graphics 관리 클래스
    private GraphicsDeviceManager _graphics;
    // 스크린 크기에 따라서 게임 화면이 자동으로 변경된다.
    private SpriteBatch _spriteBatch;
    // 볼 리소스
    private Texture2D _ballTexture;
    // 위치 함수
    private Rectangle? _rect = null;
    // 방향, 음수면 위에서 아래로 양수면 아래에서 위로 움직인다.
    private double _direction = -1;
    // 최대 높이
    private double _maxHeight = 0;
    // 가속도
    private double _acceleration = 0;

    public Game1()
    {
      _graphics = new GraphicsDeviceManager(this);
      // 리소스 디렉토리 변경
      Content.RootDirectory = @"Content\bin\DesktopGL";
      IsMouseVisible = true;
    }
    // 초기화 함수
    protected override void Initialize()
    {
      // TODO: Add your initialization logic here
      base.Initialize();
    }
    // 리소스 읽어오기
    protected override void LoadContent()
    {
      _spriteBatch = new SpriteBatch(GraphicsDevice);

      // TODO: use this.Content to load your game content here
      // 볼 리소스 가져오기
      _ballTexture = Content.Load<Texture2D>("ball");
    }
    // 리소스 값들 업데이트하기
    protected override void Update(GameTime gameTime)
    {
      if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed || Keyboard.GetState().IsKeyDown(Keys.Escape))
        Exit();

      // TODO: Add your update logic here
      // 초기 설정되어 있지 않으면
      if (_rect == null)
      {
        // 크기는 50, 50이고 화면에서 0,0은 좌측 위이다
        _rect = new Rectangle(0, 0, 50, 50);
        // 최대 높이는 화면 높이로 지정
        _maxHeight = Window.ClientBounds.Height;
      }
      // 공의 최대 높이가 볼의 크기보다 작을 경우
      else if (_maxHeight < _rect.Value.Height)
      {
        // 그냥 공을 멈춘다.
        _rect = new Rectangle(0, Window.ClientBounds.Height - _rect.Value.Height, 50, 50);
        _maxHeight = 0;
      }
      else
      {
        // Graphic 좌표계는 x좌표는 왼쪽에서 오른쪽(0 -> 화면 크기)로 이동되지만 y좌표는 위가 0이고 아래가 화면 크기이다.
        // 계산을 할때 반대로 계산하게 되므로 헤갈릴 수 있으니 여기서는 좌표를 반전한다.
        double y = (Window.ClientBounds.Height - _rect.Value.Y);
        // 가속이 마이너스로 되면 방향을 바꾼다.
        if (_acceleration < 0)
        {
          // 위에서 아래로 움직인다.
          _direction = -1;
          // 최대 높이 설정
          _maxHeight = y;
        }
        // 화면의 아래쪽 + 공 이미지의 높이를 벗어나면 방향을 아래에서 위로 움직인다.
        if (y < 0 + _rect.Value.Height)
        {
          // 방향을 바꾼다.
          _direction = 1;
        }
        // 공의 높이를 계산한다.
        y = y + (_direction * _acceleration);
        // 방향이 위에서 아래라면
        if (_direction < 0)
        {
          // 가속의 증가량은 0.8
          _acceleration += 0.8;
        }
        // 방향이 아래에서 위라면
        else
        {
          // 가속 감소량은 1.2
          _acceleration -= 1.2;
        }
        // 디버그 콘솔에 출력
        Debug.WriteLine($"y = {y} , _direction = {_direction}, _acceleration = {_acceleration}, _maxHeight = {_maxHeight}");
        // 공 좌표를 계산한다.
        _rect = new Rectangle(_rect.Value.X, Window.ClientBounds.Height - (int)y, _rect.Value.Width, _rect.Value.Height);
      }

      base.Update(gameTime);
    }

    protected override void Draw(GameTime gameTime)
    {
      // 배경색은 파란색
      GraphicsDevice.Clear(Color.CornflowerBlue);

      // TODO: Add your drawing code here
      // 화면 설정
      _spriteBatch.Begin();
      // 공을 그린다.
      _spriteBatch.Draw(_ballTexture, _rect.Value, Color.White);
      _spriteBatch.End();

      base.Draw(gameTime);
    }
  }
}

위에서 아래로 떨어지는 공이 떨어지는 화면을 구현할 수 있습니다.


최근에 블로그를 하면서 게임 프로그램에 조금 관심이 생겼습니다. 웹 프로그램에 대해서 블로그에 적으려고 하니 부분 부분적으로 코드를 설명하기도 힘들고(이유는 하나의 페이지를 만들때 Html, Javascript, Java or C# 그리고 프레임워크 설명 등의 연계) 그렇다고 프로젝트를 하기도 뭐해서 간단한 게임이나 만들면서 블로그를 운영해 볼까하는 생각도 듭니다.

게임 프로그램도 큰 프로젝트 단위로 가면 Monogame 라이브러리로는 어림도 없겠지만 작은 도트 형식의 인디 게임이나 옛날 리니지1같은 감성의 게임이라면 충분이 만들 수 있을 거라는 생각이 드네요..

예전에는 게임이라는 것은 종합 엔터테인먼트 요소가 강하기 때문에 프로그램보다는 음악이나 그래픽, 스토리 등등의 요소가 더 중요하다고 생각했습니다. 물론 지금도 프로젝트 단위가 큰 게임의 경우는 역시 좋은 엔진을 이용해서 프로그램 요소보다는 얼마나 리소스 배치를 잘하고 기획을 잘하냐가 중요하다고 생각합니다.

저같은 옛날 개발자는 IDE에 알고리즘을 생각하며 코드 작성하며서 무언가를 만들기 좋아해서....

그런데 Monogame 라이브러리를 접하고 또 인디 게임을 최근에 많이 살펴 봤는데 마인 크래프트나 XNA로 만들었던 스타튜 벨리(STARDEW VALLEY)등을 보면 간단한 도트 형식으로 프로그램의 계산을 이용한 시뮬레이션을 만드는 것도 나쁘지 않겠다라는 생각도 드네요.

생각보다 이런 쪽이 매니아들이 많아서 도트 형식의 게임을 좋아하는 사람도 많더군요... 그래서 최근에 인디게임이나 간단하게 만들어 볼까하는 생각도 많이 드네요.


출처 - Monogame Homepage


여기까지 C# 무료 게임 개발 라이브러리(Monogame, 구: XNA)을 설치하는 방법을 설치하는 방법에 대한 글이었습니다.


궁금한 점이나 잘못된 점이 있으면 댓글 부탁드립니다.