Главная » Разработка для Windows Phone 7 » Компоненты игры Windows Phone 7

0

Для завершения данной главы я припас два приложения, обеспечивающие отображение все того же старого Texture2D, который использовался в этой главе ранее. Отличие в том, что пользователь сможет определять трансформацию изображения интерактивно, перетягивая углы текстуры.

Мишени для касания и перетягивания в данных приложениях обозначены полупрозрачными дисками в углах Texture2D. Неплохо было бы создать для этих дисков код, который можно было бы использовать многократно в разных приложениях. В традиционной среде разработки графических элементов их можно было бы представить как элементы управления, но в XNA их называют компонентами игры.

Компоненты помогают разбивать XNA-приложения на модули. Они могут наследоваться от класса GameComponent (Компонент игры), но часто являются производными от DrawableGameComponent (Компонент игры с возможностью отрисовки), что обеспечивает

им возможность выводить что-то на экран в дополнение и поверх того, что отрисовывает метод Draw класса Game.

Чтобы добавить в проект класс нового компонента, щелкнем правой кнопкой мыши имя проекта, выберем Add и New Item и затем из списка выберем Game Component (Компонент игры). Если мы хотим, чтобы компонент участвовал в рисовании, понадобится изменить базовый класс на DrawableGameComponent и перегрузить метод Draw.

Как правило, игра создает необходимые экземпляры компонентов либо в конструкторе игры, либо в ходе выполнения метода Initialize. Эти компоненты официально становятся частью игры после того, как добавляются в коллекцию Components, определенную классом Game.

Как и класс Game, производные от DrawableGameComponent, как правило, перегружают методы Initialize, LoadContent, Update и Draw. Когда перегруженный Initialize класса Game вызывает метод базового класса, вызываются методы Initialize всех компонентов. Аналогично с перегруженными методами LoadComponent, Update и Draw.

Как известно, перегруженный метод Update обычно обрабатывает сенсорный ввод. Из собственного опыта я знаю, что организовать сенсорный ввод в игровые компоненты несколько проблематично. В итоге кажется, что сама игра и компоненты состязаются за ввод.

Чтобы исправить это, я решил, что мой производный Game будет единолично отвечать за вызов TouchPanel.GetState, но затем игра будет предоставлять компонентам возможность обработать этот сенсорный ввод. Для реализации данной идеи я создал следующий интерфейс для производных от GameComponent и DrawableGameComponent:

Проект XNA: Petzold.Phone.Xna Файл: IProcessTouch.cs

using Microsoft.Xna.Framework.Input.Touch;

namespace Petzold.Phone.Xna {

public interface IProcessTouch {

bool ProcessTouch(TouchLocation touch);

}

}

Если компонент игры реализует этот интерфейс, игра вызывает метод ProcessTouch (Обработать сенсорный ввод) компонента игры для каждого объекта TouchLocation. Если компонент игры собирается использовать этот TouchLocation, его метод ProcessTouch возвращает true, и игра, возможно, игнорирует этот TouchLocation.

Первым я продемонстрирую компонент Dragger (Модуль перетягивания), который входит в библиотеку Petzold.Phone.Xna. Dragger наследуется от DrawableGameComponent и реализует интерфейс IProcessTouch:

Проект XNA: Petzold.Phone.Xna Файл: Dragger.cs (фрагмент, демонстрирующий поля)

public class Dragger : DrawableGameComponent, IProcessTouch {

SpriteBatch spriteBatch; int? touchId;

public event EventHandler PositionChanged;

public Dragger(Game game) : base(game)

{ }

public Texture2D Texture { set; get; } public Vector2 Origin { set; get; } public Vector2 Position { set; get; }

}

В конструктор производного от GameComponent класса должен передаваться родительский класс Game, что позволит компоненту использовать некоторые свойства Game (такие как объект GraphicsDevice). Производный от DrawableGameComponent обычно создает SpriteBatch для собственных нужд, так же как это делает производный от Game.

Dragger также описывает поле touchId (Идентификатор касания) для помощи в обработке сенсорного ввода, открытое событие PositionChanged и три открытых свойства: Texture типа Texture2D, Vector2 под именем Origin (значением которого обычно задается центр Texture2D) и еще один Vector2 для Position.

Приложение, использующее Dragger, могло бы определять для компонента пользовательский Texture2D и задавать его через это открытое свойство Texture и в это же время, возможно, задавать свойство Origin. Однако Dragger определяет для себя свойство Texture по умолчанию во время выполнения своего метода LoadContent:

Проект XNA: Petzold.Phone.Xna Файл: Dragger.cs (фрагмент)

protected override void LoadContent() {

spriteBatch = new SpriteBatch(this.GraphicsDevice);

// Создаем текстуру по умолчанию int radius = 48;

Texture2D texture = new Texture2D(this.GraphicsDevice, 2 * radius, 2 * radius); uint[] pixels = new uint[texture.Width * texture.Height];

for (int y = 0; y < texture.Height; y++)

for (int x = 0; x < texture.Width; x++) {

Color clr = Color.Transparent;

if ((x – radius) * (x – radius) + (y – radius) * (y – radius) < radius * radius)

{

clr = new Color(0, 128, 128, 128);

}

pixels[y * texture.Width + x] = clr.PackedValue;

}

texture.SetData<uint>(pixels); Texture = texture;

Origin = new Vector2(radius, radius); base.LoadContent();

}

Класс Dragger реализует интерфейс IProcessTouch, так что у него есть метод ProcessTouch, который вызывается из производного от Game для каждого объекта TouchLocation. Метод ProcessTouch фиксирует касания самого компонента. Когда касание случается, этот метод

сохраняет его ID и фактически присваивает это касание себе до его завершения. Для каждого перемещения точки касания Dragger формирует событие PositionChanged.

Проект XNA: Petzold.Phone.Xna Файл: Dragger.cs (фрагмент)

public bool ProcessTouch(TouchLocation touch) {

if (Texture == null) return false;

bool touchHandled = false;

switch (touch.State) {

case TouchLocationState.Pressed:

if ((touch.Position.X > Position.X – Origin.X) &&

(touch.Position.X < Position.X – Origin.X + Texture.Width) && (touch.Position.Y > Position.Y – Origin.Y) && (touch.Position.Y < Position.Y – Origin.Y + Texture.Height))

{

touchId = touch.Id; touchHandled = true;

}

break;

case TouchLocationState.Moved:

if (touchId.HasValue && touchId.Value == touch.Id) {

TouchLocation previousTouch;

touch.TryGetPreviousLocation(out previousTouch); Position += touch.Position – previousTouch.Position;

// Формируем событие! if (PositionChanged != null)

PositionChanged(this, EventArgs.Empty);

touchHandled = true;

}

break;

case TouchLocationState.Released:

if (touchId.HasValue && touchId.Value == touch.Id) {

touchId = null; touchHandled = true;

}

break;

}

return touchHandled;

}

Перегруженный Draw просто отрисовывает Texture2D в новом месте:

Проект XNA: Petzold.Phone.Xna Файл: Dragger.cs (фрагмент)

public override void Draw(GameTime gameTime) {

if (Texture != null) {

spriteBatch.Begin();

spriteBatch.Draw(Texture, Position, null, Color.White, 0, Origin, 1, SpriteEffects.None, 0); spriteBatch.End();

base.Draw(gameTime);

Теперь применим компонент Dragger при рассмотрении намного более сложной трансформации.

Источник: Чарльз Петзольд, Программируем Windows Phone 7, Microsoft Press, © 2011.

По теме:

  • Комментарии