Иван Андреев

andreev. *****@***com

XNA для начинающих. Практика. Основы 2Д. Работа со SpriteBatch. Масштабирование, повороты, отражения

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

Задание на работу:

       Создать в графическом редакторе растровое изображение в одном из форматов, используемом в XNA Game Studio.

       Отобразить нарисованное изображение в центре экрана с оригинальным размером так, чтобы центр изображения совпадал с центром экрана.

Отобразить нарисованное изображение в центре экрана с  масштабом 2:1, то есть размер должен быть  увеличен в два раза.

Реализовать анимацию изменения размера изображения (увеличение до определенного размера, затем уменьшение, коэффициенты масштабирования должны изменяться от 1/2 до 2).

Реализовать эффект поворота изображения относительно:

       А. Левого верхнего угла изображения

       Б. Центра изображения

       В. Правого нижнего угла

Реализовать отражение изображения в горизонтальном и вертикальном направлениях.

Теоретическая часть:

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

НЕ нашли? Не то? Что вы ищете?

public void Draw (

  Texture2D texture,

  Vector2 position,

  Nullable<Rectangle> sourceRectangle,

  Color color,

  float rotation,

  Vector2 origin,

  Vector2 scale,

  SpriteEffects effects,

  float layerDepth

)


Название параметра

Значение

texture

Ссылка на изображение, которое необходимо нарисовать

position

Позиция изображения на экране

sourceRectange

Участок изображения, который необходимо нарисовать. Использование этого параметра удобно, например, при работе со спрайтовой анимацией. Если нужно нарисовать изображения целиком, следует передать null

color

Цвет для смешивания. Обычно имеет значение Color. White.

rotation

Угол поворота изображения относительно точки вращения. Задается в радианах.

origin

Позиция точки вращения в локальных координатах изображения.

scale

Коэффициенты масштабирования по обеим осям

effects

Набор эффектов, может принимать значения:

FlipHorizontally

Зеркально отразить по горизонтали

FlipVertically

Зеркально отразить по вертикали

None

Не производить никаких преобразований

layerDepth

«Глубина» изображения на экране. Принимает значения от 0 (наиболее близко) до 1 (наиболее далеко). Используется лишь в некоторых режимах отображения.


Реализация

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

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 Lab2

{

  /// <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;

  /// <summary>

  /// Текущее игровое состояние

  /// </summary>

  int currentState = 0;

  KeyboardState oldState;

  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

  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

  KeyboardState state = Keyboard. GetState();

  if (state. IsKeyDown(Keys. Space) && oldState. IsKeyUp(Keys. Space))

  {

  // смена текущего игрового состояния при нажатии на пробел

  // значение этой переменной изменяется от 0 до 7

  currentState = (currentState + 1) % 8;

  }

  oldState = state;

  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

  if (currentState == 0)

  {

  spriteBatch. Begin();

  // Центр изображения совпадает с центром экрана

  spriteBatch. Draw(sprite, new Vector2(width / 2 - sprite. Width / 2, height / 2 - sprite. Height / 2), Color. White);

  spriteBatch. End();

  }

  if (currentState == 1)

  {

  float scale = 2;

  spriteBatch. Begin();

  // Центр изображения совпадает с центром экрана с учетом масштаба изображения

  spriteBatch. Draw(sprite, new Vector2(width / 2 - scale * sprite. Width / 2, height / 2 - (scale * sprite. Height / 2)), null, Color. White, 0, Vector2.Zero, scale, SpriteEffects. None, 0);

  spriteBatch. End();

  }

  if (currentState == 2)

  {

  // Масштаб изображения меняется от 1/2 до 2 в зависимости от времени

  float scale = MathHelper. Lerp(0.5f, 2, (float)Math. Abs(Math. Sin(gameTime. TotalGameTime. TotalSeconds)));

  spriteBatch. Begin();

  spriteBatch. Draw(sprite, new Vector2(width / 2 - (scale * sprite. Width / 2), height / 2 - (scale * sprite. Height / 2)), null, Color. White, 0, Vector2.Zero, scale, SpriteEffects. None, 0);

  spriteBatch. End();

  }

  if (currentState == 3)

  {

  float scale = 2;

  float rotation = (float)gameTime. TotalGameTime. TotalSeconds;

  spriteBatch. Begin();

  // Центр вращения совпадает с центром экрана

  spriteBatch. Draw(sprite, new Vector2(width / 2, height / 2), null, Color. White, rotation, Vector2.Zero, scale, SpriteEffects. None, 0);

  spriteBatch. End();

  }

  if (currentState == 4)

  {

  float scale = 2;

  float rotation = (float)gameTime. TotalGameTime. TotalSeconds;

  // Центр вращения изображения находится в середине изображения

  Vector2 origin = new Vector2(sprite. Width/2, sprite. Height/2);

  spriteBatch. Begin();

  // Центр вращения совпадает с центром экрана

  spriteBatch. Draw(sprite, new Vector2(width / 2, height / 2), null, Color. White, rotation, origin, scale, SpriteEffects. None, 0);

  spriteBatch. End();

  }

  if (currentState == 5)

  {

  float scale = 2;

  float rotation = (float)gameTime. TotalGameTime. TotalSeconds;

  // Центр вращения изображения находится в правом нижнем углу изображения

  Vector2 origin = new Vector2(sprite. Width, sprite. Height);

  spriteBatch. Begin();

  // Центр вращения совпадает с центром экрана

  spriteBatch. Draw(sprite, new Vector2(width / 2, height / 2), null, Color. White, rotation, origin, scale, SpriteEffects. None, 0);

  spriteBatch. End();

  }

  if (currentState == 6)

  {

  float scale = 2;

  spriteBatch. Begin();

  // Отражения изображения по горизонтали

  spriteBatch. Draw(sprite, new Vector2(width / 2 - scale * sprite. Width / 2, height / 2 - (scale * sprite. Height / 2)), null, Color. White, 0, Vector2.Zero, scale, SpriteEffects. FlipHorizontally, 0);

  spriteBatch. End();

  }

  if (currentState == 7)

  {

  float scale = 2;

  spriteBatch. Begin();

  // Отражения изображения по вертикали

  spriteBatch. Draw(sprite, new Vector2(width / 2 - scale * sprite. Width / 2, height / 2 - (scale * sprite. Height / 2)), null, Color. White, 0, Vector2.Zero, scale, SpriteEffects. FlipVertically, 0);

  spriteBatch. End();

  }

  base. Draw(gameTime);

  }

  }

}