Главная » Разработка для Windows Phone 7 » Простой уровень нивелира Windows Phone 7

0

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

Проект XnaAccelerometer включает растровое изображение Bubble.bmp размером 48 х 48 пикселов, представляющее собой красный круг:

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

Как и в приложении на Silverlight, в данном случае понадобиться подключить библиотеку Microsoft.Devices.Sensors и пространство имен Microsoft.Devices.Sensors.

Поля класса Game1, главным образом, предназначаются для переменных, обеспечивающих позиционирование растрового изображения на экране:

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

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

const float BUBBLE_RADIUS_MAX = 25; const float BUBBLE RADIUS MIN = 12;

GraphicsDeviceManager graphics; SpriteBatch spriteBatch;

Vector2 screenCenter;

float screenRadius;            // меньше, чем BUBBLE RADIUS MAX

Texture2D bubbleTexture; Vector2 bubbleCenter; Vector2 bubblePosition; float bubbleScale;

Vector3 accelerometerVector;

object accelerometerVectorLock = new object();

}

В конце этого фрагмента можно увидеть поле accelerometerVector (Вектор ускорения) типа Vector3. Обработчик события OnAccelerometerReadingChanged будет сохранять в это поле новое значение, и метод Update будет использовать это значение при вычислении местоположения растрового изображения.

Методы OnAccelerometerReadingChanged и Update выполняются в разных потоках: один – в задающем поле, другой – в читающем. Нет никакой проблемы в том, если поле задается или читается в одной команде на машинном языке. Это было бы так, если бы Vector3 был классом, являющимся ссылочным типом, используемым преимущественно посредством указателей. Но Vector3 – это структура (тип значения), состоящая из трех свойств типа float, каждое из которых занимает четыре байта, что составляет в сумме 12 байт или 96 бит. Задание или чтение этого поля Vector3 связано с передачей такого объема данных.

Устройство, работающее под управлением Windows Phone 7, имеет как минимум 32- разрядный процессор ARM. Если взглянуть на набор команд ARM, вы не найдете среди них такую, которая обеспечивала бы передачу 12 байтов сразу. Это означает, что поток

акселерометра, сохраняющий новое значение Vector3, может быть прерван методом Update основного потока программы, извлекающим это значение. В X, Y и Z могут быть записаны значения из разных операций чтения.

Несмотря на то, что это не так уж смертельно для этого приложения, давайте будем делать все абсолютно безопасным и надежным. Для этого воспользуемся C#-выражением lock (блокировать), чтобы значение Vector3 гарантированно записывалось и читалось двумя потоками без прерывания. В этом задача переменной accelerometerVectorLock (Блокировка вектора акселерометра).

Я решил создать объект Accelerometer и задать обработчик события в методе Initialize: Проект XNA: XnaAccelerometer Файл: Game1.cs (фрагмент)

protected override void Initialize() {

Accelerometer accelerometer = new Accelerometer(); accelerometer.ReadingChanged += OnAccelerometerReadingChanged;

try {

accelerometer.Start();

}

catch {

}

base.Initialize();

}

void OnAccelerometerReadingChanged(object sender, AccelerometerReadingEventArgs

args) {

lock (accelerometerVectorLock) {

accelerometerVector = new Vector3((float)args.X, (float)args.Y,

(float)args.Z); }

}

Обратите внимание, что обработчик события использует выражение lock для задания поля accelerometerVector. Это предотвращает доступ к данному полю из метода Update в течение этого короткого промежутка времени.

Метод LoadContent загружает растровое изображение, используемое для моделирования нивелира, и инициализирует несколько переменных, используемых для его позиционирования:

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

protected override void LoadContent() {

spriteBatch = new SpriteBatch(GraphicsDevice); Viewport viewport = this.GraphicsDevice.Viewport;

screenCenter = new Vector2(viewport.Width / 2, viewport.Height / 2); screenRadius = Math.Min(screenCenter.X, screenCenter.Y) – BUBBLE RADIUS MAX;

bubbleTexture = this.Content.Load<Texture2D>("Bubble");

bubbleCenter = new Vector2(bubbleTexture.Width / 2, bubbleTexture.Height / 2);

Если свойства X и Y акселерометра равны нулю, «пузырек» отображается в центре экрана. Вот зачем нужны screenCenter (Центр экрана) и bubbleCenter (Центр «пузырька»). Значение screenRadius (радиус экрана) – это расстояние от центра, когда модуль компонентов X и Y равен 1.

Метод Update выполняет защищенный доступ к полю accelerometerVector (Вектор акселерометра) и вычисляет bubblePosition (Положение «пузырька») на основании значений компонентов X и Y. Может показаться, что я перепутал компоненты X и Y при вычислении, но это лишь потому, что в XNA по умолчанию используется альбомный режим, т.е. координаты обратны координатам вектора ускорения. По умолчанию поддерживаются оба альбомных режима (левосторонний и правосторонний), поэтому вектор ускорения важно умножать на -1, когда телефон располагается в режиме LandscapeRight (Правостороннее альбомное расположение):

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

protected override void Update(GameTime gameTime) {

// Обеспечиваем возможность выхода из игры

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

Vector3 accVector;

lock (accelerometerVectorLock) {

accVector = accelerometerVector;

}

int sign = this.Window.CurrentOrientation ==

DisplayOrientation.LandscapeLeft ? 1 : -1;

bubblePosition = new Vector2(screenCenter.X + sign * screenRadius * accVector.Y,

screenCenter.Y + sign * screenRadius *

accVector.X);

float bubbleRadius = BUBBLE_RADIUS_MIN + (1 – accVector.Z) / 2 *

(BUBBLE RADIUS MAX – BUBBLE RADIUS MIN); bubbleScale = bubbleRadius / (bubbleTexture.Width / 2);

base.Update(gameTime);

}

Кроме того, на основании компонента Z вектора вычисляется коэффициент bubbleScale (Масштаб «пузырька»). Идея в том, что «пузырек» увеличивается, когда телефон располагается экраном вверх, и уменьшается, если экран наклоняется вниз, как будто экран является одной из сторон настоящего прямоугольного резервуара с жидкостью, и размер «пузырька» отражает его удаление от поверхности.

Перегруженный Draw использует длинную версию метода Draw класса SpriteBatch. Проект XNA: XnaAccelerometer Файл: Game1.cs (фрагмент)

protected override void Draw(GameTime gameTime) {

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

spriteBatch.Draw(bubbleTexture, bubblePosition, null, Color.White, 0,

bubbleCenter, bubbleScale, SpriteEffects.None, 0); spriteBatch.End();

base.Draw(gameTime);

Обратите внимание на аргумент bubbleScale, который меняет размеры растрового изображения. Центр масштабирования задается предыдущим аргументом метода, bubbleCenter. Эта точка также совпадает со значением bubblePosition относительно экрана.

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

Акселерометр очень чувствителен и требует сглаживания данных. Это и другие связанные с акселерометром вопросы обсуждаются в главе 24

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

По теме:

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