Быть может кто-то из Вас знает игру Sims, к какому жанру она относиться? Жизненная симуляция, ролевая игра, там можно и дома строить..... |
2D-графика в XNA Game Studio 2.0.
Теперь загрузим изображение в проект и продолжим работу. Как вы помните, выше мы создали шаблон игрового компонента, который подходит для вывода на экран. Теперь доработаем этот шаблон. В листинге 6.8. вы можете видеть доработанный код игрового компонента
using System; using System.Collections.Generic; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Audio; using Microsoft.Xna.Framework.GamerServices; using Microsoft.Xna.Framework.Graphics; using Microsoft.Xna.Framework.Input; using Microsoft.Xna.Framework.Storage; using Microsoft.Xna.Framework.Content; namespace P2_3 { /// <summary> /// This is a game component that implements IUpdateable. /// </summary> public class 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 } /// <summary> /// 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. /// </summary> public override void Initialize() { // TODO: Add your initialization code here base.Initialize(); } /// <summary> /// Allows the game component to update itself. /// </summary> /// <param name="gameTime">Provides a snapshot of timing values</param> public override void Update(GameTime gameTime) { // TODO: Add your update code here base.Update(gameTime); } public override void Draw(GameTime gameTime) { SpriteBatch sprBatch = (SpriteBatch)Game.Services.GetService(typeof(SpriteBatch)); sprBatch.Draw(sprTexture, sprPosition, sprRectangle, Color.White); base.Draw(gameTime); } } }Листинг 6.8. Доработанный код игрового компонента
Рассмотрим изменения, внесенные в компонент.
Мы добавили три переменных – 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 внесены некоторые изменения, которые мы сейчас рассмотрим. В листинге 6.9. вы можете видеть этот код.
using System; using System.Collections.Generic; 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.Net; using Microsoft.Xna.Framework.Storage; namespace P2_3 { public class Game1 : Microsoft.Xna.Framework.Game { GraphicsDeviceManager graphics; SpriteBatch spriteBatch; spriteComp gameObject; Texture2D texture; public Game1() { graphics = new GraphicsDeviceManager(this); Content.RootDirectory = "Content"; } protected override void Initialize() { // TODO: Add your initialization logic here base.Initialize(); } protected override void 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(); } protected void CreateNewObject() { gameObject = new spriteComp (this, ref texture, new Rectangle (18,9,17,88), new Vector2 (100,150)); Components.Add(gameObject ); } protected override void UnloadContent() { // TODO: Unload any non ContentManager content here } protected override void Update(GameTime gameTime) { // Allows the game to exit if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed) this.Exit(); // TODO: Add your update logic here base.Update(gameTime); } protected override void Draw(GameTime gameTime) { graphics.GraphicsDevice.Clear(Color.CornflowerBlue); // TODO: Add your drawing code here spriteBatch.Begin(); base.Draw(gameTime); spriteBatch.End(); } } }Листинг 6.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) ; "просматривает" все зарегистрированные компоненты и выводит их на экран.
Именно такой подход удобнее всего использовать при разработке реальных игр.
Вот как выглядит игровой экран после вывода в него изображения (рис.6.10.).
Здесь мы рассмотрели различные подходы к выводу изображений. Однако, можно заметить, что вывод изображения, которое ассоциируется с игровым объектом – это лишь одна из задач, которую приходится решать при разработке игры. Другие задачи – например – перемещение объекта, проверка, не столкнулся ли объект с другими объектами – так же можно решать с использованием различных подходов. Можно заметить, что наиболее сложный с точки зрения реализации подход – разработка отдельного игрового компонента, является одновременно и наиболее удобным для реализации различных возможностей игрового объекта. Позже, дорабатывая функциональность игровых объектов, мы будем пользоваться именно этим подходом.
Рассмотрим вывод нескольких изображений в игровое окно. Как мы уже говорили, с использованием зарегистрированных игровых компонентов, унаследованных от DrawableGameComponent, вывод автоматизирован. Однако это не значит, что использовать более простые методы вывода не нужно.
Например, рассмотрим вывод фонового изображения в уже созданном проекте P2_3. Выше мы создали изображения, которые можно использовать для создания собственной версии игры Pong. Точно так же создадим изображение, которое будет служить фоном для игры. Стандартный игровой экран имеет разрешение 800х600 пикселей. Создадим изображение такого размера (назовем его background.png), загрузим его в проект. Для этого добавим в класс новую переменную
Texture2D backTexture;
После этого, в методе LoadContent(), загрузим изображение в эту переменную командой
backTexture = Content.Load<Texture2D>("Background") ;
Теперь выведем изображение с помощью метода Draw() объекта spriteBatch.
spriteBatch.Draw(backTexture, new Rectangle(0, 0, 800, 600), Color.White) ;
Поместим эту команду между вызовами Begin() и End() перед командой base.Draw(gameTime) ;
В результате окно игры приобретет такой вид (рис. 6.11.).
Как видите, спрайт игрового объекта выведен поверх фонового изображения.