Опубликован: 14.08.2012 | Доступ: свободный | Студентов: 880 / 20 | Оценка: 5.00 / 5.00 | Длительность: 09:59:00
Специальности: Программист
Самостоятельная работа 3:

2D-графика в XNA

Рассмотрим изменения, внесенные в компонент.

Мы добавили три переменных – sprTexture, sprRectangle, sprPosition – для хранения текстуры, прямоугольника, показывающего координаты спрайта в текстуре, и позиции вывода спрайта на экран.

Далее, мы модифицировали конструктор класса, добавив в список передаваемых параметров следующие переменные:

ref Texture2D newTexture – передача текстуры. Параметр передается по ссылке, то есть объект получает не копию текстуры, а лишь ссылку на нее – это экономит системные ресурсы.

Rectangle newRectangle – прямоугольник, задающий координаты спрайта в изображении.

Vector2 newPosition – позиция спрайта на игровом экране.

В теле конструктора мы инициализируем соответствующие свойства класса переданными параметрами.

Далее, мы дорабатываем код метода Draw. Здесь нам предстоит наиболее сложная часть модификации. Дело в том, в игре, для которой мы создаем компонент, уже есть все средства для вывода изображений. Поэтому разумнее всего воспользоваться уже существующим объектом типа SpriteBatch для того, чтобы с его помощью вывести изображение на экран. Для того, чтобы сделать однажды созданный SpriteBatch доступным для всех компонентов игры, существует специальная методика. Есть такое понятие, как игровые сервисы, которые доступны всем компонентам. Сначала мы добавим созданный в классе Game1 объект типа SpriteBatch в список игровых сервисов (мы рассмотрим это ниже), после чего сможем извлечь этот объект в игровом компоненте. Мы так и поступаем – следующая команда создает новый объект sprBatch, который содержит ссылку на исходный объект типа SpriteBatch, который ранее, в классе Game1, добавлен в список игровых ресурсов.

SpriteBatch sprBatch =  (SpriteBatch)Game.Services.GetService(typeof(SpriteBatch));

После того, как у нас появилась ссылка на исходный объект типа SpriteBatch, мы можем вывести изображение, используя перегруженный метод Draw такого объекта. Ранее мы пользовались этим методом, передавая ему текстуру, позицию текстуры в игровом окне и оттенок текстуры. Теперь же к списку передаваемых ему параметров добавляется прямоугольник, задающий позицию спрайта в файле текстуры. Этот вызов выглядит так:

sprBatch.Draw(sprTexture, sprPosition, sprRectangle, Color.White);

Выше мы выводили спрайт на экран, сначала вызвав метод Begin() объекта типа SpriteBatch, потом – вызывали метод Draw(), потом – метод End(), завершающий вывод. Здесь мы не вызываем этих методов. Дело в том, что данные методы будут вызваны в методе Draw(), который вызывает основная игра (объект типа Game1). В код класса Game1 внесены некоторые изменения, которые мы сейчас рассмотрим. В листинге 8.9 вы можете видеть этот код.

using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Input.Touch;
using Microsoft.Xna.Framework.Media;

namespace P3_3
{
    /// <summary>
    /// Это главный тип игры
    /// </summary>
    public class Game1 : Microsoft.Xna.Framework.Game
    {
        GraphicsDeviceManager graphics;
        SpriteBatch spriteBatch;
        spriteComp gameObject;
        Texture2D texture;

        public Game1()
        {
            graphics = new GraphicsDeviceManager(this);
            Content.RootDirectory = "Content";

            // Частота кадра на Windows Phone по умолчанию — 30 кадров в секунду.
            TargetElapsedTime = TimeSpan.FromTicks(333333);

            // Дополнительный заряд аккумулятора заблокирован.
            InactiveSleepTime = TimeSpan.FromSeconds(1);
        }

        /// <summary>
        /// Позволяет игре выполнить инициализацию, необходимую перед запуском.
        /// Здесь можно запросить нужные службы и загрузить неграфический
        /// контент.  Вызов base.Initialize приведет к перебору всех компонентов и
        /// их инициализации.
        /// </summary>
        protected override void Initialize()
        {
            // ЗАДАЧА: добавьте здесь логику инициализации

            base.Initialize();
        }

        /// <summary>
        /// LoadContent будет вызываться в игре один раз; здесь загружается
        /// весь контент.
        /// </summary>
        protected override void LoadContent()
        {
            // Создайте новый SpriteBatch, который можно использовать для отрисовки текстур.
            spriteBatch = new SpriteBatch(GraphicsDevice);
            Services.AddService(typeof(SpriteBatch), spriteBatch);
            texture = Content.Load<Texture2D>("BallandBats");
            CreateNewObject();

            // ЗАДАЧА: используйте здесь this.Content для загрузки контента игры
        }

        protected void CreateNewObject()
        {
            gameObject = new spriteComp(this, ref texture,
                new Rectangle(18, 9, 17, 88), new Vector2(100, 150));
            Components.Add(gameObject);
        }


        /// <summary>
        /// UnloadContent будет вызываться в игре один раз; здесь выгружается
        /// весь контент.
        /// </summary>
        protected override void UnloadContent()
        {
            // ЗАДАЧА: выгрузите здесь весь контент, не относящийся к ContentManager
        }

        /// <summary>
        /// Позволяет игре запускать логику обновления мира,
        /// проверки столкновений, получения ввода и воспроизведения звуков.
        /// </summary>
        /// >param name="gameTime">Предоставляет моментальный снимок значений времени.</param>
        protected override void Update(GameTime gameTime)
        {
            // Позволяет выйти из игры
            if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
                this.Exit();

            // ЗАДАЧА: добавьте здесь логику обновления

            base.Update(gameTime);
        }

        /// <summary>
        /// Вызывается, когда игра отрисовывается.
        /// </summary>
        /// >param name="gameTime">Предоставляет моментальный снимок значений времени.</param>
        protected override void Draw(GameTime gameTime)
        {
            GraphicsDevice.Clear(Color.CornflowerBlue);

            // ЗАДАЧА: добавьте здесь код отрисовки
            spriteBatch.Begin();
            base.Draw(gameTime);
            spriteBatch.End();
        }
    }
}
Листинг 8.9. Код класса Game1

Для начала мы добавили новые переменные, одна из которых (типа spriteComp) служит для хранения объекта класса spriteComp, а вторая (типа Texture2D) служит для хранения текстуры. Так как текстура содержит несколько спрайтов, переменная Texture2D может понадобиться при создании нескольких объектов, поэтому имеет смысл создать эту переменную, один раз загрузить в нее изображение и пользоваться ей.

В методе LoadContent() мы, как обычно, создаем объект типа SpriteBatch, и, с помощью команды Services.AddService(typeof(SpriteBatch), spriteBatch); добавляем объект SpriteBatch к списку игровых сервисов. Именно после этой команды spriteBatch можно вызывать в игровых компонентах и выводить их изображений.

Далее в этом методе мы загружаем текстуру с изображениями в переменную texture и вызываем метод CreateNewObject();. Этот метод мы создали самостоятельно – мы используем его для создания объекта gameObject.

gameObject  = new spriteComp (this, ref texture, new Rectangle (18,9,17,88), new Vector2 (100,150));

При создании этого объекта мы передаем конструктору класса spriteComp объект игры (this), ссылку на текстуру, объект Rectangle – обратите внимание на то, что мы создаем новый объект Rectangle, который инициализирован значениями координаты нужного нам спрайта в файле текстуры, вычисленными ранее. Так же мы передаем конструктору новый объект типа Vector2, который содержит координаты спрайта для вывода на игровой экран.

В методе CreateNewObject() мы разместили еще одну команду – Components.Add(gameObject );. С ее помощью мы добавляем в список компонентов игры только что созданный объект. Это очень важная команда – благодаря ей при исполнении команды base.Draw(gameTime); будет обработан метод Draw нашего игрового компонента, и изображения, которые он выведет, будут выведены на экран.

В методе Draw() класса Game1 мы используем такой код:

spriteBatch.Begin();
base.Draw(gameTime);
spriteBatch.End();

Команда spriteBatch.Begin(); готовит графическое устройтство для вывода изображений, после этого осуществляется вывод изображений – по вызову base.Draw(gameTime); обрабатываются соответствующие методы игровых компонентов, унаследованных от DrawableGameComponent. Напомню, что в методе Draw() нашего компонента spriteComp есть лишь вызов метода Draw() объекта класса SpriteBatch, без вызовов Begin() и End(). В результате, после того, как компонент выведет изображение, вывод завершается командой End() объекта spriteBatch в методе Draw() класса Game1 и изображение появляется на экране.

Такой механизм работы позволяет создавать множество компонентов, добавлять их в список компонентов игры и не заботиться о выводе каждого из них в методе Draw() класса Game1. После того, как компонент добавлен в список компонентов игры, вывод его графического представления осуществляется автоматически. Можно сказать, что метод base.Draw(gameTime); "просматривает" все зарегистрированные компоненты и выводит их на экран.

Именно такой подход удобнее всего использовать при разработке реальных игр.

Вот как выглядит игровой экран после вывода в него изображения (рис. 8.12.

Игровой экран после вывода изображения

Рис. 8.12. Игровой экран после вывода изображения

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

8.5. Вывод нескольких изображений

Рассмотрим вывод нескольких изображений в игровое окно. Как мы уже говорили, с использованием зарегистрированных игровых компонентов, унаследованных от DrawableGameComponent, вывод автоматизирован. Однако это не значит, что использовать более простые методы вывода не нужно.

Например, рассмотрим вывод фонового изображения в уже созданном проекте P2_3. Выше мы создали изображения, которые можно использовать для создания собственной версии игры Pong. Точно так же создадим изображение, которое будет служить фоном для игры. Стандартный игровой экран имеет разрешение 800х480 пикселей. Создадим изображение такого размера (назовем его background.png), загрузим его в проект. Для этого добавим в класс новую переменную

Texture2D backTexture;

После этого, в методе LoadContent(), загрузим изображение в эту переменную командой

backTexture = Content.Load<Texture2D>("Background");

Теперь выведем изображение с помощью метода Draw() объекта spriteBatch.

spriteBatch.Draw(backTexture, new Rectangle(0, 0, 800, 480), Color.White);

Поместим эту команду между вызовами Begin() и End() перед командой base.Draw(gameTime);

В результате окно игры приобретет такой вид (рис. 8.13).

Игровой экран после вывода изображения игрового объекта и фона

Рис. 8.13. Игровой экран после вывода изображения игрового объекта и фона

Как видите, спрайт игрового объекта выведен поверх фонового изображения.

Подход, требующий соблюдения очередности вывода изображений для регулирования их наложения, не очень удобен. Однако XNA Framework предусматривает специальные инструменты для настройки взаимного расположения объектов. В частности, при выводе спрайта можно указывать "глубину" (depth), на которой он расположен.

8.6. Выводы

В данной лабораторной работе мы рассмотрели основы работы с графикой в XNA-играх для Windows Phone 7

8.7. Задание

Нарисуйте несколько изображений в любом известном вам графическом редакторе. Выведите эти изображения в игровое окно XNA, пользуясь методикой, изложенной в пункте о выводе нескольких изображений на экран. Если вы разрабатываете двумерную игру, займитесь подготовкой изображений для нее, проверьте, как выбранные изображения выглядят при выводе их на экран эмулятора или телефона.

Гулич Анна
Гулич Анна
Невозможно пройти тесты, в окне с вопросами пусто