Статьи

Создание кроссплатформенной игры на ПК, Xbox и WP7 с XNA Framework Game Studio


В современном мире разработки возможность ориентироваться на несколько платформ из одной игровой кодовой базы является редкостью.
Часто один и тот же игровой проект необходимо переписать для запуска на другой платформе, что вызывает головную боль, разочарование и потерю времени. Именно здесь появляется XNA Game Studio. XNA Game Studio — это набор инструментов, созданный Microsoft, который делает процесс разработки игры простым и увлекательным. Истинная красота XNA Game Studio заключается в ее способности беспрепятственно предоставлять разработчикам способ сборки для нескольких платформ. В этом уроке мы рассмотрим, как мы можем создать простую игру XNA Framework, которая работает на Windows Phone 7, Xbox и ПК с использованием единой кодовой базы.

Что вам нужно

Прежде всего, если вы еще этого не сделали, посетите сайт http://create.msdn.com/ и найдите бесплатную загрузку Visual Studio 2010 и XNA Game Studio 4.0. Следуйте инструкциям по установке. 

Что мы будем строить

Мы собираемся создать очень простую игру на основе XNA Framework, основной целью которой является демонстрация развертывания на нескольких платформах. По сути, игра будет перемещать мяч для гольфа в цель. На Windows Phone 7 мяч будет управляться с помощью акселерометра, на Xbox он будет перемещаться с помощью джойстика на контроллере, а на компьютере — с помощью клавиш со стрелками на клавиатуре. Когда пользователь перемещает мяч для гольфа в цель, игра сбрасывается. Просто!

Шаг 1: Сборка Windows Phone 7 XNA Framework Игра

Мы начнем с создания нового проекта Windows XNA Framework, который затем изменим для запуска в Windows Phone 7 и Xbox. Перейдите в Файл -> Новый проект. В XNA Framework Game Studio 4.0 выберите «Игра для Windows Phone (4.0)» и назовите ее «CrossPlatformXNA FrameworkGame».

Давайте начнем с небольшой теории …

Далее нам нужно начать кодирование нашего проекта. Позвольте мне начать с краткого описания работы XNA Framework на более высоком уровне. На приведенной ниже диаграмме показан ход работы приложения XNA Game Studio на высоком уровне.

Код начинается с ввода конструктора, инициализации переменных, загрузки любого содержимого в ContentManager и, наконец, входа в игровой цикл. Игровой цикл — это то, где будет существовать большая часть кода. Игровой цикл запускается так часто, как хотелось бы. По умолчанию приложение Windows Phone 7 XNA Framework работает со скоростью 30 кадров в секунду. Это означает, что игровой цикл вызывается 30 раз в секунду. Игровой цикл на самом деле состоит из двух функций, названных Update () и Draw ().

Давайте импортируем некоторые текстуры в наш проект …

Поэтому, прежде чем мы начнем кодировать, давайте импортируем некоторые текстуры, которые мы можем использовать для нашей игры. Я создал текстуры для мяча для гольфа и текстур Target, которые вы можете использовать в своем проекте. Есть два разрешения для каждой текстуры. Причина этого в том, что в зависимости от платформы, на которой вы запускаете игру, разрешение может отличаться. Таким образом, я предоставил два изображения; одна предназначена для разрешения высокого разрешения на Xbox, а другая — версия с более низким разрешением для Windows Phone и ПК.

Примечание .   Как Windows Phone 7, так и Xbox включают аппаратное масштабирование, которое может увеличивать или уменьшать изображения по требованию на оборудовании с превосходными результатами.   Использование аппаратного масштабатора может уменьшить размер игры, имея всего один набор контента.

 

Сохраните файлы на своем компьютере, а затем перейдите в обозреватель решений в Visual Studio 2010, щелкните правой кнопкой мыши свой проект CrossPlatformXNA FrameworkGameContent и выберите «Добавить» -> «Существующий элемент». Выберите изображения, которые вы сохранили на свой компьютер.

Приступая к коду …

Итак, теперь мы на очень высоком уровне понимаем, как работает приложение XNA Framework, и у нас фактически есть некоторые текстуры, которые мы можем использовать для создания нашего приложения, мы можем начать кодировать.

1.     Начните с переименования Game1.cs внутри проекта CrossPlatformXNA FrameworkGame в «GolfGameMain.cs»

2.     Откройте GolfGameMain.cs и определите следующие переменные, которые будут использоваться для наших двух текстур, а также информацию о местоположении и скорости.

        // Textures

        Texture2D ball;

        Texture2D target;

 

        // Location information

        Vector2 ballPt;

        Vector2 targetPt;

 

        // normalized velocity vector

        Vector2 ballVelocity;

 

        // Used to multiply on the velocity vector

        float velocityMultiplier;

 

3.     Внутри метода Initialize () добавьте следующую строку кода, чтобы установить скорость нашего мяча для гольфа

velocityMultiplier = 13.0f;

 

4.  Напишите функцию с именем ResetGame (), которая инициализирует переменные скорости и положения. Метод должен выглядеть следующим образом.   Мы разместим мяч вначале немного правее верхнего левого угла, а цель — чуть левее правого нижнего угла. Скорость мяча начнется с нуля.

 

        private void ResetGame()

        {

            ballVelocity = Vector2.Zero;

            ballPt = new Vector2(target.Width, target.Height);

            targetPt = new Vector2(graphics.GraphicsDevice.Viewport.Width - 2*target.Width, graphics.GraphicsDevice.Viewport.Height - 2*target.Height);

        }

 

 

5.     Загрузите текстуры, которые мы импортировали, в ContentManager, добавив следующие строки кода в метод LoadContent (). После того, как мы загрузили наш контент, мы можем вызвать ResetGame () для позиционирования объектов.

ball = Content.Load<Texture2D>("ball_small");

target = Content.Load<Texture2D>("target_small");

                   ResetGame();

 

 

6.     Теперь, когда мы загрузили наш контент в менеджер контента, давайте нарисуем текстуры, которые мы загрузили на экран, основываясь на позиции, в которой были изображения.   Измените метод Draw (), чтобы он выглядел следующим образом.

protected override void Draw(GameTime gameTime)

        {

            GraphicsDevice.Clear(Color.White);

 

            spriteBatch.Begin();

 

            // Draw our objects to the screen

            spriteBatch.Draw(target, targetPt, Color.White);

            spriteBatch.Draw(ball, ballPt, Color.White);

 

            spriteBatch.End();

 

            base.Draw(gameTime);

        }

7.     В этот момент мы должны запустить программу и увидеть на экране мяч для гольфа и цель. Это должно выглядеть как на картинке ниже.

 

8.     Следующим шагом будет добавление метода для проверки столкновений. Метод должен обрабатывать, когда мяч для гольфа полностью находится внутри цели. Если это так, то мы вернем true и в противном случае вернем false.

        private bool CheckForCollision()

        {

            Rectangle ballRect = new Rectangle((int)ballPt.X, (int)ballPt.Y, ball.Width, ball.Height);

            Rectangle targetRect = new Rectangle((int)targetPt.X, (int)targetPt.Y, target.Width, target.Height);

 

            return targetRect.Contains(ballRect);

        }

 

9.     Теперь, когда у нас есть логика для обнаружения столкновений, мы можем добавить логику для перемещения мяча для гольфа к цели. Мы можем сделать это, добавив следующее внутри метода Update (). Как только мы обнаружим столкновение, мы можем сбросить игру, вызвав функцию ResetGame (), которую мы написали ранее.

KeyboardState kState = Keyboard.GetState(PlayerIndex.One);

 

Keys[] pressedKeys = kState.GetPressedKeys();

 

ballVelocity = Vector2.Zero;

 

foreach (Keys k in pressedKeys)

    switch (k)

    {

        case Keys.Right:

            ballVelocity.X = velocityMultiplier;

            break;

        case Keys.Left:

            ballVelocity.X = -velocityMultiplier;

            break;

        case Keys.Down:

            ballVelocity.Y = velocityMultiplier;

            break;

        case Keys.Up:

            ballVelocity.Y = -velocityMultiplier;

            break;

    }

 

ballPt += ballVelocity;

 

if (CheckForCollision())

    ResetGame();

 

10.  Проведите пробный запуск игры. Теперь вы должны иметь возможность перемещать мяч для гольфа, используя элементы управления стрелкой на клавиатуре. Когда вы перемещаете его к цели, он должен перезагрузить игру.  Отличная работа. Вы сделали большую часть работы. С этого момента мы изменим код для поддержки Windows Phone 7 и платформы Xbox. J

Шаг 2. Измените код для поддержки Windows Phone 7

11.  Создайте копию проекта CrossPlatformXNA FrameworkGame для Windows Phone 7. Для этого щелкните правой кнопкой мыши имя проекта и выберите «Создать копию проекта для Windows Phone». Второй проект должен быть создан и будет иметь название «Windows Phone 7 копия CrossPlatformXNA FrameworkGame».

12.  Добавьте ссылку на ваш проект, щелкнув правой кнопкой мыши «Ссылки -> Добавить ссылку», как показано ниже. Затем выберите Microsoft.Devices.Sensors и нажмите «Добавить».

13.  Теперь закройте все файлы, которые могут быть открыты, и из копии Windows Platform 7 Cross PlatformXNA FrameworkGame выберите и откройте GolfGameMain.cs.

14.  Добавьте следующее имя в начало проекта. Обернуть импорт пространства имен макросом #if WINDOWS_PHONE. Это говорит компилятору игнорировать код внутри «# if… # endif», если он не компилируется для Windows Phone 7. Мы сделаем то же самое позже для Xbox, используя «#if XBOX… #endif».

// Used for accelerometer data from Windows Phone

#if WINDOWS_PHONE

using Microsoft.Devices.Sensors;

#endif

15.  Добавьте переменную уровня класса, чтобы добавить поддержку акселерометра

        #if WINDOWS_PHONE

        Accelerometer accelerometer;

        #endif

16.  В конструкторе добавьте следующий код, чтобы помочь изменить настройки, относящиеся к тому, как отображается игра и в каком разрешении она запускается.

            #if WINDOWS_PHONE

                // Hides the battery and signal strength bar

                graphics.IsFullScreen = true;

            #else

                // Code to support changing the resolution of the game for the Xbox to handle high def screens

                graphics.IsFullScreen = false;

                graphics.PreparingDeviceSettings += new EventHandler<PreparingDeviceSettingsEventArgs>(graphics_PreparingDeviceSettings);

            #endif

17. Измените функцию Initialize (), которую вы написали ранее, чтобы инициализировать акселерометр и иметь разные скорости для мяча для гольфа в зависимости от платформы.

 

            #if WINDOWS_PHONE

                if (accelerometer == null)

                {

                    // Instantiate the accelerometer sensor

                    accelerometer = new Accelerometer();

                    accelerometer.ReadingChanged += new EventHandler<AccelerometerReadingEventArgs>(accelerometer_ReadingChanged);

                    accelerometer.Start();

                }

 

                velocityMultiplier = 10.0f;

            #elif WINDOWS

                velocityMultiplier = 13.0f;

            #else

                velocityMultiplier = 20.0f;

            #endif

 

 

18.  Добавьте функцию для обработки события, на которое мы подписываемся на предыдущем шаге. Эта функция будет обрабатывать изменения показаний акселерометра. Отсюда мы установим скорость мяча для гольфа.   Это эквивалентный код для обработки различных нажатий клавиш в версии Windows.

        #if WINDOWS_PHONE

        void accelerometer_ReadingChanged(object sender, AccelerometerReadingEventArgs e)

        {

            ballVelocity.X = -velocityMultiplier*(float)e.Y;

            ballVelocity.Y = -velocityMultiplier*(float)e.X;

        }

        #endif

19.  Измените метод LoadContent () для загрузки различных текстур в зависимости от целевой платформы. Для Windows Phone и Windows мы будем загружать изображения с низким разрешением, а для Xbox мы будем загружать изображения с высоким разрешением.

            // Create a new SpriteBatch, which can be used to draw textures.

            spriteBatch = new SpriteBatch(GraphicsDevice);

 

            // Load our game content to the ContentManager

            #if WINDOWS_PHONE || WINDOWS

                ball = Content.Load<Texture2D>("ball_small");

                target = Content.Load<Texture2D>("target_small");

            #else

                ball = Content.Load<Texture2D>("ball");

                target = Content.Load<Texture2D>("target");

            #endif

 

            ResetGame();

20.  Измените функцию Update () так, чтобы мы смотрели на ввод с клавиатуры только в том случае, если мы компилируем версию игры для Windows.                 

#if WINDOWS

                KeyboardState kState = Keyboard.GetState(PlayerIndex.One);

 

                Keys[] pressedKeys = kState.GetPressedKeys();

 

                ballVelocity = Vector2.Zero;

 

                foreach (Keys k in pressedKeys)

                    switch (k)

                    {

                        case Keys.Right:

                            ballVelocity.X = velocityMultiplier;

                            break;

                        case Keys.Left:

                            ballVelocity.X = -velocityMultiplier;

                            break;

                        case Keys.Down:

                            ballVelocity.Y = velocityMultiplier;

                            break;

                        case Keys.Up:

                            ballVelocity.Y = -velocityMultiplier;

                            break;

                    }

#endif

21.  Отлично! Теперь щелкните правой кнопкой мыши на «Windows Phone 7 копия CrossPlatformXNA FrameworkGame» в обозревателе решений и запустите «Отладка -> Запустить новый экземпляр». Он должен вызвать эмулятор Windows Phone 7, как показано ниже. К сожалению, вы не сможете проверить работоспособность акселерометра, если не развернетесь на физическом Windows Phone 7.

 

 

Почти готово! Давайте закончим с созданием версии для Xbox.

22.  Создайте копию проекта CrossPlatformXNA FrameworkGame для Xbox. Для этого щелкните правой кнопкой мыши имя проекта и выберите «Создать копию проекта для Xbox 360». Третий проект должен быть создан и будет иметь название «Копия Xbox 360 CrossPlatformXNA FrameworkGame».

23.  Измените конструктор в последний раз, чтобы добавить обработчик событий для настройки разрешения игры. Окончательный конструктор должен выглядеть следующим образом.

        public GolfGame()

        {

            graphics = new GraphicsDeviceManager(this);

            Content.RootDirectory = "Content";

 

            // Frame rate is 30 fps by default for Windows Phone.

            TargetElapsedTime = TimeSpan.FromTicks(333333);

 

            #if WINDOWS_PHONE

                // Hides the battery and signal strength bar

                graphics.IsFullScreen = true;

            #else

                // Code to support changing the resolution of the game for the Xbox to handle high def screens

                graphics.IsFullScreen = false;

                graphics.PreparingDeviceSettings += new EventHandler<PreparingDeviceSettingsEventArgs>(graphics_PreparingDeviceSettings);

            #endif

        }

24.  Теперь мы должны определить определение функции для события, на которое мы подписались.

void graphics_PreparingDeviceSettings(object sender, PreparingDeviceSettingsEventArgs e)

        {

            #if XBOX

                foreach (Microsoft.Xna.Framework.Graphics.DisplayMode displayMode in GraphicsAdapter.DefaultAdapter.SupportedDisplayModes)

                {

                    // High def resolution support for Xbox (if available)

                    if (displayMode.Width == 1920 || displayMode.Width == 1280)

                    {

                        e.GraphicsDeviceInformation.PresentationParameters.BackBufferFormat = displayMode.Format;

                        e.GraphicsDeviceInformation.PresentationParameters.BackBufferHeight = displayMode.Height;

                        e.GraphicsDeviceInformation.PresentationParameters.BackBufferWidth = displayMode.Width;

                    }

                }

            #endif

        }

25.  Теперь мы должны изменить метод Update (), чтобы добавить поддержку контроллера Xbox. Окончательный вариант функции будет выглядеть следующим образом.

protected override void Update(GameTime gameTime)

        {

            // Allows the game to exit

            if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)

                this.Exit();

 

            Vector2 velocity = Vector2.Zero;

 

            #if WINDOWS

                KeyboardState kState = Keyboard.GetState(PlayerIndex.One);

 

                Keys[] pressedKeys = kState.GetPressedKeys();

 

                ballVelocity = Vector2.Zero;

 

                foreach (Keys k in pressedKeys)

                    switch (k)

                    {

                        case Keys.Right:

                            ballVelocity.X = velocityMultiplier;

                            break;

                        case Keys.Left:

                            ballVelocity.X = -velocityMultiplier;

                            break;

                        case Keys.Down:

                            ballVelocity.Y = velocityMultiplier;

                            break;

                        case Keys.Up:

                            ballVelocity.Y = -velocityMultiplier;

                            break;

                    }

            #elif XBOX

                    ballVelocity.X = velocityMultiplier * GamePad.GetState(PlayerIndex.One).ThumbSticks.Left.X;

                    ballVelocity.Y = -velocityMultiplier * GamePad.GetState(PlayerIndex.One).ThumbSticks.Left.Y;     

            #endif

 

                ballPt += ballVelocity;

 

            if (CheckForCollision())

                ResetGame();

 

            base.Update(gameTime);

        }

26.  Вот и все! Мы написали приложение, которое работает на всех 3 платформах! Уф! Разве не так просто?