Создание игры в шаффлборд с применением Silverlight

Coding4Fun

Опубликовано 15 июня 2010 г.

Введение

В этой статье дано пошаговое описание процесса создания игры в стиле настольного шаффлборда[1] для Windows Phone 7 с применением Silverlight. Во многих барах есть такие игры на длинных деревянных столах, в которых игроки скользящим броском кидают металлические шайбы по столу, стараясь попасть как можно ближе к его противоположному краю и при этом не уронить свою шайбу.

Автор : Энди Билье (Andy Beaulieu),
Microsoft Silverlight MVP
https://www.spritehand.com
Исходный код: https://shuffleboard.codeplex.com
Сложность: средняя
Необходимое время: 4 часа
Затраты: бесплатно
ПО:   Windows Phone Developer Tools, Expression Blend 4, Expression Blend Add-in Preview 2 for Windows Phone, Expression Blend SDK Preview 2 for Windows Phone

clip_image002

Ориентация на несколько платформ

Windows Phone 7 использует версию Silverlight 3 с несколькими дополнительными средствами. Так как эта версия Silverlight очень близка к веб-версии Silverlight 3, мы создадим решение для обеих платформ. Для этого начните с шаблона проекта для приложения Silverlight 3 (web), а затем добавьте проект Windows Phone со связанными файлами, которые повторно используют решение на основе Silverlight 3. Это позволит нам развертывать одну и ту же кодовую базу на множестве платформ.

Начнем с создания нашего решения.

Создание решения и основной страницы

1. В Expression Blend 4 создайте новый проект на основе шаблона «Silverlight Application + Website» с именем Shuffleboard. Убедитесь, что вы выбрали «3.0» из раскрывающегося списка Version, так как именно эта версия поддерживается в Windows Phone. (Вскоре мы добавим и шаблон проекта Windows Phone.)
clip_image004

2. В панели Objects and Timeline выберите основной UserControl и установите его свойство Width в 480, а Height — в 800. Это размер Windows Phone UI в портретном режиме.

3. Выберите LayoutRoot Grid и установите его свойство Width в 480, а Height — в 800.

4. Для игр контейнер разметки Canvas лучше, чем Grid, поэтому давайте сменим тип контейнера LayoutRoot. Щелкните правой кнопкой мыши LayoutRoot и выберите Change Layout Type/Canvas.
clip_image006

5. Так как некоторые части нашего UI могут появляться за пределами области, выделенной игре, нужно добавить Clip к основному холсту, чтобы пользователь не видел эти внешние элементы. Введите следующее прямо под LayoutRoot Canvas. (Этот XAML-код является фрагментом MainPage Clip в snippets.txt.)

 <Canvas.Clip>

    <RectangleGeometry Rect="0,0,480,800"/>

</Canvas.Clip>

6. Мы будем использовать некоторые заранее скомпилированные поведения (Behaviors), которые упрощают введение поддержки физики в Silverlight с использованием Farseer Physics Library. Щелкните проект Silverlight правой кнопкой мыши и выберите Add Reference. Укажите следующие сборки, находящиеся в комплекте для скачивания этого примера:

\ShuffleBoard\Bin\Debug\FarseerPhysics.dll

\ShuffleBoard\Bin\Debug\Spritehand.FarseerHelper.dll

\ShuffleBoard\Bin\Debug\Spritehand.PhysicsBehaviors.dll

Примечание: Чтобы узнать больше о Physics Helper Library, посетите сайт https://physicshelper.codeplex.com.

Get Microsoft Silverlight

Создание пользовательского элемента управления игрой

Далее мы создадим отдельный пользовательский элемент управления, который содержит основную логику игры. Создавая отдельный такой элемент, мы можем легко ориентироваться на различные платформы простой заменой этого элемента управления внутри разных элементов MainPage.

1. Щелкните правой кнопкой мыши проект Shuffleboard Silverlight и выберите Add New Item. Выберите UserControl и присвойте ему имя «ucMainGame».

clip_image008

2. Смените Grid на Canvas у элемента LayoutRoot.

3. Задайте Width равным 480, а Height — 800 для элементов UserControl и LayoutRoot Canvas.

4. Установите цвет в Background в Black для LayoutRoot Canvas.

5. Скомпилируйте проект нажатием комбинации клавиш Ctrl+Shift+B.

6. Вернитесь к MainPage, добавьте экземпляр ucMainGame в страницу, перейдя на панель Assets и раскрыв Controls | All. Перетащите экземпляр ucMainGame на рабочую область дизайнера.

clip_image010

7. Переименуйте элемент управления ucMainGame в ucMainGame1.

8. Обнулите свойства Left и Top элемента ucMainGame1.

Создание игрового стола

Для игрового стола мы используем заранее созданное изображение в формате JPG.

1. Создайте новую подпапку images в проекте Silverlight.

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

\ShuffleBoard\images\shuffleboardTable.jpg.

3. Откройте пользовательский элемент управления ucMainGame.

4. Вставьте новый элемент Canvas с именем cnvTable и задайте атрибуты:

    • Width = 480
    • Height = 800
    • Left = 0
    • Top = 0

5. На панели Assets раскройте категорию Behaviors и перетащите PhysicsControllerBehavior на cnvTable. Это поведение позволяет включить в игру базовую симуляцию физики. Задайте значения свойств Physics Contoller, как показано на картинке (тем самым вы отключите гравитацию и установите некоторые другие физические параметры):

clip_image012

6. В cnvTable Canvas добавьте второй Canvas с именем cnvTableInner, который будет хранить изображение стола. Задайте следующие свойства cnvTableInner:

    • Width = 360
    • Height = 1077
    • Left = 60
    • Top = –277

7. Перетащите изображение shuffleboardTable.jpg из панели Projects на cnvTableInner. Задайте следующие свойства изображения:

    • Width = 360
    • Height = 1077
    • Left = 0
    • Top = 0

8. Панель Objects and Timeline должна выглядеть так:

clip_image014

9. Нам нужно будет распознавать попадание шайбы на край стола и момент ее «падения». Давайте добавим элементы Rectangle, а к ним — Physics Behaviors.

clip_image016

10. Вставьте новый Rectangle с именем rectSensorLeft и измените его размер так, чтобы он охватывал всю левую сторону стола. Задайте следующие свойства:

    • Width = 60
    • Height = 1190
    • Left = –40
    • Top = –334
    • Opacity = 20%

11. Перетащите PhysicsObjectBehavior с Assets Panel Behaviors на rectSensorLeft, а затем установите его свойство IsStatic в true.

12. Добавьте еще три Rectangle копированием rectSensorLeft, чтобы они окружали границы стола:

    • rectSensorRight
    • rectSensorTop
    • rectSensorBottom

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

clip_image018

14. Теперь добавим целевые Rectangle в конце стола. С их помощью будет определяться число очков, получаемых для данной шайбы. Нарисуйте три Rectangle, которые охватывают всю ширину стола и имеют высоту по 100 пикселей. Присвойте им имена rectPoints3, rectPoints2 и rectPoints1. Установите их свойства Stroke в Red и Fill в No brush.

15. Добавьте элемент TextBlock в каждый Rectangle для обозначения количества очков. Установите их свойства Text в 3, 2 и 1, чтобы игровая доска выглядела примерно так:

clip_image020

16. Нам также понадобится Rectangle, представляющий «зону скольжения», где игроки на законных основаниях могут разгонять шайбу для последующего броска (мы не собираемся разрешать двигать шайбу по всему столу!). Добавьте Rectangle с именем rectInBounds и разместите его на нижнем конце стола:

    • Width=360
    • Height=292
    • Left = 60
    • Top = 508
    • Fill = No Brush
    • Stroke = Red

Get Microsoft Silverlight

Добавление шайб

Для шайб мы добавим существующий элемент управления с уже законченным внешним видом.

1. Щелкните правой кнопкой мыши проект и выберите Add Existing Item.

2. Перейдите к двум файлам из комплекта для скачивания этого примера:

\ShuffleBoard\ucPuck.xaml

\ShuffleBoard\ucPuck.xaml.cs

3. Откройте ucPuck.xaml в рабочей области дизайнера и обратите внимание на следующее в панели Objects and Timeline:

clip_image022

a. Мы имеем Canvas с именем Puck и примененным PhysicsObjectBehavior. Это позволяет каждому экземпляру данного Canvas вести себя как физический объект, поэтому он анимируется на основе параметров скорости и приложенной силы и участвует в соударениях. Заметьте, что в этом Behavior значение MomentOfInertia очень велико. Это не дает объекту вращаться из-за крутящего момента и соударений. Также обратите внимание на RestitutionCoefficient, которое дает возможность объекту «отскакивать».

clip_image024

b. Внешний вид шайбы определяется Canvas с именем cnvInner.

c. У нас есть уже определенный StoryBoard с именем sbLostPuck, который будет выполняться при «падении» шайбы с «края» игрового стола.

d. Откройте файл отделенного кода, ucPuck.xaml.cs, и обратите внимание на свойство get/set для цвета шайбы. Оно позволит создавать красные и синие шайбы для каждого игрока.

4. Скомпилируйте проект нажатием Ctrl+Shift+F5 и вернитесь к ucMainGame.

5. На панели Assets раскройте Controls | All и найдите элемент управления ucPuck. Перетащите его экземпляр на игровую доску. Присвойте ему имя bluePuck1.

6. Скопируйте и вставьте bluePuck1 два раза, чтобы у вас появилось три синих шайбы. Присвойте новым шайбам имена bluePuck2 и bluePuck3.

7. Скопируйте и вставьте четвертую шайбу и присвойте имя redPuck1. На панели Properties перейдите в категорию Miscellaneous и задайте в свойствах ColorHighlight и ColorMain красный цвет (Red):

clip_image026

8. Скопируйте и вставьте redPuck1 два раза и переименуйте новые шайбы в redPuck2 и redPuck3.

9. Проверим нашу доску. На панели Objects and Timeline найдите PhysicsControllerBehavior под cnvTable Canvas и установите свойство MousePickEnabled в true.

10. Запустите проект нажатием F5. Попробуйте перемещать шайбы мышью.

Get Microsoft Silverlight

Добавлениетабло счета

Нам нужен какой-то способ отслеживания очков, набранных каждым игроком, поэтому давайте добавим простое табло счета «синие против красных».

1. Щелкните проект правой кнопкой мыши и выберите Add Existing Item.

2. Перейдите к следующим двум файлам в комплекте для скачивания этого примера и выберите их:

\ShuffleBoard\ShuffleBoard\ucScoreBoard.xaml

\ShuffleBoard\ShuffleBoard\ucScoreBoard.xaml.cs

3. Откройте ucScoreBoard и обратите внимание на следующее:

a. в нем есть TextBlock для отображения очков красных и TextBlock для отображения очков синих;

b. в отделенном коде присутствуют аксессоры set и get для задания счета.

4. Скомпилируйте проект нажатием Ctrl+Shift+B.

5. Вернитесь к ucMainGame.

6. Перетащите экземпляр ucScoreBoard из панели Assets на LayoutRoot.

7. Присвойте элементу имя ucScoreBoard1 и разместите его в верхнем левом углу Canvas:

clip_image028

Добавление окошка, разрешающего игроку бросок

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

  1. Щелкните правой кнопкой мыши проект Silverlight и выберите Add Existing Item.

  2. Перейдите к следующим двум файлам в комплекте для скачивания этого примера и выберите их:

    \ShuffleBoard\ucPlayerUp.xaml

    \ShuffleBoard\ucPlayerUp.xaml.cs

3. Обратите внимание на следующее в ucPlayerUp:

· в файле отделенного кода есть простое свойство IsBlueTurn, которое показывает соответствующее сообщение в UI.

4. Скомпилируйте проект нажатием Ctrl+Shift+B.

  1. Вернитесь к ucMainGame, перетащите экземпляр ucPlayerUp на cnvTableInner.

  2. Присвойте этому элементу управления имя ucPlayerUp1 и задайте его свойства так:

    clip_image030

Придание объемности

Давайте придадим нашему столу более-менее трехмерный вид добавлением преобразования Perspective, чтобы с расстоянием казалось, что стол сужается.

1. Создайте новый StoryBoard с именем sbRotateTable.

clip_image032

2. Выберите элемент cnvTable Canvas.

3. Продвиньте временной график на секунду вперед.

4. На панели Properties в разделе Projection задайте значение X равным –40.

clip_image034

5. Ваш игровой стол теперь должен выглядеть примерно так:

clip_image036

6. Закройте Storyboard, чтобы прекратить запись.

clip_image038

7. Нам нужно поворачивать стол после инициализации PhysicsController, чтобы Physics Helper корректно определил границы объектов, к которым применяются «законы» физики. Начните с добавления нескольких выражений using в верхней части ucMainGame.xaml.cs.

(Этот код является фрагментом ucMainGameImports в snippets.txt.)

 using System.Collections.Generic;

using Spritehand.FarseerHelper;

using FarseerGames.FarseerPhysics.Mathematics;

using System.ComponentModel;

8. Затем добавим ряд объявлений переменных уровня класса, в которых будут храниться контроллер физики, список шайб, а также некоторых переменных для отслеживания бросков и счета.

(Этот код является фрагментом ucMainGameDeclarations в snippets.txt.)

 PhysicsControllerMain _physicsController;

List<PhysicsSprite> _pucks = new List<PhysicsSprite>();

List<PhysicsSprite> _redPucks = new List<PhysicsSprite>();

Point _ptStartShot;

bool _draggingPuck;

int _draggingStartTick;

int _currentPuckToShoot = 0;

int _currentPuckShot = -1;

int _gameOverScore = 15;

9. В конструктор ucMainGame() вставьте код для присваивания MaxFrameRate значения 30 (в Windows Phone действует ограничение на 30 кадров в секунду) и подключения обработчика события Loaded.

(Этот код является фрагментом ucMainGame Constructor в snippets.txt.)

 this.Loaded += new RoutedEventHandler(ucMainGame_Loaded);

Application.Current.Host.Settings.MaxFrameRate = 30;

10. Реализуйте обработчик ucMainGame_Loaded; он получает ссылку на Physics Controller и подключает наши обработчики событий.

(Этот код является фрагментом ucMainGame Loaded в snippets.txt.)

 void ucMainGame_Loaded(object sender, RoutedEventArgs e)
{
    if (DesignerProperties.GetIsInDesignMode(this))
        return;

    _physicsController = 
        cnvTable.GetValue(
            PhysicsControllerMain.PhysicsControllerProperty
        ) as PhysicsControllerMain;
    _physicsController.Initialized += _physicsController_Initialized;
    _physicsController.Collision += _physicsController_Collision;
    _physicsController.TimerLoop += _physicsController_TimerLoop;
}

11. Теперь мы добавим обработчики событий, только что подключенные в обработчике события Loaded. Заметьте, что событие Initialized запускает анимацию Rotate Table StoryBoard.

(Этот код является фрагментом ucMainGame Event Handlers в snippets.txt.)

 void _physicsController_Initialized(object source)
{
    sbRotateTable.Begin();
}

void _physicsController_Collision(string sprite1, string sprite2)
{ }

void _physicsController_TimerLoop(object source)
{ }

12. Запустите проект нажатием F5. Обратите внимание на объемный вид игрового стола и шайб и попробуйте поиграть шайбами, используя мышь.

Управлениешайбами

Следующий этап — написание логики для управления очередностью бросков игроками, шайбами и счетом. Заметьте: когда мы чуть позже займемся реализацией версии для Windows Phone, мы сможем воспользоваться преимуществами событий Multitouch Manipulation. Но таких событий в веб-версии Silverlight 3 нет, поэтому мы задействуем простой механизм ввода с помощью мыши.

1. Давайте отключим операции с мышью по умолчанию. Выберите PhysicsControllerBehavior прямо под cnvTable Canvas и установите свойство MousePickEnabled в false.

2. Нам нужно получать ссылки на наши шайбы, которые Physics Helper преобразует в объекты PhysicsSprite. PhysicsSprite содержит XAML UI для PhysicsObject, а также атрибуты Physics Engine для нижележащих объектов физики, в том числе формы границы, массы, скорости и др.

(Этот код является фрагментом ucMainGame Initialized в snippets.txt.)

3. Теперь нам понадобится обработка событий мыши применительно к шайбам, чтобы управлять броском игрока. Когда игрок нажимает кнопку мыши, указывая на шайбу, мы отслеживаем ее позицию наряду со временем нажатия кнопки. Когда игрок двигает мышь, мы обновляем позицию шайбы, а также проверяем, не остановил ли игрок шайбу или не перемещает ее обратно. Это делается на случай, если игрок просто выбирает позицию для броска шайбы. Наконец, в обработчике события MouseUp мы освобождаем шайбу и позволяем ей двигаться в указанном направлении, применяя к ней соответствующее ускорение. Скопируйте фрагмент PuckMouseControl из snippets.txt. Здесь этот код не показан из-за нехватки места.

4. Творя свою магию, Physics Helper Library преобразует существующие UI-элементы в объекты PhysicsSprite. Как же быть, если мы хотим получить исходные элементы управления, например для выполнения StoryBoard, определенного для них? Для этого можно применить метод FindName и получить экземпляр исходного пользовательского элемента управления. Поэтому добавьте показанный ниже код, который позволит нам получать ссылку на Puck Storyboard с именем sbLostPuck.

 ucPuck GetPuckControl(string spriteName)
{
    ucPuck puck;
    var parent = PhysicsControllerMain.ParentCanvas;
    switch (spriteName)
    {
        case "Puck":
            puck = parent.FindName("bluePuck1") 
                    as ucPuck;
            break;
        case "Puck_1":
            puck = parent.FindName("bluePuck2") 
                    as ucPuck;
            break;
        case "Puck_2":
            puck = parent.FindName("bluePuck3") 
                    as ucPuck;
            break;
        case "Puck_3":
            puck = parent.FindName("redPuck1") 
                    as ucPuck;
            break;
        case "Puck_4":
            puck = parent.FindName("redPuck2") 
                    as ucPuck;
            break;
        default:
            puck = parent.FindName("redPuck3") 
                    as ucPuck;
            break;
    }

    return puck;
}

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

(Этот код является фрагментом Get Points for Puck в snippets.txt.)

 int GetPointsForPuck(PhysicsSprite puck)
{
    int score = 0;
    Vector2 puckPos = puck.BodyObject.Position;

    double left = Convert.ToDouble(
        rectPoints3.GetValue(Canvas.LeftProperty));
    double top = Convert.ToDouble(
        rectPoints3.GetValue(Canvas.TopProperty));

    if ((puckPos.X > left && puckPos.X < left + rectPoints3.Width) 
     && (puckPos.Y > top && puckPos.Y < top + rectPoints3.Height))
        score = 3;

    left = Convert.ToDouble(rectPoints2.GetValue(Canvas.LeftProperty));
    top = Convert.ToDouble(rectPoints2.GetValue(Canvas.TopProperty));
    if ((puckPos.X > left && puckPos.X < left + rectPoints2.Width) 
     && (puckPos.Y > top && puckPos.Y < top + rectPoints2.Height))
        score = 2;

    left = Convert.ToDouble(rectPoints1.GetValue(Canvas.LeftProperty));
    top = Convert.ToDouble(rectPoints1.GetValue(Canvas.TopProperty));
    if ((puckPos.X > left && puckPos.X < left + rectPoints1.Width) 
     && (puckPos.Y > top && puckPos.Y < top + rectPoints1.Height))
        score = 1;

    return score;
}

Реализация « игровогоцикла »

Большинство игр управляются «игровым циклом», выполняемым много раз в секунду. Внутри этого цикла можно осуществлять проверки на соударения, контролировать ИИ (искусственный интеллект) врагов и вести подсчет результатов. Для этих целей мы можем использовать событие TimerLoop, генерируемое PhysicsController.

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

(Этот код является фрагментом Timer Loop в snippets.txt.)

 void _physicsController_TimerLoop(object source)
{
    // Проверяем, закончен ли текущий бросок
    if (_currentPuckShot >= 0)
    {
    var puck = _pucks[_currentPuckShot].BodyObject;

        if (puck.Enabled == false ||
          Math.Abs(puck.LinearVelocity.X) < 3                                   
          && 
          Math.Abs(puck.LinearVelocity.Y) < 3)
        {
            // Попала ли шайба в конечную зону?
            if (!PointWithinBounds(
              new Point(puck.Position.X, 
                       puck.Position.Y)))
            {
                _currentPuckShot = -1;
                _currentPuckToShoot++;
                SetupThePuck();
            }
        }
    }
}

void _physicsController_Collision(string sprite1, string sprite2)
{
    // Не упала ли шайба со стола?
    if (sprite1.StartsWith("rectSensor") && sprite2.StartsWith("Puck"))
    {
        ucPuck puck = GetPuckControl(sprite2);

        _physicsController.PhysicsObjects[sprite2].
            BodyObject.Enabled = false;

        puck.sbLostPuck.Begin();
    }
}

Ориентация на платформу Windows Phone

До сих пор мы создавали игру в шаффлборд под веб-версию Silverlight 3. А теперь мы быстренько перенесем ее на платформу Windows Phone, задействовав при этом некоторые возможности этой платформы, например мультисенсорную поддержку (multitouch). Мы сделаем это, используя связанные файлы, которые указывают обратно на наш существующий проект Silverlight 3.

1. Щелкните правой кнопкой мыши решение Silverlight в Blend и выберите Add New Project.

2. Выберите шаблон Windows Phone Application и введите в качестве имени ShuffleBoard . WindowsPhone.

clip_image040

3. Удалите элементы TitleGrid и ContentGrid на панели Objects and Timeline.

4. Преобразуйте LayoutRoot в Canvas, щелкнув его правой кнопкой мыши и выбрав Change Layout Type | Canvas.

5. Щелкните правой кнопкой мыши проект и выберите Add Reference. Найдите следующие сборки в комплекте для скачивания этого примера (это версии Physics Helper and Farseer для Windows Phone):

\ShuffleBoard.WindowsPhone\Bin\Debug\FarseerPhysics.dll

\ShuffleBoard.WindowsPhone\Bin\Debug\Spritehand.FarseerHelper.dll

\ShuffleBoard.WindowsPhone\Bin\Debug\Spritehand.PhysicsBehaviors.dll

6. Нам нужны ссылки на сборки, используемые для поведений. Простой способ получить такую ссылку — добавить Behavior к какому-либо элементу, затем удалить его. Перетащите PhysicsControllerBehavior из панели Assets на LayoutRoot, а потом удалите его. Заметьте, что в проекте появится ссылка на System.Windows.Interactivity.

7. Далее мы добавим связанные файлы из существующего проекта Silverlight 3. Щелкните правой кнопкой мыши проект ShuffleBoard.WindowsPhone и выберите Add/Link to Existing Item. Выберите следующие файлы:

· ucMainGame.xaml;

· ucMainGame.xaml.cs;

· ucPlayerUp.xaml;

· ucPlayerUp.xaml.cs;

· ucPuck.xaml;

· ucPuck.xaml.cs;

· ucScoreBoard.xaml;

· ucScoreBoard.xaml.cs.

8. Добавьте в проект новую папку images.

9. Щелкните правой кнопкой мыши папку images и выберите Link to Existing Item, затем перейдите к следующему изображению в папке примера:

\ShuffleBoard\images\shuffleboardTable.jpg.

10. Скомпилируйте проект нажатием Ctrl+Shift+B.

11. На панели Assets под Controls | All найдите ucMainGame и перетащите его экземпляр на LayoutRoot. Задайте нулевые значения для свойств Left и Top.

12. Щелкните правой кнопкой мыши проект WindowsPhone и выберите Startup, чтобы сделать данный проект стартовым.

13. Запустите проект нажатием F5.

Get Microsoft Silverlight

Сенсорныйвводв Windows Phone

До сих пор для броска шайб мы использовали простой механизм ввода на основе событий мыши. Но на платформе Windows Phone 7 есть кое-что получше: события Multitouch (события мультисенсорного ввода). Эти события включают свойство инерции, которое сделает физику поведения шайб более реалистичной.

1. Так как теперь мы собираемся поддерживать две разные платформы (веб и Windows Phone), мы должны ввести символ условной компиляции, чтобы компилятор мог различать код для каждой платформы.

2. Откройте решение в Visual Studio, щелкнув его правой кнопкой мыши в панели Projects, а затем выбрав Edit in Visual Studio:

clip_image042

3. Щелкните правой кнопкой мыши проект ShuffleBoard.WindowsPhone и выберите Properties.

4. Добавьте новый символ условной компиляции WINDOWS_PHONE.

Примечание: в ближайших выпусках WP7 Tools скорее всего будет определен символ компиляции по умолчанию, поэтому вы, возможно, уже видели такой здесь:

clip_image044

5. Теперь мы добавим обработчики событий, специфичных для Windows Phone и предназначенных для операций с использованием сенсорного ввода. Откройте ucMainGame.xaml.cs и найдите обработчик событий _physicsController_Initialized. Замените обработчики событий мыши следующим кодом, который будет генерировать либо события манипуляций (для Windows Phone), либо события мыши (для веб-версии Silverlight).

(Этот код является фрагментом ManipulationEvents в snippets.txt.)

 #if WINDOWS_PHONE
    puck.ManipulationStarted += 
      new EventHandler<ManipulationStartedEventArgs>
      (puck_ManipulationStarted);
    puck.ManipulationCompleted += 
      new EventHandler<ManipulationCompletedEventArgs>
      (puck_ManipulationCompleted);
    puck.ManipulationDelta += 
      new EventHandler<ManipulationDeltaEventArgs>
      (puck_ManipulationDelta);
#else
    puck.MouseLeftButtonDown += 
      new MouseButtonEventHandler(puck_MouseLeftButtonDown);
    puck.MouseMove += 
      new MouseEventHandler(puck_MouseMove);
    puck.MouseLeftButtonUp += 
      new MouseButtonEventHandler(puck_MouseLeftButtonUp);
#endif

6. Теперь мы просто реализуем эти обработчики событий манипуляций. Главное, что здесь нужно учесть, — событие ManipulationCompleted передается в параметре FinalVelocity.LinearVelocity, что можно использовать для создания реалистичной физики движения шайбы после ее броска игроком.

(Добавьте фрагмент кода Manipulation Event Handlers из snippets.txt.)

 Vector2 force = new Vector2(
    (float)(e.FinalVelocities.LinearVelocity.X * scalePower), 
    (float)(e.FinalVelocities.LinearVelocity.Y * scalePower));

7. Запустите проект нажатием F5 и попробуйте покидать шайбы.

Производительность

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

1. В конструкторе ucMainGame включите счетчик частоты смены кадров для наблюдения за текущей (моментальной) скоростью.

(Этот код является фрагментом Frame Rate Counter в snippets.txt.)

Application.Current.Host.Settings.EnableFrameRateCounter = true;

2. Запустите проект нажатием F5 и отметьте текущую частоту смены кадров.

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

Одна из легко реализуемых оптимизаций — использование аппаратного ускорения. Silverlight позволяет задействовать видеокарту при рендеринге элементов, что может значительно повысить производительность. С этой целью добавим атрибут Cachemode="BitmapCache" ко всем элементам, которым нужна поддержка аппаратного ускорения.

1. Откройте ucPuck.xaml и отметьте, что для Puck Canvas задан атрибут CacheMode.

2. Откройте ucMainGame.xaml и добавьте атрибут CacheMode к элементу cnvTable Canvas.

 <Canvas x:Name="cnvTable" Width="480" Height="800" 

d:LayoutOverrides="Width, Height" CacheMode="BitmapCache">

3. Запустите проект и наблюдайте за частотой смены кадров.

Get Microsoft Silverlight

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

Поддержка звука — одна из областей, где Windows Phone и веб-версия Silverlight несколько различаются. В веб-версии Silverlight можно использовать класс MediaElement для воспроизведения аудио в формате WMA и MP3. Несколько экземпляров класса MediaElement обеспечивают одновременное проигрывание сразу нескольких звуков, а аудиовывод микшируется автоматически.

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

Библиотека Physics Helper Library предлагает полезный класс-оболочку для поддержки звука, которым мы воспользуемся для проигрывания звука «удара шайбы» на обеих платформах.

  1. В проекте ShuffleBoard (для веб-версии Silverlight) щелкните правой кнопкой мыши и выберите Add Folder.

  2. Присвойте новой папке имя sounds.

  3. Добавьте в папку sounds существующий элемент:

    \ShuffleBoard\ShuffleBoard\sounds\hitPuck.wma.

  4. Установите Build Action этого файла в Content.

  5. В проекте ShuffleBoard.WindowsPhone щелкните правой кнопкой мыши и выберите Add Folder.

  6. Присвойте новой папке имя sounds.

  7. Добавьте в папку sounds существующий элемент:

    \ShuffleBoard.WindowsPhone\sounds\hitPuck.wav.

  8. Установите Build Action этого файла в Content.

  9. В ucMainGame.xaml.cs добавьте объявление для класса SoundMain (он определен в Physics Helper Library).

    (Этот код является фрагментом Declare the Sound в snippets.txt.)

    SoundMain _soundPuckHit;

  10. В обработчик события ucMainGame_Loaded вставьте код, объявляющий два звука. Обратите внимание на то, что Windows Phone использует формат WAV без префикса «/» корня.

    (Этот код является фрагментом Declare the Sound в snippets.txt.)

 #if WINDOWS_PHONE
    _soundPuckHit = new SoundMain(this.LayoutRoot,
                   "sounds/hitPuck.wav", 2, 0);
#else
    _soundPuckHit = new SoundMain(this.LayoutRoot, 
                   "/sounds/hitPuck.wma", 2, 0);
#endif

11. В обработчик _physicscontroller_collision включите код для воспроизведения звука соударения шайб при скорости больше 10.

(Этот код является фрагментом PlaytheSound в snippets.txt.)

 // Проверка на соударение шайб
if (sprite1.StartsWith("Puck") && sprite2.StartsWith("Puck"))
{
    PhysicsSprite sprite = _physicsController.PhysicsObjects[sprite1];

    if (Math.Abs(sprite.BodyObject.LinearVelocity.X) > 10
        || Math.Abs(sprite.BodyObject.LinearVelocity.Y) > 10)
    {
        _soundPuckHit.Play();
    }
}

12. Запустите проект и попробуйте ударить шайбой по шайбе.

Get Microsoft Silverlight

Резюме

Silverlight с момента выпуска версии 1.1 Alpha предлагает превосходную среду разработки казуальных игр. С появлением Windows Phone и демонстраций работы Silverlight на других встраиваемых устройствах вы можете смело рассчитывать на светлое будущее игр на основе Silverlight.

Вот несколько ресурсов, где можно найти дополнительную информацию:

· блог Энди (Andy)

https://www.andybeaulieu.com

· демонстрации Энди на основе Silverlight

https://www.spritehand.com

· библиотека Physics Helper Library

https://physicshelper.codeplex.com

· отличный бесплатный ресурс для изучения Expression Blend и Silverlight

https://www.microsoft.com/design/toolbox/

Теги: gaming, Silverlight, Windows Phone, Expression Blend, XAML, Physics, Windows Phone 7, Mobile


[1] Шаффлборд — игра со скользящим бросанием деревянных кружочков по размеченной доске или столу. ‒ Прим. перев.