company_banner

Введение в разработку игр для Windows 8 с использованием XNA и MonoGame

    Как вы наверное заметили, в нашем блоге появился ряд материалов, которые раскрывают азы создания прикладных приложений WinRT для Windows 8. В данной серии статей будет рассказано о том как вы можете создавать игровые приложения WinRT на языке C#.




    Если вы интересовались вопросом создания игр для WinRT то наверняка знаете о том что единственный путь который предлагает Visual Studio 2012 это DirectX. Это очень мощная технология, требует знаний С++, и в целом скорее является низкоуровневым API для работы с интенсивной графикой, ее изучение требует времени. Если вы делаете первые шаги, то куда проще использовать более высокоуровневые библиотеки. Но в силу молодости платформы пока существует не так уж много игровых и физических движков, тем не менее их список пополняется. Вот некоторые из них:



    Последние два – SharpDX и MonoGame заслуживают отдельного внимания. SharpDX является оберткой над DirectX и позволяет работать с этой технологией из .NET а так же поддерживает WinRT. Еще более интересным является то что MonoGame – кроссплатформенный порт библиотеки XNA так же работает на WinRT.
    XNA это популярная библиотека для Windows, Xbox360 и Windows Phone, с помощью которой созданы тысячи игр. Благодаря тому что MonoGame не поменял основные области видимости, перенос с XNA не требует больших изменений в существующем коде приложений.

    Конфигурируем среду



    Для того чтобы начать разрабатывать игры для WinRT на MonoGame вам необходимо сконфигурировать Visual Studio 2012 и установить на компьютер необходимые компоненты для компиляции проекта. Конечно вы можете скачать уже собранные версии SharpDX и MonoGame но лучше скачать исходные файлы этих библиотек, собрать их локально. Это позволит в дальнейшем осуществлять детальную отладку и повысит ваше понимание того как работает MonoGame.

    Если вы решили пойти легким путем то вот ссылки: sharpdx.org/download (или в Package Manager console наберите Install-Package SharpDX) и monogame.codeplex.com/releases/view/96421

    Если же вы хотите собрать все сами
    :
    Создаем каталог в котором у нас будут лежать исходные файлы библиотек. Например D:\Gamedev

    1. Устанавливаем Git: msysgit.github.com
    2. Запускаем Git Bash (консоль git) и заходим в каталог D:\Gamedev
    3. Получаем исходники SharpDX: git clone github.com/sharpdx/SharpDX
    4. Получаем исходники MonoGame: git clone github.com/mono/MonoGame
    5. Запускаем Visual Studio 2012 x86 Native Command Line Tools (найдите поиском по слову Native в стартовом экране)
    6. Заходим в каталог cd D:\Gamedev\SharpDX и командуем MakeSharpDX.cmd win8 build примерно через 3-4 минуты у вас соберется SharpDX и в каталоге D:\gamedev\sharpdx\Bin\Standard-winrt\ будут расположены бинарные сборки
    7. Теперь нам надо сконфигурировать и скомпилировать проект MonoGame. Для этого с помощью Visual Studio 2012 открываем проект D:\gamedev\MonoGame\MonoGame.Framework.Windows8.sln сейчас MonoGame не знает где расположены собранные сборки SharpDX и показывает в референсах ошибки:

    8. В свойствах проекта (контекстное меню на проекте, Properties) в разделе Reference Paths указываем путь D:\gamedev\sharpdx\Bin\Standard-winrt\
    9. Компилируем проект, после чего у вас появится сборка D:\gamedev\MonoGame\MonoGame.Framework\bin\Release\MonoGame.Framework.Windows8.dll
    10. Устанавливаем шаблоны проектов MonoGame в Visual Studio. Для этого скопируйте файлы из каталога D:\gamedev\MonoGame\ProjectTemplates\VisualStudio2012\ в каталог C:\Users\USERNAME\Documents\Visual Studio 2012\Templates\ProjectTemplates\Visual C#\Mono\ после чего перезапускаем Visual Studio и создаем на базе этого шаблона проект:

    11. Не забываем так же указать Reference Paths для вновь созданного проекта D:\gamedev\MonoGame\MonoGame.Framework\bin\Release\


    После чего компилируем и запускаем проект. Поздравляем, вы создали свое первое приложение MonoGame для WindowsRT. Хотя выглядит оно пока совсем просто, чистый голубой экран, так как ничего не делает и ничего кроме фона не рисует.
        public class Game1 : Game
        {
            GraphicsDeviceManager _graphics;
            SpriteBatch _spriteBatch;
     
            public Game1()
            {
                _graphics = new GraphicsDeviceManager(this);
                Content.RootDirectory = "Content";
            }
     
            ///Инициализация
            protected override void Initialize()
            {
                base.Initialize();
            }
     
            /// загрузка игровых ресурсов
            protected override void LoadContent()
            {
                _spriteBatch = new SpriteBatch(GraphicsDevice);
               //Content.Load<SpriteFont>...
            }
     
            protected override void UnloadContent()
            {
            }
     
            /// логика игры
            protected override void Update(GameTime gameTime)
            {
                base.Update(gameTime);
            }
     
            /// отрисовка игры
            protected override void Draw(GameTime gameTime)
            {
                GraphicsDevice.Clear(Color.CornflowerBlue);
                base.Draw(gameTime);
            }
        }
    
    


    Что дальше



    Если вы совсем не знакомы с XNA то попробуйте поискать по ключевому слову XNA tutorial материалы в интернет. Существует очень много статей которые поясняют азы программирования графики на базе этой технологии. Так же вы можете скачать пример платформера CastleX который запускается на Windows 8.



    Это классический платформер в котором игрок прыгает, лазает и собирает какие-то нужные по сюжету вещи, заодно пытаясь уберечься от всевозможных опасностей.
    Пока эта игра еще не полностью поддерживается Windows 8 так как в ней нет важных компонент. Внесены только минимальные изменения, по сравнению с Windows, которые позволили запустить это приложение под WinRT.
    В каталоге Assets игры Castle X находится множество файлов необходимых для работы – это графика, звуковые эффекты и уровни. Многие из этих файлов имеют необычное расширение XNB.



    Одной из сложностей с которой придется неизбежно столкнуться при разработке игр для MonoGame является отсутствие поддержки Content Pipeline. В XNA 4.0 и дополнении для Visual Studio 2010 существовал набор расширений которые запаковывали графические и звуковые ресурсы игры в специальный формат данных XNB. В Visual Studio 2012 такого расширения нет, поэтому рекомендованный путь это дополнительно установить Visual Studio 2010 и XNA Game Studio 4.0 Refresh так же обязательно установите обновление для XNA Runtime под платформу Windows 8 Desktop.
    Далее вы сможете собирать ресурсы в VS2010 или использовать стороннюю утилиту XNA Content Compiler

    Как устроена игра CastleX


    Программная логика игры начинается с концепции отображаемых экранов. Существует несколько типов экранов ( их классы находятся в фолдере /View/Screens/), например экран заставки, экран проигрывания демо, экран уровня, экран игрового меню и так далее. Управляет экранами специальный класс ScreenManager унаследованый от типа DrawableGameComponent который регистрируется при старте приложения.

    Components.Add(screenManager);
    


    В методе Update главного класса игры осуществляется изменение состояния менеджера экранов которые в зависимости от обработанных событий влияют на то что видит игрок.
    Сам ScreenManager отображает себя в методе Draw, логика которого очевидна из кода:

            public override void Draw(GameTime gameTime)
            {
                ViewPort = GraphicsDevice.Viewport;
     
                foreach (GameScreen screen in screens)
                {
                    if (screen.ScreenState == ScreenState.Hidden)
                        continue;
                    try
                    {
                        SpriteBatch.Begin();
                    }
                    catch { }
                    screen.Draw(gameTime, SpriteBatch);
                    try
                    {
                        SpriteBatch.End();
                    }
                    catch { }
                }
            }
    
    


    Сами экраны в свою очередь занимаются обработкой пользовательского ввода в методе HandleInput и своим собственным отображением в зависимости от тех событий которые были обработаны.

    Экран уровня игры



    Основной экран уровня игры содержит объект «текущий уровень» в переменной level зависящей от того где находится игрок. Уровни подгружаются динамически. В методе Draw самого экрана осуществляется передача вызова уже методу Draw самого уровня (leve.Draw) а так же отображается информационная панель с количеством жизней, собранных ключей — DrawHud(gameTime, spriteBatch);

    Уровень игры


    Сам уровень представляет собой массив объектов Tile которые могут находиться в различных слоях, указывающих порядок их отображения (что поверх чего):

            private Tile[,] tiles;
            private Layer[] layers;
    
    


    Обьекты Tile в свою очередь могут быть чем угодно – дверью, кусочком кирпичной стены, водой. Тайлы так же могут быть прозрачными и по сути такие тайлы являются триггерами, так как тайл по мимо всего прочего обрабатывает коллизии – важный элемент игры:

        public enum TileCollision
        {
            /// <summary>
            /// Проходной тайл, который не мешает передвижению игрока
            /// </summary>
            Passable = 0,
     
            /// <summary>
            /// Не проходной тайл сквозь который нельзя пройти
            /// </summary>
            Impassable = 1,
     
            /// <summary>
            /// Тайл платформы, через который можно пройти со всех сторон кроме верха
            /// </summary>
            Platform = 2,
     
            /// <summary>
            /// лестница, особый случай
            /// </summary>
            Ladder = 3
        }
    
    


    Еще у тайла есть текстура – собственно то что будет отображаться на экране, подгружаемая через метод content.Load<>().
    Так же на уровне присутствуют и другие объекты, которые взаимодействуют с тайлами и сами с собой — анимированные тайлы, персонаж игрока, монстры, ключи, монетки.

    Сам уровень целиком закодирован в текстовом файле. Различные символы означают разные типы тайлов и объектов в их начальном состоянии:

    ####################
    X4001.................!..
    .x2..EG0102...............!X1003.
    \
    ##L.######..########
    ##L.........#C.C.C.C.C.C.X3003.
    ##L.......N..#C.C.C.C.C.Cx5..
    ##L.......EG0102..########
    ##L....N..#####......
    ##L...EG0102.......#.....X2003.
    ##L..####....#....x4..
    ##L..........#W....##
    ##L...N....N...#....##
    ##L...EG0102......i#www###
    #############www####
    wwwwwwwwwwwwwww#####
    X6001wwwwwwwwwwwwwww#####
    wx6wwwwwwwwwwwww######
    2!2!2!2!2!2!2!2!2!2!
    tit.Platforms and ghosts
    des.Face your first enemies inside the castle!
    Layer2.Bricks.Bricks.Bricks


    Например L – это лестница, E – опасный персонаж, С – монетка,! – непроходной тайл, и так далее. Все коды можно найти в методе LoadTile(char tileType, int x, int y, String code)

    Обработка их состояния и позиции обьектов осуществляется в методе Level::Update. Например в этом методе может быть обработано нажатие клавиши влево, обьект игрока переместится на тайл двигающейся платформы, будет обработана коллизия с этим тайлом (поменяются свойства объекта игрока)
    
            private void UpdateMovingItems(GameTime gameTime)
            {
                if (MovingItemsAreActive)
                {
                    for (int i = 0; i < MovingItems.Count; ++i)
                    {
                        MovingItem movingItem = MovingItems[i];
                        movingItem.Update(gameTime);
     
                        if (movingItem.PlayerIsOn)
                        {
                            //двигаем персонаж игрока если он стоит на двигающейся платформе   
                            screenManager.Player.Position += movingItem.Velocity;
                        }
                    }
                }
            }  
    
    


    В игре нет какого то особого физического движка, объекты у которых нужно рассчитывать физику, присутствуют методы которые влияют на их свойства (координаты, цвет, и.т.п.) Вот например как рассчитывается позиция объекта который падает:

           
            private void ApplyPhysics(GameTime gameTime)
            {
                float elapsed = (float)gameTime.ElapsedGameTime.TotalSeconds;
                if (isFalling)
                {
                    velocity.Y = MathHelper.Clamp(velocity.Y + GravityAcceleration * elapsed, -MaxFallSpeed, MaxFallSpeed);
                    Position += velocity * elapsed;
                }
            }
    
    


    После того как были осуществлены все обработки событий и изменены свойства объектов, наступает момент отображения уровня в методе Draw(), который просто рисует все объекты что есть на уровне:

    
                DrawTiles(spriteBatch);
                foreach (Item item in items)
                    item.Draw(gameTime, spriteBatch);
                foreach (MultipleStateItem item in multipleStateItems)
                    item.Draw(gameTime, spriteBatch);
                foreach (AnimatedItem item in animatedItems)
                    item.Draw(gameTime, spriteBatch);
                foreach (FallingTile FallingTile in FallingTiles)
                    FallingTile.Draw(gameTime, spriteBatch);
                foreach (Exit exit in exits)
                    exit.Draw(gameTime, spriteBatch);
                foreach (Boss boss in bosses)
                    boss.Draw(gameTime, spriteBatch);
    
    


    В целом код игры и его логика достаточно очевидны. Потратив совсем немного времени можно разобраться как все работает.

    Что дальше



    Поскольку код игры, по сравнению с оригинальной версией изменен весьма незначительно, и только с целью обеспечить совместимость c WinRT и MonoGame, в этом приложении требуются дополнительные изменения:

    • Поддержка сохранения состояния игры в local application storage.
    • Подсистема сохранения файлов. Из за этого сейчас отключен редактор уровей
    • Поддержка Multitouch и управления касаниями
    • Обязательный экран privacy policy


    Эти компоненты будут рассмотрены и доработаны в следующем посте.
    Ссылка на проект работающий в Windows 8 и оригинальную игру — в случае если вы решите поменять XNB файлы (графику и звуки).
    Microsoft 370,60
    Microsoft — мировой лидер в области ПО и ИТ-услуг
    Поделиться публикацией
    Комментарии 11
    • +2
      Спасибо за статью. Только так до сих пор не понимаю. зачем было убивать XNA и полностью перекладывать его на Xamarin? Чем он мешал в том же WP8? На нем инди разработчики делали шикарные игры типа Magicka…
      • 0
        Перебросили на самом деле не на Xamarin, а все таки сообщество пилит. Причина его выбрасывания кроется скорее всего в том что нет смысла развивать две технологии, которые выполняют одну и ту же задачу, вспомните COM+, OLE, теперь WinRT.
        И еще XNA как-то остановилась на DirectX 9
        • 0
          В твиттере Мигеля натыкался на мысль, что C++ снова захватывает первенство внутри МС, и никто уже и не вспоминает о старичках типа Managed DX… =(

          Ну и тогда стоило бы просто заопенсорсить все, и передать все права на TM и так дапее мэйнтэйнерам.
          • 0
            Какие 2 технологии простите?
            По моему вы путаете теплое с мягким.
          • –3
            шикарные игры типа Magicka
            Которые умудряются тормозить на компьютерах, на которых работает Crysis.
            • +2
              нет
              • 0
                Что «нет»? Конкретно игра Magicka более требовательна к ресурсам, чем Crysis или Diablo III.
                • +1
                  Вопрос не в требовательности. Вопрос в том, что на C++ мы бы скорее всего не увидели эту игру вообще. Разработчику и так есть чем заняться, а не разбирать 2х гиговые дампы памяти…
                  • 0
                    Не заметил что то. Даже на старичке-ноуте с 945 интелом.
            • +1
              Вместо MonoGame можно еще использовать SharpDX Toolkit — более высокоуровневый API, который относительно недавно добавился в SharpDX и который очень похож на XNA.
              • +2
                Немного напрягает то, что папа проекта немного отходит от дел и все будет зависеть от коммьюнити…

              Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

              Самое читаемое