Опубликован: 12.02.2013 | Уровень: для всех | Доступ: платный
Лекция 8:

Создание приложений XNA

19.3. Использование средств Windows Phone в играх

Сенсорный экран — не единственное новое устройство ввода в Windows Phone. Можно использовать акселерометр для создания игр, в которых управление сводится к перемещению и вращению телефона. Акселерометр может измерять ускорение по трём осям, а также определять ориентацию телефона, поскольку на акселерометр действует сила тяжести, которая задаёт постоянное ускорение к центру земли.

Визуально можно представить акселерометр как груз, подвешенный к центру задней части телефона (рис. 19.4).

Расположение осей измерения акселерометра

Рис. 19.4. Расположение осей измерения акселерометра

Если держать телефон горизонтально, то подвес будет расположен перпендикулярно плоскости телефона. Если выразить координаты груза в системе координат X, Y, Z относительно точки, в которой груз прикреплён к телефону, его можно считать равным (0, 0, –1), если принять длину подвеса равной 1.

Если повернуть на некоторый угол телефон от себя, груз удаляется на некоторое расстояние относительно начального положения, и значение координаты Y будет больше нуля. Если повернуть телефон к себе, чтобы было видно его экран, значение координаты Y будет меньше нуля. Если расположить телефон вертикально экраном к себе, то Y = –1, а Z = 0. При наклоне телефона влево или влево будет соответственно уменьшаться или увеличиваться значение координаты X.

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

Получение значений ускорения с помощью класса Accelerometer

Драйвер акселерометра встроен в Windows Phone, и его можно применять для получения значений как в программах XNA, так и в программах Silverlight.

В программе необходимо создать экземпляр класса Accelerometer и назначить обработчик события ReadingChanged, которое происходит каждый раз, когда акселерометр фиксирует новые значения. В программе Silverlight может использовать эти значения как обычные переменные, а также их можно связать со свойствами визуальных элементов. В программе XNA нужно сделать локальную копию новых значений и использовать их в методе Update.

Чтобы использовать класс Accelerometer, нужно добавить в проект ссылку на библиотеку Microsoft.Devices.Sensors, а также директиву using в файл кода. Для создания экземпляра класса Accelerometer и добавления обработчика события можно использовать следующий код:

protected override void Initialize()
{
    Accelerometer acc = new Accelerometer();
    acc.ReadingChanged +=
        new EventHandler<AccelerometerReadingEventArgs>(acc_ReadingChanged);
    acc.Start();
    base.Initialize();
}

Метод XNA Initialize вызывается при запуске программы. После добавления обработчика вызывается метод Start, чтобы программа начала получать значения от акселерометра.

Метод-обработчик acc_ReadingChanged будет вызываться при каждом изменении значений:

Vector3 accelState = Vector3.Zero;

void acc_ReadingChanged(object sender, AccelerometerReadingEventArgs e)
{
    accelState.X = (float)e.X;
    accelState.Y = (float)e.Y;
    accelState.Z = (float)e.Z;
}

Этот метод сохраняет полученные значения в переменной типа Vector3, и их можно использовать для управления движением платформы в создаваемой нами игре:

lPaddleY = lPaddleY - (accelState.X * lPaddleSpeed);

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

Акселерометр довольно просто использовать для создания игр, в которых для управления объектом нужно перемещать и наклонять телефон, например, игра Лабиринт.

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

Решение возможных проблем

Созданная в нашем примере игра может время от времени работать некорректно, поскольку код программы не учитывает некоторые важные моменты.

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

  • акселерометр генерирует и сохраняет новые значения;
  • метод Update использует эти значения для управления игровым объектом.

Однако, в Windows Phone одновременно может быть активным только один процесс, а параллельная работа сводится к быстрому переключению между активными процессами.

Проблема может возникнуть при выполнении следующей последовательности действий:

  1. Метод Update запускается и считывает значение по оси X.
  2. Происходит событие класса Accelerometer, которое генерирует новые значения по осям X, Y и Z.
  3. Метод Update считывает новые значения по осям Y и Z.

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

Эту проблему можно решить, если указать, что операции получения значений по осям должны выполняться как одна операция, чтобы между ними не выполнялся код другого процесса, который использует эти значения. В языке C# для этого существует инструкция lock, которая не позволяет получить доступ к заданному объекту блокировки до тех пор, пока не выполнится полностью указанный блок кода. Для этого в программе нужно создать и использовать объект блокировки:

object accelLock = new object();

void acc_ReadingChanged(object sender, AccelerometerReadingEventArgs e)
{
    lock (accelLock)
    {
        accelState.X = (float)e.X;
        accelState.Y = (float)e.Y;
        accelState.Z = (float)e.Z;
    }
}

Переменная accelLock имеет самый простой тип данных object. В этой переменной не хранятся никакие данные. Вместо этого он используется в качестве маркера, который может быть назначен только одному процессу. В методе Update должна использоваться изменённая версия кода, которая также будет использовать этот объект блокировки:

lock (accelLock)
{
    lPaddleY = lPaddleY - (accelState.X * lPaddleSpeed);
}

Когда процесс попытается выполнить блок кода программы, связанный с инструкцией lock, он попытается получить доступ к объекту блокировки. Если объект будет недоступен (его будет использовать другая секция lock), то процесс приостановит работу и будет ожидать, пока объект не будет освобождён. Соответственно, теперь один метод не сможет прервать выполнение другого метода.

Однако, такой подход требует повышенного внимания со стороны программиста. Очень важно, чтобы любой код в секции lock выполнялся как можно быстрее, чтобы другим процессам не приходилось долго ожидать доступа к объекту блокировки. Другая проблема, которую необходимо избегать, — это взаимная блокировка. Она возникает в случае, если один процесс заблокировал объект A и ожидает, пока не освободится объект B, а другой процесс заблокировал объект B (перед тем как доступ к нему запросил первый процесс) и ожидает, пока не освободится объект A (который заблокировал первый процесс). В результате получается, что оба процесса приостанавливают работу и могут ждать сколь угодно долго. При создании программы необходимо всячески избегать потенциальной возможности создания подобной ситуации.

Добавление звуков в игру

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

Звуковые эффекты загружаются в игру как контент. Они представляют собой WAV-файлы, загружаются в память при запуске игры и воспроизводятся по запросу. Музыкальные файлы также могут являться частью игрового контента, но воспроизводятся медиапроигрывателем.

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

Использовать в игре звуковые элементы позволяет класс SoundEffect:

// звуковые эффекты
SoundEffect dingSound;
SoundEffect explodeSound;

dingSound = Content.Load<SoundEffect>("ding");
explodeSound = Content.Load<SoundEffect>("explode");

Класс SoundEffect содержит метод Play, при вызове которого звук воспроизводится:

if (ballRectangle.Intersects(rPaddleRectangle))
{
    ballXSpeed = -ballXSpeed;
    dingSound.Play();
}

Этот код воспроизводит звук при столкновении шара с платформой. Это самая простая форма воспроизведения звукового эффекта. Звук воспроизводится сразу при вызове метода, и программа продолжает работать, не ожидая окончания его воспроизведения. Windows Phone может одновременно воспроизводить несколько звуковых эффектов. Аппаратных средства Windows Phone поддерживает до 64 звуковых каналов.

Использование класса SoundEffectInstance

Класс SoundEffect удобно использовать для быстрого воспроизведения звуков. Однако, в некоторых случаях звуковые эффекты игры должны многократно повторяться, изменять тон или использовать панорамирование. Для получения дополнительного контроля над звуком можно создать экземпляр класса SoundEffectInstance для особого звукового эффекта. Его можно рассматривать как дескриптор звука игры.

Например, можно создать звук непрерывно работающего двигателя. Для этого можно использовать экземпляр класса SoundEffect, который содержит образец звука двигателя, загруженный из элемента игрового контента. Используя этот объект, нужно создать экземпляр класса SoundEffectInstance, который будет представлять дескриптор звука:

SoundEffect engineSound;
  
SoundEffectInstance engineInstance = engineSound.CreateInstance();

Теперь можно использовать методы и свойства класса SoundEffectInstance для управления воспроизведением звука и добавления звуковых эффектов:

// начать воспроизведение
engineInstance.Play();

// приостановить воспроизведение
engineInstance.Pause();

// остановить воспроизведение
engineInstance.Stop();

engineInstance.Volume = 1;      // громкость в интервале от 0 до 1

engineInstance.Pitch = 0;       // тон в интервале от -1 до +1
// -1 – на октаву ниже
// +1 – на октаву выше

engineInstance.Pan = 0;         // панорамирование в интервале от -1 до +1
// -1 – звук слева
// +1 – звук справа

engineInstance.IsLooped = true; // циклическое повторение

Также в программе можно проверить состояние воспроизведения звука:
if (engineInstance.State == SoundState.Stopped)
{
    engineInstance.Play();
}

Этот код воспроизводит звук, если бы он был остановлен.

Класс SoundEffectInstance рассматривается как дескриптор звукового канала в устройстве Windows Phone. Не рекомендуется создавать их в большом количестве, чтобы приложение не исчерпало звуковые каналы.

Воспроизведение звука в программе Silverlight

Программа Silverlight может использовать XNA для воспроизведения звуков. Для этого нужно добавить в программу Silverlight ссылки на библиотеки XNA и загрузить звук из ресурсов проекта. Рекомендуется использовать директиву using в файлах кода, где будет вызываться воспроизведение звуков:

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;

При этом, нельзя использовать многие элементы XNA, но можно использовать классы аудио.

Для добавляемых звуковых ресурсов можно создать в проекте отдельную папку, например, Sound. Звуковые ресурсы должны добавляться как элемент контента.

Поскольку в программе Silverlight нет контент-менеджера, для загрузки звука из файла нужно использовать другие возможности, например, можно использовать методы, загружающие звук из потока:

SoundEffect beep =
    SoundEffect.FromStream(TitleContainer.OpenStream("Sounds/beep.wav"));

Класс TitleContainer является частью решения Silverlight и может открыть любой элемент контента в проекте как поток. После выполнения этой команды в программе Silverlight можно использовать звук, загруженный в переменную beep класса SoundEffect.

Воспроизводить звук в программе Silverlight можно так же, как и в программе XNA:

beep.Play();

Также можно использовать SoundEffectInstance для управления звуковыми эффектами.

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

FrameworkDispatcher.Update();

Этот метод нужно вызывать 30 раз в секунду, иначе воспроизведение звука может завершиться неудачей, а сама программа может быть остановлена. Для этого можно использовать таймер, который генерирует события через определённые промежутки времени, и к обработчику события можно добавить нужный код.

В пространстве имён System.Windows.Threading описан класс DispatcherTimer. Код для создания и запуска таймера может выглядеть так:

using System.Windows.Threading;

DispatcherTimer timer = new DispatcherTimer();
timer.Tick += new EventHandler(timer_Tick);
timer.Interval = TimeSpan.FromTicks(333333);
timer.Start();

Метод-обработчик timer_Tick должен просто обновлять XNA:

void timer_Tick(object sender, EventArgs e)
{
    FrameworkDispatcher.Update();
}

С помощью таймера в программе Silverlight можно создавать различные дополнительные эффекты, например, анимированный фон, тикающие часы и т.п.

Управление размерами и ориентацией экрана

Программы Windows Phone могут поддерживать разные типы ориентации. По умолчанию при создании игры XNA используется альбомная ориентация, при которой телефон повёрнут влево. Однако, иногда бывает удобнее использовать в игре портретную ориентацию. Игры XNA могут поддерживать разные типы ориентации телефона и получать события при изменении ориентации телефона.

Объект игры graphics предоставляет много свойств, которые можно использовать в играх для управления размером и ориентацией экрана:

graphics.SupportedOrientations = DisplayOrientation.Portrait
    | DisplayOrientation.LandscapeLeft | DisplayOrientation.LandscapeRight;

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

Если в игре нужно обработать событие изменения ориентации экрана, можно создать метод-обработчик события OrientationChanged:

Window.OrientationChanged += new EventHandler<EventArgs>(Window_OrientationChanged);

В методе Window_OrientationChanged можно изменить размеры всех объектов на экране для новой ориентации игры. Ориентацию по умолчанию лучше всего задать в конструкторе класса Game.

Выбор размера экрана

В игре можно указать определённый размер экрана:

graphics.PreferredBackBufferWidth = 480;
graphics.PreferredBackBufferHeight = 800;

Если задать определённый произвольный размер экрана, игра вынуждает экран работать в заданном разрешении. Размеры также влияют на ориентацию экрана. При этом, аппаратные средства экрана Windows Phone будут масштабировать изображение с указанным разрешением под реальный размер экрана. Таким образом, игра будет выглядеть одинаково и на будущих устройствах с другим разрешением экрана.

Автоматическое масштабирование экрана можно использовать для повышения производительности разрабатываемой игры:

graphics.PreferredBackBufferWidth = 240;
graphics.PreferredBackBufferHeight = 400;

Эти строки указывают размер экрана, который меньше чем экран большинства устройств Windows Phone. Фактически экран с такими размерами в четыре раза меньше стандартного экрана. Однако, аппаратные средства Windows Phone масштабируют изображение, и оно будет выглядеть правильно и на большем экране. В играх, в которых изображение быстро изменяется, это не будет заметно, но фактически в четыре раза меньшее разрешение значительно повысит эффективность работы графической подсистемы.

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

graphics.IsFullScreen = true;

По умолчанию значение свойства IsFullScreen равно false.

Отключение тайм-аута экрана

Владелец Windows Phone может установить тайм-аут экрана, то есть, время бездействия, по истечении которого экран будет выключен и заблокирован для сохранения срока службы аккумулятора. Телефон контролирует сенсорный экран для обнаружения ввода данных от пользователя, и если нет в течение заданного времени ввод не был осуществлён, экран выключится, или запустится экранная заставка. При этом, телефон не проверяет акселерометр. Однако, если в игре управление осуществляется перемещением и поворотом телефона, лучше отключить тайм-аут. Это можно сделать с помощью класса XNA Guide:

Guide.IsScreenSaverEnabled = false;

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

park mr
park mr
Таиланд, thai
An Nguyen
An Nguyen
Вьетнам, 30/9 Thong Nhat street, ward 13, Go Vap district