Невозможно пройти тесты, в окне с вопросами пусто |
Акселерометр
Цель работы: Научиться обрабатывать данные акселерометра для использования их в игровых приложениях
12.1. Получение данных акселерометра
Устройства на Windows Phone имеют встроенный акселерометр, который позволяет организовывать управление программами путем перемещения телефона в пространстве. Это открывает богатые возможности для интерактивного управления играми. Если речь идёт о двумерных играх, использование акселерометра в игровом процессе позволяет смоделировать что-то вроде шарика, который катается по горизонтальной плоскости. Если речь идет об играх трехмерных, то здесь мы можем получить интерфейс управления неким объектом, например, автомобилем, напоминающий руль.
Рассмотрим работу с акселерометром на примере, который получает данные с устройства и выводит сведения о них на экран.
Создадим новый стандартный игровой проект, подготовим его для вывода текста в игровое окно XNA, добавим новый ресурс .spritefont с именем MyFont. Для доступа к акселерометру нам понадобится, во-первых, подключить библиотеку Microsoft.Devices.Sensors, во-вторых – пространство имен Microsoft.Devices.Sensor.
Для подключения библиотеки нужно сделать щелчок правой кнопкой мыши в Обозревателе решений по группе Ссылки в проекте P6_1, выбрать команду Добавить ссылку и в появившемся окне, на закладке .NET выбрать Microsoft.Devices.Sensor, рис. 12.1.
В итоге окно Обозреватель решений должно принять вид, показанный на рис. 12.2
При работе с акселерометром нужно учитывать, что данные, которые можно от него получить, представляют собой трехмерный вектор, содержащий данные об ускорении устройства в гравитационных единицах. Например, (1,0,0). Если сопоставить эти данные с телефоном, то, если расположить телефон кнопками, расположенными под экраном, вниз, лицевой стороной к наблюдателю, окажется, что ось Y проходит вдоль длинной стороны экрана (положительное направление оси – вверх), X – вдоль короткой (положительное направление оси – вправо), ось Z располагается перпендикулярно экрану (положительное направление – в сторону наблюдателя). Это остаётся справедливым и при поворотах телефона.
Когда телефон неподвижен, акселерометр, фактически, регистрирует силу земного притяжения, что отражается в его показаниях (они, по соответствующей оси, близки к 1), перемещения телефона в пространстве приводят к изменению показаний.
Таким образом, оказывается, например, если телефон лежит на горизонтальной поверхности неподвижно, экраном вверх, показания акселерометра выглядят как (0, 0, -1). Если расположить телефон вертикально, кнопками вниз, мы имеем в показаниях (0, -1, 0), перевернув телефон "вверх ногами" – (0, 1, 0). Если повернуть телефон на 90° против часовой стрелки – мы получим (-1,0,0). Акселерометр очень чувствителен, даже при неподвижном устройстве показания не бывают в точности равными идеальным, к тому же, они колеблются в небольших пределах.
Мы реализовали процедуру получения данных с акселерометра в коде основной программы. Вы можете видеть этот код в листинге 12.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.Input.Touch; using Microsoft.Xna.Framework.Media; using Microsoft.Devices.Sensors; namespace P6_1 { /// <summary> /// Это главный тип игры /// </summary> public class Game1 : Microsoft.Xna.Framework.Game { GraphicsDeviceManager graphics; SpriteBatch spriteBatch; //Строка для вывода string text; //Для хранения позиции вывода текста Vector2 textPosition=new Vector2(100,100); //Для хранения шрифта SpriteFont MyFont; //Показатели акселерометра Vector3 accVector; //Акселерометр Accelerometer accelerometer; //Объект для блокировки object accLock = new object(); public Game1() { graphics = new GraphicsDeviceManager(this); Content.RootDirectory = "Content"; //Отображение игрового окна на полный экран, без строки состояния graphics.IsFullScreen = true; // Частота кадра на Windows Phone по умолчанию — 30 кадров в секунду. TargetElapsedTime = TimeSpan.FromTicks(333333); // Дополнительный заряд аккумулятора заблокирован. InactiveSleepTime = TimeSpan.FromSeconds(1); } /// <summary> /// Позволяет игре выполнить инициализацию, необходимую перед запуском. /// Здесь можно запросить нужные службы и загрузить неграфический /// контент. Вызов base.Initialize приведет к перебору всех компонентов и /// их инициализации. /// </summary> protected override void Initialize() { //Новый объект для работы с акселерометром accelerometer = new Accelerometer(); //Задаем время обновления accelerometer.TimeBetweenUpdates = TimeSpan.FromMilliseconds(1000); //Подключаем обработчик события accelerometer.CurrentValueChanged += new EventHandler<SensorReadingEventArgs <AccelerometerReading>>(accelerometer_CurrentValueChanged); //Пытаемся запустить акселерометр try { accelerometer.Start(); } catch { } base.Initialize(); } //Обработчик события акселерометра void accelerometer_CurrentValueChanged(object sender, SensorReadingEventArgs<AccelerometerReading> e) { //Блокировка обеспечивает получение данных акселерометра в одном потоке lock (accLock) { //Получаем данные accVector = e.SensorReading.Acceleration; } } ///// <summary> /// LoadContent будет вызываться в игре один раз; здесь загружается /// весь контент. /// </summary> protected override void LoadContent() { // Создайте новый SpriteBatch, который можно использовать для отрисовки текстур. spriteBatch = new SpriteBatch(GraphicsDevice); //Загружаем шрифт MyFont = Content.Load<SpriteFont>("MyFont"); } /// <summary> /// UnloadContent будет вызываться в игре один раз; здесь выгружается /// весь контент. /// </summary> protected override void UnloadContent() { // ЗАДАЧА: выгрузите здесь весь контент, не относящийся к ContentManager } /// <summary> /// Позволяет игре запускать логику обновления мира, /// проверки столкновений, получения ввода и воспроизведения звуков. /// </summary> /// <param name="gameTime">Предоставляет моментальный снимок значений времени.</param> protected override void Update(GameTime gameTime) { // Позволяет выйти из игры if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed) this.Exit(); //Блокировка обеспечивает чтение данных в одном потоке lock (accLock) { //Записываем в строку для вывода строковое представление //данных, полученных с акселерометра text = accVector.ToString(); } base.Update(gameTime); } /// <summary> /// Вызывается, когда игра отрисовывается. /// </summary> /// <param name="gameTime">Предоставляет моментальный снимок значений времени.</param> protected override void Draw(GameTime gameTime) { GraphicsDevice.Clear(Color.CornflowerBlue); spriteBatch.Begin(); //Выводим строку spriteBatch.DrawString(MyFont, text, textPosition, Color.Red, 0, new Vector2(0,0), 1.0f, SpriteEffects.None, 0.5f); spriteBatch.End(); base.Draw(gameTime); } } }Листинг 12.1. Код класса Game1
Рассмотрим ключевые моменты этого кода. Здесь мы собираемся вывести значение показаний акселерометра на экран. Эти показания будут храниться в переменной accVector типа Vector3. Получение данных акселерометра и игровой цикл работают в разных потоках, для того, чтобы обеспечить правильность работы программы, то есть – исключить считывание в методе Update данных из переменной accVector, которые принадлежат различным результатам измерений показаний акселерометра, мы блокируем, используя конструкцию lock, критически важные операции, в нашем случае это – установка переменной accVector в обработчике события accelerometer_CurrentValueChanged и в методе Update.
В методе Initialize мы создаем новый объект типа Accelerometer, устанавливаем его параметр TimeBetweenUpdates, равный 1000 миллисекунд, то есть показатели акселерометра будут сниматься 1 раз в секунду. Это время может быть и другим – всё зависит от задач, которые стоят перед приложением.
Для получения данных с акселерометра мы подключаем обработчик события CurrentValueChanged, которое происходит при поступлении новых данных с устройства. Этот обработчик реализован процедурой accelerometer_CurrentValueChanged, здесь мы получаем данные (они представлены в виде трёхмерного вектора) и записываем их в переменную accVector.
Прежде чем использовать акселерометр, нужно выполнить метод Start() объекта Accelerometer. Попытка исполнить этот метод связана с работой с физическим устройством, здесь возможны ошибки, поэтому мы заключаем эту попытку в конструкцию try {} catch {}. Здесь можно настроить поведение программы в том случае, если данная попытка не удастся, то есть, акселерометр окажется не инициализированным.
В методе Update мы приводим содержимое переменной accVector к текстовому виду и записываем полученную строку в переменную text, текст, содержащийся в которой, выводим на экран в методе Draw.
Запуск программы на реальном устройстве, рис. 12.3. позволит лучше понять, как именно работает акселерометр и какие данные с него можно получить. Уменьшите время снятия показаний акселерометра, изменив параметр TimeBetweenUpdates при запуске приложения на устройстве для того, чтобы увидеть изменения показателей при быстрых перемещениях телефона.
Приложения, использующие физические сенсоры, безусловно, нужно отлаживать с использованием реального устройства, однако, в случае с акселерометром, большую помощь в отладке могут оказать дополнительные инструменты, доступные в эмуляторе.