Презентацию к данной лекции Вы можете скачать здесь.
Технология XNA является библиотекой объектов C#, которая используется для создания игровых программ. XNA позволяет разработчикам игр использовать C# для создания высокопроизводительных игр для различных платформ, включая компьютеры под управлением Windows, Xbox 360 и Windows Phone. При грамотном проектировании структуры программы возможно повторное использование большой части кода для портирования игр под эти три платформы.
Игры могут быть двухмерными (2D) (изображения, представленные в одной плоскости), или трёхмерными (3D) (визуальное моделирование трёхмерной окружающей среды). XNA позволяет создавать игры обоих типов, и аппаратное ускорение Windows Phone способствует реалистичному изображению трёхмерных игровых миров. В этой лекции мы ограничимся рассмотрением 2D игр, основанных на спрайтах.
При создании проекта приложения для Windows Phone можно выбрать тип приложения: Silverlight или XNA. Visual Studio использует соответствующий шаблон и создаёт решение с необходимыми элементами для создания программы. Шаблоны для создания приложений XNA собраны в группу XNA Game Studio 4.0 в диалоговом окне Создание нового проекта. Здесь представлены шаблоны для создания игр XNA для Windows Phone и Xbox 360. Также можно использовать существующий проект XNA для одной платформы в качестве основы приложения для другой платформы.
Решение XNA похоже на решение Silverlight, но между ними есть некоторые различия. В решении XNA создаётся два проекта, из которых один предназначен для хранения игрового контента. Им управляет
Необходимо учитывать, что экран Windows Phone имеет меньший размер по сравнению с компьютером и платформой Xbox 360. Максимальное разрешение игры для Windows Phone составляет 800x480 пикселей. Чтобы игры XNA загружались быстрее и занимали меньше памяти, стоит изменить размеры игровых ресурсов для платформы Windows Phone.
Программы на основе Silverlight выполняют какие-либо действия обычно только в ответ на события, например, при нажатии на кнопку, или при завершении сетевой операции, или при загрузке новой страницы приложения.
В отличие от приложений Silverlight, программы XNA всегда выполняют какие-либо действия: обновляют игровой мир и перерисовывают экран. Вместо ожидания ввода от устройства, игра XNA проверяет устройства, в которых, возможно, пользователь выполнил ввод, и используют их, чтобы обновить игровую модель и как можно быстрее перерисовать экран. В контексте игры это имеет большое значение. В XNA игровой мир всё время обновляется вокруг игрока, а не игрок инициирует действия.
При запуске игры XNA фактически выполняются следующие действия:
Эти действия выполняются в трёх методах класса Game, который создаёт Visual Studio как часть нового проекта игры.
partial class Game1 : Microsoft.Xna.Framework.Game
{
protected override void LoadContent(bool loadAllContent)
{
}
protected override void Update(GameTime gameTime)
{
}
protected override void Draw(GameTime gameTime)
{
}
}
При создании игры нужно добавить в эти методы программный код. Эти методы XNA автоматически вызывает во время работы игры. Метод LoadContent вызывается при запуске игры. Метод Draw вызывается настолько часто, насколько это возможно, а метод Update вызывается с частотой 30 раз в секунду, в отличие от версии игр для компьютеров и Xbox 360, где этот метод вызывается 60 раз в секунду, что позволяет уменьшить потребление электроэнергии.
Рассмотрим пример создания приложения, в котором по экрану будет перемещаться белый шар.
Под словом контент понимаются все ресурсы игры: все
Управление контентом выполняется в Visual Studio. Управление элементами контента выполняется таким же образом, как и файлами кода программы. Их можно добавить в проект и затем просмотреть их и управлять их свойствами.
В нашем примере будет использоваться изображение белого шара, которое хранится в файле WhiteDot.png. Соответственно, в рамках приложения у этого ресурса будет имя WhiteDot. Как и в приложениях Silverlight, все добавленные ресурсы будут являться частью решения Visual Studio. Однако,
Метод LoadContent вызывается для загрузки контента в игру один раз при запуске игры:
protected override void LoadContent()
{
// создание группы спрайтов, которая можно использовать для отрисовки текстур
spriteBatch = new SpriteBatch(GraphicsDevice);
}
Этот метод генерируется автоматически и создаёт новый экземпляр класса SpriteBatch, который будет использоваться для отрисовки элементов на экране. Сюда можно добавить код для загрузки изображения в игру:
Texture2D ballTexture = Content.Load<Texture2D>("WhiteDot");
Метод Load входит в состав XNA Texture2D представляет собой двухмерную текстуру, которую можно выводить на экран.
В компьютерных играх используется понятие "
XNA использует систему координат, в которой начало координат находится в верхнем левом углу экрана, как и во многих других графических инструментах. Соответственно, при увеличении значения координаты Y объекта он будет располагаться ниже.
В большинстве устройств Windows Phone разрешение экрана составляет 800*480 пикселей. Если попытаться вывести объекты за пределами этой области, то XNA их просто не выведет.
Для определения положения и размера области на экране в XNA используется класс Rectangle.
Rectangle ballRectangle = new Rectangle(0, 0, ballTexture.Width, ballTexture.Height);
Этот код создаёт прямоугольник в главном левом углу экрана с заданными размерами, которые совпадают с размерами
Для вывода Draw, который при создании проекта содержит следующий код:
protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.CornflowerBlue);
base.Draw(gameTime);
}
Этот метод задаёт синий фон экрана. Необходимо добавить в этот метод код для вывода на экран изображение шара.
В современных устройствах есть специализированные графические аппаратные средства, которые выполняют все операции вывода изображений на экран. В Windows Phone есть графический процессор (GPU), которому нужно указать SpriteBatch, который выполняет группировку инструкций:
protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.CornflowerBlue);
spriteBatch.Begin();
// сюда нужно добавить код для отрисовки объектов
spriteBatch.End();
base.Draw(gameTime);
}
Необходимо помнить, что код в методе Draw должен вызывать методы Begin и End класса SpriteBatch для начала и окончания операций отрисовки, иначе отрисовка не будет выполняться.
Для отрисовки Draw, которому необходимо указать текстуру, её положение на экране и оттенок, который применяется к Color.White, то оттенок применяться не будет:
spriteBatch.Draw(ballTexture, ballRectangle, Color.White);
Полный код метода будет выглядеть так:
protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.CornflowerBlue);
spriteBatch.Begin();
spriteBatch.Draw(ballTexture, ballRectangle, Color.White);
spriteBatch.End();
base.Draw(gameTime);
}
При запуске программы шар будет выводиться в верхнем левом углу экрана телефона. При этом, по умолчанию игра XNA ожидает, что игрок будет держать телефон горизонтально, т.е. в альбомной ориентации. При необходимости игра может изменять ориентацию экрана.
В программе можно изменять положение и размер
(рис 19.1) Масштабирование текстуры при различных размерах экрана
При создании игры важно, чтобы она выглядела одинаково, независимо от размера экрана используемого устройства. Устройства Windows Phone используют ограниченный набор разрешений, как и на компьютерах и платформе Xbox 360. Чтобы игра выглядела одинаково на каждом устройстве, она должна определять размеры экрана устройства, и в зависимости от размеров масштабировать объекты.
Игра XNA может узнать размер экрана из свойств окна просмотра, используемого графическим адаптером:
ballRectangle = new Rectangle(0, 0, GraphicsDevice.Viewport.Width / 20,
GraphicsDevice.Viewport.Width / 20);
Этот код создает прямоугольную область для шара, размер которого составит 1/20 часть ширины экрана, независимо от его размера.
При перемещении игровых объектов их положение на экране изменяется перед перерисовкой экрана. Положение объектов нужно обновлять в методе Update, который вызывается с частотой 30 раз в секунду во время работы игры. Для обновления положения шара можно использовать следующий код:
protected override void Update(GameTime gameTime)
{
ballRectangle.X++;
ballRectangle.Y++;
base.Update(gameTime);
}
Этот метод увеличивает значения координат X и Y прямоугольника шара на один пиксель при каждом вызове. При этом, шар спускается вниз и в сторону на 30 пикселей каждую секунду. Через 15 секунд шар выходит за пределы экрана, но продолжает перемещаться и дальше.
Если нужно, чтобы шар перемещался по экрану медленнее, для задания положения шара можно использовать вещественные переменные, которые могут хранить дробные числа. При этом, перед выводом шара эти переменные необходимо преобразовать в целые числа, так как свойства, определяющие положение, имеют целочисленный тип:
float ballX; float ballY; // значения скорости шара по соответствуюзей координате float ballXSpeed = 3; float ballYSpeed = 3; // новые координаты шара ballRectangle.X = (int)(ballX + ballXSpeed); ballRectangle.Y = (int)(ballY + ballYSpeed);
В этом коде используются вещественные значения в форме с плавающей точкой, которые округляются до целых чисел.
Теперь необходимо сделать так, чтобы шар отлетал от краёв экрана. Игра должна обнаружить, когда шар достигает края экрана, и обновить направление его движения. Если шар касается края экрана в горизонтальном направлении, игра должна изменить значение скорости шара по оси X, а если шар касается верхней или нижней части экрана — значение скорости шара по оси Y. Для того чтобы шар двигался в обратном направлении, достаточно изменить знак значения скорости по соответствующей координате. Программый код, выполняющий эти действия, может выглядеть так:
if (ballX < 0 || ballX + ballRectangle.Width > GraphicsDevice.Viewport.Width)
{
ballXSpeed = -ballXSpeed;
}
if (ballY < 0 || ballY + ballRectangle.Height > GraphicsDevice.Viewport.Height)
{
ballYSpeed = -ballYSpeed;
}
Практически в любой игре игровой процесс зависит от действия пользователя. Пользователь управляет каким-то игровым объектом. В нашем примере таким объектом будет прямоугольная вертикальная платформа (рис. 19.2), которая может двигаться вверх и вниз.
(рис 19.2) Игровой объект "Платформа"
// игровой мир Texture2D ballTexture; Rectangle ballRectangle; float ballX; float ballY; float ballXSpeed = 3; float ballYSpeed = 3; Texture2D lPaddleTexture; Rectangle lPaddleRectangle; float lPaddleSpeed = 4; float lPaddleY; Texture2D rPaddleTexture; Rectangle rPaddleRectangle; float rPaddleY; // значение отступа платформ от краёв экрана int margin;
Эти переменные представляют игровой мир. Метод Update должен обновлять значения переменных, а метод Draw использует их для вывода платформ и шара на экран в нужном месте.
При запуске игры метод LoadContent загружает изображения с помощью
protected override void LoadContent()
{
// создание группы спрайтов, которая можно использовать для отрисовки текстур
spriteBatch = new SpriteBatch(GraphicsDevice);
ballTexture = Content.Load<Texture2D>("ball");
lPaddleTexture = Content.Load<Texture2D>("lpaddle");
rPaddleTexture = Content.Load<Texture2D>("rpaddle");
ballRectangle = new Rectangle(
GraphicsDevice.Viewport.Width / 2, GraphicsDevice.Viewport.Height / 2,
GraphicsDevice.Viewport.Width / 20, GraphicsDevice.Viewport.Width / 20);
margin = GraphicsDevice.Viewport.Width / 20;
lPaddleRectangle = new Rectangle(margin, 0,
GraphicsDevice.Viewport.Width / 20, GraphicsDevice.Viewport.Height / 5);
rPaddleRectangle = new Rectangle(
GraphicsDevice.Viewport.Width - lPaddleRectangle.Width - margin, 0,
GraphicsDevice.Viewport.Width / 20, GraphicsDevice.Viewport.Height / 5);
lPaddleY = lPaddleRectangle.Y;
rPaddleY = rPaddleRectangle.Y;
}
В результате выполнения этого метода на экран будет выведено изображение, представленное на рис. 19.3.
(рис 19.3) Игровые объекты на экране
После запуска игры шар будет перемещаться по экрану. При этом, игрок должен управлять движением платформы.
Для управления движением платформы можно использовать сенсорный экран Windows Phone. Использовать сенсорный экран для управления игровыми объектами можно по-разному. В нашем примере будет применяться следующий принцип: если игрок коснётся верхней части экрана, то его платформа будет двигаться вверх, а если коснётся нижней части экрана — то вниз.
Для взаимодействия с сенсорным экраном используется класс TouchPanel. Его метод GetState возвращает коллекцию объектов, которые описывают точки касания:
TouchCollection touches = TouchPanel.GetState();
Каждый элемент коллекции является экземпляром класса TouchLocation и содержит много свойств, которые хранят информацию о месте экрана, к которому прикоснулся пользователь. Можно использовать свойство Y этого класса, чтобы управлять движением платформы.
if (touches.Count > 0)
{
if (touches[0].Position.Y > GraphicsDevice.Viewport.Height / 2)
{
lPaddleY = lPaddleY + lPaddleSpeed;
}
else
{
lPaddleY = lPaddleY - lPaddleSpeed;
}
}
Первая строка этого блока программы проверяет, был ли сенсорный ввод. Если да, то проверяется, в какой части экрана пользователь коснулся экрана — выше или ниже середины экрана — и в соответствии с этим изменяется положение левой платформы.
Также можно использовать расширенные возможности сенсорного ввода, среди которых — несколько одновременных прикосновений, отслеживание местоположение и движения особого типа сенсорных жестов.
Обнаружение столкновений. В настоящее время платформа и шар никак не взаимодействуют друг с другом. Необходимо, чтобы шар отскакивал от платформы. Для этого необходимо определить, пересекаются ли два прямоугольника.
Для этого можно использовать метод Intersects класса Rectangle:
if (ballRectangle.Intersects(lPaddleRectangle))
{
ballXSpeed = -ballXSpeed;
}
Этот код проверяет, коснулся ли шар левой платформы, и если да — направление движения шара по горизонтали меняется на противоположное.
Ведение счёта. Часто в играх ведётся счёт, который выводится на экран. В нашем примере очки будет зарабатывать противник игрока, который пропустил шар, и в этом случае шар касается боковой границы экрана. Для этого можно использовать существующий код, который изменяет направление движения шара по горизонтали при касании боковой границы экрана, и добавить переменную для ведения счёта для каждого игрока, увеличивая её каждый раз, когда забит "гол". Кроме этого, необходимо, чтобы текущий счёт отображался на экране. Для этого необходимо рассмотреть, как на экран вывести произвольный текст.
Среда XNA не предоставляет прямой доступ к выводу текста. Чтобы вывести текст на экран, игра должна загрузить символьный шрифт и использовать это для рисования символов. Используемый шрифт является элементом игрового контента, который нужно добавить в игру и настроить его размер и начертание. При построении проекта игры
Чтобы добавить в игру шрифт, нужно в
<?xml version="1.0" encoding="utf-8"?>
<!--
Этот файл содержит XML-описание шрифта и будет считываться конвейером контента
платформы XNA. Следуйте комментариям, чтобы настроить внешний вид
шрифта в игре и изменить символы, которыми можно
рисовать.
-->
<XnaContent xmlns:Graphics="Microsoft.Xna.Framework.Content.Pipeline.Graphics">
<Asset Type="Graphics:FontDescription">
<!--
Измените эту строку, чтобы сменить импортируемый шрифт.
-->
<FontName>Segoe UI Mono</FontName>
<!--
Size — это вещественное значение, задаваемое в пунктах. Измените его,
чтобы поменять размер шрифта.
-->
<Size>14</Size>
<!--
Spacing — это вещественное значение, задаваемое в пикселях. Измените его,
чтобы поменять расстояние между символами.
-->
<Spacing>0</Spacing>
<!--
UseKerning задает кернинг шрифта. Если это значение равно "true", сведения
о кернинге будут использованы при размещении символов.
-->
<UseKerning>true</UseKerning>
<!--
Style задает стиль шрифта. Допустимые значения: "Regular", "Bold", "Italic"
и "Bold, Italic", регистр учитывается.
-->
<Style>Regular</Style>
<!--
Если снять знак комментария с этой строки, символ по умолчанию будет подставляться
при отрисовке или измерении текста вместо символов, не включенных в шрифт.
-->
<!-- <DefaultCharacter>*</DefaultCharacter> -->
<!--
CharacterRegions указывает, какие буквы доступны в шрифте. Все символы
от Start до End будут построены и доступны для отрисовки. Диапазон
по умолчанию: от 32 (пробел ASCII) до 126 ("~"), что покрывает базовый набор
символов латиницы. Символы упорядочены согласно Юникоду.
Дополнительные сведения см. в документации.
-->
<CharacterRegions>
<CharacterRegion>
<Start>#32;</Start>
<End>#126;</End>
</CharacterRegion>
</CharacterRegions>
</Asset>
</XnaContent>
В этом файле выбран шрифт Segoe UI Моно размером 14 пт. После добавления шрифта в
SpriteFont font;
font = Content.Load<SpriteFont>("MessageFont");
После этого в программе можно использовать загруженный шрифт для вывода текста на экран:
spriteBatch.DrawString(font, "Hello", new Vector2(50, 100), Color.White);
Метод DrawString принимает четыре параметра: шрифт, выводимый на экран текст, положение текста на экране и цвет текста. Позиция на экране задаётся с помощью класса Vector2.
Сенсорный экран — не единственное новое устройство ввода в Windows Phone. Можно использовать акселерометр для создания игр, в которых управление сводится к перемещению и вращению телефона. Акселерометр может измерять ускорение по трём осям, а также определять ориентацию телефона, поскольку на акселерометр действует сила тяжести, которая задаёт постоянное ускорение к центру земли.
Визуально можно представить акселерометр как груз, подвешенный к центру задней части телефона (рис. 19.4).
(рис 19.4) Расположение осей измерения акселерометра
Если держать телефон горизонтально, то подвес будет расположен перпендикулярно плоскости телефона. Если выразить координаты груза в системе координат $$X, Y, Z$$ относительно точки, в которой груз прикреплён к телефону, его можно считать равным (0, 0, –1), если принять длину подвеса равной 1.
Если повернуть на некоторый угол телефон от себя, груз удаляется на некоторое расстояние относительно начального положения, и значение координаты $$Y$$ будет больше нуля. Если повернуть телефон к себе, чтобы было видно его экран, значение координаты $$Y$$ будет меньше нуля. Если расположить телефон вертикально экраном к себе, то $$Y = –1$$, а $$Z = 0$$. При наклоне телефона влево или влево будет соответственно уменьшаться или увеличиваться значение координаты $$X$$.
Такие значения будет выдавать акселерометр в телефоне. Эти координаты позволяют определить ориентацию телефона. Аналогичным образом измеряется ускорение, если телефон перемещать в пространстве. В этом случае груз будет отклоняться от вертикали, на некоторый угол, что можно использовать для измерения ускорения.
Драйвер акселерометра встроен в 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 одновременно может быть активным только один процесс, а параллельная работа сводится к быстрому переключению между активными процессами.
Проблема может возникнуть при выполнении следующей последовательности действий:
Update запускается и считывает значение по оси X.X, Y и Z.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 звуковых каналов.
Класс 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 может использовать 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, игровых достижениях и т.д. Использовать возможность отключения экранной заставки следует с осторожность, так как при этом будет расходоваться энергия аккумулятора, пока игра не будет закрыта, даже если пользователь не будет выполнять никаких действий.
Можно создать решение Visual Studio, содержащее и приложение Silverlight, и компонент игры XNA. Эту возможность удобно использовать, например, для создания игровых меню, или можно добавить элементы 3D графики в приложение Silverlight.
Для этого нужно создать решение Visual Studio, используя шаблон Приложение Silverlight и XNA для Windows Phone. Новое решение содержит три проекта.
Главным проектом будет приложение Silverlight, которое является начальным проектом. Второй проект содержит набор необходимых библиотек. Третий предназначен для хранения контента XNA для игровой части решения.
При создании объединённого приложения также будет создана новая страница Silverlight, которая будет содержать игру XNA. Игровой процесс XNA размещается на странице GamePage.
При запуске игры XNA вызывается набор методов для инициализации игры, загрузки игрового контента и периодического выполнения обновления игрового мира и его перерисовки на экране. Когда игра работает на странице Silverlight, необходимо обеспечить выполнение этих действий.
protected override void OnNavigatedTo(NavigationEventArgs e) protected override void OnNavigatedFrom(NavigationEventArgs e) private void OnUpdate(object sender, GameTimerEventArgs e) private void OnDraw(object sender, GameTimerEventArgs e)
Эти методы находятся на странице игры, и их необходимо заполнить. Метод OnNavigatedTo выполняет функции методов Initialise и LoadContent, а методы OnUpdate и OnDraw выполняют функции методов Update и Draw соответственно. Метод OnNavigatedTo всегда вызывается, когда пользователь переходит на страницу, и здесь игра должна загрузить контент и начать выполнение. Этот метод также создаёт таймер, который выполняет вызовы методов OnUpdate и OnDraw во время работы игры.
Обратите внимание, что программа XNA работает на странице Silverlight, и пользователь может перейти на другую страницу приложения. При этом вызывается метод OnNavigatedFrom, который можно использовать, чтобы автоматически приостановить игру при переходе на другую страницу приложения.
Чтобы запустить игру при нажатии на кнопку, в обработчик нажатия на кнопку нужно добавить следующий код:
private void Button_Click(object sender, RoutedEventArgs e)
{
NavigationService.Navigate(new Uri("/GamePage.xaml", UriKind.Relative));
}
Если во время работы игры нажать кнопку телефона Назад, то произойдёт переход к предыдущей странице, и игра закроется. Обратите внимание, что игра перезапускается каждый раз при переходе на страницу. При этом, игра перезагружает все
Существует возможность наложить визуальные элементы Silverlight поверх игры XNA. Это позволяет легко добавить к игре кнопки и текст, используя элементы Button и TextBlock. Для добавления элементов Silverlight на экран игры сначала нужно создать элементы и добавить их в файл GamePage.xaml:
<Grid x:Name="LayoutRoot">
<StackPanel>
<TextBlock x:Name="ScoreTextBlock" Text="0:0" TextAlignment="Center"
Style="{StaticResource PhoneTextTitle1Style}" />
<Button Content="Quit" Name="quitButton" Width="480"
Click="quitButton_Click" />
</StackPanel>
</Grid>
Этот код создаёт элемент StackPanel с кнопкой и блоком текста. Кнопка будет завершать игру, а в текстовом блоке будет отображаться счёт игры.
Теперь эти элементы нужно вывести на экран. Рендеринг элементов Silverlight выполняется в классе UIElementRenderer. Экземпляр этого класса создаётся при загрузке страницы:
UIElementRenderer elementRenderer;
if (elementRenderer == null)
{
elementRenderer = new UIElementRenderer(this, (int)ActualWidth,
(int)ActualHeight);
}
Этот код проверяет, существует ли elementRenderer, и если нет — создаёт его. Обратите внимание, конструктор этого элемента принимает в качестве параметров ссылку на используемую страницу, ширину и высота области для отрисовки.
Теперь этот объект можно использовать его в методе OnDraw для создания элементов Silverlight:
private void OnDraw(object sender, GameTimerEventArgs e)
{
// рендеринг элементов Silverlight с помощью объекта UIElementRenderer
elementRenderer.Render();
SharedGraphicsDeviceManager.Current.GraphicsDevice.Clear(Color.CornflowerBlue);
spriteBatch.Begin();
spriteBatch.Draw(ballTexture, ballRectangle, Color.White);
...
spriteBatch.Draw(elementRenderer.Texture, Vector2.Zero, Color.White);
spriteBatch.End();
}
Элементы Silverlight будут выводиться поверх игровых объектов. Если нужно использовать элементы Silverlight в качестве фона для игры, их нужно выводить первыми. Во время работы игры XNA элементы Silverlight отвечают на действия пользователя, и их можно связать с обработчиками событий. Обработчики событий элементов можно использовать даже если элементы не выводятся на экран.
LoadContent, Update и Draw, в которые нужно добавить код игры. Метод LoadContent должен загружать весь необходимый игровой контент. Метод Update вызывается с частотой 30 раз в секунду для обновления игрового контента, а метод Draw вызывается для отрисовки игры на экране.SoundEffectInstance, который является дескриптором звука игры.Silverlight используется класс UIElementRenderer.Для выполнения этих упражнений рекомендуется использовать физическое устройство Windows Phone для возможности мультисенсорного ввода и использования акселерометра.
Упражнение 1. Создание многопользовательской игры
В этом упражнении вы доработаете созданную на лекции игру.
Ограничение движения игрового объекта
На текущий момент левая платформа может перемещаться за пределы экрана. Необходимо предотвратить такую возможность.
Draw следующий код, который не позволит левой платформе уйти за пределы экрана:
if (lPaddleY < 0)
{
lPaddleY = 0;
}
if (lPaddleY + lPaddleRectangle.Height > GraphicsDevice.Viewport.Height)
{
lPaddleY = GraphicsDevice.Viewport.Height - lPaddleRectangle.Height;
}
Добавление возможности играть вдвоём
На текущий момент правая платформа управляется компьютером. Благодаря возможности мультисенсорного ввода, можно добавить в игру возможность управления правой платформы вторым игроком.
TouchCollection touches = TouchPanel.GetState();
foreach (TouchLocation touch in touches)
{
if (touch.Position.Y > GraphicsDevice.Viewport.Height / 2)
{
lPaddleY = lPaddleY + lPaddleSpeed;
}
else
{
lPaddleY = lPaddleY - lPaddleSpeed;
}
}
Теперь нужно изменить программный код для управления правой платформой, чтобы ей мог управлять второй игрок. Для этого экран можно поделить на четыре части. При обнаружении касания в каждой из областей определяется направление перемещения соответствующей платформы. Условия принадлежности касания соответствующей области показаны на рисунке:

Упражнение 2. Использование акселерометра для управления в игре
В этом упражнении вы создадите приложение, которое использует акселерометр Windows Phone для определения положения телефона в пространстве. Существующая программа выводит на экран зелёный шарик, расположенный над красной точкой, которая расположена в центре экрана. Шарик должен управляться с помощью акселерометра.
Game1 для указания поддерживаемой в приложении ориентации телефона, размеров экрана и задания полноэкранного режима.
graphics.SupportedOrientations = DisplayOrientation.LandscapeLeft; graphics.PreferredBackBufferWidth = 480; graphics.PreferredBackBufferHeight = 800; graphics.IsFullScreen = true;
Microsoft.Devices.Sensors.Microsoft.Devices.Sensors.Initialise метод следующий код для создания и запуска объекта для работы с акселерометром:
Accelerometer acc = new Accelerometer();
acc.ReadingChanged +=
new EventHandler<AccelerometerReadingEventArgs>(acc_ReadingChanged);
acc.Start();
Initialise следующий код для определения обработчика события акселерометра:
Vector3 accVector = Vector3.Zero;
void acc_ReadingChanged(object sender, AccelerometerReadingEventArgs e)
{
accVector.X = (float)e.X;
accVector.Y = (float)e.Y;
accVector.Z = (float)e.Z;
}
Для управления движением шарика можно использовать значения, получаемые от акселерометра и сохраняемые в переменную accVector. Переменные bubbleX и bubbleY содержат координаты шарика, когда он находится в центре экрана.Update удалите следующие строки кода:
bubbleRectangle.X = (int)(bubbleX + 0.5f); bubbleRectangle.Y = (int)(bubbleY + 0.5f);и добавьте код для вычисления новых координат шарика, в которые он будет перемещаться:
float accBubbleX = bubbleX - (accVector.X * 200); float accBubbleY = bubbleY + (accVector.Y * 200); bubbleRectangle.X = (int)(accBubbleX + 0.5f); bubbleRectangle.Y = (int)(accBubbleY + 0.5f);
Для получения официальных документов о завершении программы дополнительного профессионального образования (удостоверения о повышении квалификации, дипломов о профессиональной переподготовке и MBA) необходимо предоставить:
Внимание! Вы можете не заказывать доставку бумажной версии официального документы, а скачать его в электронном виде и распечатать самостоятельно. Информация о выданном документе в течение 1 месяца загружается в Федеральную информационную систему «Федеральный реестр сведений о документах об образовании и (или) о квалификации, документах об обучении» - ФИС ФРДО.
Доступ на новый сайт осуществляется с использованием адреса электронной почты, который был указан вами при регистрации на "старом". Мы постарались перенести все ваши данные с прежнего ресурса, однако не исключена вероятность потери части информации.
При возникновении проблемы со входом, воспользуйтесь функцией сброса пароля
Если вы обнаружите несоответствия, пожалуйста, сообщите нам.