Главная » Разработка для Windows Phone 7 » Использование сенсорного ввода в игровых приложениях Windows Phone 7

0

Часто при изучении новой среды разработки мы обзаводимся набором техник, которые не имеют никакого отношения к навыкам, необходимым для создания законченного приложения. Данная глава призвана компенсировать эту проблему, представив два весьма типовых приложения для телефона: PhingerPaint и PhreeCell. Первое – это простое приложение для рисования; второе – версия классической игры пасьянс. Третье приложение, SpinPaint, использует часть кода PhingerPaint, но обеспечивает совершенно другую функциональность.

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

Еще больше компонентов игры

Когда мы впервые рассматривали динамические объекты Texture2D в главе 21, я описывал некоторые простые приложения для рисования посредством сенсорного ввода. Как можно понять из данного снимка экрана, приложение PhingerPaint несколько сложнее:

PhingerPaint включает в общей сложности 14 экземпляров двух классов, ColorBlock и Button, которые наследуются от DrawableGameComponent. Чтобы выбрать цвет для рисования, пользователь касается одного из цветных квадратиков вверху экрана. Внизу экрана предусмотрены кнопки для полной очистки экрана или для сохранения рисунка в специальном зарезервированном для приложений альбоме библиотеки фотографий телефона под названием Saved Pictures. Из библиотеки фотографий изображение может быть отправлено по электронной почте или перенесено на ПК. (Нельзя лишь продолжить работу

над изображением, повторно открыв его в другом сеансе работы. Возможно, когда-нибудь я добавлю эту функциональность.)

Поведение Button практически ничем не отличается от традиционной графической кнопки. В обычном состоянии он отображает белый текст и белую рамку. Когда пользователь касается поверхности кнопки, цвета меняются на инверсные, и на кнопке уже отображается черный текст на белом фоне. Если сдвинуть палец с кнопки, цвета возвращаются к исходным, но кнопка продолжает отслеживать это касание. Верните палец на кнопку, и цвета снова поменяются. При снятии касания кнопка формирует событие Click.

Я собираюсь использовать этот Button в нескольких приложениях, поэтому сделал его частью библиотеки Petzold.Phone.Xna. В данном листинге представлено начало этого класса с описаниями закрытых полей, открытого события, конструктора и открытых свойств:

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

public class Button : DrawableGameComponent, IProcessTouch {

SpriteBatch spriteBatch; Texture2D tinyTexture; Vector2 textPosition; bool isPressed; int? touchId = null;

public event EventHandler Click;

public Button(Game game, string text) : base(game)

{

Text = text;

}

public Rectangle Destination { set; get; } public SpriteFont SpriteFont { set; get; } public string Text { set; get; }

}

Как правило, конструктор компонента игры включает аргумент типа Game, который указывает на родителя этого компонента, и из которого базовый класс GameComponent получает данные о GraphicsDevice. Для текста кнопки я добавил в конструктор аргумент типа string, но этот текст также может быть задан позднее через открытое свойство Text.

Я решил, что шрифт Button и крайне важное свойство Destination (Место назначения), которое определяет месторасположения и размер Button относительно экрана, будут задаваться в родительском производном от Game классе.

Перегруженный метод LoadContent в компоненте игры выполняет те же функции, что и в классе игры. Класс Button создает крошечный белый Texture2D размером 1×1 пиксел для отображения рамки кнопки и фона, инверсного по отношению к рамке цвета.

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

protected override void LoadContent() {

spriteBatch = new SpriteBatch(this.GraphicsDevice);

tinyTexture = new Texture2D(this.GraphicsDevice, 1, 1); tinyTexture.SetData<uint>(new uint[] { Color.White.PackedValue });

base.LoadContent();

Открытые свойства SpriteFont (Шрифт спрайта) и Destination класса Button могут быть заданы в любой момент после создания Button. Поэтому доступ к этим сведениям для определения размера и местоположения текста выполняет метод Update:

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

public override void Update(GameTime gameTime) {

if (SpriteFont != null && IString.IsNullOrEmpty(Text)) {

Vector2 textSize = SpriteFont.MeasureString(Text); textPosition =

new Vector2((int)(Destination.Left + (Destination.Width – textSize.X) /

2),

(int)(Destination.Top + (Destination.Height – textSize.Y) /

2));

}

base.Update(gameTime);

}

Класс Button реализует интерфейс IProcessTouch, который был рассмотрен в предыдущей главе. Это означает, что у класса есть метод ProcessTouch, который вызывается из класса Game1 для каждого объекта TouchLocation. Когда палец впервые касается экрана, ProcessTouch проверяет, попадает ли точка касания в прямоугольник, определенный Destination. Если попадает, он сохраняет идентификатор касания и, по сути, владеет им до снятия касания.

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

public bool ProcessTouch(TouchLocation touch) {

bool touchHandled = false;

bool isInside = Destination.Contains((int)touch.Position.X,

(int)touch.Position.Y);

switch (touch.State) {

case TouchLocationState.Pressed:

if (isInside) {

isPressed = true; touchId = touch.Id; touchHandled = true;

}

break;

case TouchLocationState.Moved:

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

isPressed = isInside; touchHandled = true;

}

break;

case TouchLocationState.Released:

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

if (isInside && Click != null)

Click(this, EventArgs.Empty);

touchId = null; isPressed = false; touchHandled = true;

break;

return touchHandled;

}

Если снятие касания происходит в момент, когда точка касания располагается внутри прямоугольника Destination, класс Button формирует событие Click.

Перегруженный Draw отрисовывает кнопку. По сути, это рамка, включающая белый прямоугольник, поверх которого располагается несколько меньший черный прямоугольник со строкой текста:

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

public override void Draw(GameTime gameTime) {

spriteBatch.Begin();

if (isPressed) {

// Отрисовываем инвертированный фон

spriteBatch.Draw(tinyTexture, Destination, Color.White);

}

else {

// Отрисовываем рамку и фон кнопки Rectangle rect = Destination;

spriteBatch.Draw(tinyTexture, rect, Color.White); rect.Inflate(-3, -3);

spriteBatch.Draw(tinyTexture, rect, Color.Black);

}

// Отрисовываем текст кнопки

if (SpriteFont != null && IString.IsNullOrEmpty(Text))

spriteBatch.DrawString(SpriteFont, Text, textPosition,

isPressed ? Color.Black : Color.White);

spriteBatch.End(); base.Draw(gameTime);

}

С другой стороны, ColorBlock (Блок цвета) является частью приложения PhingerPaint и не реализует интерфейс IProcessTouch. Рассмотрим код этого класса полностью:

Проект XNA: PhingerPaint Файл: ColorBlock.cs (полностью)

using System;

using Microsoft.Xna.Framework;

using Microsoft.Xna.Framework.Graphics;

using Microsoft.Xna.Framework.Input.Touch;

namespace PhingerPaint {

public class ColorBlock : DrawableGameComponent {

SpriteBatch spriteBatch;

Texture2D block;

public ColorBlock(Game game) : base(game) {

}

public Color Color { set; get; }

public Rectangle Destination { set; get; }

public bool IsSelected { set; get; }

public override void Initialize() {

base.Initialize();

}

protected override void LoadContent() {

spriteBatch = new SpriteBatch(this.GraphicsDevice); block = new Texture2D(this.GraphicsDevice, 1, 1); block.SetData<uint>(new uint[] { Color.White.PackedValue });

base.LoadContent();

}

public override void Update(GameTime gameTime) {

base.Update(gameTime);

}

public override void Draw(GameTime gameTime) {

Rectangle rect = Destination; spriteBatch.Begin();

spriteBatch.Draw(block, rect, IsSelected ? Color.White : Color.DarkGray);

rect.Inflate(-6, -6);

spriteBatch.Draw(block, rect, Color); spriteBatch.End();

base.Draw(gameTime);

}

}

}

За внешний вид ColorBlock отвечают три открытых свойства: Color, Destination и IsSelected (Выбран). Обратите внимание, что в методе LoadContent создается Texture2D размером один пиксел. Этот объект block (блок) в методе Draw отрисовывается дважды. Сначала он отрисовывается соответственно полным размерам прямоугольника Destination темно-серого или белого цвета, в зависимости от значения IsSelected. Затем он уменьшается на 6 пикселов со всех сторон и отрисовывается снова с использованием свойства Color.

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

По теме:

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