Универсальная игра под Windows 8.1 RT и Windows Phone 8.1

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



    Причем движения в сторону объединения платформ они начинают делать уже сейчас. Если открыть Visual Studio 2013 Update 1, то в разделе разработки под Windows Store можно увидеть относительно новый пункт меню «Универсальные приложения». Сейчас со стороны Microsoft льётся активная реклама этих «универсальных приложений» в IT-уши, и я поддался этой рекламе. Но первый вопрос который я задал себе: «Почему только приложения? А не написать бы мне универсальную игру на C# и XAML». Если стало интересно что из этого получилось, жмите «Читать дальше».

    Сразу скажу, что к майкрософту я не имею отношения, да и программированием на C# и XAML занимаюсь в свободное от работы время. Итак, начнем.

    ИНСТРУМЕНТЫ

    Для того чтобы можно было писать такие вот универсальные приложения, нужна Visual Studio 2013 min c Update 1. По поводу младших версий не знаю. Также сказать, что осваивал программирование под Win8 платформу методом тыка + по книге Чарльза Петцольда «Программировании под Windows 8».

    ИДЕЯ

    Конечно самым простым вариантом было написать еще один «Flappy Bird», но от него уже тошнило даже меня. Идея появилась сама собой. Мы с женой, когда жили на окраине Минска и ехали на работу, то имели пол часа свободного времени которое дружно убивали на разные мобильные игры. Сначала у каждого были свои игры, но потом мы просто заразились одной игрой на двоих. Игра эта состояла из нескольких мини-игр, таких как «Алгебраические выражения», «Флаги стран», «5 разных картинок» и т.д., и выигрывал тот, кто быстрее и правильнее нажмет на свою кнопку. Играли мы еще на моем стареньком Android-е, причем настолько увлеченно, что часто даже проезжали свою станцию. Потом оба обзавелись виндоусфонами, и прониклись к ним большой любовью. Но порывшись по местному маркету, пришло огорчение — таких игр под эту платформу просто не было, а те, что были представляли из себя жалкую пародию. Поэтому мне вдруг очень захотелось написать такую игру и под Windows Phone.

    ЦЕЛИ

    Целей было несколько:
    1. Очень хотелось снова играть с женой на двоих в игру на реакцию но уже под Windows Phone-ом.
    2. Хотелось написать код так, чтобы читая его текла слеза умиления он отвечал требованиям паттерна MVVM, ведь в процессе я очень проникся идеей разделения кода модели от кода фьюшки.
    3. Давно хотелось написать что-то, что пусть и не будет великим, но приятным и во что будешь играть сам. Мой опыт разработки ограничивается работой в крупных компаниях (учетные системы на объектах розничной торговли, ERP-системы, банковское ПО). А там соответственно готовый продукт — это не результат энтузиазма, а уравнения в котором слагаемые это «ТЗ» и «дедлайн».

    РАЗРАБОТКА

    При разработке модели, можно сразу разделить два основных класса: Игроки и Мини-игры. Игроков может быть от двух до бесконечности, но учитывая размер экрана – ограничился шестью, по три с каждой стороны. Мини-игры тоже могут быть разные, с разным количеством заданий. Все это добро находится в управляющем классе «Игра». Для мини-игры был сделан родительский класс с базовым функционалом. Он много раз менялся, и как положительный результат этих изменений – некоторые из предков заданий стали занимать со ста до менее чем двадцати строк кода. Реализованы были такие мини-игры:
    • Внезапная картинка
    • Шесть разных картинок
    • Арифметика
    • Цвет и название
    • Обратный отсчет
    • Флаг страны
    • Столица страны и другие (10 различных мини игр)
    С кодированием модели каких-либо проблем не возникло, но вот разработка представления откровенно огорчила… Мои ожидания по поводу «здесь все прям как в WPF» разбились уже на элементарном: когда я захотел сделать псевдотридешную кнопку игрока, не смог найти радиальный градиент, который есть в WPF. Сначала подумал, что это ограничения дизайнера VS, но открыв Blend (ну мало ли) понял, что его нет нигде, пришлось довольствоваться линейным:



    Подобные мелочи хоть и встречались, но к каждой из них либо находился бубен, либо приходилось идти в ущерб функционалу.
    Несмотря на хронический недостаток свободного времени результат вырисовывался достаточно быстро. Буквально через пару недель я уже тестил игру под десктоп (Windows 8) и радовался жизни. Почему тестил только на десктопе? Да потому, что не мог понять, в чем может быть ошибка парсинга XAML кода для Windows Phone платформы:



    Одной рукой удаляя частями XAML-разметку, вторым глазом читал интернет и выяснил следующее: создание панелей

    <Page.BottomAppBar>
    <AppBar />
    </Page.BottomAppBar>


    это Windows RT код, а для универсальных приложений нужно создавать уже

    <Page.BottomAppBar>
    <CommandBar />
    </Page.BottomAppBar>


    Но ведь этот код мне автоматически создал сам редактор Visual Studio 2013! Причем он это делает до сих пор и даже с update 3 ситуация не изменилась.
    После всего прочитанного в этой статье habrahabr.ru/company/microsoft/blog/218441 предложение:
    «…язык разметки XAML также был унифицирован между платформами»
    осознавалось мной уже не просто с радостью (ведь это стало почти так, как и пишут), но и с некоторой досадой: жаль Visual Studio об этом еще не знает.
    Что касается общего кода – он действительно общий! В моем случае общим было все от модели до вьюшки (все кроме ресурсных файлов задающих размеры под определенную платформу).
    И теперь дело оставалось за реализацией платформенных фишек, типа интеграции в чудо-панель или поддержка закрепленного вида страницы.
    Чтобы пользователю удобно было воспринимать приложение как на десктопе, так и на телефоне сделал аналогичную чудо-панель и на телефоне. Выглядит это так:



    И я увлекся общим кодом настолько, что даже меню сделал не просто хардкодное для каждой платформы, а формирующееся из общего списка объектов (элементов меню). А вот распихивались эти элементы меню уже под директивами, каждый под свою платформу, в свое меню. В Windows RT – это чудо-панель (под директивой #if WINDOWS_APP), а в Windows Phone – это обычная менюшка прикрепляемая сбоку (директивой #if WINDOWS_PHONE_APP), которая в Windows RT версии делалась Visible=false.
    Немного добавлю, что чудо-панель оказалась удобной штукой, но я к ней долго привыкал и хронически во всех приложениях искал вызов меню настроек какой-нибудь кнопкой расположенной в интерфейсе.
    Чтобы перемещаться по страницам, используется соответствующая визуальная кнопка со стрелочкой влево (<-). Там где этой стрелочки нет (например в процессе игры) использовал хардварные кнопки. На десктопе обрабатывается событие KeyPressed(Esc), а вот на телефоне, если нажать хардварную кнопку назад, то приложение схлопывалось и пользователь оказывался в главном плиточном меню. Все обработки события для версии 8 не помогали, в 8.1. Случайно наткнулся на событие, которое действительно начало вызываться при нажатии на хардварную кнопку назад:

    using Windows.Phone.UI.Input;

    HardwareButtons.BackPressed += HardwareButtons_BackPressed;


    Для десктопа нужно было решить вопрос с закрепленным режимом, и опять же если раньше в 8 версии было только три варианта размещения окна:
    • приложение развернуто на весь экран (Full Screen);
    • приложение закреплено сбоку экрана (слева или cправа) (Snapped). Ширина приложения в таком режиме составляет 320 пикселов;
    • приложение работает совместно с другим закрепленным сбоку приложением и занимает все оставшееся пространство (Filled)
    То в 8.1 окно уже не цеплялось к этим вариантам, а размер окна можно стало менять в некоторых размещениях попиксельно. В идеале для каждого из режимов нужно создавать свой View, с измененным расположением элементов, размера шрифта и т.д. Я выбрал другой вариант – относительные координаты. Т.е. например поле с заданиями занимает 2/3 от длины экрана (сколько бы она не была в пикселях), а вот все компоненты с текстом запихнул в Parent-объект ViewBox. Данный объект автоматически масштабирует все что находится внутри него, в том числе и уменьшает размер шрифта в зависимости от своего размера, у всех Child-ах. Результат выглядит так:



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



    Т.е. изображения в процессе анимации сжимается так, что мягко говоря выглядит страшновато. Причем не важно, что это был за объект, результат одинаковый и для TextBlock и для Image. Насколько я могу догадываться, объект перед анимацией сжимается ооочень сильно, чтобы в процессе все выглядело плавно. Выхода из данной ситуации я к сожалению, не нашел, поэтому оставил все как есть. К тому же после того как анимация заканчивается, объект снова приобретает изначальное разрешение.
    По итогу нарисовал с десяток иконок разных монстриков (с маленькой «зелено-алой пасхалочкой» которая особенно порадует людей живущих в Беларуси), локализовал приложение на французский, английский языки с помощью друга Василия Лапицкого (за что ему большое спасибо), и на португальский, испанский и хинди с помощью Bing-транслита. Кстати был очень приятно удивлен на сколько просто делается локализация (в любой книге есть примеры, поэтому не буду на этом заострять внимания).

    РЕЗУЛЬТАТЫ

    Результаты буду писать из целей:
    1. Теперь у нас с женой есть совместный тайм киллер не только на телефонах, но и на компьютере. Через него мы часто решаем кто пойдет в магазин или приготовит поесть.
    2. В целом, не смотря на некоторые мелочи, разрабатывать под платформу Windows 8.1 мне очень понравилось, но именно под 8.1, а не под 8! Хотя данная статья была немного акцентирована на имеющихся недоработках Microsoft, в целом простота разработки у меня часто вызывала простую программистскую радость. В универсальном проекте получилось 98% общего кода и лишь только 2% кода, заключенного в директивы – этот результат меня очень удивил, и на подобное я не рассчитывал, когда только брался за написание «универсальной игры».

    Вам не передать того восторга который я испытываю каждый раз, когда прихожу с работы домой и меняю Borland Builder 6.0 на VS2013, вижу свой код и горжусь им! И хоть понимаю, что совершенству кода нет предела, и что через некоторое время мне возможно опять что-то захочется переписать или улучшить. И пусть на работе днём мы с коллегами строим огромные небоскребы кода, вечером дОма я построил своё бунгало кода которое пусть и маленькое, но в нём так приятно каждый раз находиться. А как приятно достать телефон, поиграть со знакомым, тут же получить фитбэк от него, а потом сказать «да, это я написал».

    ВМЕСТО ТЫСЯЧИ СЛОВ

    Это был хороший опыт, и сейчас у меня на свободное время приходится разработка еще одной игры, но уже на Unity.
    Что касается этой игры «Red Reactor», то она уже доступна как в Windows Store, так и в Windows Phone Store. Прямых ссылок приводить не буду, чтобы не сочли за рекламу.
    Получилось так, как я и хотел: интересно и весело. Мы периодически устраиваем турниры с друзьями, как по некоторым видам мини-игр, так и по всем. Кстати очень полезна игра будет и для детей, так как в ней развивается не только реакция и внимательность, но и арифметика + много географии.

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

    Similar posts

    AdBlock has stolen the banner, but banners are not teeth — they will be back

    More
    Ads

    Comments 3

      0
      С девушкой постоянно играли в 2-player reactor, классная игра. Однажды хотел её перенести на WP, но увы, игры делать я не люблю :)

      >>не смог найти радиальный градиент
      Его убрали, потому что нарушает гайдлайны

      >>На десктопе обрабатывается событие KeyPressed(Esc), а вот на телефоне, если нажать хардварную кнопку назад, то приложение схлопывалось и пользователь оказывался в главном плиточном меню.

      Используйте NavigationHelper из шаблона Windows Phone Application. Более того, он предоставляет команды GoBackCommand и GoForwardCommand и обрабатывает нажатия боковых кнопок мыши для навигации, что очень помогает в Windows 8.1 приложениях.

      >>В идеале для каждого из режимов нужно создавать свой View, с измененным расположением элементов, размера шрифта и т.д. Я выбрал другой вариант – относительные координаты.

      Я бы вам за это по рукам надавал :) Лучше сделать все фиксированным (по мне как чуток свободного места по бокам выглядит приятнее). Можно еще менять ориентацию вьюшки, если приложение занимает меньше половины экрана.

      >>Т.е. изображения в процессе анимации сжимается так, что мягко говоря выглядит страшновато. Причем не важно, что это был за объект, результат одинаковый и для TextBlock и для Image.

      Есть два варианта:
      1) Использовать BitmapCache и установить свойство BitmapCache.RenderAtScale в нужную величину.
      2) Сделать картинку\текст в полный размер и потом сжать с помощью ScaleTransform допустим до 0.1, а потом в анимации масштабирования просто возвращать к 1.
        0
        2-player reactor, классная игра.
        Именно в нее мы и играли!
        NavigationHelper
        Спасибо за наводку
        чуток свободного места по бокам выглядит приятнее… Лучше сделать все фиксированным
        Тут я с Вами не соглашусь. Свободное место по краям — зачем? Мне кажется пользователь сам волен выбирать размер экрана под контент, а не подстраивать размер под фиксированные размеры определенные заранее программистом (иногда даже прицеливаясь и сужая полосу так, чтобы не осталось свободного места). Изначально игра делалась с фиксированными координатами, и делались разные представления под разные варианты прикрепления к краям, и разной ориентацией. Но:
        — во-первых: получалась огромная туча XAML кода, которая возможно и нужна, в тех случаях, когда нужно перекомпоновать компоненты например или отмасштабировать отдельные из них, но это не тот случай.
        — во-вторых: когда игра переворачивалась получалось два игрового поля, из которого использовался по факту только один. Для этого и была введена настройка представления, которая меняла представление с двух вертикальных полей под одно горизонтальное. Поэтому в этот раз мои руки, думаю, останутся целыми :)
        сжать с помощью ScaleTransform
        Сейчас так и делается Storyboard-ом: сначала одна картинка сжимается по ScaleX до 0, потом туда загружается из кэша другая картинка, и растягивается обратно до ScaleX. Получается вышеописанный jpeg-эффект. Надеюсь ваша подсказка с
        Использовать BitmapCache
        поможет.
        В целом спасибо за конструктивный и полезный комментарий!

          0
          >>Сейчас так и делается Storyboard-ом: сначала одна картинка сжимается по ScaleX до 0, потом туда загружается из кэша другая картинка, и растягивается обратно до ScaleX.

          Имею в виду, что ScaleX не должен быть больше 1, тогда не будет мыла.

      Only users with full accounts can post comments. Log in, please.