Иван Андреев

advertisement
Иван Андреев
andreev.ia@gmail.com
XNA для начинающих. Практика. Перемещение объектов в 2D
пространстве
Целью данной работы является получение базовых навыков работы с
2D графикой и игровым временем.
Задание на работу:
Создать XNA приложение, установить размеры окна. Нарисовать в
левом верхнем углу экрана спрайт произвольного размера (лучше установить
достаточно большой размер). При прохождении некоторого количества
времени (либо при нажатии на кнопку на клавиатуре) спрайт должен
переместиться в правый верхний угол, затем в правый нижний угол, левый
нижний угол и, наконец, в центр экрана, причем центр спрайта должен
совпасть с центром экрана. Далее, можно либо зациклить перемещение,
либо оставить спрайт в центре экрана.
Теоретическая часть:
Основы 2Д графики
Перед тем как перейти к работе следует вспомнить теоретические
основы.
В
двумерной
графике
каждый
пиксель
характеризуется
координатами и цветом. На экране монитора (а также в любом
компьютерном изображении, например, картинке, созданной в Paint)
началом координат является левый верхний угол, ось X направлена слева
направо, а ось Y сверху вниз. Координаты пикселя отсчитываются от 0,
соответственно левый верхний угол экрана имеет координаты {0,0}, а правый
нижний – {MaxX-1, MaxY-1}, где MaxX, MaxY – разрешение экрана.
1
Для того чтобы установить разрешение окна в XNA Framework
необходимо написать следующий код:
public Game1()
{
graphics = new GraphicsDeviceManager(this);
Content.RootDirectory = "Content";
graphics.PreferredBackBufferWidth = 640;
graphics.PreferredBackBufferHeight = 480;
}
В данном примере ширина устанавливается равной 640, а высота – 480.
Цвет пикселя составляется из трех компонент: красной, синей и
зеленой (также имеется альфа-канал, который отвечает за прозрачность
данного
пикселя
для
изображений,
для
пикселя
монитора
альфа
составляющая не имеет смысла). Значения цвета по каждой из этих
компонент лежат в диапазоне от 0 до 255 либо от 0 до 1. Так, если значения
каждой из компонент равняются 255, получается белый цвет, а если
значения равняются 0, то получается черный свет.
Для работы с цветами в XNA существует класс Color, который
поддерживает огромное количество стандартных
цветов
(например,
Color.Red – красный, Color.Black - черный), а также позволяет создавать
собственные цвета.
SpriteBatch
Основным объектом для работы с 2D графикой является SpriteBatch,
более подробно работа со SpriteBatch будет рассмотрена в следующих
практических занятиях, в рамках данного занятия необходимо знать лишь
основные особенности работы со SpriteBatch.
2
Вся работа по отображению изображений на экран осуществляется в
рамках
блоков
отображения
SpriteBatch.Begin
изображений
на
–
SpriteBatch.End.
экран
нужно
Для
собственно
использовать
метод
SpriteBatch.Draw. Существуют большое количество перегруженных вариантов
вызова данного метода, однако в рамках данной работы будет рассмотрен
наиболее простой.
public void Draw (
Texture2D texture,
Vector2 position,
Color color
)
texture – изображение, которое необходимо нарисовать на экране.
position – позиция левого верхнего угла изображения на экране.
color – цвет, используемый для смешивания перед выводом
изображения на экран. Чаще всего, этот параметр будет принимать значение
Color.White. В таком случае изображение будет иметь цвета, которые были
заданы
при
создании
изображения.
Вы
можете
самостоятельно
поэксперементировать с данным параметром.
Загрузка содержимого (контента) игры
Перед тем как работать с изображениями (или другими типами
игрового содержимого: моделями, звуками, шрифтами и т.д.) необходимо
загрузить их в игру при помощи ContentManager.
Для
загрузки
игрового
содержимого
в
каркасе
приложения
предусмотрен специальный метод LoadContent.
Загрузка определенного типа содержимого осуществляется вызовом
метода Content.Load<ТипСодержимого>(“НазваниеФайлаСодержимого”).
3
Например, работы с друмерными изображениями в XNA Frawework
используется класс Texture2D.
sprite = Content.Load<Texture2D>("filename");
Объект Texture2D содержит различные свойства, которые пригодятся
нам для реализации данной работы.
В частности, Width и Height возращают значения ширины и высоты
исходного изображения.
Игровое время
Теперь вспомним основы работы с игровым временем в XNA
Framework. Методы Update и Draw получают в качестве параметра
единственный объект GameTime, который отвечает за игровое время.
Рассмотрим основые свойства объекта GameTime:
ElapsedGameTime
Игровое
время,
предыдущего
прошедшее
вызова
с
метода
Draw/Update
ElapsedRealTime
Реальное
время,
предыдущего
прошедшее
вызова
с
метода
Draw/Update
IsRunningSlowly
Если это свойство имеет значение
true, то время, проходящее между
последовательными
метода
вызовами
Update/Draw
значения,
больше
указанного
в
TargetElapsedTime. Это означает, что
игра
4
работает
медленнее,
чем
требуется.
Если
IsRunningSlowly
равняется true, то стоит задуматься
над оптимизацией
TotalGameTime
Игровое время, прошедшее с запуска
игры
TotalRealTime
Реальное
время,
прошедшее
с
запуска игры
Для нас наиболее важными свойствами являются ElapsedGameTime и
TotalGameTime. Они возвращают объект типа TimeSpan, который имеет
множество различных свойств. Рассмотрим лишь наиболее важные:
Days
Целое количество дней
Hours
Целое количество часов
Milliseconds
Целое количество миллисекунд
Minutes
Целое количество минут
Seconds
Целое количество секунд
Ticks
Целое количество системных «тиков»
TotalDays
Вещественное значение дней
TotalHours
Вещественное значение часов
TotalMilliseconds
Вещественное значение миллисекунд
TotalMinutes
Вещественное значение минут
TotalSeconds
Вещественное значение секунд
Обратите внимание на то, что свойства, название которых не
начинается с Total, возвращают целое число, то есть, например, Days будет
иметь значение 0 до тех пор, пока не пройдет целый день. В то же время
5
значение TotalDays будет постоянно увеличиваться и по-прошествие дня
примет значение 1.
Реализация:
В
следующем
листинге
приведена
простая
реализация
без
зацикливания и обработки пользовательского ввода:
using
using
using
using
using
using
using
using
using
using
using
using
System;
System.Collections.Generic;
System.Linq;
Microsoft.Xna.Framework;
Microsoft.Xna.Framework.Audio;
Microsoft.Xna.Framework.Content;
Microsoft.Xna.Framework.GamerServices;
Microsoft.Xna.Framework.Graphics;
Microsoft.Xna.Framework.Input;
Microsoft.Xna.Framework.Media;
Microsoft.Xna.Framework.Net;
Microsoft.Xna.Framework.Storage;
namespace Lab1
{
/// <summary>
/// This is the main type for your game
/// </summary>
public class Game1 : Microsoft.Xna.Framework.Game
{
GraphicsDeviceManager graphics;
SpriteBatch spriteBatch;
Texture2D sprite;
int width;
int height;
public Game1()
{
graphics = new GraphicsDeviceManager(this);
Content.RootDirectory = "Content";
width = graphics.PreferredBackBufferWidth = 800;
height = graphics.PreferredBackBufferHeight = 600;
}
/// <summary>
/// Allows the game to perform any initialization it needs to before
starting to run.
/// This is where it can query for any required services and load any
non-graphic
/// related content. Calling base.Initialize will enumerate through
any components
/// and initialize them as well.
/// </summary>
protected override void Initialize()
{
// TODO: Add your initialization logic here
6
base.Initialize();
}
/// <summary>
/// LoadContent will be called once per game and is the place to load
/// all of your content.
/// </summary>
protected override void LoadContent()
{
// Create a new SpriteBatch, which can be used to draw textures.
spriteBatch = new SpriteBatch(GraphicsDevice);
// TODO: use this.Content to load your game content here
sprite = Content.Load<Texture2D>("hero");
}
/// <summary>
/// UnloadContent will be called once per game and is the place to
unload
/// all content.
/// </summary>
protected override void UnloadContent()
{
// TODO: Unload any non ContentManager content here
}
/// <summary>
/// Allows the game to run logic such as updating the world,
/// checking for collisions, gathering input, and playing audio.
/// </summary>
/// <param name="gameTime">Provides a snapshot of timing
values.</param>
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);
}
/// <summary>
/// This is called when the game should draw itself.
/// </summary>
/// <param name="gameTime">Provides a snapshot of timing
values.</param>
protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.CornflowerBlue);
// TODO: Add your drawing code here
// Координаты левого верхнего угла экрана
Vector2 pos = new Vector2(0, 0);
if (gameTime.TotalGameTime.TotalSeconds > 1)
{
// Координаты правого верхнего угла экрана
pos = new Vector2(width - sprite.Width, 0);
7
}
if (gameTime.TotalGameTime.TotalSeconds > 2)
{
pos = new Vector2(width - sprite.Width, height sprite.Height);
}
if (gameTime.TotalGameTime.TotalSeconds > 3)
{
pos = new Vector2(0, height - sprite.Height);
}
if (gameTime.TotalGameTime.TotalSeconds > 4)
{
pos = new Vector2(width / 2 - sprite.Width / 2, height/2 sprite.Height/2);
}
spriteBatch.Begin();
spriteBatch.Draw(sprite, pos, Color.White);
spriteBatch.End();
base.Draw(gameTime);
}
}
}
8
Download