Главная » Разработка для Windows Phone 7 » Принципы движения

0

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

Эффект перемещения текста создается путем задания различных координат в методе DrawString в ходе последовательных вызовов метода Draw класса Game. В главе 1, как мы помним, переменной textPosition просто задавалось фиксированное значение в ходе выполнения метода LoadContent. Данный код обеспечивает размещение текста в центре экрана:

Vector2 textSize = segoe14.MeasureString(text); Viewport viewport = this.GraphicsDevice.Viewport; textPosition = new Vector2((viewport.Width – textSize.X) / 2,

(viewport.Height – textSize.Y) / 2);

В большинстве приложений данной главы значение textPosition пересчитывается при каждом вызове Update. Благодаря этому метод Draw каждый раз отрисовывает текст в разных местах. Обычно ничего сверхъестественного не происходит, текст просто перемещается по экрану сверху вниз, затем снизу вверх и опять вниз. Как в известном анекдоте: «Нанести, смыть, повторить».

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

Простейший подход

Начнем с самого простого: просто переместим текст вверх и вниз по вертикали, реализовывая, таким образом, перемещение в одном измерении. Для этого достаточно последовательно увеличивать, а затем уменьшать координату Y значения textPosition.

Чтобы поэкспериментировать, создадим в Visual Studio проект под именем NaiveTextMovement (Простое перемещение текста) и добавим в папку Content шрифт Segoe UI Mono размером 14 пунктов. Поля класса Game1 описываются следующим образом:

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

public class Game1 : Microsoft.Xna.Framework.Game {

const float SPEED = 240f;                           // пикселов в секунду

const string TEXT = "Hello, Windows Phone 7!";

GraphicsDeviceManager graphics;

SpriteBatch spriteBatch;

SpriteFont segoe14;

Viewport viewport;

Vector2 textSize;

Vector2 textPosition;

bool isGoingUp = false;

Здесь ничего не должно вызывать удивления. И SPEED, и TEXT определены как константы. Для SPEED задано значение 240 пикселов в секунду. Поле isGoingUp (Перемещается вверх) типа Boolean указывает на направление перемещения текста в настоящий момент, вверх или вниз.

Метод LoadContent хорошо знаком нам из приложения главы 1. Единственное отличие в данном случае в том, что окно просмотра сохраняется как поле:

Проект XNA: NaiveTextMovement Файл: Game1.cs (фрагмент)

protected override void LoadContent() {

spriteBatch = new SpriteBatch(GraphicsDevice); viewport = this.GraphicsDevice.Viewport; segoe14 = this.Content.Load<SpriteFont>("Segoe14"); textSize = segoe14.MeasureString(TEXT);

textPosition = new Vector2(viewport.X + (viewport.Width – textSize.X) / 2, 0);

}

Обратите внимание, что значение textPosition обеспечивает центрирование текста по горизонтали, но размещает его вверху экрана. Все основные вычисления, как и в большинстве приложений на XNA, осуществляются в методе Update:

Проект XNA: NaiveTextMovement Файл: Game1.cs (фрагмент)

protected override void Update(GameTime gameTime) {

if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed) this.Exit ();

if (!isGoingUp) {

textPosition.Y += SPEED * (float)gameTime.ElapsedGameTime.TotalSeconds;

if (textPosition.Y + textSize.Y > viewport.Height) {

float excess = textPosition.Y + textSize.Y – viewport.Height; textPosition.Y -= 2 * excess; isGoingUp = true;

}

}

else {

textPosition.Y -= SPEED * (float)gameTime.ElapsedGameTime.TotalSeconds;

if (textPosition.Y < 0) {

float excess = – textPosition.Y; textPosition.Y += 2 * excess; isGoingUp = false;

}

}

base.Update(gameTime);

}

Аргумент GameTime (Время игры) метода Update имеет два критически важных свойства типа TimeSpan: TotalGameTime (Общее время игры) и ElapsedGameTime (Истекшее время игры).

«Время игры» не всегда синхронизируется с реальным временем, существует несколько аппроксимаций, которые обеспечивают плавность анимаций, но оно близко к нему. TotalGameTime отражает время, прошедшее с начала игры. ElapsedGameTime – это время с момента предыдущего вызова метода Update. В общем случае ElapsedGameTime всегда будет иметь одно и то же значение – 33-1/3 миллисекунды, что соответствует частоте обновления экрана телефона 30 Гц.

Для задания темпа перемещения можно использовать TotalGameTime или ElapsedGameTime. В данном примере при первом вызове Update вычисляется textPosition, обеспечивая размещение текста у верхнего края экрана, и isGoingUp задается значение false. Приращение textPosition.Y рассчитывается на основании значения SPEED (которое выражается в пикселах в секунду) и общего времени в секундах, истекшего с момента последнего вызова Update, что будет составлять 1/30 секунды.

Такие вычисления могут приводить к перемещению текста на слишком большую дистанцию и выходу его за границы экрана. В этом случае сумма координаты текста по вертикали и его высоты будет больше значения свойства Bottom высоты окна просмотра. Чтобы предотвратить такую ситуацию, мною предусмотрено так называемое превышение. Это величина, на которую координата текста по вертикали превышает размер экрана. Для компенсации я умножаю величину превышения на два, чтобы создать эффект, как будто текст «отскакивает» вверх от нижнего края экрана на величину этого превышения. И в этой точке свойству isGoingUp присваивается значение true.

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

Проект XNA: NaiveTextMovement Файл: Game1.cs (фрагмент)

protected override void Draw(GameTime gameTime) {

GraphicsDevice.Clear(Color.Navy); spriteBatch.Begin();

spriteBatch.DrawString(segoe14, TEXT, textPosition, Color.White); spriteBatch.End();

base.Draw(gameTime);

}

Большая проблема в том, что этот простой подход не включает никакого математического инструментария, который позволил бы нам сделать что-либо боле сложное, например, переместить текст по диагонали, а не просто в одном направлении.

В приложении NaiveTextMovement нет никакой концепции направления, которая позволила бы уйти от просто движения по горизонтали или вертикали. Для этого нам необходимы векторы.

Краткий обзор векторов

Вектор – это математическая сущность, объединяющая в себе концепции как направления, так и величины. Обычно вектор представляют в виде линии со стрелкой. Данные три вектора имеют одинаковое направление, но различную величину (или модуль):

Механизм отображения пытается сгладить зазубрины, но это еще вопрос, что лучше, «ступеньки» или размытость. Масштабировать текст и обеспечивать гладкие векторные контуры поможет Silverlight. Или всегда можно использовать шрифт большого размера и просто работать с его уменьшенными версиями.

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

По теме:

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