Нетривиальная реализация крестиков-ноликов под Android

в 19:09, , рубрики: Без рубрики

В этой статье будет рассказано про крестики-нолики на Java под Android за 30 строк кода пять с половиной тысяч строк кода, и зачем потребовалось так много кода на такую простую игру.
Ровно год назад, 9 декабря я выложил свою игру на Google Play. Теперь я хочу рассказать про то, что произошло за этот год, как росла популярность игры, что сработало, а что — нет.
Вы увидите чистую статистику загрузок и удалений за год существования игры, цифры. Узнаете, сколько пользователей можно собрать с Google Play, как создавалась игра, как тестировалась. Вы узнаете, возможно ли создать инди-игру с нулевым бюджетом, какие трудности могут возникнуть у небольшой игры в большом мире.
Нетривиальная реализация крестиков ноликов под Android
Кому интересно, добро пожаловать.

Введение

Я давно пробовал делать игрушки, в основном простые. После изучения Java решил попробовать сделать что-то полезное на этом языке. Узнав, что можно на Java под Android игры писать, я решил попробовать сделать что-то простое, чтобы научиться делать приложения под смартфоны.

Идея

Теперь нужна идея, слишком простая, чтобы не было жалко времени в случае неудачи, и слишком уникальная, чтобы приложение не осталось просто очередным названием в многомиллионном списке приложений.
Я выбрал крестики-нолики. Слишком просто? Таких игр много? Вот теперь нужно было придумать что-то уникальное. Почему сейчас все компьютерные крестики-нолики работают по принципу кликнул – поставил? Вот и решил я, что пусть фигуры рисуют, а игра сама определит, что нарисовано.
Теперь, как сделать реализацию? Варианты:

  • Каждая клетка – растровое изображение. Недостаток: нельзя рисовать за границей, иначе выглядит криво, расход памяти. Достоинства: простота. Аналог такого уже есть в Интернете, но не для мобильного.
  • Всё поле – растровое изображение. Недостаток: вот и ищи фигуры по всему изображению, наверное печатный текст проще распознать, чем кривые фигуры найти, проблема разъединения склеенных фигур, большая нагрузки на ЦП. Достоинство: можно рисовать крестик, наезжая на соседнюю клетку.
  • Фигуры – векторные. Недостатки: возможно перерисовка долгая (но решить можно с помощью буферизации). Достоинства: можно разделять наехавшие друг на друга фигуры, легко можно изменить положение отдельных фигур.

Погуглив немного, я подобного не нашёл.
Чем идея хороша: рисование фигур придаёт игре свободу выбора, игрок сам определяет форму фигуры. По этому идея игры предоставить свободу рисования, свободу «читерства» — зарисовывать фигуры противника, и свободу настроек и правил игры (размер поля, и т.д.).
Есть и недостатки: нарисовать крестик или нолик намного дольше, чем просто кликнуть по клетке. Поэтому не всем нравится эта игра.

Название игры и поиск похожих игр

Перед созданием игры я быстро поискал похожие игры. Под Android никаких подобных игр я не нашёл. Игру я думал назвать как «крестики-нолики» + рисование. Так как крестиков ноликов очень много под Android, я решил отложить этот трудный, короткий этап создания игры и придумать окончательное название после того, как будет готов рабочий прототип.
После рабочего прототипа я начал сочинять название игры. Тогда, переведя «крестики-нолики» на английский, и приставив к ним слово «Draw» нашёл, пожалуй, единственного конкурента крестиков-ноликов с рисованием. Мне нужно было включить «рисование» в название игры. Подумав над словом Draw и необходимость включить в название игры слово о рисовании, было решено назвать игру «Tic-Tac-Toe Drawing!». Если вы скажете, что название очень созвучно с другими, то как тогда назвать игру в крестики нолики, если только платных крестиков-ноликов более 250 в GooglePlay, а бесплатных более 1000?

Реализации

Выбор среды разработки и общего плана создания

Пока я искал на чём писать под Android, скачивался Eclipse, изучалась документация, я решил начать на том что было установлено у меня — на NetBeans. Я подумал, что в андройде Java такая же, как и простая Java, они должны быть почти одинаковы, значит логику (движок) можно писать отдельно.
По моему плану игра должна была разделяться на два пакета: Engine и GUI. Engine — движок игры, игровая логика, этот пакет почти не зависит от реализации GUI и ОС, имеет интерфейс взаимодействия с GUI: приём входящих событий, и вывод. GUI — зависит от ОС. Для портирования на Андройд нужно заменить только GUI, не трогая Engine, или почти не трогая. Такой был мой план.
Нетривиальная реализация крестиков ноликов под Android
Идея была хорошая, но в момент перевода под Android возникла проблема с адаптацией (описано дальше).

Разработка игры

Цикл разработки программы

Чтобы точно знать, что делать, я решил записать по пунктам, что моя игра должна делать, что нужно реализовать. Сначала я взял листок, на котором записал основные разделы: Движок, ИИ, Графика, Баги и т. п., затем по каждой категории расписывал проблемы, которые нужно решить. По мере решения я отмечал галочками, что было сделано, по мере бездельниченья пополнял списки новыми идеями, по мере отладки увеличивал раздел «баги» и иногда другие разделы. Потом у меня закончился второй листок, но двух листов вполне хватило.
Так как разработка и поддержка планировались не за пару дней, а на длительный период, для планирования и лучшей поддержки, использовались не только листы с пунктами, также было решено хорошо комментировать код (см Javadoc).

Разработка игры была разделена на 2 этапа: сделать версию под PC, перенести на Android.
Первая версия (альфа) была сделана под PC. Было реализовано по этапам:

  1. простой GUI, рисование линий мышкой, клетки. Распознавание линий как крестиков и ноликов. (Раскрашивание фигур, в соответствии с распознанной фигурой было сделано для отладки распознавания фигур, затем, так как это получилось красиво, это было оставлено в игре)
  2. правила победы, игра человек с человеком.
  3. игровой ИИ, режим игры с компьютером.

Жизненный цикл каждого из вышеперечисленных этапов:

  1. составление перечня основных требований
  2. программирование
  3. проверка результата и отладка. При выявлении ошибок, несовместимых с жизнью работой программы, переход на пункт 1)

На следующим этапе алгоритмы доводились до совершенства.
Цикл этих улучшений похож на вышеописанный:

  1. составление перечня проблем
  2. программирование
  3. проверка результата и отладка. Теперь искалось что можно улучшить.

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

На втором этапе игра была портирована в Eclipse, и началось создание GUI для Android.
Тут появились проблемы с совместимостью движка с Android. В обычном Java координаты холста (Graphics) — int, в Андройде — float. Так же отличались названиями многие функции рисования и классы (Canvas = Drawable, и т. д.), и структура данных для drawLine. Пришлось писать «конвертер форматов» (костыль) и заменять названия типов данных. Всё это успел сделать за час с лишним, так как в основном были отличия в именах, а не в логике.
Потом дальнейшее улучшения, рисование картинок.
После окончания интеграции движка и нового GUI, начались тестирования и отладки.
Жизненный цикл этих улучшений похож на вышеописанный:

  1. Проверка игры: запуск на эмуляторе с разными размерами экрана, игра.
  2. Составление списка проблем
  3. Исправление проблем

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

Как ИИ писался

Разработка Бота (в игре для сокращения применялся термин ИИ) для простых крестиков ноликов выглядит несложной задачей. Но я хотел, чтобы этот ИИ был универсальным: работал на поле любого размера, и не только в игре 3 в ряд, но и n-в ряд. Некоторые реализации ИИ работали хорошо на большом поле, но легко проигрывали на маленьком поле, или наоборот.
Так как каждая реализация ИИ требовала проверки не только на какую-то работоспособность, но и на возможность победить в игре, то сначала мне приходилось тестировать ИИ вручную — просто играть с ним. Но, это было слишком медленно, и оценка успеха была субъективна, поэтому я нашёл другой способ тестировать ИИ. ИИ был целиком реализован ввиде класса, поэтому была сделана отдельная программа по тестированию ИИ: запускались два разных класса ИИ, которые играли на виртуальном игровом поле, данные по результатам выводились в консоль (кто победил, за сколько ходов). Так же, если было нужно, выводились данные по каждому ходу в текстовом формате (игровая ситуация, что ИИ думает о выгодности каждой клетки).

Нетривиальная реализация крестиков ноликов под Android

За эталон хорошо играющего противника был взять open-source ИИ из игры 5 в ряд, которую нашёл на launchpad. Тесты проводились несколько раз, чтобы уменьшить влияние случайности.
Так было сделано 2 ИИ: нормальный и сложный. Они сильно отличались по реализации: сложный ИИ работал по принципу трассировок возможных путей, а средний по простому принципу: перебор клеток, куда поставить чтобы победить, или не проиграть, или хотя-бы близко что-то стояло, иначе random. По этому, пока я не оптимизировал алгоритм анализа победы (версия 1.5), сложный ИИ был во много раз быстрее, чем средний и лёгкий, в играх с полем больше чем 3x3. После того, как из среднего ИИ было выкинуто не лишнее, появился лёгкий уровень сложности.

Код

Кратко про код игры на Java: всего чисто исходники занимают 275 кБ, из них 75% Engine. Код разбит на три пакета, использовано около 22 классов. Всего 5670 строк кода, но много комментариев, закомментированных частей и пустых строк, поэтому чистого кода около 4000, в среднем по 250 строк на 1 класс (файл). Основной движок писался за пару недель, можно было ускорить и до одной недели.

При написании кода, опасные места, где могла бы возникнуть ошибка (например, конструкция получилась слишком запутанная), я помечал комментариями // FIXME и // TODO. Концентрация комментариев: один FIXME на 400 строк кода и около 1 TODO на 60 строк кода. Когда в процессе тестирования выявлялись недостатки я либо сразу понимал что исправить, либо ошибки находились именно в этих строках.
Также, перед каждым релизом, я тестировал игру, играя по 5-10 раз с разными настройками.

Спойлер про тест

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

В результате получилось надёжное приложение, о чём свидетельствует отчёт «Сбои и ANR».
Нетривиальная реализация крестиков ноликов под Android
Однако, есть ещё объяснения этому: использовались try — catch блоки, что не очень хорошо, но в некоторых случаях при сбое, приложение автоматом должно отключать «экспереминтальные» функции и переходить на их более старые версии; либо у Google эта страница не работает; или слишком мало людей используют игру.

Релизы и переводы

Перевод приложений для Android делается очень просто: все строки хранятся в strings.xml файле, который находится в папке values. По умолчанию язык Английский. Чтобы добавить Русский язык (и другие), нужно создать папку values-ru, и создать копию файла strings.xml, в котором будут находиться переведённые замены строк. Все строки в приложении получаются через API.
Для ускорения перевода можно пользоваться электронными переводчиками, например, я использовал Google Translator. До перевода игры я не был уверен в своём знании Английского языка, после перевода неуверенность в знании Английского перешла к Google Translator. При переводе, как минимум, пробуйте перевести полученные предложения на исходный язык. А лучше, дополнительно переведите по словам сами и сравните с тем, как перевёл переводчик. Были случаи, когда компьютерный переводчик слишком сильно искажал смысл и принимал некоторые слова, как другие по значению. Приложение было переведено на Английский и Русский язык. Про влияние переводов на загрузки я расскажу дальше.

Первый релиз игры был вечером 9 декабря 2012 года. Уже на следующий день в статистике я увидел, что в первый день было 1 скачивание.
Перед релизом я думал, какой номер дать первой версии: 0.99 или 1.0? Так как я решил, что на эмуляторе почти всё работало и я мог на нём играть, то я имею право дать номер 1.0. Я тогда сильно ошибался, т. к. многие пользователи жаловались, что они не могли играть в эту игру.

Пользователи жаловались на то, что у них были большие тормоза в игре, другие на неправильное отображение графики. И они были правы, на 90% реальных устройств играть было невозможно.
Проблема с тормозами была из-за того, что графика рисовалась посредством перерисовки всего холста Drawable, а на это тратилось очень много ресурсов. Пришлось быстро искать решение, и оно было найдено. Нет, не через OpenGL. Перерисовку можно осуществлять как всего холста ( repaint(), onPaint() ), так и фрагментами ( repaintCliping(), onPaint(rect) ). Пришлось учить игру определять нужные области перерисовки, и заранее планировать зоны перерисовки (объединять пересекающиеся зоны, разделять разнесённые зоны перерисовки). Все эти улучшения и советы некоторых людей были собраны в новой версии, которая вышла через неделю.

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

Тестирование релизов производились сначала только на эмуляторе. Настоящее устройство мне пригодилось только для двух целей: 1) осознать как криво рисуется, что эмулятор не показывал; 2) тестировать мультитач.
Пока я так долго разрабатывал и тестировал игру, я привык рисовать ровные фигуры, и не замечал, что не все игроки рисуют такие ровные фигуры фигуры, поэтому часто фигуры не распознавались. По этому, когда я дал поиграть знакомым, я удивился, что программа почти не понимала их рисунки. Пришлось улучшать распознавание ещё раз.

Так как список изменений есть на странице об игре, перечислю, какие основные проблемы решали обновления.
Версия 1.1: так как поверхность реализована в виде перегруженного View, и перерисовывался весь холст (Canvas), графика тормозила. Реализована поддержка PaintClipping(), что позволяло перерисовывать не весь экран, а только изменяющуюся часть, что значительно ускорило рисование.
Следующие обновления: устранил проблемы с анимацией перемещения, отключив её; дизайн; мультитач; рекорды. Первые оптимизации: заметил, что случайно закопипастил 2 раза отрисовку, рисовал по 2 раза одно и то же; оптимизация с помощью кэшей.

Ошибки компилятора? Они бывают?

Где-то я читал статью про то, что ошибки компилятора — на самом деле ошибки программиста. Но я столкнулся с реальными ошибками, конечно не самими ошибками компилятора, а ошибками SDK и плагинов:

  • Эмулятор эмулирует «идеальные» устройства. При тестировании графика в эмуляторе выглядела нормально. На некоторых устройствах тоже выглядела нормально. Но на большинстве реальных устройствах при анимации фигур графика выглядела ужасно: фигуры раздваивались и съезжались (предположительно причина в применении матриц трансформации Matrix, для Java это AffineTransform).
  • Ошибка компилятора? Не мало часов потратил на отладку вывода времени игры: время игры выводилось внутри кнопки, как её название, а не в текстовое поле. Решение: оказывается надо было перед компиляцией нажать кнопку «очистить проект» и тогда всё починилось (в официальной документации от Google рекомендовано жать эту кнопку каждый раз перед сборкой apk, но кто её читает?). Возможно это связано с обновлением SDK, но напутать имена ресурсов — это можно отнести к ошибкам компилятора.
  • Так же были ошибки API, некоторые устаревшие (deprecated) функции, опять, в эмуляторе вели себя как положено, а на реальном устройстве возвращали непонятные значения.

Отдельно хочу сказать про встроенный ProGuard. Вопреки мнению большинства, он работает, но с условиями: в пути к SDK, домашней папке, папке с проектом и его названием, не должно быть ни пробелов, ни кирилицы. Тогда он работает (не забудте раскоментировать нужные строки в project.properties).
Можно провести обфуксацию вручную (пришлось это делать пока не наладил ProGuard). Для этого сначала сделаем копию проекта. Затем идём в настройки проекта (куда дописать) и снимаем ненужные галочки в разделе «Java Compiler» в группе «Classfile Generation». Далее с помощью инструмента «Refractor>Move...» и «Refractor>Rename...» превращаем код во что-то непонятное. По мере этого процесса не забывать проверять остаточную работоспособность кода.

Графика

Для рисования графики использовались: GIMP — растровый редактор, и Inkscape — векторный редактор.
На первый взгляд GIMP мне показался намного хуже фотошопа, некоторых функцй в нём нет (или я не нашёл). Сначала он кажется очень неудобным, и создаётся ощущение, что ты не сможешь на нём даже линию нарисовать. Но со временем привыкаешь, и понимаешь что это как впервые открыть фотошоп, когда до этого видел только Paint. В GIMP можно рисовать.
Inkscape тоже проще CorelDraw, но мне от него большего и не нужно было. Достаточно простой, понятный редактор.
Удачный дизайн получился не сразу.
Нетривиальная реализация крестиков ноликов под Android

Ещё картинки

Нетривиальная реализация крестиков ноликов под Android
Нетривиальная реализация крестиков ноликов под Android

Статистика и маркетинг

Я не надеялся с помощью игры получить деньги, поэтому я ни где не рекламировал её. Так же, решил собрать реальную статистику в условии отсутствия рекламы, поэтому, иногда даже не продвигал её, но некоторые попытки продвижения всё-же были.

Продвижение

В первую неделю я попробовал написать про игру на сайте 4PDA. Писал в некоторые ветки форума (прибавка к скачиванию +15 — +40), не прочитав их правила, за что был забанен на пару дней. Затем писал на ветки их форума для разработчиков, откуда получил ценные советы, пару лишних установок (+10), и несколько хороших отзывов в Google Play (+2). У них был проект по поддержке геймдевов из стран СНГ, он заключался в том, что пишешь обзор игры, а они у себя его бесплатно размещают, говорят сейчас они закрыли этот проект. Но меня почему-то не приняли в этот проект, то-ли им не понравился сайт с «.com», то-ли не нашли мой меил на сайте.

Отзывы в Google Play

Говорят, что не нужно беспокоится про одиночные отзывы и оценки в Google Play. Это так — люди разные, разные мнения, кто-то ожидает совсем другое, чем вы думаете. Не читают описание игры, тем самым скачивая то, что кому-то не нужно, или пропускают интересную игру.
Нетривиальная реализация крестиков ноликов под Android
Однако, если несколько человек пишут, что «невозможно играть», а то и более подробно описывают «несуществующие» баги («крестик рисуется выше см на 2»), то это не просто так. Эмулятор может вас обманывать, ваше устройство тоже может действовать не так как другие. В этом случае не стоит пропускать подобные отзывы, а попробуйте выяснить что происходит на устройствах пользователей и разобраться с этим.
Хочу отметить, что за год, при более чем 5000 загрузках и 1000 пользователях на e-mail пришло всего 5 писем: 4 из них — спам (предлагали мне прорекламировать мою игру и накликать хороший рейтинг, за это они просили от 100$ до 60000$). И одно письмо от Opera Mobile Store (apps.opera.com), где сообщалось, что можно бесплатно размещать в их магазине приложения, но я решил пока остаться только на Google Play.

Конкуренция и SEO

Конкуренция за первые позиции высока, особенно в таком жанре игр, как «крестики-нолики». Их тысячи, тысячи одинаковых крестиков-ноликов с почти одинаковыми названиями. Их так много, потому что их чуть сложнее написать, чем «Hello world». Понятно, что пользователи не будут утруждаться листать более чем за двадцатую вторую страницу в поисках лучшей игры, а страниц всего более чем (3000/20=100...).
Конкуренция среди тысяч крестиков ноликов… Моя игра находится в первой двадцатке страниц (на 20 странице), это не так плохо, как у более чем 70% других конкурентов.
Стоит отметить, что как и в поисковиках, позиция игры определяется не только выбранной категории, но и в поиске по разным ключевым запросам. Так, по запросу «крестики-нолики» моя игра на 20 странице, а по запросу «крестики-нолики рисуй» на первой, среди десятка конкурентов, но мало кто так набирает. Однако, по запросу «крестики нолики на двоих», который появляется в подсказке при наборе словосочетания «крестики нолики» конкуренция всего среди двух страниц, и это набирают часто.
Результатом добавления ключевого слова «на двоих» в описание игры стало увеличение загрузки русскоязычных пользователей в 3,636 раза (на основе базиса загрузок англоязычной части пользователей).
Стоит отметить, что не нужно спамить всеми возможными ключевыми словами, так как если вы в напишите то, что игроки ожидают увидеть, но не увидят в игре, то они не просто удалят игру, ещё они добавят плохой отзыв.

Как некоторые, возможно догадались, раз на поиск так влияет конкретные словосочетания, то на разных языках крестики-нолики будут называться по разному. Возникает вопрос: на сколько языков нужно перевести приложение? Основное количество пользователей установило игру со следующими языками: русский (58%), английский (США 20% + Великобритания 11% + другие английские 2% = 33% ), испанский (3%), остальные 6%. Поэтому можно сделать вывод, что обязательно должен быть английский язык; язык, которым вы хорошо владеете; далее языки по популярности, которые знают ваши знакомые.

Нетривиальная реализация крестиков ноликов под Android
На графике зелёным показаны русскоязычные пользователи, остальные две линии — англоязычные. Точка резкого подъёма русскоязычных пользователей — добавление «на двоих» в описание игры.

Средняя статистика по использованию языка в разделе «Головоломки» (предоставляется в консоли разработчика Google Play):
Нетривиальная реализация крестиков ноликов под Android

Видео

Так как некоторые минусующие писали комментарии примерно такого содержания «можно играть и на бумаге», то я сделал вывод, что многие, кто устанавливают игру не читают её описание. Картинки (скриншоты) плохо объясняют особенность этой игры — рисование. По этому я решил сделать видео. Не для того, чтобы привлечь новых игроков, а чтобы показать геймплей и отсеять тех, кому игра не понравится (чтобы не портили рейтинг игры).
Как и предлагалось в документации к google play, я сделал видео, показывающее особенность игры. Старался сделать видео без особых спец эффектов, так как предполагал, что его не много будут смотреть. Но я в 1,5 минут так и не смог урезать 15 минут видео, захваченного с экрана с помощью ffmpeg. А зря.
Результат видео был неоднозначный. Приблизительно 2 просмотра в день, 1 просмотр длился 48 секунд (недосматривают). Из этого можно сделать вывод, что с учётом времени, потраченного на перематывании видео лучшая длительность видео 1 минуты. Изменения колличества удалений/установок были незначительны.

Статистика загрузок

Общая статистика за год: установок 5113, удалений 4017 (78%), осталось пользователей 1096.
За первые 10 дней (первая версия): 56 загрузок, 22 удалений, 34 остались (+3,4 пользователя в день). Такая хорошая статистика из за того, что в первые недели игра обсуждалась на 4PDA.
В разные периоды загрузки и удаления колебались равномерно пропорционально. Загрузок и удалений летом менее 10 в день, с января более 20 в день.

Нетривиальная реализация крестиков ноликов под Android
Нетривиальная реализация крестиков ноликов под Android

Хабраэффект будет замерен и выложен. Предположительно будет резкое увеличение загрузок, затем такое же увеличение удалений.

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

Среды разработки:

  • NetBeans
  • Eclipse (+Android SKD)

Изображения и видео:

  • GIMP — растровый графический редактор
  • Inkscape — векторный редактор
  • OpenShot — видеоредактор
  • ffmpeg — использовал для захвата видео с экрана

Все эти программы бесплатны.

Затраты

0 (ноль) за разработку, графику, софт.
Более ~$30 за поддержку сайта, банковские операции, и оплату аккаунта разработчика GooglePlay.
Что получил: 0 рублей, опыт разработки приложения для Android, статистику по установкам игры на Google Play, хорошую статью.

В заключение

Можно, имея компьютер, 30$ и время сделать простую и хорошую игру. Для хорошей игры нужен и геймплей (идея) и красивая реализация (графика) одновременно. Чтобы заработать получить много пользователей, нужно сделать то, что нужно пользователям, пусть они даже не подозревают о существовании этого, и то, чего ещё нет, так же делать в менее конкурентной сфере, чем тысячи крестиков-ноликов.
Сейчас игра бесплатна, без рекламы, не требует никаких дополнительных разрешений (например сбор личных данных, отправка платных смс, и т.п.).
Нетривиальная реализация крестиков ноликов под Android
(кликабельно, на страничку игры на Google Play)

Автор: godAlex

Источник


* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js