Кроссплатформенный канделябр

    В статье GameDev и канделябр мы описали процесс портирования игры Марьяж под Android. Игра вышла достаточно успешной и практически сразу после релиза мы начали планировать кроссплатформенную версию с дальнейшим прицелом на онлайн. В качестве платформы была выбрана unity3d. Процесс разработки занял около шести месяцев.

    Интересно? Заходите под кат!


    Задание


    Итак, платформа выбрана, исполнители тоже, вроде все просто и понятно, никаких видимых проблем не видно на горизонте.
    Разработку условно можно разделить на 2 части — backend (далее — логику) и frontend (GUI с особенностями платформы, далее — клиент). В качестве платформы была выбрана unity3d (именно так будет дальше по тексту, чтобы там не говорили про ребрендинг и отсутствие “3d” сами юнитеки). Логика уже была написана и прекрасно функционировала. Единственной проблемой было то, что языком, на котором была написана логика, была java, а для выбранной платформы подходил только C#, либо JS. Соответственно, главной задачей стало портирование существующего кода в валидный код на C#, причем с сохранением работоспособности один в один.
    Клиентская часть так же уже была реализована, но как и логика — была жестко завязана на реализацию GUI в Android. Соответственно, клиента пришлось писать с нуля, по технически грамотному ТЗ в виде фразы “чтобы было так же как в Android-версии” :)

    Портирование логики


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

    В C# нету расширяемого типа enum, а в нашем Преферансе этот тип использовался очень широко: масти и ранги карт, сами карты, конвенции, ставки и висты, состояния игры. В некоторых случаях удалось обойтись и стандартным enum-ом в C#. Но в большинстве случаев enum-ы были больше, чем просто перечислимыми типами: к ним были привязаны разнообразные данные и имелись вспомогательные методы. Все это не хотелось терять в портированном коде. В результате был сделан минимально подобный джавовскому класс Enum для C#. Все оригинальные enum-ы стали отдельными классами с сохранением прежних данных и функционала, к ним привязывались обычные enum-ы из C#, которые задавали последовательность (метод Enum.ordinal()) и имя (Enum.name()) перечислимого типа, а также использовались в switch..case.

    Тяжелое наследие Delphi дало о себе знать и при портирования на C#. Большая часть логики была написана во внутренних процедурах с общими переменными. В Java это решалось сравнительно просто — внутренними классами. В C# внутренние классы хоть и есть, но аналогичны статическим классам в Java, то есть не могут использовать поля и методы класса-контейнера. Пришлось добавлять дополнительные ссылки.

    Не смотря на более богатый выбор массивов в C#, аналогичного Java фунционала там не оказалось. Проблема в том, что массив вида int[][] не может быть создан одним оператором вроде new int[10][20]. Перейти на прямоугольные массивы удалось не везде, в некоторых случаях отдельно использовались подмассивы. Пришлось писать вспомогательные методы, полностью создающие многомерный массив.

    Были и другие неприятные неудобства:
    • switch..case, требующий break даже в самом последнем условии, включая default;
    • массовая замена boolean на bool;
    • зачистка final, либо замена на readonly/sealed;
    • отсутствие беззнакового сдвига вправо (>>>) ввиду наличия беззнаковых типов;
    • оператор goto, который в Java управляет переходами во вложенных циклах, а в C# сохраняет свое древнее предназначение.

    Но это все выявлял компилятор. А вот выпиливать знаковый Java тип byte и менять его на аналог sbyte из С# пришлось без посторонней помощи.

    Реализация клиента


    Весь графический контент был полностью (с небольшими фиксами) позаимствован из Android-версии. В качестве GUI-фреймворка был выбран NGUI — тут вариантов не было в принципе: работа с атласами, оптимизация по отрисовке с минимизацией DrawCalls, вменяемая система сообщений, использующая систему сообщений unity3d. Не зря главный разработчик этого фреймворка сейчас пилит стандартный GUI для unity3d.

    Весь контент был рассортирован по логическим группам и собран в атласы исходя из ограничения 2048х2048 точек на атлас:


    Атлас иконок городов (еще есть место минимум под 6 городов).


    Атлас основных контролов, использующихся на всех “экранах”.

    Подобная организация графического контента позволила снизить количество DrawCalls в пике до 15 (например, при открытии скролируемой панели ставок и открытии меню с визуальными настройками).

    Одной из проблем стала необходимость “сделать все так же, как на Android”, а именно — подстраивание контролов под разрешение экрана. Обычно все контролы “приклеиваются” к краям экрана и раздвигаются / сдвигаются вместе с ними, в данном случае такой вариант был неприемлем. В результате было написано решение, работающее примерно по следующей схеме:
    • выставляем эталонное разрешение (было выбрано 1280х800);
    • размещаем контролы как они должны выглядеть правильно;
    • вешаем на контролы хелперы для пропорционального масштабирования / перепозиционирования;
    • профит.

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

    Эталонное разрешение (1280х800).


    Более “широкий” аспект (1136х640) — видны полосы по краям.


    То же разрешение (1136х640), результат отработки хелперов — видно, что контролы размещены пропорционально шире, а фон отмасштабирован.

    В результате все делалось на эталонном разрешении + тестировалось на самом узком аспекте (4:3).

    Потребовалось так же реализовать нестандартное поведение контролов, что, благодаря унифицированной системе сообщений NGUI, получилось сделать относительно просто. Так были реализованы LongPress-кнопки, “бегущая строка” с клипированием и Grid с анимированным перепозиционированием элементов. Так же пришлось пропатчить пару мест стандартных компонентов для адаптации под проект (UIPopup получил возможность показа всплывающей части в указанном родителе, а не в текущем — решились проблемы с клипированием всплывающего контента UIPanel-компонентами; центрирование скролящейся панели по дочернему элементу научилось игнорировать отключенные элементы и прочие мелкие фиксы).

    Вся работа клиента была разбита на “экраны” с подвязкой локальной логики поведения на конкретном “экране”: экран главного меню, экран настроек “учебки”, экран выбора города в “турнире”, экран самой игры и т.д. Это позволило снизить нагрузку на графическую часть в плане потребления памяти в каждый момент времени — при загрузке следующего “экрана” все загруженные объекты уничтожались и движок мог почистить освобожденную память или использовать ее для загрузки новых ресурсов. Между сценами “катаются” всего несколько неубиваемых объектов, обеспечивающих доступ к данным пользователя в плане настроек, последних выбранных элементов и т.п.

    К моменту начала работ по клиентской части не было утверждено API взаимодействия с логикой, поэтому было принято решение разделить всю игровую механику на изолированные модули (управление набором карт каждого пользователя, управление “летанием” карт, управление ставками, управление текстовыми сообщениями для игроков и информационной панели, управление настройками игры, локальным состоянием и т.д.) с доступом к ним из любой точки игрового процесса через синглтоны. Параллельно писалось что-то типа тестов прямо внутри клиента, дергающих все доступные методы для проверки корректности работы всего этого хозяйства. Все анимации и прочие штуки работают в одном потоке, через сoroutine-ы, ничего особенного тут нет.

    Внешние системы


    Сюда можно отнести реализацию IAP-функционала, банерной рекламы, Tapjoy-клиента для получения внутриигровой валюты бесплатно за определенные действия рекламной площадки и GoogleAnalytics.

    В качестве IAP-фреймворка изначально была выбрана soom.la: кроссплатформенное API, поддержка для двух основных мобильных платформ, бесшовная интеграция без костылей вокруг патчинга стартап-активити в Android и это все за даром. Достаточно сравнить ужасы интеграции поделий prime31, когда они принудительно перетирали манифесты исключительно под себя любимых + имели разное API на разных платформах, чтобы понять, что данный фреймворк — просто гениален. В дальнейшем (если будет потребность в winphone-версии и soomla не получит штатной поддержки) возможен переход на Unibill.

    В качестве банерного провайдера выступал admob, а клиентом к нему был выбран продукт от http://www.neatplug.com. Причина та же — легкая интеграция, унифицированное API, поддержка для двух основных мобильных платформ, стоимость универсальной версии под 2 платформы — $56 (против $50 за каждую платформу у prime31 и косяков, описанных выше для IAP). Было найдено одно не совсем адекватное поведение фреймворка при абсолютном позиционировании банеров на разном DPI, поэтому в результате было решено ограничиться стандартной привязкой к границам экрана.

    В качестве клиента к Tapjoy выступил стандартный фреймворк от самой площадки. Был немного допилен код для корректной работы в редакторе, общая интеграция не доставила каких-либо проблем, достаточно было 4-5 запусков билда в эмуляторе для проверки срабатывания событий по логам.

    GoogleAnalytics самопальный, адаптированный с версии для Web-сайтов. В результате получилось кроссплатформенное решение через WWW, не требующее native-версий библиотек для разных платформ по $50.

    Сопряжение логики и клиента


    Через 4 месяца появилась первая версия логики и ее можно было начинать стыковать с клиентом. Сама логика разрабатывалась как независимое приложение, крутящееся в бесконечном цикле, позволяющее ставить в очередь команды и дергающее методы подписчика в случае наличия исходящих команд. В результате логика в unity3d была обернута в код, управляющий ее стартом в отдельном потоке и обеспечивающий транзит команд в обе стороны (не забываем, что типы данных, сигнатуры методов и т.п. полностью несовместимы, т.к. обе системы разрабатывались в изоляции друг от друга и двумя разными исполнителями). Как ни странно, все срослось на удивление просто и команды управления хорошо легли на модули клиента.

    Проблемы, связанные с целевой платформой исполнения


    С работой логики в отдельном потоке приключилась одна не очень хорошая история. Как известно, на iOS запрещено использования любого не native-кода, поэтому unity3d при экспорте проекта прогоняет managed-сборки через AOT, генерируя на выходе нейтивные библиотеки, которые уже линкуются с системными + добавляются всякие glue-врапперы на obj-c для стыковки всего этого добра. Причем для эмулятора делается отдельный билд, в котором есть JIT и все будет работать как работает в редакторе — это важно, ибо ошибки, возникающие на реальном железе, могут никогда не возникнуть ни в редакторе, ни в эмуляторе.
    Т.е. фактически при запуске на реальном железе — никакой отладки проекта, только на ощупь по логам и колстеку.
    Итак, возвращаясь к проблеме с потоком. Вроде все работало, а потом внезапно стало падать, роняя приложение полностью. По логам xcode получалось, что краш происходит глубоко в рантайме Mono после вызова Thread.Sleep() или Thread.Current.IsAlive если поток был прерван (но не каждый раз). Т.е. получали краш на вызове стандартных методов при вроде как бы стандартной попытке прерывания потока. Самое смешное, что на JIT-версии билда (например, Android, standalone, webplayer) все отрабатывало как надо. Методом тыка были определены указанные выше методы, приводящие к крашу. В результате было решено отказаться от принудительно прерывания потока, а вместо этого сигнализировать логике, что пора бы и завершиться, а потом ждать закрытия потока. Краши пропали и больше не возвращались.

    Второй проблемой стал неверный расчет поведения ИИ при использовании типа double именно после обработки AOT и при запуске на реальном железе. Узнать это пришлось через сбор дикого количества логов, содержащих расчеты, используемые при анализе стратегии игры. Размер лога каждого тестовой игры превышел 40Мб plain-текста. Это все надо было как-то собирать внутри приложения, запущенного на реальном железе а так же извлекать наружу. Сначала была попытка сбора логов через GoogleDocs, но скорость и разрешенный объем удручали, поэтому по-быстрому был написан сервер на node.js, принимающий данные через post-запросы и склеивающий их в локальный текстовый файлик. Со стороны приложения все было реализовано в виде кеширующей очереди записей лога + скидывания при переполнении через стандартный WWW на самопальный http-сервер. Получилось быстро и относительно просто.

    Тестирование


    Весь процесс тестирования проходил с использованием платформы https://testflightapp.com, как для промежуточных Android-билдов, так и для всех стадий разработки под iOS. Были собраны UIID-номера устройств тестировщиков (автоматически, через GUI клиента после регистрации), обновлен provision-профиль и билды собирались уже с разрешениями на установку по перечисленным удаленным устройствам. После сборки ipa-билда он просто заливался через админку на тестфлайт и раздавался доступ с оповещениями по email для всех тестировщиков. Им было достаточно зайти в клиент и нажать кнопку установки билда, все происходило автоматически. Никакой дополнительной интеграции с сервисами тестфлайта не производилось, ибо это повлекло бы очередную интеграцию с native-библиотеками, а профита бы практически не принесло.

    Хочешь сделать хорошо — сделай сам!


    Мы не заказывали обзоров для продвижения Android версии игры, а вот для iOS решили заказать один обзор на популярном ресурсе. О его финансовой стороне говорить пока рано, но хочется поделиться впечатлениями об качестве обзора. После оплаты было получено письмо с подтверждением заказа и пожелания к обзору. Сам обзор на утверждение пришел вовремя, но лучше бы его было не открывать. Мало того что тот, кто писал обзор даже не удосужился нажать аж три(!) кнопки на главном экране и даже не пытался понять как функционирует игра (согласен, это не пасьянс косынка), но он вообще не умел писать. Помимо технических ляпов статья пестрила фразами типа «вам нужно набрать как можно больше очков, которые подсчитываются из очков». Копипаст википедии и нашего хелпа под соусом троечника из восьмого класса нас явно не устраивал и мы написали обзор сами. Стоит отметить, что после критики мы получили и вторую версию обзора, но она была так же очень далека от того, что можно показать пользователям.

    Итоги


    Unity3d — на данный момент лучшая платформа для создания кроссплатформенных игр. У нас не возникло особых проблем с созданием GUI, хотя интегрирование различных плагинов заняло намного больше времени, чем предполагалось изначально. Так же не стоит обновлять плагины или саму Unity3d, если у вас все работает и нет парочки лишних дней для приятного времяпровождения. И не стоит забывать, что гарантированной кроссплатформенности не будет, надо быть готовым к отладке логики на всех платформах.
    Для Android мы пока что решили не использовать кросплатформенную версию, так как устройства на базе 2.2 и 2.3 (которые используют 4% и 10% пользователей соответственно) её просто не тянут, вываливаясь по OutOfMemory. Надеемся, что скоро эти устройства уйдут в прошлое, тем самым упростив разработку онлайн версии.
    AdBlock похитил этот баннер, но баннеры не зубы — отрастут

    Подробнее
    Реклама

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

      +6
      оператор goto, который в Java управляет переходами во вложенных циклах, а в C# сохраняет свое древнее предназначение.
      goto? Суровые вы программисты.))
        0
        в джаве goto используется ТОЛЬКО для перехода во вложенных циклах
          +2
          Думаю, IgorFedorchuk имел ввиду, что использование goto в принципе моветон.
            +3
            Суровое наследие алгоритма на дельфе, наверное. Меня другое интересует, почему рожи на картинках такие страшные? :)
              0
              Иногда goto можно использовать, бывает без меток код становится громоздким очень и лучше поставить метку, чем добавлять ветвления. Хотя тут можно и по другому сказать, скорее всего плохо продумал изначально структуру кода и все загромоздилось.
              –1
              Не стоит относиться к этому правилу, как к заповеди. В C goto очень полезен для освобождения ресурсов перед return, а в Java — для выхода из вложенного цикла (заводить для этого переменную и писать условие — вот уж точно моветон).
                –1
                Оценка без комментария — это как заключение в тюрьму без выдвигания обвинений и объявления приговора. В чём я неправ? Таки goto — абсолютное зло?
                  0
                  Можете попробовать доказать свою правоту, приведя пример кода, когда goto действительно оправдан. И помним, что мы говорим про Java/C#.
                    0
                    Фишка в том, шо можно все переписать без goto, но код получится не в пример кривее и длиннее. Это же авторская логика поведения ботов, ее надо портировать очень аккуратно, а то боты соберут скайнет и паработят всех :)
                      0
                      С ботами шутки плохи)
                      0
                      C#

                          for (int i = 0; i < 4; ++i)
                          {
                      	for (int j = 0; j < 4; ++j)
                      	{
                      	    Console.WriteLine("i = {0}, j = {1}", i, j);
                      
                      	    if (j == 2 && i == j)
                      		goto EXIT_OUTER_LOOP;
                      	}
                          }
                      
                      EXIT_OUTER_LOOP:
                          Console.WriteLine("After the outer loop");
                      


                      Как выяснилось, в Java goto не используется, хотя и является зарезервированным словом, а для выхода из внешнего цикла делается так:

                      OUTER_LOOP:
                          for (int i = 0; i < 4; ++i)
                          {
                              for (int j = 0; j < 4; ++j)
                              {
                                  System.out.println(String.format("i = %s, j = %s", i, j));
                                  
                                  if (j == 2 && i == j)
                                      break OUTER_LOOP;
                              }
                          }
                          
                          System.out.println("After the outer loop");
                      


                      Каюсь, не знал. Пишу на Java всего несколько месяцев, вложенных циклов стараюсь избегать (не в ущерб читабельности, разумеется) — вот и не понадобилось пока.
                        0
                        Вот у нас есть код на джаве и на шарпе:
                        protected final int processSuitMax(final int[] ranks, final int size, final int initialValue) {
                        		final int[] Z0 = this.Z0;
                        		int result = initialValue;
                        		int resultSuit = result << 3; // * 8
                        		
                        		Next:
                        			for (;;) {
                        				for (int j = 0; j < size; j++) {
                        					if (ranks[j] > Z0[resultSuit + j]) {
                        						result++;
                        						resultSuit = result << 3; // * 8
                        						continue Next;
                        					}
                        				}
                        				break;
                        			}
                        		
                        		return result;
                        	}
                        


                        protected int processSuitMax(int[] ranks, int size, int initialValue)
                                {
                                    int[] Z0 = this.Z0;
                                    int result = initialValue;
                                    int resultSuit = result << 3; // * 8
                        
                        
                                    for (; ; )
                                    {
                                    Next:
                                        for (int j = 0; j < size; j++)
                                        {
                                            if (ranks[j] > Z0[resultSuit + j])
                                            {
                                                result++;
                                                resultSuit = result << 3; // * 8
                                                goto Next;
                                            }
                                        }
                                        break;
                                    }
                        


                        Просьба учесть, что этот метод вызывается 100500 раз на каждом ходу.
              +1
              О море надо спрашивать у рыбака ;)
              Вам написать обзор? Я в преферанс играть люблю.
                +1
                Ваш преферанс на Android, пожалуй, лучший из реализаций под мобильные платформы. А вот ссылку на него для iOs мне не удалось отыскать. Даже на горе-обзор.Не подскажете?
                0
                Не было альтернатив NGUI? Например, Daikon GUI. Отличная альтернатива. А так в атласы паковать и прочее умеют дофига фреймоворков и часть из них это умеет делать лучше чем NGUI(я про удобство использования например), Daikon в том числе. Да и ИМХО зря разработчик NGUI сейчас пилит GUI для юньки, потому что NGUI не самый удобный фреймворк.

                PS игра хорошая кстати.
                  0
                  Подтверждаю, альтернативы нет и в ближайшем будущем вообще не потребуется, ибо весь функционал станет штатным, а новые версии нгуя можно будет рассматривать как обкатку будущего функционала. К тому же когда был выпущен «Daikon GUI»? В статье есть указание, что они пилили его полгода, думаю, тогда еще дела обстояли не так хорошо у Daikon. Если не секрет, в чем заключается неудобство? Все поставляется в сорцах, можно расширять своими контролами, патчить базовые, если сильно потребуется.
                    0
                    Я ничего не имею против NGUI. Но мне не нравятся следующие вещи.
                    Он плохо оптимизирован. Это незаметно на десктопах, но на мобилах мне его производительность не нравится.
                    Не удобный интерфейс. Слишком много телодвежений нужно делать, что бы сделать простые вещи.
                    Не удобная архитектура. Опять же это на фоне других, что работа с их камерой, что панели, что виджеты.
                    Проблемы со слоями. Вот это самый большой минус этого фреймворка, настроить нормально глубину. когда много панелей и пару анхоров — очень геморно. Всегда что-то перекроет кого-то непонятным образом.

                    Может быть ему не было альтернатив год назад, сейчас же их хватает. К примеру я использовал и 2D Toolkit, он умеет сейчас столько же и лишен тех недостатков(ну и плюс у них очень удобная система Layout, а не простые анхоры, а так же скейл под разные разрешения сделан удобней). Но есть еще лучше чем 2D Toolkit — это, как я писал уже выше Daikon GUI, он чисто заточен под GUI в отличии от 2D Toolkit.
                      0
                      >> Он плохо оптимизирован. Это незаметно на десктопах, но на мобилах мне его производительность не нравится.
                      >> Но есть еще лучше чем 2D Toolkit
                      Он прекрасно оптимизирован и прекрасно работает на мобилках. Просто используйте его по назначению, а не как 2d фреймворк для спрайтов, он заточен под работу с гуй-виджетами, которые не часто двигаются. Разумеется, если вы будете двигать все виджеты для симуляции 2д-спрайтовой игрушки, то это будет фейл и нгуй будет перестраивать резульирующий меш геометрии каждый фрейм. Т.е. у 2д-тулкита и нгуя абсолютно разное назначение — первый расчитан на спрайтовые динамически 2д-штуки, второй — под статичный сложный гуи с эффектами и развесистой системой сообщений.

                      >> Не удобный интерфейс. Слишком много телодвежений нужно делать, что бы сделать простые вещи.
                      Прекрасный интерфейс. Какие простые вещи показались вам сложными?

                      >> Не удобная архитектура. Опять же это на фоне других, что работа с их камерой, что панели, что виджеты.
                      С 3.х ветки ведется унификация и виджет становится независимым базовым невизуальным контролом. Смысл панелей только в одном — создание изолированных скроллящихся блоков с отсечением по области. Так же панели служили во второй ветке для изолирования контента (содержимое рендерилось за отдельный drawcall) — полезно для всяких всплывающих окон.

                      >> Проблемы со слоями. Вот это самый большой минус этого фреймворка, настроить нормально глубину. когда много панелей и пару анхоров — очень геморно.
                      Тут согласен, хотя объяснение разраба тоже логично — zfighting и сортировка по глубине уходят в прошлое с учетом правильно настроенной глубины. С какой-то 3.х версии появилась возможность указания глубины у панели целиком, т.е. сортировка внутри панели становится локальной.
                      По поводу UIAnchor-ов. Они в преферансе не используются в принципе, там своя система пропорционального позиционирования. Во всех фреймворках почему-то упорно цепляются к сторонам экрана, тут по требованиям была нужна «резинка».
                        0
                        Например, очень не удобно работать с атласами. У 2д тулкит есть возможность вставлять GUI контролы, эта новая часть фреймворка и она не сильно развита, но она уже как минимум не хуже NGUI. А вот насчет оптимизации, вы конечно имеете право на свое мнение, но чисто объективно он оптимизирован плохо. Я не хочу сейчас вдаватся в детали и вас переубеждать, ваше право. :)

                        Кстати в него уже добавили обновление атласов после того, как ты редактируешь отдельно текстуру, которая входит в атлас?
                          0
                          Обновление было всегда — выделяешь текстуру и в редакторе атласа напротив текстуры появляется надпись update. Если нажать кнопку апдейта, то все выделенные текстуры будут проапдейчены в атласе.
                  +1
                  Подскажите, а почему было принято решение мигрировать на Unity3d, если весь код уже был написан на java? Не проще ли было использовать какой-то java-фреймворк вроде libgdx, который замечательно справляется с 2d и работает на мобильных платформах и через gwt может быть в javascript скомпилирован (при очень большом желании использования этого же кода в онлайне)?
                    0
                    Во-первых онлайн это главная цель разработки. Очень надеемся, что сможем поддерживать только одну версию игры. По поводу libgdx — мы попробовали его, даже сделали демку. Скорее всего, мы не умеем его готовить, поэтому эксперимент оказался не удачным. Кроме того, в нашей игре есть плагины (покупки, реклама и т д) которые оказалось довольно сложно подобрать.

                    Портирование кода — это шарп затянуло достаточно долго потому что мы переписали структуру движка, чтобы он был готов к онлайну. На Android версии сейчас тоже используем его.
                    0
                    Хотел бы заменить, что по моему ощущению игры на Unity гораздо прожорливее в плане батареи. Я подозреваю, что многим пользователем все равно, но было бы здорово если бы разработчики уделяли хоть немного времени оптимизации энергопотребления. Я например люблю играть в дороге, и я просто не могу играть в игры которые быстро сажают батарею.
                      0
                      Тут нужно понимать, что юнити — это полноценный realtime движок, где экран очищается, перерисовывается полностью каждый кадр с достаточно высокой скоростью. Т.е. принцип работы отличается от рендера, допустим, вебстраницы. Скорость обновления в секунду обычно 30-60фпс. Тут тоже нужно думать о том, что лучше — плавная анимация (высокий фпс) с различными эффектами или максимальная энергоэффективность (низкий фпс).
                        0
                        Да я понимаю, но вот вопрос то как раз в эффектах. Например говоря о карточной игре — эффекты меня интересуют в последнюю очередь.
                          0
                          Так ведь все зависит от задачи. Если в постановке есть «сделать максимально энергоэффективным любыми способами», то это одно, а когда есть задача выйти на гламурный апстор, то появляются совершенно другие требования.
                            +2
                            Мы сегодня уже получили запросы на тему «хочу такой же преферанс, но розовенький, так как мой айфон 5s беленький». Наша сила — в алгоритмах, но внешний вид так же немаловажен.
                              0
                              Это… это печально. На самом деле я все понимаю, просто скорее опечален современными реалиями. А вам желаю удачи!
                        0
                        Т.е. фактически при запуске на реальном железе — никакой отладки проекта, только на ощупь по логам и колстеку.

                        это не совсем так, там есть возможность отладчиком приатачится к приложению запущенном на девайсе если собрать билд с парой дополнительных галочек
                          0
                          На реальном девайсе всегда нейтив + вкрапления комментов из вызовов методов после трансляции АОТ-ом. Вы говорите про подключение профайлера к девайсу в юнити, или подключение реальной отладки (пусть даже в штатном monodevelop-е) на шарпе? Нужна отладка именно шарпа.
                            0
                            да я про отладку на устройстве вот:

                            iOS remote debugging instructions
                            In addition to the instructions described above, Unity iOS applications require some additional steps for successful debugging:

                            Attach your iDevice to your WiFi network (the same requirement as for remote profiling).
                            Hit build & run in the Unity editor.
                            When the application builds, installs & launches via Xcode, click Stop in Xcode.
                            Manually find & launch your application on your iDevice. (Note: if the application is launched via Xcode you won't be able to resume after reaching a breakpoint).
                            When the app is running on the device, switch to MonoDevelop and click on the attach icon in the debugging toolbar. Select your device from the available instances list (if there are several instances shown, then select the bottom one).


                            отсюда Manual/Debugger

                            т.е это удаленная сетевая отладка шарп кода по сети

                            вот как это выглядит из monodevelop:


                            но это все мягко говоря работает не очень стабильно, в последней юнити 4.3 даже с отладкой в редакторе куча проблем
                              0
                              Понятно. Собирать надо с dsym или достаточно dwarf?
                                0
                                c dwarf не пробовал
                          0
                          Будет ли версия под Windows Store и Windows Phone?
                            0
                            В планах пока нет. Потому что есть большое опасение того, что в ноль мы не выйдем. Но если вдруг Майкрософт подкинет нам устройств для отладки, то мы сделаем версию практически мгновенно.
                            0
                            Cкажите, я туплю или почему в таблице результатов не видно сколько вистов записано у компьютерных противников друг на друга? В обеих — в той, которая появляется после каждой сыгранной раздачи, и которая появляется по вызову во время игры, сверху плашка закрывает часть результатов. Версия под iOs.
                              0
                              Пришлите, пожалуйста, нам скриншот на brainfitnessltd@gmail.com
                                0
                                отправил. кстати, еще один вопрос — в купленной игре все равно отображается контекстная реклама (см. приложенный скриншот) — это так нужно или забыли поправить?
                                  0
                                  С рекламой — это наша бага. Нам стыдно, но мы исправим.
                                    0
                                    с последним обновлением все работает — и висты и реклама пропала, спасибо!
                                    еще можно одно замечание? каждый раз при начале новой игры длина пули по умолчанию 5, и приходится «накликивать» до 20 или выше. Можно добавить где-то в настройках чтобы ее по умолчанию выставить?
                                      0
                                      В путешествии при открытии нового города выставляется минимальное возможное для этого города длина пули, в учебке должно все сохранятся. Мы посмотрим и если что не так — обязательно исправим. Спасибо!
                                        0
                                        Можно зажать и подержать немного — значения будут меняться по ±5.
                                0
                                Спасибо, прекрасный продукт. Начал играть на iOS.
                                Интерфейс немного удивляет:
                                * снос и ход делаются двумя тапами, вместо естественного на тачскрине drag-n-drop;
                                * иногда нет паузы там, где она необходима — например, при вскрытии прикупа.
                                * прочее: синхронизация путешествия на планшете с iCloud не сработала, на телефоне я в самом начале 8(
                                В остальном — всё супер!
                                  0
                                  И ссылка на «оцените нашу программу в AppStore» сломана, ведёт на Apple Music. Почините, и рейтинги, может, поправятся :)

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

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