Как изгонять вирусов на Corona SDK

    image

    $google = "We don't allow apps that lack reasonable sensitivity towards or capitalize on a natural disaster, atrocity, conflict, death, or other tragic event";
    mysql_query("UPDATE cvirus_users SET winners = winners+19, message='$google' WHERE imea='$imea' ");
    

    Без паники, парни, статья не заразная! К тому же её автор умер еще в прошлом году. Делать на том свете нечего, кроме как игры писать и публиковать сами-знаете-где.

    Рубятся в свежие игры не более 7 пользователей в день. Преодолеть магическую планку ни одна из них не в силах. Еще и черти смеются, — Слабо сделать игру, чтобы в неё залипло больше 7 человек? Скажем, тысяча?

    Пришлось постараться.

    Как пришла идея


    Не в ванной в этот раз, а в процессе разработки. Две новых игры было карточных, одна — трехмерный BlockOut II (ветераны помнят), еще три на кубиках (yatzee и doodo) и, внимание!, пара головомоек за битву смайликов против корона-вирусов.

    Последние две идеи родились благодаря анимированным стикерам, которые мне прислал Telegram-бот. В них смайлики прятались от забавных вирусов.

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

    Движок


    Уже года 3, как я подсел на Corona SDK. Я умудрялся писать на нем трехмерные игры и даже был в штате компании, однако в 2018 всех бездельников разогнали, а движок ушел в open-source. Напомню, что Corona SDK — это кросс-платформенный 45 (смотри значение в hex) движок под все мобилы, десктопы и ХТМЛ5.

    Взгляд внутрь
    Передаю приветы Северной Америке, Украине, Робу, Владу и спасибо за классный продукт. Коммичу ошибку: при билде на 11-ый iPhone из симулятора на телефон приложение не ставится. Приходится использовать обходной путь Xcode->Window->Devices->Drug and Drop.

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

    Арт


    Повторю, картинки взял из анимированных стикеров в Телеграмме — там они хранятся в tgs формате, в самом деле это переименованный gzip, он разархивируется в json-файл и выглядит примерно так:

    {"tgs":1,"v":"5.5.2","fr":60,"ip":0,"op":180,"w":512,"h":512,"nm":"05_STAYHOME_OUT","ddd":0,"assets":[{"id":"comp_0","layers":[{"ddd":0,"ind":1,"ty":3,"nm"

    Сгоряча, я стал было изобретать собственную программу для отрисовки json-а, но, обжёгшись, плюнул в интернет. И получил в ответ телеграмм-бота #GIF Export Bot, конвертирующего tgs-стикеры в GIF файл. Боже, какой я старый, помню GIF-а еще вот таким маленьким… Каждая GIF-ка хранит примерно 100-200 фреймов размером 512х512. На GIF-ку пускается GIMP.app, затем скейл, трансформ, ну вы знаете, главное не залипнуть на редактировании арта

    image

    Вот и один из отрицательных героев. Короновирус — отъе… ись (с) Слепаков.

    Гейм и физика


    В Corona SDK волшебный, невероятный box2D движок. Смотрите, как пишутся на нем игровое поле и ко-вирусы

     physics.start() 
     physics.setGravity( 0, 0 )
     physics.addBody( downBox, "static", {shape=rect }  )
     physics.addBody( cvirus, "dynamic", {shape=octogon,  density=2, 
              friction=1, bounce=0.2 } )
     physics.setGravity( 0, 9.8 )
    
    -- тут играем
    
     physics.stop() 
    
    -- а тут уже не играем
    
     

    В принципе, добавить что-либо к этому куску кода мне нечего — вы все поняли.

    Сама идея игры


    Помните хит Панда? Там (в поле силы тяжести) мишек-панд и мамочку разлучают цветные кубики. Их надо убить (кубики, а не мишек), но они противные множатся, как головы лирнейской гидры и не хотят соединения банды Панды.

    Банды панды
    Александр Сергеич рифму бы одобрил. Он тут в соседнем котле варится.

    Так вот, цветные кубики в этой игре расположены на равномерной ортогональной регулярной сетке. Лет пять назад Панда была бестселлером, даже я написал к ней ремейк Hex Rabbit. На такой же сетке, только шестиугольной и с такими же пандами, только кроликами. Я удивился, что кроликов до сих пор гоняют — семь (черт вас побери!) человек.

    А если проверить?
    Проверить мои слова вы не сможете, потому что Apple меня забанил на год.

    Hex-кроликов тогда нарисовал мне художник Андрюха Чесноков. Светлая ему память. Хотя он жив, слава Богу, идём дальше.

    Итак? Скажите, а зачем вообще сетка в этой панда-игре? Уберем её прочь, добавим физику. Злобные кубики заменим на восьмиугольные квадраты (да простит меня профессор Пономарев и аналитическая геометрия) и банду панд превратим в колобков.
    Ко-вирусы против ко-лобков.
    Неприлично, но смешно. Гоняю колобков вторую неделю, жду когда надоест. Спасибо самоизоляции, времени хватает.

    Дебаг физики


    Для отладки физики в Corona есть соответствующая функция, вот такая

     physics.setDrawMode( "debug" )  
     

    В этом режиме схематично изображены элементы физического мира

    image

    Кстати, здесь был косяк — стенки стакана изначально задавались не очень высокими (три экрана) и на некоторых уровнях колобки вываливались наружу. Я недоумевал — куда они подевались? Наверное, до сих пор летят к центру Земли… Хотя нет, скорее всего, уже долетели с учетом сопротивления воздуха K_{air} = 0.75.

    Выключаем debug-режим

    --  physics.setDrawMode( "debug" )  
     

    и получаем арт

    image

    Признаюсь вам, как художник художнику — анимацию спрайтов я ограничил двумя кадрами — добавил моргание героев, и хорош. Пусть Киса Воробьянинов рисует, я лично — пас.

    Редактор уровней


    Кнопочки на экране выбора уровней нарисовал сам, в коронавирус стиле.

    image

    Изначально определил 12 уровней, думая постепенно увеличивать на доске число ко-вирусов и кроликов, которые #онижеколобки.

    Рассуждал примерно так:
    На первых двух уровнях живут только 2 вида злодеев. Зеленые самые опасные (с) ДМБ.
    На следующих двух — посложнее, живут 3 вида злодеев.
    Затем — 4 вида.
    Пять — планету не спасти.
    Ш(Ж)есть — импосибль.

    С удовольствием бросился отлаживать игру и через 24 часа заметил скукоту на уровнях с 5-ю или 6-ю типами монстров. Сложно и думать надо.

    Что делать? Эх, взял я лыжи, вышел на улицу, вернулся, заменил лыжи на палки и побежал в лес. На 19-ой минуте бега включились мозги и я резко понял — добавлю горизонтальных палок в поле игры! По типу перегородок в open-space. Так-то open-space — редкая гадость по жизни, а в игре норм.

    Пошло дело — всё задышало, каждый уровень начал демонстрировать свой характер, пульс и температуру. Вы тоже проверьте! В смысле, температуру… Количество игровых полей в такой постановке, сами понимаете, бесконечно. Но счётно. Я ограничился дюжиной уровней и привлек сына-балбеса-третьеклассника к отладке, сунув ему в руку один из пятых iPhone. А он и счастлив, есть чем заняться на видео-уроке.

    Вуаля, уроки сделаны, уровни отлажены и именованы местами на планете Земля, где мне было хорошо.

    И хорошо бы эти места спасти. Сейчас не шутил.

    Еще геометрии


    Кто сказал, что герои игры — восьмиугольники? Я добавил квадраты, шестиугольники, круги, пентагоны. В честь профессоров Дубровского, Мищенко и Фоменко. Дифф. Геометрия вирусов заметно поменяла настроение игры. Сами посмотрите.

    image

    А можно колобков сделать квадратными. Русский народный колобок и не такое стерпит.

    image

    Ой! А куда делись вирусы? Убрал… Гугл со зверской серьезностью относится к словам вирусы, инфицированы, вылечи… Пришлось редактировать мета-данные, ко-вирусы стали цветными монстриками, все умерли стали попробуй еще и пр.

    Параллельно заменил Telegram-картинки. Эх, эх, прости Павел Дуров, ирония и смех не спасут мир.

    Ошибки, без них скучно


    Box2D иногда вылетал по ошибке в функции onGlobalCollision. Особенно на реальных устройствах, то есть айФонах. Смешно, но у меня ни одного Андроида в доме, а приложение в ГуглПлее…

    Код выглядит предельно простым, а ломается, как девочка.

    Отключаю звук — не ломается. Девочка, выходит, глухая?

    Смотрим код:

    
    local function onGlobalCollision( event )
      if ( event.phase == "began" ) then
    
        local ball = event.object1
        local u, v = ball:getLinearVelocity()
        local speed = u*u + v*v
    --    print( "speed: " .. speed  )
         if speed>10000 then
           if speed<20000 then
             audio.play( rockSound )
           else
             audio.play( woodenSound )
           end
        end
      end
    end
    

    Видите, в функции onGlobalCollision я ловлю столкновения колобков и проигрываю 2 типа звуков от соударений. Так вот, на некоторых уровнях синус равнялся двум сталкивались более 500 объектов одновременно (или одновременно? Гришковец). В этот редкий момент при вызове sounds внутри collision происходит страшный crash в runtime. <kznm! Не отключать же звук, верно?

    Как пофиксить? Решил не вызывать напрямую play(sound) из функции onGlobalCollision, а копить число соударений в переменных boom1 и boom2. Затем в цикле runTimeLoop (эта функция вызывается в игре 10 раз в секунду, не пойми зачем) проверять boom1 и boom2 и играть звук при необходимости.

           if boom1>0 then
              audio.play( rockSound )
              boom1=0
           end
    

    Ошибка изчезла, но звук стал неприятно ритмичным (оно и понятно, 10 раз в секунду)
    Тогда я просто ввел time-delay шум. Рандом-задержка на 100 миллисекунд — и оппа! все стало звучать очень натурально:

    
    local function play1()
      audio.play( rockSound )
    end
    
    local function play2()
      audio.play( woodenSound )
    end
    
    local function playSound()
      local t = math.random(100)
      if boom1>0 then
        boom1=0
        timer.performWithDelay(t, play1)
      end
      if boom2>0 then
        boom2=0
        timer.performWithDelay(t, play2)
      end
    end
      
    

    Красиво? Кто молодец? Знаю-знаю, вы сами бы так сделали.

    Google Play и YouTube


    Подготовил скриншоты и снял ролик для ю-тьюба, за роялем — автор. Выглядит тормознуто, но я такой по жизни, это уже не исправить:


    Видео записывал родной мак-программой QuickTime.app. Для Apple store полученное видео требуется отредактировать (установить frame-rate в 30). Для этого я обычно использую приложение handBrake.app, очень рекомендую.

    Сервер, без него никак


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

    Ладно, забудем обиду… На сервере я храню лучшие результаты дня и ранжирую игроков. Игроки, разумеется, хотят стать первыми и взять Кубок.

    Каждую полночь (по MSK timezone) таблица рекордов на сервере обнуляется и дневной Король превращается в ночную Тыкву. You turns Kings into bergers… Хорошая песня.

    Победитель дня уходит в зал Славы, наступает новая битва за следующий Кубок.

    Как сделал backend?

    Создал 4-е mysql таблицы: cvirus_users, cvirus_events, cvirus_today, cvirus_ticks.
    В таблице cvirus_ticks всего одна запись и два поля day и tick.

    UPD. Сейчас понял, что поле tick — лишнее.

    Каждую полночь запись в таблице сверяется с текущей датой и обновляется следующим образом.

        $new_day_flag = 0;
        $day = date("d");  // текущий день
        $result = mysql_query("SELECT * FROM cvirus_ticks LIMIT 1 ");
        if ($row = mysql_fetch_array($result)) {
          $d=$row['day'];
          if ($day<>$d) {
             $new_day_flag = $day;
          }
        } 
    
        if ($newday <> 0) {
          mysql_query( "UPDATE cvirus_ticks SET day = '$new_day_flag', tick = tick + 1 ");
        } 
    

    После срабатывания скрипта, все результаты игроков записываются в таблицу cvirus_today с обновленным тегом tick. Таким образом, в таблице cvirus_today хранятся рекорды игроков, сортированные по дням. Неудачники могут посмотреть, кто становился победителем в предыдущие дни.

    Любопытно наблюдать географию пользователей — для этого вытаскиваю из запроса игрока его ip-адрес, стравливаю адрес сервису api.wipmania.com, взамен получаю имя домена страны (например RU или UA) и показываю флаг RU.PNG или UA.PNG напротив фамилии игрока. В некоторых играх география пользователей впечатляет и наводит на размышления. Но мы не будем предаваться мечтам о путешествиях (какая нафик география в режиме самоизоляции?!), а посмотрим на php-пример, как превратить ip -> country

    $ipAddress = $_SERVER['REMOTE_ADDR'];
    $ipCode = file_get_contents('http://api.wipmania.com/' . $ipAddress . '?k=Е3g-Y6ХренВамКодQrmGQ7');
     if (strlen($ipCode)!=2) $ipCode = 'HN';  // да-да Гондурас)
    

    We don't allow apps that lack reasonable sensitivity towards or capitalize on a natural disaster, atrocity, conflict, death, or other tragic event.

    Это не я, это гугл-нло…

    Список использованных ссылок


    Тем временем в Google Play N-ый 12-ый день ждет проверки моя несчастная игра. Совсем одна — без хозяина, без рекламы, без поддержки. Но я не плачу. Я уже заменил в ней слова, картинки и звуки. И спасибо Гуглу, стало лучше, кроме того исправил пару неровностей.
    So, в итоге получилась игра в Google Play. Без рекламы и без цены. Я давно понял, что время казуальщиков-одиночек ушло.

    Тем не менее, напомни мне, сынок, ссылок на скачивание давать нельзя? А то превратишься в злобного Буратино, дважды подлеца ( подлец + подлец) и негодяя, верно?

    Ссылка на скачивание
    Ага! Купились?!

    Да, кто меня только не банил за неизвестно что — Google банил, Apple — банил, Habr — дважды банил… Во времена были.

    А я не унываю — сижу дома, починяю примус, ем гречку. Нафига столько купил?

    Html5 всех спасает


    -Позвольте, а как же hтml5 версия? Где хваленная кроссплатформенность?- воскликнет внимательный читатель.

    И будет прав, Github ему в руку.

    Напомню, что на Github-е у каждого пользователя есть возможность держать один бесплатный сайт — я потратил этот шанс для веб-версии игры.
    Предупреждаю, д-дизайн старый, нецензурированный. Нервных (и без юмора) прошу не запускать.

    Подскажите, умники
    Досадно, в remote web-версии (в отличие от локальной) почему-то невозможно сохранять рекорды и смотреть статистику по другим игрокам. Говорят, надо прописать в файле .htaccess атрибуты доступа Access-Control-Allow-Origin: papabubadiop.github.io, а у меня не получается. Возможно есть другое решение — подскажите, умники.

    Чуть не забыл! Была еще одна браузерная ошибка. При запуске игры в Сафари получал дюжину одинаковых сообщений/предупреждений

    libpng warning: iCCP: CRC error
    

    Ошибка не критическая (жмешь десять раз ОК и играешь), но неприятная. Интернет уверяет, что виноват редактор картинок GIMP. Он б-бесплатный и сохраняет PNG файлы с неправильной контрольной суммой цветовой палитры. Советы из сети не помогли,
    сними галку, конвертни
    — не, не работает.

    Исправил проблему тупо — открыл все PNG- файлы в приложении Preview (системное приложение Mac OS для просмотра и редактирования всех типов файлов), сделал два раза Flip и закрыл. Все картинки пересохранились в правильном формате.

    Возможно у кого-то из читателей могут вылезти другие баги, что ж, я не святой HelloWorld, кодю быстро и небрежно.
    Пишите про ошибки в комментариях, поржём.

    Эпилог


    Желаю вашим семьям здоровья. Тебе, читатель — терпения.

    P.S. Все опечатки — умышленные, не пишите в личку.
    AdBlock похитил этот баннер, но баннеры не зубы — отрастут

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

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

      0
      На превью столкнулся с возможной sql-inj через параметр $imei в прямом sql запросе
      UPDATE cvirus_users SET winners = winners+19, message='$google' WHERE imea='$imea'


        0
        Я уже 20 лет, как не php-шник. Нарисуйте как надо, с удовольствием поправлю древние скрипты.

        +1
        -Позвольте, а как же hml5 версия? Где хваленная кроссплатформенность?- воскликнет внимательный читатель.
        Пишите про ошибки в комментариях, поржём.
        Все опечатки — умышленные, не пишите в личку.
        Сами напросились. Я грамотный читатель и воскликну: «А как же html5 версия?»

        И да, ссылки на свои проекты выкладывать вроде можно (тем более, в технической статье). Особенно можно, когда комментаторы сами просят.
          0
          Какой наблюдательный!
          Как-то раз Достоевскому его почитатели указали на ошибку

          -Посреди комнаты стоял овальный стол круглой формы.

          Гений взглянул на текст, пожевал губами и сказал — Оставьте так…

          Я не Достоевский, исправил, спасибо!

          Blooms — color escape

          А ссылку на Github-проект легко получить, пройдя 49 уровень игры.
            0
            Я не Достоевский, исправил, спасибо!
            Ох уж эти ваши шуточки. Я же перепроверил.
          +1
          Раздражает задержка, когда я кликнул по колобкам, они испарились, надо доооооолго ждать пока появится новая порция снизу, и только потом кликать еще раз. Драйв теряется очень сильно.
            0
            Да, в новой версии это убрано (задержка в 1 секунду оставлена), в html версии осталось — игра ждет пока скорости колобков не упадут ниже определенного уровня.

            Короче, надо еще подумать
            0
            Странно как-то, если гугл так серьезно относится к таким словам, то как вообще какой-нибудь хитман или PlagueInc вышли.
              0
              Видимо, сейчас шутить над вирусом неуместно. Две игры завернули, где никакого криминала, кроме изображения вируса и слова самоизоляция. И предупредили — еще раз пошутишь — навечно забаним.

              Please note that additional suspensions of any nature may result in the termination of your developer account, and investigation and possible termination of related Google accounts.
              0
              А за что Apple забанил? Сам столкнулся с тем, что account is under investigation, хотя ничего такого не выкладывал
                0
                Они не говорят конкретно, только ссылаются на правила, в котором список преступлений от разрушения церквей до продажи наркотиков. Короче, где-то я часовню разрушил…
                0

                Ну и как? Получилось выложить? =)

                  +1
                  Грустно это всё :(
                  Подохли все ламповые игровые движочки, остались только Unity и Unreal.
                  Впрочем, не жалко. Их разработчики же понятия не имеют, как себя позиционировать.
                  Если невозможно конкурировать с Unity, то могли хотя бы нацелиться на instant-платформы.

                  Скоро выйдет W4 Game Game Creation Kit, это пример хорошего позиционирования — заточен под крохотный размер билда и instant-платформы.
                    0
                    С 1 мая Corona SDK делает ребрендинг. Новый движок чуть быстрее и OpenAL еще круче. И для iPhone вместо OpenGL — Metal.

                    Ждем Solar2d.
                      0
                      Дык а толку?
                      Если бы поддерживали все Instant-платформы, у движка могла бы быть своя ниша.

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

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