Как сделать морской бой онлайн на Silverlight 4 (подробная статья)

    Добрый день! Этот текст является подробной статьей о том, как я делал морской бой на Silverlight 4. Вдохновили меня на написание статьи Ваши комментарии. Исходные коды можно взять здесь. Тестовый логин test@mail.ru, пароль 123456. Но есть ограничение на то, что игроки должны иметь разные логины. Поэтому кто-то один должен быть обязательно зарегистрирован, иначе возникнет ошибка, которая будет скоро устранена.
    image

    Итак, я являюсь сертифицированным специалистом в различных областях, но по WPF (то, на чем базируется Silverligh) у меня знаний было 0. Поэтому я решил изучить основы этой технологии на базе живого примера, которой и стал знакомый с детства морской бой. Приняв во внимание вышесказанное, эта статья не описывает оптимальных механизмов использования технологий, т.к., наверняка, существует более изящное решение.
    В общем, что нам понадобится: Visual studio 2010 (у меня версия Ultimate, но, думаю, можно попробовать и на бесплатной Visual Web Developer 2010 Express версии), а также надо установить Silverlight 4 tools for VS 2010. После того, как все скачалось и поставилось, можно браться за дело! Для начала нам понадобится создать хост-приложение на ASP.NET (File-New Project – Web Application). Оно у меня уже было заранее создано, а также там должен быть настроен Membership (MS SQL). Подробнее об этом можете почитать здесь. Основным элементом membership'а будет являться таблица из базы Users, так как на нее опирается логика работы с пользователями. Также надо добавить, что используются профили. Для их настройки надо вставить в web.config следующие строки:
    элемент profile enabled=«true» defaultProvider=«SqlProfileProvider», добавить в субэлемент properties элемент
    add name=«NickName».

    Теперь все готово для создания сильверлайт-приложения! Заходим в File-AddNewProject, выбираем Silverlight Application, указываем наше хост-приложение, а также на будущее поставьте галочку: Net RIA Services. Итак, наше приложение готово. При создании автоматически появится в хостинг-приложении тестовая страница для запуска сильверлайт-программы. Подкорректируем параметры для запуска приложения и добавим такую вот строку <asp:Literal ID=«LiteralParams» runat=«server»></asp:Literal> в элемент object. Такой литерал нам послужит параметрами, в которые мы передадим адрес нашего веб-сервиса (поскольку для локального тестирования он один, а на хостинге будет совсем другой):
    StringBuilder SB = new StringBuilder();
    SB.Append("треуг.скобкавлево param name=\"InitParams\" value=\"");

    SB.Append("SFServiceUrl="+GetServiceUrl());

    SB.Append("\" /треуг.скобкавправо");

    LiteralParams.Text = SB.ToString();


    Затем мы прочитаем эти параметры уже в сильверлайт-программе:
    private void Application_Startup(object sender, StartupEventArgs e)
    {
    // Получаем адрес сервиса
    ServiceUrl = e.InitParams["SFServiceUrl"];


    Итак, дальше логичнее рассказать про внутреннюю кухню программы. Нам понадобится создать 5 таблиц в базе:
    image
    Связи устанавливать необязательно, т.к. в нашем ядре они пока что явно не используются. Обрезанные полоски идут к таблице Users, полю UserId. Затем надо будет создать контекст данных (Linq To SQl) и добавить эти таблицы в него. В дальнейшем контекст данных будет иметь имя класса SDC. Его следует разместить в отдельной сборке (скажем, SDataAccess). Как это делать я описывать не буду, т.к. это тема отдельной статьи. Видимо, чтобы использовать Domain services, лучше сделать контекст данных на базе LinqTo Entities, но я решил делать по-старинке.
    В качестве ядра я использую библиотеку активностей Workflow (SWorkflows):
    image

    В нем создаем папку Activities/SeaFight. Ее содержимое есть в исходниках. Там зашита основная логика игры, как-то: создание, завершение, проверка ходов, получение данных об игроках, ведение чата, работа с координатами кораблей и т.д. Более подробно Вы можете ознакомиться с классами в исходниках, которые лежат в папке Activities\SeaFight. Там используются запросы Linq to Sql, а результаты запросов выдаются через Workflow service в наше Silverlight-приложение.
    После того, как наша сборка с активностями успешно скомпилируется, можно смело приступать к созданию Workflow service. Для этого в нашем хост-приложении создадим ее: File-Add New Item – Workflow Service:
    image
    Затем надо открыть этот файл XAMLX и начать создавать сервис. В отличие от Workflow из .Net 3.5 здесь используется авторинг Workflow-first, что облегчает задачу создания интерфейса. Ранее нужно было сначала сделать интерфейс с атрибутами ServiceContract, OperationContract и т.д., а затем использовать его для создания воркфлоу, а теперь этот интерфейс генерируется автоматически на основе воркфлоу. Итак, для того чтобы наш сервис мог обрабатывать сразу несколько операций внутри одного Workflow, нам нужно в корень вставить Peek Activity, а затем в нее добавлять последовательно в отдельные Branch (ветви) Receive Activity и Receive-Send Activity. Внутрь Receive Activity нужно вставлять наши активности, которые мы сделали в отдельной сборке. Они будут автоматически доступны на вкладке Toolbox:
    image
    Теперь, последовательно создавая отдельные ветви, надо расположить активности на Workflow. Далее для нужных активностей добавить переменные (внизу слева вкладка variables), а затем все это связать (подробнее смотри исходники — SeaFightService.xamlx). У Receive activity надо задать интерфейс (ServiceContractName) и название операции, а также привязать входные параметры к переменным. Не забудьте поставить галку CanCreateInstance у каждой Receive Activity. Подробнее об этом читайте на сайте workflow.
    Вот как получилось у меня:
    image
    Теперь, когда наша бизнес-логика готова для игры, мы добавляем Service Reference в наш Silverlight проект (Add Service reference, затем выбрать Discover):
    image
    Теперь остается только лишь сделать наш игровой интерфейс и использовать бизнес-логику посредством вызова прокси-объекта. Вот как использовал прокси-объект я:
    var proxy = ProxyReference.GetProxy();
    try
    {
    proxy.GetGameListCompleted += new EventHandler треуг.скобкавлево GetGameListCompletedEventArgs треуг.скобкавправо (proxy_GetGameListCompleted);
    proxy.GetGameListAsync(proxy);
    }
    catch
    {
    proxy.Abort();
    }



    А вот метод, обрабатывающий ответ WCF сервиса:

    // Обновляем список доступных игр
    void proxy_GetGameListCompleted(object sender, GetGameListCompletedEventArgs e)
    {
    var games = e.Result;
    dataGridGames.ItemsSource = games;
    ((SeaFightServiceClient)e.UserState).CloseAsync();
    }



    Проект имеет 5 окон: окно создания игры, окно ожидания второго игрока, окно расстановки кораблей, окно битвы и окно результатов. Соответственно, все они есть в исходниках, и Вы можете ознакомиться с их составом более подробно. В качестве игрового поля я использовал стандартный компонент – Grid, а позиции кораблей обозначены стандартными ToggleButton с измененным свойством Content. Опять-таки, чтобы узнать, как все сделано в деталях, смотрите исходники проекта SeaFight.
    Для переключения окон используется элемент PageSwitcher, который хранит в своем корневом свойстве Content текущее окно. Для переключения между окнами я использую такой вот код:

    if (this.Parent is PageSwitcher)
    {
    PageSwitcher ps = this.Parent as PageSwitcher;
    ps.Navigate(new MainPage());
    }


    Одним из важнейших недостатков проекта является отсутствие дуплексного режима в сетевом сервисе. Поэтому для обновления состояния используется таймер System.Windows.Threading.DispatcherTimer. Из-за этого чувствуется задержка между получением сообщений и ходами в игре. В будущем все же планируется перейти на дуплексный режим, но это непростая задача для Silverlight. Вот так выглядит окно размещения кораблей:
    image

    Хотелось бы отметить, что есть еще 1 вспомогательный проект: SEntities, который содержит общие классы для ядра и сильверлайт-приложения. Правильнее на будущее использовать специальный тип сборки – Silverlight Library, а не обычный Library, который есть сейчас.
    В общем, на этом я заканчиваю данную статью, так как основные моменты мной описаны, а на описание отдельных функций может уйти еще по одной такой статье на каждую функцию. Поэтому за более подробными деталями (как сделать размещение элементов на форме и т.д. можете обращаться в MSDN или ко мне). Если есть желающие помочь в развитии этого проекта – пишите: squalsoft{at}mail.ru.
    Реклама
    AdBlock похитил этот баннер, но баннеры не зубы — отрастут

    Подробнее

    Комментарии 10

      +1
      «треуг.скобкавлево» — это особенности коммуникации вашей игры или вашего хабра-редактора?

      Еще в SL есть возможность создавать «дуплексные» подключения, которые позволяют клиенту реагировать сразу же при поступлении новых данных на сервере.
      Т.е. как только первый выстрелил, второй сразу же видит это в своем клиенте.
        +1
        Я, наверно, минут 40 выкладывал тут статью =( множество тегов вырезается редактором, и еще картинки нельзя тут загружать. треуг.скобкавлево — это <. У меня от нее вообще текст далее не отображался… Про дуплексный режим в сильверлайт я знаю. Ожидается это сделать в следующих версиях.
          +1
          Да, редактор хабра оставляет желать лучшего.

          Вам отедльное спасибо за Workflow. Всё думал, когда же появится пример, в котором он будет использоваться.
            0
            Да, workflow — это мощная тема. Особенно здесь хорошо бы подошел Workflow с Flowchart, где были бы обозначены состояния игры, и происходила бы смена состояний. А если игрок отключался, то все бы сохранялось с помощью Persistence, а потом бы возвращалось на то место, где он вышел. Но, к сожалению, все на хостинге, а там так сделать нельзя, да и еще постоянно все перезагружается.
              0
              И еще одно:
              просто убивает, когда любая страница, даже та, на которую я зашел всего на 10 минут, требует, чтобы я зарегистрировался.

              Есть же OpenId, когда уже веб-разработчики примут его…
              Я понимаю, что тогда уже не получится использовать стандартный membership provider, но всё же…
        –1
        перенесите пожалуйста пост в блог silverlight, посты из личного блога на главную Хабра не попадают
          0
          странно зачем вы выбрали использование БД для игры. Зачем она, по-моему, лишнее. Хватило бы сервера, который передавал бы информацию с приложения на приложение.
            0
            Здорово, а можно это прикрутить к российским социальным сетям? Они Silverlight поддерживают
            • НЛО прилетело и опубликовало эту надпись здесь
              0
              P.S. Для синхронизации с пользователем ранее использовались таймеры, но теперь этот механизм переделан на дуплексный WCF сервис, использующий PollingDuplexHttpBinding.

              Подробнее об этом можно прочитать тут: msdn.microsoft.com/en-us/library/cc645027%28v=VS.95%29.aspx

              Также я сделал, чтобы в игру могли играть незарегистрированные пользователи.
              Если интересуют подробности, то спрашивайте =)

              Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

              Самое читаемое