Цель работы: Научиться работать с данными о географическом положении, использовать жесты для управления программами
Для управления играми, написанными для Windows Phone, можно пользоваться самыми разными датчиками. В частности, датчиком местоположения. В XNA-проекте мы можем получать данные о местоположении устройства, выраженные в географических координатах, от встроенного
Данные о местоположении, получение которых мы сейчас рассмотрим, позволяют определить географические координаты устройства, скорость перемещения, высоту над уровнем моря, курс движения.
Создадим новый стандартный XNA-проект (P7_1). Для использования служб работы с местоположением нам понадобится подключить (в разделе Ссылки игрового проекта) библиотеку System.Device, пространство имён System.Device.Location.
В данном проекте, листинг 13.1., мы будем получать данные о местоположении устройства и выводить их на экран.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Device.Location;
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;
namespace P7_1
{
/// <summary>
/// Это главный тип игры
/// </summary>
public class Game1 : Microsoft.Xna.Framework.Game
{
GraphicsDeviceManager graphics;
SpriteBatch spriteBatch;
//Строка для вывода
string text="Please wait";
//Для хранения позиции вывода текста
Vector2 textPosition = new Vector2(100, 100);
//Для хранения шрифта
SpriteFont MyFont;
//Объект для работы с данными о местоположении
GeoCoordinateWatcher GeoWatch;
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()
{
//Новый объект для работы с местоположением
GeoWatch = new GeoCoordinateWatcher();
//Обработчик события, которое происходит при изменении местоположения
GeoWatch.PositionChanged += new EventHandler<GeoPositionChangedEventArgs<GeoCoordinate>>(GeoWatch_PositionChanged);
//Запуск сервиса
GeoWatch.Start();
base.Initialize();
}
void GeoWatch_PositionChanged(object sender, GeoPositionChangedEventArgs<GeoCoordinate> e)
{
//Записываем в переменную для вывода на экран следующие данные:
//Широта
//Долгота
//Высота
//Курс
//Скорость
//Отметка времени
text = String.Format("Latitude: {0}\r\n" +
"Longitude: {1}\r\n" +
"Altitude: {2}\r\n" +
"Course: {3}\r\n"+
"Speed: {4}\r\n"+
"Current time: {5}",
e.Position.Location.Latitude,
e.Position.Location.Longitude,
e.Position.Location.Altitude,
e.Position.Location.Course,
e.Position.Location.Speed,
e.Position.Timestamp);
}
///// <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();
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);
}
}
}
В данном проекте мы пользуемся классом GeoCoordinateWatcher(), создаем обработчик события PositionChanged, в коде которого записываем полученные от устройства данные в текстовую переменную для вывода на экран.
(рис 13.1) Запуск программы в эмуляторе
На одном из предыдущих занятий мы рассматривали
Windows Phone поддерживает распознавание множества жестов. В частности, таких, как FreeDrag – то есть – свободное перетаскивание по экрану, DoubleTap – быстрое двойное касание экрана. Flick – касание с быстрым перемещением, – касание и удержание пальца на панели, Pinch – сведение или разведение двух пальцев на панели, обычно используется для целей масштабирования объектов. Эти и другие жесты позволяют реализовать самые разные схемы управления объектами на экране. Например, объекты можно "раскладывать" по экрану в нужной последовательности, управлять скоростью и направлением их перемещения, масштабировать – здесь всё зависит от целей и фантазии разработчика. В нашем примере мы используем обработку двух жестов – FreeDrag и DoubleTap.
Кроме того, мы включили в пример работу с виброзвонком телефона. В определенный момент мы будем включать вибрацию. Этот приём часто используется в играх для того, чтобы добавить им реалистичности. Например, устройство может вибрировать при столкновении каких-то объектов, при падении и во многих других случаях.
Обработка жестов не требует каких-то дополнительных действий, всё, что нужно, уже подключено в стандартном проекте. Для работы с вибрацией нам понадобится подключить библиотеку Microsoft.Phone и пространство имен Microsoft.Devices
Кроме того, здесь мы, в учебных целях, рассмотрим удобный механизм
Новый стандартный XNA-проект (P7_2) содержит игровой компонент, spriteComp.cs – это модифицированная версия использованного ранее компонента, один графический ресурс –
(рис 13.2) Игровой проект P7_2
В листинге 13.2 вы можете видеть код игрового компонента
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.Media;
//Подключим для отладочных целей
using System.Diagnostics;
namespace P7_2
{
/// <summary>
/// Это игровой компонент, реализующий интерфейс IUpdateable.
/// </summary>
public class spriteComp : Microsoft.Xna.Framework.DrawableGameComponent
{
//Изображение
private Texture2D sprTexture;
//Прямоугольник, ограничивающий спрайт
private Rectangle sprRectangle;
//Координата спрайта
private Vector2 sprPosition;
//Границы экрана
private Rectangle scrBounds;
//Направление движения спрайта
private Vector2 sprMove = new Vector2(0, 0);
public spriteComp(Game game, ref Texture2D newTexture,
Rectangle newRectangle, Vector2 newPosition
)
: base(game)
{
sprTexture = newTexture;
sprRectangle = newRectangle;
sprPosition = newPosition;
//Работаем в портретном режиме, высота - 480, ширина - 800
scrBounds = new Rectangle(0, 0,
game.Window.ClientBounds.Height,
game.Window.ClientBounds.Width
);
}
/// <summary>
/// Позволяет игровому компоненту выполнить необходимую инициализацию перед\r\запуском.
Здесь можно запросить нужные службы и загрузить контент.
///
/// </summary>
public override void Initialize()
{
// ЗАДАЧА: добавьте здесь код инициализации
base.Initialize();
}
/// <summary>
/// Позволяет игровому компоненту обновиться.
/// </summary>
/// <param name="gameTime">Предоставляет моментальный снимок значений времени.</param>
public override void Update(GameTime gameTime)
{
//Изменение координат в соответствии с данными, имеющимися в sprMove
sprPosition += sprMove;
//Если вышли за пределы экрана - исправляем
if (sprPosition.X < scrBounds.Left)
{
sprPosition.X = scrBounds.Left;
}
if (sprPosition.X > scrBounds.Width - sprRectangle.Width)
{
sprPosition.X = scrBounds.Width - sprRectangle.Width;
}
if (sprPosition.Y < scrBounds.Top)
{
sprPosition.Y = scrBounds.Top;
}
if (sprPosition.Y > scrBounds.Height - sprRectangle.Height)
{
sprPosition.Y = scrBounds.Height - sprRectangle.Height;
}
base.Update(gameTime);
}
public override void Draw(GameTime gameTime)
{
SpriteBatch sprBatch =
(SpriteBatch)Game.Services.GetService(typeof(SpriteBatch));
sprBatch.Draw(sprTexture, sprPosition, sprRectangle, Color.White);
base.Draw(gameTime);
}
//Метод для установки направления движения спрайта
public void Move(Vector2 move)
{
//Зададим скорость и направление движени спрайта
sprMove = move;
//Выведем данные для целей отладки
Debug.WriteLine(move.ToString());
}
//Получение позиции спрайта для внешних целей
public Vector2 GetSprPos()
{
return sprPosition;
}
//Получение прямоугольника, ограничивающего спрайт, для врешних целей
public Rectangle GetSprRectangle()
{
return sprRectangle;
}
}
}
В целом этот код уже вам знаком по предыдущим примерам. Опишем его ключевые особенности. Метод Move принимает параметр move, который записывается в переменную sprMove.
В методе компонента Update эта переменная используется для модификации позиции Move мы выводим в окно отладочных сообщений переданный в объект параметр move, на рис. 13.3 вы можете видеть это окно.
(рис 13.3) Отладочные сообщения
Рассмотрим теперь код
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;
namespace P7_2
{
/// <summary>
/// Это главный тип игры
/// </summary>
public class Game1 : Microsoft.Xna.Framework.Game
{
GraphicsDeviceManager graphics;
SpriteBatch spriteBatch;
spriteComp gameObject;
//Для хранения ID касаний
int gameObjectId;
//Изображение для вывода
Texture2D texture;
//Следует ли перемещать объект
bool gameObjectMove;
public Game1()
{
graphics = new GraphicsDeviceManager(this);
Content.RootDirectory = "Content";
graphics.IsFullScreen = true;
// Частота кадра на Windows Phone по умолчанию — 30 кадров в секунду.
TargetElapsedTime = TimeSpan.FromTicks(333333);
// Дополнительный заряд аккумулятора заблокирован.
InactiveSleepTime = TimeSpan.FromSeconds(1);
//Включить обработку жестов - FreeDarag и DoubleTap
TouchPanel.EnabledGestures = GestureType.FreeDrag | GestureType.DoubleTap;
}
/// <summary>
/// Позволяет игре выполнить инициализацию, необходимую перед запуском.
/// Здесь можно запросить нужные службы и загрузить неграфический
/// контент. Вызов base.Initialize приведет к перебору всех компонентов и
/// их инициализации.
/// </summary>
protected override void Initialize()
{
// ЗАДАЧА: добавьте здесь логику инициализации
base.Initialize();
}
/// <summary>
/// LoadContent будет вызываться в игре один раз; здесь загружается
/// весь контент.
/// </summary>
protected override void LoadContent()
{
// Создайте новый SpriteBatch, который можно использовать для отрисовки текстур.
spriteBatch = new SpriteBatch(GraphicsDevice);
Services.AddService(typeof(SpriteBatch), spriteBatch);
texture = Content.Load<Texture2D>("rectangle");
//Создаём игровой объект
CreateNewObject();
}
protected void CreateNewObject()
{
gameObject = new spriteComp(this, ref texture,
new Rectangle(0, 0, 150, 100),
new Vector2(0, 0));
Components.Add(gameObject);
}
/// <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();
//Получаем коллекцию объектов, содержащих информацию о касаниях экрана
TouchCollection touchLocations = TouchPanel.GetState();
//Перебираем коллекцию, присваивая объекту координаты касания
foreach (TouchLocation touchLocation in touchLocations)
{
//Если обнаружено касание в области, соответствующей игровому объекту
if (touchLocation.State == TouchLocationState.Pressed)
{
if (touchLocation.Position.X > gameObject.GetSprPos().X
touchLocation.Position.X < gameObject.GetSprPos().X + gameObject.GetSprRectangle().Width
touchLocation.Position.Y > gameObject.GetSprPos().Y
touchLocation.Position.Y < gameObject.GetSprPos().Y + gameObject.GetSprRectangle().Height)
{
//Разрешаем перемещать объект
gameObjectMove = true;
//Запоминаем идентификатор касания
gameObjectId = touchLocation.Id;
}
}
//Если касание окончилось
if (touchLocation.State == TouchLocationState.Released)
{
//Проверяем ID
if (gameObjectId == touchLocation.Id)
{
//Запрещаем перемещение
gameObjectMove = false;
}
}
}
//До тех пор, пока разрешена обработка жестов
while (TouchPanel.IsGestureAvailable)
{
//прочитаем жест
GestureSample gs = TouchPanel.ReadGesture();
//Если жест - FreeDrag и разрешено перемещение
//то есть, пользователь касается объекта на экране
if (gs.GestureType == GestureType.FreeDrag gameObjectMove==true)
{
//Начнем перемещать объект со скоростью, заданной параметром Delta
//Фактически - чем быстрее был выполнен жест - тем быстрее будет двигаться объект
//в направлении жеста
gameObject.Move(gs.Delta);
}
//Если обнаружен жест двойного касания экрана
if (gs.GestureType == GestureType.DoubleTap)
{
//Остановим движение объекта
gameObject.Move(new Vector2());
//Включим вибраацию на 0,1 секунды
VibrateController.Default.Start(TimeSpan.FromMilliseconds(100));
}
}
base.Update(gameTime);
}
/// <summary>
/// Вызывается, когда игра отрисовывается.
/// </summary>
/// <param name="gameTime">Предоставляет моментальный снимок значений времени.</param>
protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.CornflowerBlue);
spriteBatch.Begin();
base.Draw(gameTime);
spriteBatch.End();
}
}
}
Командой TouchPanel.EnabledGestures = GestureType.FreeDrag | GestureType.DoubleTap; мы включаем обработку жестов и указываем, с какими именно жестами собираемся работать. В нашем случае это FreeDrag и DoubleTap.
Мы хотим реализовать следующий функционал. Когда пользователь касается игровой объект на экране и проводит пальцем по экрану (жест FreeDrag), объект начинает перемещаться в направлении, в котором пользователь провёл пальцем, причем, скорость перемещения объекта задаётся скоростью перемещения пальца. Если пользователь коснётся точки, которая не принадлежит объекту, тот реагировать не будет – за это отвечает код, который, прежде чем разрешить движение объекта, проверяет точку касания.
Для этого используется обычная, уже знакомая вам, обработка касания. Объект в нашем примере напоминает лёгкую пластинку, которая плавает по поверхности прямоугольной ёмкости, наполненной жидкостью. Если пользователь приведет объект в движение – тот будет двигаться с заданными параметрами до тех пор, пока не упрётся в границу экрана. Для того, чтобы остановить движение объекта, мы применяем жест DoubleTap. Этот жест напоминает двойной щелчок мыши. Когда пользователь выполнит этот жест, объект останавливается и на 0,1 секунды включается вибрация телефона.
Мы используем здесь параметр жеста FreeDrag для реализации управления объектом, если, например, использовать координаты касания (они постоянно обновляются при перемещении пальца по экрану), можно организовать управление объектом, подобное перемещению объекта по плоской поверхности, когда его можно "придавить" пальцем и переместить в нужную позицию.
На рис. 13.4 вы можете видеть игровой экран приложения
(рис 13.4) Игровой экран приложения
В предыдущих практических работах мы разобрали различные способы управления игровым процессом с использованием сенсорной панели, акселерометра,
Кроме того, различные устройства можно комбинировать – именно так поступают разработчики многих современных игр. Например, если речь идёт о симуляторе автогонок Need For VerticalDrag (проведя пальцем вдоль правой части экрана в ландшафтной ориентации), а задействовать тормоза – коснувшись левой части экрана.
Обилие способов управления игровым процессом даёт огромный простор для творчества. Разрабатывая систему управления игрой, обратите внимание на Combined Motion API (пространство имён Microsoft.Device.Sensors), класс Motion. Это API позволяет получать данные от гироскопа, акселерометра и электронного компаса и предоставляет полезные инструменты для применения этих данных в управлении игровым процессом.
В этой лабораторной работе была рассмотрена методика работы с географическими координатами, методика обработка жестов, работа с виброзвонком, дан обзор дополнительных возможностей по управлению программами с помощью встроенных сенсоров телефона.
Реализуйте распознавание различных жестов в XNA-программе, изучите особенности распознавания жестов, посмотрите, распознавание каких видов жестов может накладываться друг на друга, используя отладочные механизмы.
К данной лабораторной работе прилагается видеоурок
и демонстрационный проект.
Цель работы: Научиться работать с данными о географическом положении, использовать жесты для управления программами
Для управления играми, написанными для Windows Phone, можно пользоваться самыми разными датчиками. В частности, датчиком местоположения. В XNA-проекте мы можем получать данные о местоположении устройства, выраженные в географических координатах, от встроенного
Данные о местоположении, получение которых мы сейчас рассмотрим, позволяют определить географические координаты устройства, скорость перемещения, высоту над уровнем моря, курс движения.
Создадим новый стандартный XNA-проект (P7_1). Для использования служб работы с местоположением нам понадобится подключить (в разделе Ссылки игрового проекта) библиотеку System.Device, пространство имён System.Device.Location.
В данном проекте, листинг 13.1., мы будем получать данные о местоположении устройства и выводить их на экран.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Device.Location;
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;
namespace P7_1
{
/// <summary>
/// Это главный тип игры
/// </summary>
public class Game1 : Microsoft.Xna.Framework.Game
{
GraphicsDeviceManager graphics;
SpriteBatch spriteBatch;
//Строка для вывода
string text="Please wait";
//Для хранения позиции вывода текста
Vector2 textPosition = new Vector2(100, 100);
//Для хранения шрифта
SpriteFont MyFont;
//Объект для работы с данными о местоположении
GeoCoordinateWatcher GeoWatch;
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()
{
//Новый объект для работы с местоположением
GeoWatch = new GeoCoordinateWatcher();
//Обработчик события, которое происходит при изменении местоположения
GeoWatch.PositionChanged += new EventHandler<GeoPositionChangedEventArgs<GeoCoordinate>>(GeoWatch_PositionChanged);
//Запуск сервиса
GeoWatch.Start();
base.Initialize();
}
void GeoWatch_PositionChanged(object sender, GeoPositionChangedEventArgs<GeoCoordinate> e)
{
//Записываем в переменную для вывода на экран следующие данные:
//Широта
//Долгота
//Высота
//Курс
//Скорость
//Отметка времени
text = String.Format("Latitude: {0}\r\n" +
"Longitude: {1}\r\n" +
"Altitude: {2}\r\n" +
"Course: {3}\r\n"+
"Speed: {4}\r\n"+
"Current time: {5}",
e.Position.Location.Latitude,
e.Position.Location.Longitude,
e.Position.Location.Altitude,
e.Position.Location.Course,
e.Position.Location.Speed,
e.Position.Timestamp);
}
///// <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();
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);
}
}
}
В данном проекте мы пользуемся классом GeoCoordinateWatcher(), создаем обработчик события PositionChanged, в коде которого записываем полученные от устройства данные в текстовую переменную для вывода на экран.
(рис 13.1) Запуск программы в эмуляторе
На одном из предыдущих занятий мы рассматривали
Windows Phone поддерживает распознавание множества жестов. В частности, таких, как FreeDrag – то есть – свободное перетаскивание по экрану, DoubleTap – быстрое двойное касание экрана. Flick – касание с быстрым перемещением, – касание и удержание пальца на панели, Pinch – сведение или разведение двух пальцев на панели, обычно используется для целей масштабирования объектов. Эти и другие жесты позволяют реализовать самые разные схемы управления объектами на экране. Например, объекты можно "раскладывать" по экрану в нужной последовательности, управлять скоростью и направлением их перемещения, масштабировать – здесь всё зависит от целей и фантазии разработчика. В нашем примере мы используем обработку двух жестов – FreeDrag и DoubleTap.
Кроме того, мы включили в пример работу с виброзвонком телефона. В определенный момент мы будем включать вибрацию. Этот приём часто используется в играх для того, чтобы добавить им реалистичности. Например, устройство может вибрировать при столкновении каких-то объектов, при падении и во многих других случаях.
Обработка жестов не требует каких-то дополнительных действий, всё, что нужно, уже подключено в стандартном проекте. Для работы с вибрацией нам понадобится подключить библиотеку Microsoft.Phone и пространство имен Microsoft.Devices
Кроме того, здесь мы, в учебных целях, рассмотрим удобный механизм
Новый стандартный XNA-проект (P7_2) содержит игровой компонент, spriteComp.cs – это модифицированная версия использованного ранее компонента, один графический ресурс –
(рис 13.2) Игровой проект P7_2
В листинге 13.2 вы можете видеть код игрового компонента
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.Media;
//Подключим для отладочных целей
using System.Diagnostics;
namespace P7_2
{
/// <summary>
/// Это игровой компонент, реализующий интерфейс IUpdateable.
/// </summary>
public class spriteComp : Microsoft.Xna.Framework.DrawableGameComponent
{
//Изображение
private Texture2D sprTexture;
//Прямоугольник, ограничивающий спрайт
private Rectangle sprRectangle;
//Координата спрайта
private Vector2 sprPosition;
//Границы экрана
private Rectangle scrBounds;
//Направление движения спрайта
private Vector2 sprMove = new Vector2(0, 0);
public spriteComp(Game game, ref Texture2D newTexture,
Rectangle newRectangle, Vector2 newPosition
)
: base(game)
{
sprTexture = newTexture;
sprRectangle = newRectangle;
sprPosition = newPosition;
//Работаем в портретном режиме, высота - 480, ширина - 800
scrBounds = new Rectangle(0, 0,
game.Window.ClientBounds.Height,
game.Window.ClientBounds.Width
);
}
/// <summary>
/// Позволяет игровому компоненту выполнить необходимую инициализацию перед\r\запуском.
Здесь можно запросить нужные службы и загрузить контент.
///
/// </summary>
public override void Initialize()
{
// ЗАДАЧА: добавьте здесь код инициализации
base.Initialize();
}
/// <summary>
/// Позволяет игровому компоненту обновиться.
/// </summary>
/// <param name="gameTime">Предоставляет моментальный снимок значений времени.</param>
public override void Update(GameTime gameTime)
{
//Изменение координат в соответствии с данными, имеющимися в sprMove
sprPosition += sprMove;
//Если вышли за пределы экрана - исправляем
if (sprPosition.X < scrBounds.Left)
{
sprPosition.X = scrBounds.Left;
}
if (sprPosition.X > scrBounds.Width - sprRectangle.Width)
{
sprPosition.X = scrBounds.Width - sprRectangle.Width;
}
if (sprPosition.Y < scrBounds.Top)
{
sprPosition.Y = scrBounds.Top;
}
if (sprPosition.Y > scrBounds.Height - sprRectangle.Height)
{
sprPosition.Y = scrBounds.Height - sprRectangle.Height;
}
base.Update(gameTime);
}
public override void Draw(GameTime gameTime)
{
SpriteBatch sprBatch =
(SpriteBatch)Game.Services.GetService(typeof(SpriteBatch));
sprBatch.Draw(sprTexture, sprPosition, sprRectangle, Color.White);
base.Draw(gameTime);
}
//Метод для установки направления движения спрайта
public void Move(Vector2 move)
{
//Зададим скорость и направление движени спрайта
sprMove = move;
//Выведем данные для целей отладки
Debug.WriteLine(move.ToString());
}
//Получение позиции спрайта для внешних целей
public Vector2 GetSprPos()
{
return sprPosition;
}
//Получение прямоугольника, ограничивающего спрайт, для врешних целей
public Rectangle GetSprRectangle()
{
return sprRectangle;
}
}
}
В целом этот код уже вам знаком по предыдущим примерам. Опишем его ключевые особенности. Метод Move принимает параметр move, который записывается в переменную sprMove.
В методе компонента Update эта переменная используется для модификации позиции Move мы выводим в окно отладочных сообщений переданный в объект параметр move, на рис. 13.3 вы можете видеть это окно.
(рис 13.3) Отладочные сообщения
Рассмотрим теперь код
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;
namespace P7_2
{
/// <summary>
/// Это главный тип игры
/// </summary>
public class Game1 : Microsoft.Xna.Framework.Game
{
GraphicsDeviceManager graphics;
SpriteBatch spriteBatch;
spriteComp gameObject;
//Для хранения ID касаний
int gameObjectId;
//Изображение для вывода
Texture2D texture;
//Следует ли перемещать объект
bool gameObjectMove;
public Game1()
{
graphics = new GraphicsDeviceManager(this);
Content.RootDirectory = "Content";
graphics.IsFullScreen = true;
// Частота кадра на Windows Phone по умолчанию — 30 кадров в секунду.
TargetElapsedTime = TimeSpan.FromTicks(333333);
// Дополнительный заряд аккумулятора заблокирован.
InactiveSleepTime = TimeSpan.FromSeconds(1);
//Включить обработку жестов - FreeDarag и DoubleTap
TouchPanel.EnabledGestures = GestureType.FreeDrag | GestureType.DoubleTap;
}
/// <summary>
/// Позволяет игре выполнить инициализацию, необходимую перед запуском.
/// Здесь можно запросить нужные службы и загрузить неграфический
/// контент. Вызов base.Initialize приведет к перебору всех компонентов и
/// их инициализации.
/// </summary>
protected override void Initialize()
{
// ЗАДАЧА: добавьте здесь логику инициализации
base.Initialize();
}
/// <summary>
/// LoadContent будет вызываться в игре один раз; здесь загружается
/// весь контент.
/// </summary>
protected override void LoadContent()
{
// Создайте новый SpriteBatch, который можно использовать для отрисовки текстур.
spriteBatch = new SpriteBatch(GraphicsDevice);
Services.AddService(typeof(SpriteBatch), spriteBatch);
texture = Content.Load<Texture2D>("rectangle");
//Создаём игровой объект
CreateNewObject();
}
protected void CreateNewObject()
{
gameObject = new spriteComp(this, ref texture,
new Rectangle(0, 0, 150, 100),
new Vector2(0, 0));
Components.Add(gameObject);
}
/// <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();
//Получаем коллекцию объектов, содержащих информацию о касаниях экрана
TouchCollection touchLocations = TouchPanel.GetState();
//Перебираем коллекцию, присваивая объекту координаты касания
foreach (TouchLocation touchLocation in touchLocations)
{
//Если обнаружено касание в области, соответствующей игровому объекту
if (touchLocation.State == TouchLocationState.Pressed)
{
if (touchLocation.Position.X > gameObject.GetSprPos().X
touchLocation.Position.X < gameObject.GetSprPos().X + gameObject.GetSprRectangle().Width
touchLocation.Position.Y > gameObject.GetSprPos().Y
touchLocation.Position.Y < gameObject.GetSprPos().Y + gameObject.GetSprRectangle().Height)
{
//Разрешаем перемещать объект
gameObjectMove = true;
//Запоминаем идентификатор касания
gameObjectId = touchLocation.Id;
}
}
//Если касание окончилось
if (touchLocation.State == TouchLocationState.Released)
{
//Проверяем ID
if (gameObjectId == touchLocation.Id)
{
//Запрещаем перемещение
gameObjectMove = false;
}
}
}
//До тех пор, пока разрешена обработка жестов
while (TouchPanel.IsGestureAvailable)
{
//прочитаем жест
GestureSample gs = TouchPanel.ReadGesture();
//Если жест - FreeDrag и разрешено перемещение
//то есть, пользователь касается объекта на экране
if (gs.GestureType == GestureType.FreeDrag gameObjectMove==true)
{
//Начнем перемещать объект со скоростью, заданной параметром Delta
//Фактически - чем быстрее был выполнен жест - тем быстрее будет двигаться объект
//в направлении жеста
gameObject.Move(gs.Delta);
}
//Если обнаружен жест двойного касания экрана
if (gs.GestureType == GestureType.DoubleTap)
{
//Остановим движение объекта
gameObject.Move(new Vector2());
//Включим вибраацию на 0,1 секунды
VibrateController.Default.Start(TimeSpan.FromMilliseconds(100));
}
}
base.Update(gameTime);
}
/// <summary>
/// Вызывается, когда игра отрисовывается.
/// </summary>
/// <param name="gameTime">Предоставляет моментальный снимок значений времени.</param>
protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.CornflowerBlue);
spriteBatch.Begin();
base.Draw(gameTime);
spriteBatch.End();
}
}
}
Командой TouchPanel.EnabledGestures = GestureType.FreeDrag | GestureType.DoubleTap; мы включаем обработку жестов и указываем, с какими именно жестами собираемся работать. В нашем случае это FreeDrag и DoubleTap.
Мы хотим реализовать следующий функционал. Когда пользователь касается игровой объект на экране и проводит пальцем по экрану (жест FreeDrag), объект начинает перемещаться в направлении, в котором пользователь провёл пальцем, причем, скорость перемещения объекта задаётся скоростью перемещения пальца. Если пользователь коснётся точки, которая не принадлежит объекту, тот реагировать не будет – за это отвечает код, который, прежде чем разрешить движение объекта, проверяет точку касания.
Для этого используется обычная, уже знакомая вам, обработка касания. Объект в нашем примере напоминает лёгкую пластинку, которая плавает по поверхности прямоугольной ёмкости, наполненной жидкостью. Если пользователь приведет объект в движение – тот будет двигаться с заданными параметрами до тех пор, пока не упрётся в границу экрана. Для того, чтобы остановить движение объекта, мы применяем жест DoubleTap. Этот жест напоминает двойной щелчок мыши. Когда пользователь выполнит этот жест, объект останавливается и на 0,1 секунды включается вибрация телефона.
Мы используем здесь параметр жеста FreeDrag для реализации управления объектом, если, например, использовать координаты касания (они постоянно обновляются при перемещении пальца по экрану), можно организовать управление объектом, подобное перемещению объекта по плоской поверхности, когда его можно "придавить" пальцем и переместить в нужную позицию.
На рис. 13.4 вы можете видеть игровой экран приложения
(рис 13.4) Игровой экран приложения
В предыдущих практических работах мы разобрали различные способы управления игровым процессом с использованием сенсорной панели, акселерометра,
Кроме того, различные устройства можно комбинировать – именно так поступают разработчики многих современных игр. Например, если речь идёт о симуляторе автогонок Need For VerticalDrag (проведя пальцем вдоль правой части экрана в ландшафтной ориентации), а задействовать тормоза – коснувшись левой части экрана.
Обилие способов управления игровым процессом даёт огромный простор для творчества. Разрабатывая систему управления игрой, обратите внимание на Combined Motion API (пространство имён Microsoft.Device.Sensors), класс Motion. Это API позволяет получать данные от гироскопа, акселерометра и электронного компаса и предоставляет полезные инструменты для применения этих данных в управлении игровым процессом.
В этой лабораторной работе была рассмотрена методика работы с географическими координатами, методика обработка жестов, работа с виброзвонком, дан обзор дополнительных возможностей по управлению программами с помощью встроенных сенсоров телефона.
Реализуйте распознавание различных жестов в XNA-программе, изучите особенности распознавания жестов, посмотрите, распознавание каких видов жестов может накладываться друг на друга, используя отладочные механизмы.
К данной лабораторной работе прилагается видеоурок
и демонстрационный проект.
Для получения официальных документов о завершении программы дополнительного профессионального образования (удостоверения о повышении квалификации, дипломов о профессиональной переподготовке и MBA) необходимо предоставить:
Внимание! Вы можете не заказывать доставку бумажной версии официального документы, а скачать его в электронном виде и распечатать самостоятельно. Информация о выданном документе в течение 1 месяца загружается в Федеральную информационную систему «Федеральный реестр сведений о документах об образовании и (или) о квалификации, документах об обучении» - ФИС ФРДО.
Доступ на новый сайт осуществляется с использованием адреса электронной почты, который был указан вами при регистрации на "старом". Мы постарались перенести все ваши данные с прежнего ресурса, однако не исключена вероятность потери части информации.
При возникновении проблемы со входом, воспользуйтесь функцией сброса пароля
Если вы обнаружите несоответствия, пожалуйста, сообщите нам.