В этой лабораторной работе мы рассмотрим систему координат, которая используется при создании двумерных игр, поговорим о выводе двумерных изображений в XNA, рассмотрим вопросы наложения изображений, настройки размеров игрового окна и работы в оконном и полноэкранном режимах.
Цель работы:
Освоить основные концепции и приемы работы с двумерными изображениями в XNA Game Studio 2.0/3.0
Задачи работы:
Ознакомиться с двумерной системой координат
Создать двумерное изображение в графическом редакторе
Научиться загружать изображения в игровой проект
Научиться выводить изображения в игровое окно
Разработать стандартный класс, который используется для конструирования игровых объектов и хранения информации о текстуре и позиции изображения
Разработать класс, унаследованный от DrawableGameComponent, научиться выводить изображение, ассоциированное с ним
Система координат
В разработке двумерных игр используется двумерная система координат. На рис. 2.1. вы можете видеть обычную систему координат и объект, изображенный в этой системе.
Рис. 2.1. Традиционная система координат
В данной системе координат начало координат расположено в левом нижнем углу системы – именно здесь находится точка 0,0. Координаты по оси Х возрастают слева направо, по оси Y – снизу вверх.
При разработке компьютерных игр используется экранная система координат. Главное отличие этой системы от традиционной заключается в том, что начало координат расположено в левом верхнем углу экрана. Координаты по оси Х возрастают слева направо, а вот ось Y оказывается перевернутой – ее координаты возрастают сверху вниз. На рис. 2.2. вы можете видеть эту систему координат, в которой нарисован тот же треугольник.
Рис. 2.2. Экранная система координат
Границы экранной системы координат – это либо границы игровой области окна – при оконном режиме работы, либо – разрешение экрана при экранном режиме. Например, если в оконном режиме мы имеем размер окна, равным 800х600 пикселей, то именно эти числа являются границами системы координат.
Прежде чем заниматься работой с изображениями в XNA, эти изображения, для начала, нужно создать. Создадим простое изображение в стандартном графическом редакторе Windows – Paint. Для запуска редактора воспользуемся командой Пуск(Все программы(Стандартные(Paint. Нарисуем в редакторе прямоугольник (рис. 2.3.), сохраним его под именем P2_1.png. Как следует из расширения, мы сохранили рисунок в формате PNG.
Рис. 2.3. Создаем простое изображение в редакторе Paint
Создадим новый стандартный игровой проект с именем P2_1. В панели Solution Explorer щёлкнем правой кнопкой мыши по папке Content и в появившемся контекстном меню выберем пункт Add(Existing Item. Появится стандартное окно для работы с файлами. Найдём с его помощью графический файл и добавим в проект. В результате панель Solution Explorer должна выглядеть так, как изображено на рис. 2.4.
Рис. 2.4. Изображение, добавленное в проект
Обратите внимание на то, что при добавлении изображения в проект, оно физически копируется в папку Content, которая находится в папке, содержащей файлы проекта. Файл, добавленный в проект имеет то же самое имя, которое он имел при сохранении. Выделив этот файл, мы можем просмотреть его свойства – в окне Properties. Это окно можно видеть на том же рис. 2.4. В данном случае обратите внимание на параметр Asset Name – здесь мы можем видеть имя изображения в проекте. Как и можно ожидать, оно носит имя P2_1.
Теперь выполним действия, необходимые для вывода нашего изображения на игровой экран. Для этого в раздел объявления свойств класса добавим две переменные (листинг 2.1.).
Листинг 2.1. Переменные для работы с изображением
1 2 3
private Texture2D MySprite; private Vector2 position =new Vector2(150,200);
Первая переменная – MySprite – имеет тип Texture2D. Этот тип данных предназначен для хранения двумерных изображений, или текстур. Именно в эту переменную мы будем загружать изображение, которое ранее добавили в проект. Вторая переменная - position – нужна для хранения позиции, в которую наша текстура будет выведена на экран. Мы сразу же инициализируем эту переменную значениями 150,200 – это координаты вывода изображения. Координаты привязаны к левому верхнему углу изображения. То есть – именно его левый верхний угол будет находиться в позиции 150,200 при выводе. Если бы мы инициализировали эту переменную значениями 0,0, изображение было бы выведено в начале координат – то есть – его верхний угол находился бы в позиции 0,0, а само оно располагалось бы в верхнем левом углу игрового поля.
Теперь загрузим текстуру в переменную MySprite. Для этого модифицируем метод LoadContent следующим образом (листинг 2.2.).
Листинг 2.2. Загрузка изображения в переменную MySprite
Здесь, для наглядности, удалены стандартные комментарии. Итак, для загрузки мы пользуемся методом Load объекта Content. Причем, обратите внимание на то, что метод Load вызывается с указанием типа загружаемого игрового ресурса. В нашем случае это – двумерное изображение, поэтому тип ресурса указывается как Texture2D. Несложно заметить, что этот тип соответствует типу переменной MySprite.
Теперь настала очередь вывода изображения на игровой экран. Для этого нам понадобится модифицировать метод Draw. В листинге 2.3. приведен его код.
Метод Begin() объекта spriteBatch подготавливает графическое устройство к выводу изображения. Метод Draw того же объекта принимает в качестве параметров переменную типа Texture2D (MySprite), переменную типа Vector2 (position) и цвет, в соответствии с которым будет изменен оттенок изображения. В данном случае это белый цвет – это значит, что цвета изображения останутся неизменными. Метод End() завершает процедуру вывода – изображения выводятся на экран.
Метод Draw базового класса в данном случае не используется для вывода изображений. Мы рассмотрим его роль в выводе изображений ниже.
Запустив программу, мы получим такое изображение (рис. 2.5.):
Рис. 2.5. Изображение, выведенное на игровой экран
Мы рассмотрели простейший способ вывода изображения. Однако использование такого способа вывода изображений при разработке реальных игровых проектов не слишком удобно. Дело в том, что игровой объект, который стоит за каждым изображением, может иметь множество свойств. Например – скорость и направление движения. Так же количество объектов в игре может меняться. К тому же, не очень удобно хранить множество свойств различных игровых объектов, используя область объявления свойств основного игрового проекта. Удобнее было бы выделить данные об игровых объектах в отдельный класс и пользоваться объектами, сгенерированными на основе этого класса для вывода изображений и организации игровой логики. Собственно говоря, на практике так и поступают, создавая отдельные игровые компоненты (классы) для представления игровых объектов. Как правило, в реальных проектах создается целая система классов игровых объектов, связанных отношениями наследования. Способ, описанный выше, может использоваться для вывода некоторых объектов, например – так можно вывести фоновый рисунок.
Рассмотрим подход, который позволяет упростить подход к выводу изображений методом, описанным выше.
Разработка класса для хранения графической информации
Создадим новый стандартный проект с именем P2_2. Добавим в проект изображение, которое мы создали выше (P2_1.png).
Теперь подумаем, каким должен быть класс для представления игрового объекта. Сейчас нам нужен класс, объект которого сможет хранить текстуру спрайта и информацию о его позиции на экране. При необходимости этот список можно расширить – причем – как свойствами – так и методами. Но над подобным проектом мы будем работать на одном из следующих занятий. Ограничимся пока двумя вышеназванными свойствами.
Щёлкнем по значку проекта P2_2 в панели Solution Explorer и выберем в появившемся меню пункт Add(Class. Появится окно добавления нового элемента, в котором будет выделен шаблон пустого класса (рис. 2.6.).
Рис. 2.6. Добавляем в проект новый класс
Зададим имя новому классу – пусть это будет spriteClass, и нажмем кнопку Add. В проект будет добавлен новый пустой класс. Вот как выглядит наш проект после всех перечисленных действий (рис. 2.7.).
Рис. 2.7. Внешний вид окна проекта после добавления нового класса
Модифицируем код класса spriteClass следующим образом.
Во-первых, в раздел подключения пространств имен добавим директивы using, содержащие те пространства имен, которые содержат объявления типов данных, необходимых для хранения текстуры и её позиции.
Во-вторых – создадим необходимые свойства класса.
В-третьих – построим конструктор класса, который будет инициализировать эти свойства.
Вот как выглядит код класса после всех модификаций (листинг 2.4.):
Здесь мы подключили к нашему классу пространство имен Microsoft.Xna.Framework.Graphics – оно нужно нам, так как содержит объявление типа данных Texture2D. Пространство имен Microsoft.Xna.Framework содержит объявление типа данных Vector2.
В раздел объявления свойств класса мы добавили две переменных. Переменная spTexture типа Texture2D используется для хранения текстуры, переменная spPosition типа Vector2 – для хранения данных о положении объекта на экране. В конструкторе класса мы инициализируем эти переменные значениями соответствующих типов, переданными при создании объекта типа spriteClass в основном тексте программы.
ПРИМЕЧАНИЕ
Свойства класса объявлены с модификаторами доступности public. Это значит, что мы сможем модифицировать эти свойства извне. В данном случае такой подход оправдан. Однако надо отметить, что при конструировании классов рекомендуется защищать свойства от прямой модификации или чтения. Для организации доступа к таким свойствам можно создавать дополнительные свойства-методы, пользуясь директивами get/set.
Теперь внесем изменения в основной текст игры, который расположен в файле Game1.cs. В листинге 2.5. приведены эти изменения с комментариями, указывающими на их местонахождение.
Листинг 2.5. Изменения, внесенные в код файла Game1.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
/// Объект mySpriteObj для хранения данных об изображении для вывода на экран spriteClass mySpriteObj;/// Добавляем в метод LoadContent команду создания нового объекта типа spriteClass. /// Передаем в качестве текстуры изображения текстуру, загруженную с помощью команды Content.Load,/// в качестве позиции – новый объект типа Vector2mySpriteObj =new spriteClass(Content.Load<Texture2D>("P2_1"), new Vector2(100f, 150f));/// В метод Draw добавляем соответствующие вызовы методов объекта spriteBatch. /// Обратите внимание на то, что при вызове метода Draw мы пользуемся свойствами объекта mySpriteObj.spriteBatch.Begin();spriteBatch.Draw(mySpriteObj.spTexture, mySpriteObj.spPosition, Color.White);spriteBatch.End();/// В метод UnloadContent добавляем команды для освобождения системных ресурсов.mySpriteObj.spTexture.Dispose();spriteBatch.Dispose();
Как видите, если сравнить предыдущий подход с хранением всех данных о выводимом изображении в классе Game1 и подход с хранением этих же данных в специально разработанном классе, можно заметить, что выделение отдельного класса для спрайта позволяет удобно организовывать все необходимые свойства и методы для работы с объектом. Однако, и этот подход не является единственно возможным. Еще более интересный способ вывода изображений и создания игровых объектов – это создание игровых объектов на базе игровых компонентов.
Разработка игрового компонента
Создадим новый игровой проект, назовем его P2_3. Щёлкнем правой кнопкой мыши по значку проекта в панели Solution Explorer, выберем пункт Add(New Item. В появившемся окне выберем миниатюру Game Component, зададим имя для компонента – spriteComp и нажмем на кнопку Add. В проект будет добавлен новый игровой компонент. В листинге 2.6. вы можете видеть полный код этого компонента.
Листинг 2.6. Код игрового компонента, унаследованного от GameComponent
usingSystem;usingSystem.Collections.Generic;usingMicrosoft.Xna.Framework;usingMicrosoft.Xna.Framework.Audio;usingMicrosoft.Xna.Framework.GamerServices;usingMicrosoft.Xna.Framework.Graphics;usingMicrosoft.Xna.Framework.Input;usingMicrosoft.Xna.Framework.Storage;usingMicrosoft.Xna.Framework.Content;namespace P2_3{/// /// This is a game component that implements IUpdateable./// publicclass spriteComp : Microsoft.Xna.Framework.GameComponent{public spriteComp(Game game):base(game){// TODO: Construct any child components here}/// /// Allows the game component to perform any initialization it needs to before starting/// to run. This is where it can query for any required services and load content./// publicoverridevoid Initialize(){// TODO: Add your initialization code herebase.Initialize();}/// /// Allows the game component to update itself./// /// Provides a snapshot of timing values.publicoverridevoid Update(GameTime gameTime){// TODO: Add your update code herebase.Update(gameTime);}}}
Можно отметить, что игровой компонент представляет собой класс, который наследует класс Microsoft.Xna.Framework.GameComponent. Наследование класса GameComponent подходит для создания игровых компонентов, которые не имеют графических представлений. Мы же хотим создать компонент, который планируется визуализировать. Поэтому нужно заменить GameComponent на DrawableGameComponent.
Обратите внимание на то, что созданный компонент имеет несколько методов. Так, метод spriteComp – это конструктор класса, метод Initialise предназначен для инициализации компонента, метод Update – это игровой цикл компонента.
Здесь не хватает еще одного метода – метода Draw. Добавим этот метод в класс (после ввода текста public override void нам автоматически предложат список, из которого достаточно выбрать Draw и нажать Enter – набор входных переменных и структура метода будет создана автоматически. В листинге 2.7. представлен измененный код компонента, готовый для дальнейшей работы.
Листинг 2.7. Код игрового компонента, унаследованного от DrawableGameComponent
usingSystem;usingSystem.Collections.Generic;usingMicrosoft.Xna.Framework;usingMicrosoft.Xna.Framework.Graphics;usingMicrosoft.Xna.Framework.Content;namespace P2_3{/// /// This is a game component that implements IUpdateable./// publicclass spriteComp : Microsoft.Xna.Framework.DrawableGameComponent{public spriteComp(Game game):base(game){// TODO: Construct any child components here}/// /// Allows the game component to perform any initialization it needs to before starting/// to run. This is where it can query for any required services and load content./// publicoverridevoid Initialize(){// TODO: Add your initialization code herebase.Initialize();}/// /// Allows the game component to update itself./// /// Provides a snapshot of timing values.publicoverridevoid Update(GameTime gameTime){// TODO: Add your update code herebase.Update(gameTime);}publicoverridevoid Draw(GameTime gameTime){base.Draw(gameTime);}}}
Выше мы рассматривали вывод прямоугольного изображения, которое занимало всю площадь графического файла. Нам не требовалось никаких дополнительных настроек для вывода такого изображения. На практике не очень удобно пользоваться множеством отдельных файлов, которые хранят изображения. К тому же, далеко не все изображения – это прямоугольники. Здесь нам понадобится, во-первых – более совершенный, чем Paint, инструмент для рисования, а во-вторых – несколько другая методика вывода изображений. Ранее для вывода изображения нам требовалась текстура и координата изображения в игровом окне. Теперь же понадобятся координаты положения нужного нам изображения в графическом файле.
Создадим новый графический файл. Воспользуемся для этого графическим редактором Adobe Photoshop CS. На рис. 2.8. вы можете видеть изображение в окне редактора. Обратите внимание на то, что один графический файл содержит несколько изображений, а так же на то, что фон сделан прозрачным – такая техника позволяет выводить в игровое окно рисунки сложной формы.
Рис. 2.8. Создание изображений в Adobe Photoshop
Здесь мы создали три изображения – эти изображения на следующих занятиях пригодятся нам для создания собственного варианта классической игры Pong.
Сохраним изображение в виде PNG-файла. Такие файлы поддерживают прозрачность фона. Теперь нам нужно решить еще одну задачу – определить координаты отдельных спрайтов в нашем большом изображении. Это можно сделать следующим образом. Откроем сохраненное изображение в каком-нибудь графическом редакторе, можно – в том же Photoshop CS – мы рассмотрим вопрос определения координат именно для этой программы. Увеличим масштаб изображения, переключимся на панель Info, и, наведя мышь на пиксель левого верхнего угла изображения, запишем отобразившиеся координаты указателя (рис. 2.9.). В нашем случае это (18, 9).
Рис. 2.9. Определяем координаты спрайта в графическом файле
Точно так же узнаем координату правого нижнего угла изображения. В нашем случае это – (35, 97). Для дальнейшей работы нам понадобится знание координаты левой верхней точки прямоугольника, ограничивающего спрайт, а так же – его ширина и высота. В нашем случае ширина составляет 17 пикселей (35– 18), высота – 88 пикселей (97 – 9). В итоге получаем следующие параметры прямоугольника, в который заключен наш спрайт:
Х: 18
Y: 9
Width: 17
Height: 88
Ниже эти данные понадобятся нам.
Теперь загрузим изображение в проект и продолжим работу. Как вы помните, выше мы создали шаблон игрового компонента, который подходит для вывода на экран. Теперь доработаем этот шаблон. В листинге 2.8. вы можете видеть доработанный код игрового компонента
usingSystem;usingSystem.Collections.Generic;usingMicrosoft.Xna.Framework;usingMicrosoft.Xna.Framework.Graphics;usingMicrosoft.Xna.Framework.Content;namespace P2_3{/// /// This is a game component that implements IUpdateable./// publicclass spriteComp : Microsoft.Xna.Framework.DrawableGameComponent{private Texture2D sprTexture;private Rectangle sprRectangle;private Vector2 sprPosition;public spriteComp(Game game, ref Texture2D newTexture, Rectangle newRectangle, Vector2 newPosition):base(game){ sprTexture = newTexture; sprRectangle = newRectangle; sprPosition = newPosition;// TODO: Construct any child components here}/// /// Allows the game component to perform any initialization it needs to before starting/// to run. This is where it can query for any required services and load content./// publicoverridevoid Initialize(){// TODO: Add your initialization code herebase.Initialize();}/// /// Allows the game component to update itself./// /// Provides a snapshot of timing values.publicoverridevoid Update(GameTime gameTime){// TODO: Add your update code herebase.Update(gameTime);}publicoverridevoid Draw(GameTime gameTime){ SpriteBatch sprBatch =(SpriteBatch)Game.Services.GetService(typeof(SpriteBatch)); sprBatch.Draw(sprTexture, sprPosition, sprRectangle, Color.White);base.Draw(gameTime);}}}
Рассмотрим изменения, внесенные в компонент.
Мы добавили три переменных – sprTexture, sprRectangle, sprPosition – для хранения текстуры, прямоугольника, показывающего координаты спрайта в текстуре, и позиции вывода спрайта на экран.
Далее, мы модифицировали конструктор класса, добавив в список передаваемых параметров следующие переменные:
ref Texture2D newTexture – передача текстуры. Параметр передается по ссылке, то есть объект получает не копию текстуры, а лишь ссылку на нее – это экономит системные ресурсы.
Rectangle newRectangle – прямоугольник, задающий координаты спрайта в изображении.
Vector2 newPosition – позиция спрайта на игровом экране.
В теле конструктора мы инициализируем соответствующие свойства класса переданными параметрами.
Далее, мы дорабатываем код метода Draw. Здесь нам предстоит наиболее сложная часть модификации. Дело в том, в игре, для которой мы создаем компонент, уже есть все средства для вывода изображений. Поэтому разумнее всего воспользоваться уже существующим объектом типа SpriteBatch для того, чтобы с его помощью вывести изображение на экран. Для того, чтобы сделать однажды созданный SpriteBatch доступным для всех компонентов игры, существует специальная методика. Есть такое понятие, как игровые сервисы, которые доступны всем компонентам. Сначала мы добавим созданный в классе Game1 объект типа SpriteBatch в список игровых сервисов (мы рассмотрим это ниже), после чего сможем извлечь этот объект в игровом компоненте. Мы так и поступаем – следующая команда создает новый объект sprBatch, который содержит ссылку на исходный объект типа SpriteBatch, который ранее, в классе Game1, добавлен в список игровых ресурсов.
После того, как у нас появился ссылка на исходный объект типа SpriteBatch, мы можем вывести изображение, используя перегруженный метод Draw такого объекта. Ранее мы пользовались этим методом, передавая ему текстуру, позицию текстуры в игровом окне и оттенок текстуры. Теперь же к списку передаваемых ему параметров добавляется прямоугольник, задающий позицию спрайта в файле текстуры. Этот вызов выглядит так:
Выше мы выводили спрайт на экран, сначала вызвав метод Begin() объекта типа SpriteBatch, потом – вызывали метод Draw(), потом – метод End(), завершающий вывод. Здесь мы не вызываем этих методов. Дело в том, что данные методы будут вызваны в методе Draw(), который вызывает основная игра (объект типа Game1). В код класса Game1 внесены некоторые изменения, которые мы сейчас рассмотрим. В листинге 2.9. вы можете видеть этот код.
usingSystem;usingSystem.Collections.Generic;usingMicrosoft.Xna.Framework;usingMicrosoft.Xna.Framework.Audio;usingMicrosoft.Xna.Framework.Content;usingMicrosoft.Xna.Framework.Graphics;usingMicrosoft.Xna.Framework.Input;namespace P2_3{publicclass Game1 : Microsoft.Xna.Framework.Game{ GraphicsDeviceManager graphics; SpriteBatch spriteBatch; spriteComp gameObject; Texture2D texture;public Game1(){ graphics =new GraphicsDeviceManager(this); Content.RootDirectory="Content";}protectedoverridevoid Initialize(){// TODO: Add your initialization logic herebase.Initialize();}protectedoverridevoid LoadContent(){// Create a new SpriteBatch, which can be used to draw textures. spriteBatch =new SpriteBatch(GraphicsDevice); Services.AddService(typeof(SpriteBatch), spriteBatch); texture = Content.Load<Texture2D>("BallandBats"); CreateNewObject();}protectedvoid CreateNewObject(){ gameObject =new spriteComp (this, ref texture,new Rectangle (18,9,17,88), new Vector2 (100,150)); Components.Add(gameObject );}protectedoverridevoid UnloadContent(){// TODO: Unload any non ContentManager content here}protectedoverridevoid Update(GameTime gameTime){// Allows the game to exitif(GamePad.GetState(PlayerIndex.One).Buttons.Back== ButtonState.Pressed)this.Exit();// TODO: Add your update logic herebase.Update(gameTime);}protectedoverridevoid Draw(GameTime gameTime){ graphics.GraphicsDevice.Clear(Color.CornflowerBlue);// TODO: Add your drawing code here spriteBatch.Begin();base.Draw(gameTime); spriteBatch.End();}}}
Для начала мы добавили новые переменные, одна из которых (типа 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);обрабатываются соответствующие методы игровых компонентов, унаследованных от DrawableGameComponent. Напомню, что в методе Draw() нашего компонента spriteComp есть лишь вызов метода Draw() объекта класса SpriteBatch, без вызовов Begin() и End(). В результате, после того, как компонент выведет изображение, вывод завершается командой End() объекта spriteBatch в методе Draw() класса Game1 и изображение появляется на экране.
Такой механизм работы позволяет создавать множество компонентов, добавлять их в список компонентов игры и не заботиться о выводе каждого из них в методе Draw() класса Game1. После того, как компонент добавлен в список компонентов игры, вывод его графического представления осуществляется автоматически. Можно сказать, что метод base.Draw(gameTime); «просматривает» все зарегистрированные компоненты и выводит их на экран.
Именно такой подход удобнее всего использовать при разработке реальных игр.
Вот как выглядит игровой экран после вывода в него изображения (рис.2.10.).
Рис. 2.10. Игровой экран после вывода компонента
Здесь мы рассмотрели различные подходы к выводу изображений. Однако, можно заметить, что вывод изображения, которое ассоциируется с игровым объектом – это лишь одна из задач, которую приходится решать при разработке игры. Другие задачи – например – перемещение объекта, проверка, не столкнулся ли объект с другими объектами – так же можно решать с использованием различных подходов. Можно заметить, что наиболее сложный с точки зрения реализации подход – разработка отдельного игрового компонента, является одновременно и наиболее удобным для реализации различных возможностей игрового объекта. Позже, дорабатывая функциональность игровых объектов, мы будем пользоваться именно этим подходом.
Рассмотрим вывод нескольких изображений в игровое окно. Как мы уже говорили, с использованием зарегистрированных игровых компонентов, унаследованных от DrawableGameComponent, вывод автоматизирован. Однако это не значит, что использовать более простые методы вывода не нужно.
Например, рассмотрим вывод фонового изображения в уже созданном проекте P2_3. Выше мы создали изображения, которые можно использовать для создания собственной версии игры Pong. Точно так же создадим изображение, которое будет служить фоном для игры. Стандартный игровой экран имеет разрешение 800х600 пикселей. Создадим изображение такого размера (назовем его background.png), загрузим его в проект. Для этого добавим в класс новую переменную
Texture2D backTexture;
После этого, в методе LoadContent(), загрузим изображение в эту переменную командой
backTexture = Content.Load("Background");
Теперь выведем изображение с помощью метода Draw() объекта spriteBatch.
spriteBatch.Draw(backTexture, new Rectangle(0, 0, 800, 600), Color.White);
Поместим эту команду между вызовами Begin() и End() перед командой base.Draw(gameTime); В результате окно игры приобретет такой вид (рис. 2.11.).
Рис. 2.11. Окно вывода игры после добавления фона
Как видите, спрайт игрового объекта выведен поверх фонового изображения.
ПРИМЕЧАНИЕ
Подход, требующий соблюдения очередности вывода изображений для регулирования их наложения, не очень удобен. Однако, XNA Framework предусматривает специальные инструменты для настройки взаимного расположения объектов. В частности, при выводе спрайта можно указывать «глубину» (depth), на которой он расположен.
Вопросы
1) Где находится начало экранной системы координат?
a. В правом нижнем углу экрана
b. В правом верхнем углу экрана
c. В левом верхнем углу экрана
d. В левом нижнем углу экрана
e. В центре экрана
2) В экранной системе координат координата У возрастает при движении вдоль границы экрана
a. Справа налево
b. Слева направо
c. Снизу вверх
d. Сверху вниз
3) В экранной системе координат координата X возрастает при движении вдоль границы экрана
a. Справа налево
b. Слева направо
c. Снизу вверх
d. Сверху вниз
4) Переменную какого типа можно использовать для хранения двумерной текстуры?
a. Vector2
b. Texture2D
c. Rectangle
d. SpriteBatch
5) Переменную какого типа можно использовать для хранения координат в двумерном пространстве?
a. Vector2
b. Texture2D
c. Rectangle
d. SpriteBatch
6) Переменную какого типа можно использовать для хранения параметров прямоугольника?
a. Vector2
b. Texture2D
c. Rectangle
d. SpriteBatch
7) Переменная какого типа используется для создания объекта, который применяется для групповой обработки изображений?
a. Vector2
b. Texture2D
c. Rectangle
d. SpriteBatch
8) Какой метод объекта типа SpriteBatch используется для подготовки графического устройства к выводу изображения?
a. Begin()
b. End()
c. Dispose()
d. Draw()
9) Какой метод объекта типа SpriteBatch используется для завершения операций вывода и фактического отображения объектов на экране?
a. Begin()
b. End()
c. Dispose()
d. Draw()
10) От какого объекта должен быть унаследован игровой объект, который будет иметь графическое представление?
a. Microsoft.Xna.Framework.GameComponent
b. Microsoft.Xna.Framework.DrawableGameComponent
c. Microsoft.Xna.Framework.GraphicsDeviceManager
d. Microsoft.Xna.Framework.CurveKeyCollection
11) Services.AddService(typeof(SpriteBatch), spriteBatch); Приведенная команда из метода LoadContent класса Game1 стандартного игрового проекта нужна для того, чтобы:
a. Добавить объект типа SpriteBatch в список игровых сервисов для того, чтобы сделать его доступным игровым компонентам.
b. Очистить системные ресурсы, занятые объектом SpriteBatch.
c. Вывести на игровой экран изображение, воспользовавшись средствами объекта типа SpriteBatch
d. Создать новый игровой компонент
12) Rectangle a = new Rectangle(100, 120, 150, 140); Эта команда создает новый прямоугольник со следующими параметрами:
a. Координаты левого верхнего угла прямоугольника (100,200), координаты правого нижнего – (150,140)
b. Координаты левого верхнего угла – (100,200), ширина 150, высота – 140.
c. Координаты центра – (100,200), длины сторон – 150 и 140.
d. Длина 100, ширина 100, координаты левого верхнего угла – (150,140).
3375 Прочтений • [2D-графика в XNA Game Studio 2.0/3.0] [08.08.2012] [Комментариев: 0]