Нетривиальная реализация крестиков-ноликов под Android
В этой статье будет рассказано про крестики-нолики на Java под Android за 30 строк кода пять с половиной тысяч строк кода, и зачем потребовалось так много кода на такую простую игру.Ровно год назад, 9 декабря я выложил свою игру на Google Play. Теперь я хочу рассказать про то, что произошло за этот год, как росла популярность игры, что сработало, а что — нет.Вы увидите чистую статистику загрузок и удалений за год существования игры, цифры. Узнаете, сколько пользователей можно собрать с Google Play, как создавалась игра, как тестировалась. Вы узнаете, возможно ли создать инди-игру с нулевым бюджетом, какие трудности могут возникнуть у небольшой игры в большом мире.
Введение
Я давно пробовал делать игрушки, в основном простые. После изучения Java решил попробовать сделать что-то полезное на этом языке. Узнав, что можно на Java под Android игры писать, я решил попробовать сделать что-то простое, чтобы научиться делать приложения под смартфоны.Идея
Теперь нужна идея, слишком простая, чтобы не было жалко времени в случае неудачи, и слишком уникальная, чтобы приложение не осталось просто очередным названием в многомиллионном списке приложений.Я выбрал крестики-нолики. Слишком просто? Таких игр много? Вот теперь нужно было придумать что-то уникальное. Почему сейчас все компьютерные крестики-нолики работают по принципу кликнул – поставил? Вот и решил я, что пусть фигуры рисуют, а игра сама определит, что нарисовано.Теперь, как сделать реализацию? Варианты:- Каждая клетка – растровое изображение. Недостаток: нельзя рисовать за границей, иначе выглядит криво, расход памяти. Достоинства: простота. Аналог такого уже есть в Интернете, но не для мобильного.
- Всё поле – растровое изображение. Недостаток: вот и ищи фигуры по всему изображению, наверное печатный текст проще распознать, чем кривые фигуры найти, проблема разъединения склеенных фигур, большая нагрузки на ЦП. Достоинство: можно рисовать крестик, наезжая на соседнюю клетку.
- Фигуры – векторные. Недостатки: возможно перерисовка долгая (но решить можно с помощью буферизации). Достоинства: можно разделять наехавшие друг на друга фигуры, легко можно изменить положение отдельных фигур.
Название игры и поиск похожих игр
Перед созданием игры я быстро поискал похожие игры. Под Android никаких подобных игр я не нашёл. Игру я думал назвать как «крестики-нолики» + рисование. Так как крестиков ноликов очень много под Android, я решил отложить этот трудный, короткий этап создания игры и придумать окончательное название после того, как будет готов рабочий прототип.После рабочего прототипа я начал сочинять название игры. Тогда, переведя «крестики-нолики» на английский, и приставив к ним слово «Draw» нашёл, пожалуй, единственного конкурента крестиков-ноликов с рисованием. Мне нужно было включить «рисование» в название игры. Подумав над словом Draw и необходимость включить в название игры слово о рисовании, было решено назвать игру «Tic-Tac-Toe Drawing!». Если вы скажете, что название очень созвучно с другими, то как тогда назвать игру в крестики нолики, если только платных крестиков-ноликов более 250 в GooglePlay, а бесплатных более 1000?Реализации
UPD:Особенности игры, которые требовалось реализовать:- Рисование фигур игроком, распознавание нарисованных фигур
- Разный размер игрового поля: от 3x3 до 12x12 (можно и больше, но ограничения накладывают размеры экрана многих устройств)
- Соответственно, игра не только 3 в ряд, но и 4, 5, 6 в ряд
Выбор среды разработки и общего плана создания
Пока я искал на чём писать под Android, скачивался Eclipse, изучалась документация, я решил начать на том что было установлено у меня — на NetBeans. Я подумал, что в андроиде Java такая же, как и простая Java, они должны быть почти одинаковы, значит логику (движок) можно писать отдельно.По моему плану игра должна была разделяться на два пакета: Engine и GUI. Engine — движок игры, игровая логика, этот пакет почти не зависит от реализации GUI и ОС, имеет интерфейс взаимодействия с GUI: приём входящих событий, и вывод. GUI — зависит от ОС. Для портирования на Андроид нужно заменить только GUI, не трогая Engine, или почти не трогая. Такой был мой план.Разработка игры
Цикл разработки программы
Чтобы точно знать, что делать, я решил записать по пунктам, что моя игра должна делать, что нужно реализовать. Сначала я взял листок, на котором записал основные разделы: Движок, ИИ, Графика, Баги и т. п., затем по каждой категории расписывал проблемы, которые нужно решить. По мере решения я отмечал галочками, что было сделано, по мере бездельниченья пополнял списки новыми идеями, по мере отладки увеличивал раздел «баги» и иногда другие разделы. Потом у меня закончился второй листок, но двух листов вполне хватило.Так как разработка и поддержка планировались не за пару дней, а на длительный период, для планирования и лучшей поддержки, использовались не только листы с пунктами, также было решено хорошо комментировать код (см Javadoc).Разработка игры была разделена на 2 этапа: сделать версию под PC, перенести на Android.Первая версия (альфа) была сделана под PC. Было реализовано по этапам:- простой GUI, рисование линий мышкой, клетки. Распознавание линий как крестиков и ноликов. (Раскрашивание фигур, в соответствии с распознанной фигурой было сделано для отладки распознавания фигур, затем, так как это получилось красиво, это было оставлено в игре)
- правила победы, игра человек с человеком.
- игровой ИИ, режим игры с компьютером.
- составление перечня основных требований
- программирование
- проверка результата и отладка. При выявлении ошибок, несовместимых с
жизньюработой программы, переход на пункт 1)
- составление перечня проблем
- программирование
- проверка результата и отладка. Теперь искалось что можно улучшить.
- Проверка игры: запуск на эмуляторе с разными размерами экрана, игра.
- Составление списка проблем
- Исправление проблем
Как ИИ писался
Разработка Бота (в игре для сокращения применялся термин ИИ) для простых крестиков ноликов выглядит несложной задачей. Но я хотел, чтобы этот ИИ был универсальным: работал на поле любого размера, и не только в игре 3 в ряд, но и n-в ряд. Некоторые реализации ИИ работали хорошо на большом поле, но легко проигрывали на маленьком поле, или наоборот.Так как каждая реализация ИИ требовала проверки не только на какую-то работоспособность, но и на возможность победить в игре, то сначала мне приходилось тестировать ИИ вручную — просто играть с ним. Но, это было слишком медленно, и оценка успеха была субъективна, поэтому я нашёл другой способ тестировать ИИ. ИИ был целиком реализован ввиде класса, поэтому была сделана отдельная программа по тестированию ИИ: запускались два разных класса ИИ, которые играли на виртуальном игровом поле, данные по результатам выводились в консоль (кто победил, за сколько ходов). Так же, если было нужно, выводились данные по каждому ходу в текстовом формате (игровая ситуация, что ИИ думает о выгодности каждой клетки).Код
Кратко про код игры на Java: всего чисто исходники занимают 275 кБ, из них 75% Engine. Код разбит на три пакета, использовано около 22 классов. Всего 5670 строк кода, но много комментариев, закомментированных частей и пустых строк, поэтому чистого кода около 4000, в среднем по 250 строк на 1 класс (файл). Основной движок писался за пару недель, можно было ускорить и до одной недели.При написании кода, опасные места, где могла бы возникнуть ошибка (например, конструкция получилась слишком запутанная), я помечал комментариями // FIXME и // TODO. Концентрация комментариев: один FIXME на 400 строк кода и около 1 TODO на 60 строк кода. Когда в процессе тестирования выявлялись недостатки я либо сразу понимал что исправить, либо ошибки находились именно в этих строках.Также, перед каждым релизом, я тестировал игру, играя по 5-10 раз с разными настройками.Релизы и переводы
Перевод приложений для 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) функции, опять, в эмуляторе вели себя как положено, а на реальном устройстве возвращали непонятные значения.
Графика
Для рисования графики использовались: GIMP — растровый редактор, и Inkscape — векторный редактор.На первый взгляд GIMP мне показался намного хуже фотошопа, некоторых функцй в нём нет (или я не нашёл). Сначала он кажется очень неудобным, и создаётся ощущение, что ты не сможешь на нём даже линию нарисовать. Но со временем привыкаешь, и понимаешь что это как впервые открыть фотошоп, когда до этого видел только Paint. В GIMP можно рисовать.Inkscape тоже проще CorelDraw, но мне от него большего и не нужно было. Достаточно простой, понятный редактор.Удачный дизайн получился не сразу.Статистика и маркетинг
Я не надеялся с помощью игры получить деньги, поэтому я ни где не рекламировал её. Так же, решил собрать реальную статистику в условии отсутствия рекламы, поэтому, иногда даже не продвигал её, но некоторые попытки продвижения всё-же были.Продвижение
В первую неделю я попробовал написать про игру на сайте 4PDA. Писал в некоторые ветки форума (прибавка к скачиванию +15 — +40), не прочитав их правила, за что был забанен на пару дней. Затем писал на ветки их форума для разработчиков, откуда получил ценные советы, пару лишних установок (+10), и несколько хороших отзывов в Google Play (+2). У них был проект по поддержке геймдевов из стран СНГ, он заключался в том, что пишешь обзор игры, а они у себя его бесплатно размещают, говорят сейчас они закрыли этот проект. Но меня почему-то не приняли в этот проект, то-ли им не понравился сайт с «.com», то-ли не нашли мой меил на сайте.Отзывы в Google Play
Говорят, что не нужно беспокоится про одиночные отзывы и оценки в Google Play. Это так — люди разные, разные мнения, кто-то ожидает совсем другое, чем вы думаете. Не читают описание игры, тем самым скачивая то, что кому-то не нужно, или пропускают интересную игру.Конкуренция и SEO
Конкуренция за первые позиции высока, особенно в таком жанре игр, как «крестики-нолики». Их тысячи, тысячи одинаковых крестиков-ноликов с почти одинаковыми названиями. Их так много, потому что их чуть сложнее написать, чем «Hello world». Понятно, что пользователи не будут утруждаться листать более чем заВидео
Так как некоторые минусующие писали комментарии примерно такого содержания «можно играть и на бумаге», то я сделал вывод, что многие, кто устанавливают игру не читают её описание. Картинки (скриншоты) плохо объясняют особенность этой игры — рисование. По этому я решил сделать видео. Не для того, чтобы привлечь новых игроков, а чтобы показать геймплей и отсеять тех, кому игра не понравится (чтобы не портили рейтинг игры).Как и предлагалось в документации к google play, я сделал видео, показывающее особенность игры. Старался сделать видео без особых спец эффектов, так как предполагал, что его не много будут смотреть. Но я в 1,5 минут так и не смог урезать 15 минут видео, захваченного с экрана с помощью ffmpeg. А зря.Результат видео был неоднозначный. Приблизительно 2 просмотра в день, 1 просмотр длился 48 секунд (недосматривают). Из этого можно сделать вывод, что с учётом времени, потраченного на перематывании видео лучшая длительность видео 1 минуты. Изменения колличества удалений/установок были незначительны.Статистика загрузок
Общая статистика за год: установок 5113, удалений 4017 (78%), осталось пользователей 1096. За первые 10 дней (первая версия): 56 загрузок, 22 удалений, 34 остались (+3,4 пользователя в день). Такая хорошая статистика из за того, что в первые недели игра обсуждалась на 4PDA.В разные периоды загрузки и удаления колебались равномерно пропорционально. Загрузок и удалений летом менее 10 в день, с января более 20 в день.Список программ, используемых для создания игры и других материалов
Среды разработки:- NetBeans
- Eclipse (+Android SKD)
- GIMP — растровый графический редактор
- Inkscape — векторный редактор
- OpenShot — видеоредактор
- ffmpeg — использовал для захвата видео с экрана