Иван Андреев
andreev. *****@***com
XNA для начинающих. Практика. Перемещение объектов в 2D пространстве
Целью данной работы является получение базовых навыков работы с 2D графикой.
Задание на работу:
Создать XNA приложение, установить размеры окна. Нарисовать в левом верхнем углу экрана спрайт произвольного размера (лучше установить достаточно большой размер). При прохождении некоторого количества времени (либо при нажатии на кнопку на клавиатуре) спрайт должен переместиться в правый верхний угол, затем в правый нижний угол, левый нижний угол и, наконец, в центр экрана, причем центр спрайта должен совпасть с центром экрана. Далее, можно либо зациклить перемещение, либо оставить спрайт в центре экрана.
Перед тем как перейти к работе следует вспомнить теоретические основы. В двумерной графике каждый пиксель характеризуется координатами и цветом. На экране монитора (а также в любом компьютерном изображении, например, картинке, созданной в Paint) началом координат является левый верхний угол, ось X направлена слева направо, а ось Y сверху вниз. Координаты пикселя отсчитываются от 0, соответственно левый верхний угол экрана имеет координаты {0,0}, а правый нижний – {MaxX-1, MaxY-1}, где MaxX, MaxY – разрешение экрана.
Для того, чтобы установить разрешение окна в 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 - черный), а также позволяет создавать собственные цвета.
Следующий пример кода заполняет все окно приложения пикселями со случайно выбранным цветом:
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. Media;
using Microsoft. Xna. ;
using Microsoft. Xna. Framework. Storage;
namespace BlendModeTest
{
/// <summary>
/// This is the main type for your game
/// </summary>
public class Game1 : Microsoft. Xna. Framework. Game
{
GraphicsDeviceManager graphics;
SpriteBatch spriteBatch;
Texture2D tex;
public Game1()
{
graphics = new GraphicsDeviceManager(this);
Content. RootDirectory = "Content";
graphics. PreferredBackBufferWidth = 640;
graphics. PreferredBackBufferHeight = 480;
}
/// <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
Color[] colors = new Color[640 * 480];
Random rand = new Random();
for (int i = 0; i < colors. Length; i++)
{
byte r = (byte)rand. Next(255);
byte g = (byte)rand. Next(255);
byte b = (byte)rand. Next(255);
colors[i] = new Color(r, g, b);
}
tex = new Texture2D(GraphicsDevice, 640, 480);
tex. SetData<Color>(colors);
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
}
/// <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
spriteBatch. Begin();
spriteBatch. Draw(tex, new Vector2(0, 0), Color. White);
spriteBatch. End();
base. Draw(gameTime);
}
}
}

Результирующее изображение
Теперь вспомним основы работы с игровым временем в XNA Framework. Методы Update и Draw получают в качестве параметра единственный объект GameTime, который отвечает за игровое время.
Рассмотрим основые свойства объекта GameTime:
ElapsedGameTime | Игровое время, прошедшее с предыдущего вызова метода Draw/Update |
ElapsedRealTime | Реальное время, прошедшее с предыдущего вызова метода Draw/Update |
IsRunningSlowly | Если это свойство имеет значение true, то время проходящее между последовательными вызовами метода Update/Draw больше значения, указанного в TargetElapsedTime. Это означает, что игра работает медленнее, чем требуется. Если IsRunningSlowly равняется true, то стоит задуматься над оптимизацией |
TotalGameTime | Игровое время, прошедшее с запуска игры |
TotalRealTime | Реальное время, прошедшее с запуска игры |
Для нас наиболее важными свойствами являются ElapsedGameTime и TotalGameTime. Они возвращают объект типа TimeSpan, который имеет множество различных свойств. Рассмотрим лишь наиболее важные:
Days | Целое количество дней |
Hours | Целое количество часов |
Milliseconds | Целое количество миллисекунд |
Minutes | Целое количество минут |
Seconds | Целое количество секунд |
Ticks | Целое количество системных «тиков» |
TotalDays | Вещественное значение дней |
TotalHours | Вещественное значение часов |
TotalMilliseconds | Вещественное значение миллисекунд |
TotalMinutes | Вещественное значение минут |
TotalSeconds | Вещественное значение секунд |
Обратите внимание на то, что свойства, название которых не начинается с Total возвращают целое число, то есть, например, Days будет иметь значение 0 до тех пор пока не пройдет целый день. В то же время значение TotalDays будет постоянно увеличиваться и по прошествии дня примет значение 1.
В следующем примере кода на консоль вывода отборажаются значения всех описанных свойств. Для того, чтобы увидеть окно вывода в Visual Studio нужно выбрать в меню View пункт Output.
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
System. Diagnostics. Debug. WriteLine("Total game time: days " + gameTime. TotalGameTime. Days);
System. Diagnostics. Debug. WriteLine("Total game time: hours " + gameTime. TotalGameTime. Hours);
System. Diagnostics. Debug. WriteLine("Total game time: minutes " + gameTime. TotalGameTime. Minutes);
System. Diagnostics. Debug. WriteLine("Total game time: seconds " + gameTime. TotalGameTime. Seconds);
System. Diagnostics. Debug. WriteLine("Total game time: milliseconds " + gameTime. TotalGameTime. Milliseconds);
System. Diagnostics. Debug. WriteLine("Total game time: total days " + gameTime. TotalGameTime. TotalDays);
System. Diagnostics. Debug. WriteLine("Total game time: total hours " + gameTime. TotalGameTime. TotalHours);
System. Diagnostics. Debug. WriteLine("Total game time: total minutes " + gameTime. TotalGameTime. TotalMinutes);
System. Diagnostics. Debug. WriteLine("Total game time: total seconds " + gameTime. TotalGameTime. TotalSeconds);
System. Diagnostics. Debug. WriteLine("Total game time: total milliseconds " + gameTime. TotalGameTime. TotalMilliseconds);
System. Diagnostics. Debug. WriteLine("Elapsed game time: days " + gameTime. ElapsedGameTime. Days);
System. Diagnostics. Debug. WriteLine("Elapsed game time: hours " + gameTime. ElapsedGameTime. Hours);
System. Diagnostics. Debug. WriteLine("Elapsed game time: minutes " + gameTime. ElapsedGameTime. Minutes);
System. Diagnostics. Debug. WriteLine("Elapsed game time: seconds " + gameTime. ElapsedGameTime. Seconds);
System. Diagnostics. Debug. WriteLine("Elapsed game time: milliseconds " + gameTime. ElapsedGameTime. Milliseconds);
System. Diagnostics. Debug. WriteLine("Elapsed game time: total days " + gameTime. ElapsedGameTime. TotalDays);
System. Diagnostics. Debug. WriteLine("Elapsed game time: total hours " + gameTime. ElapsedGameTime. TotalHours);
System. Diagnostics. Debug. WriteLine("Elapsed game time: total minutes " + gameTime. ElapsedGameTime. TotalMinutes);
System. Diagnostics. Debug. WriteLine("Elapsed game time: total seconds " + gameTime. ElapsedGameTime. TotalSeconds);
System. Diagnostics. Debug. WriteLine("Elapsed game time: total milliseconds " + gameTime. ElapsedGameTime. TotalMilliseconds);
base. Update(gameTime);
}
При запуске приложения в окне вывода появляются записи о текущем состоянии игрового времени, в следующем фрагменте приведена часть этих записей:
Total game time: days 0
Total game time: hours 0
Total game time: minutes 0
Total game time: seconds 0
Total game time: milliseconds 0
Total game time: total days 0
Total game time: total hours 0
Total game time: total minutes 0
Total game time: total seconds 0
Total game time: total milliseconds 0
Elapsed game time: days 0
Elapsed game time: hours 0
Elapsed game time: minutes 0
Elapsed game time: seconds 0
Elapsed game time: milliseconds 0
Elapsed game time: total days 0
Elapsed game time: total hours 0
Elapsed game time: total minutes 0
Elapsed game time: total seconds 0
Elapsed game time: total milliseconds 0
Total game time: days 0
Total game time: hours 0
Total game time: minutes 0
Total game time: seconds 0
Total game time: milliseconds 0
Total game time: total days 0
Total game time: total hours 0
Total game time: total minutes 0
Total game time: total seconds 0
Total game time: total milliseconds 0
Elapsed game time: days 0
Elapsed game time: hours 0
Elapsed game time: minutes 0
Elapsed game time: seconds 0
Elapsed game time: milliseconds 16
Elapsed game time: total days 1,9290162037037E-07
Elapsed game time: total hours 4,62963888888889E-06
Elapsed game time: total minutes 0,000277778333333333
Elapsed game time: total seconds 0,0166667
Elapsed game time: total milliseconds 16,6667
Total game time: days 0
Total game time: hours 0
Total game time: minutes 0
Total game time: seconds 0
Total game time: milliseconds 16
Total game time: total days 1,9290162037037E-07
Total game time: total hours 4,62963888888889E-06
Total game time: total minutes 0,000277778333333333
Total game time: total seconds 0,0166667
Total game time: total milliseconds 16,6667
Elapsed game time: days 0
Elapsed game time: hours 0
Elapsed game time: minutes 0
Elapsed game time: seconds 0
Elapsed game time: milliseconds 16
Elapsed game time: total days 1,9290162037037E-07
Elapsed game time: total hours 4,62963888888889E-06
Elapsed game time: total minutes 0,000277778333333333
Elapsed game time: total seconds 0,0166667
Elapsed game time: total milliseconds 16,6667
Total game time: days 0
Total game time: hours 0
Total game time: minutes 0
Total game time: seconds 0
Total game time: milliseconds 33
Total game time: total days 3,85803240740741E-07
Total game time: total hours 9,25927777777778E-06
Total game time: total minutes 0,000555556666666667
Total game time: total seconds 0,0333334
Total game time: total milliseconds 33,3334
Elapsed game time: days 0
Elapsed game time: hours 0
Elapsed game time: minutes 0
Elapsed game time: seconds 0
Elapsed game time: milliseconds 16
Elapsed game time: total days 1,9290162037037E-07
Elapsed game time: total hours 4,62963888888889E-06
Elapsed game time: total minutes 0,000277778333333333
Elapsed game time: total seconds 0,0166667
Elapsed game time: total milliseconds 16,6667
Total game time: days 0
Total game time: hours 0
Total game time: minutes 0
Total game time: seconds 0
Total game time: milliseconds 50
Total game time: total days 5,78704861111111E-07
Total game time: total hours 1,38889166666667E-05
Total game time: total minutes 0,000833335
Total game time: total seconds 0,0500001
Total game time: total milliseconds 50,0001
Elapsed game time: days 0
Elapsed game time: hours 0
Elapsed game time: minutes 0
Elapsed game time: seconds 0
Elapsed game time: milliseconds 16
Elapsed game time: total days 1,9290162037037E-07
Elapsed game time: total hours 4,62963888888889E-06
Elapsed game time: total minutes 0,000277778333333333
Elapsed game time: total seconds 0,0166667
Elapsed game time: total milliseconds 16,6667


