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

В этой статье будет рассказано про крестики-нолики на 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, или почти не трогая. Такой был мой план.Идея была хорошая, но в момент перевода под 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-в ряд. Некоторые реализации ИИ работали хорошо на большом поле, но легко проигрывали на маленьком поле, или наоборот.Так как каждая реализация ИИ требовала проверки не только на какую-то работоспособность, но и на возможность победить в игре, то сначала мне приходилось тестировать ИИ вручную - просто играть с ним. Но, это было слишком медленно, и оценка успеха была субъективна, поэтому я нашёл другой способ тестировать ИИ. ИИ был целиком реализован ввиде класса, поэтому была сделана отдельная программа по тестированию ИИ: запускались два разных класса ИИ, которые играли на виртуальном игровом поле, данные по результатам выводились в консоль (кто победил, за сколько ходов). Так же, если было нужно, выводились данные по каждому ходу в текстовом формате (игровая ситуация, что ИИ думает о выгодности каждой клетки).За эталон хорошо играющего противника был взять 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».
Однако, есть ещё объяснения этому: использовались 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, но мне от него большего и не нужно было. Достаточно простой, понятный редактор.Удачный дизайн получился не сразу.

Ещё картинки

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

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

Продвижение

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

Отзывы в Google Play

Говорят, что не нужно беспокоится про одиночные отзывы и оценки в Google Play. Это так - люди разные, разные мнения, кто-то ожидает совсем другое, чем вы думаете. Не читают описание игры, тем самым скачивая то, что кому-то не нужно, или пропускают интересную игру.
Однако, если несколько человек пишут, что «невозможно играть», а то и более подробно описывают «несуществующие» баги («крестик рисуется выше см на 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%. Поэтому можно сделать вывод, что обязательно должен быть английский язык; язык, которым вы хорошо владеете; далее языки по популярности, которые знают ваши знакомые.
На графике зелёным показаны русскоязычные пользователи, остальные две линии - англоязычные. Точка резкого подъёма русскоязычных пользователей - добавление «на двоих» в описание игры.Средняя статистика по использованию языка в разделе «Головоломки» (предоставляется в консоли разработчика Google Play):Как люди попадают на страницу с игрой, кто её скачивает и кто оставляет игру? Это покажет следующая картинка.
Видео
Так как некоторые минусующие писали комментарии примерно такого содержания «можно играть и на бумаге», то я сделал вывод, что многие, кто устанавливают игру не читают её описание. Картинки (скриншоты) плохо объясняют особенность этой игры - рисование. По этому я решил сделать видео. Не для того, чтобы привлечь новых игроков, а чтобы показать геймплей и отсеять тех, кому игра не понравится (чтобы не портили рейтинг игры).Как и предлагалось в документации к google play, я сделал видео, показывающее особенность игры. Старался сделать видео без особых спец эффектов, так как предполагал, что его не много будут смотреть. Но я в 1,5 минут так и не смог урезать 15 минут видео, захваченного с экрана с помощью ffmpeg. А зря.Результат видео был неоднозначный. Приблизительно 2 просмотра в день, 1 просмотр длился 48 секунд (недосматривают). Из этого можно сделать вывод, что с учётом времени, потраченного на перематывании видео лучшая длительность видео 1 минуты. Изменения колличества удалений/установок были незначительны.

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

Общая статистика за год: установок 5113, удалений 4017 (78%), осталось пользователей 1096. За первые 10 дней (первая версия): 56 загрузок, 22 удалений, 34 остались (+3,4 пользователя в день). Такая хорошая статистика из за того, что в первые недели игра обсуждалась на 4PDA.В разные периоды загрузки и удаления колебались равномерно пропорционально. Загрузок и удалений летом менее 10 в день, с января более 20 в день.Хабраэффект будет замерен и выложен. Предположительно будет резкое увеличение загрузок, затем такое же увеличение удалений.UPD: Статья через 2 дня собрала 13000 просмотров, далее расскажу про «хабраэффект».Если коротко: из всех 13000 просмотров заинтересовались игрой и скачали лишь менее 1% человек, зато добавили немало хороших отзывов к игре.Подробнее:Количество загрузок игры за два дня увеличилось на +180 (1,38% от просмотров статьи). В основном были устройства с Android 4.1 и 4.2. Удаления тоже выросли, их было по 50 в дни публикации статьи. В среднем, в эти дни удаляли 43% от всех установок (40% удалений читателей статьи, 70% от других пользователей).Количество пользователей игры за пару дней увеличилось с 1110 до 1230, прирост составил 120 (из них доля людей, читавших статью, оказалась 0,8% от просмотров статьи). Для сравнения, обычный рост за пару дней составляет 3-4 пользователя.Через пару дней после публикации статьи, все показатели стали приближаться к обычным.

Как читатели повлияли на загрузки, в графиках

Установки
Удаления
Прирост пользователей

Заметил, что если некоторых пользователей привлекать к продукту личным общением (ветки в форумах, статьи), то количество комментариев и оценок возрастает. За счёт публикации этой статьи появилось много отзывов в Google Play на игру, и отзывы были положительные, не смотря на критику реально существующих недостатков, которые нужно будет исправить.

Средняя оценка приложения выросла с 2,13 до 3,81.

Просмотров видео прибавилось всего на 7 в день, все просмотры с мобильных устройств. Как ни странно, больше просматривали страничку на моём сайте про игру, чем видео.На e-mail пришли первые два письма от тех, кто пробовал установить игру. До этого приходил только спам.

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

Среды разработки:
  • NetBeans
  • Eclipse (+Android SKD)
Изображения и видео:
  • GIMP - растровый графический редактор
  • Inkscape - векторный редактор
  • OpenShot - видеоредактор
  • ffmpeg - использовал для захвата видео с экрана
Разработка велась в Linux.Все эти программы бесплатны.

Затраты

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

В заключение

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

Теги: Добавить метки

Мы с вами установили IDE Eclipse и создали в ней свой первый проект. Но пришла пора заняться чем-то посерьезней! Поскольку обучение легче всего происходит в игровой форме, предлагаю вашему внимаю небольшую консольную игру. Крестики-нолики, так же известную как tic-tac-toe:-).

Теперь нам необходимо определиться с тем, как мы собственно будем писать нашу игру? Прежде всего нам нужно игровое поле. Пусть это будет двумерный символов размером 3×3 (почитать про разные ). Если в какой-то клетке игрового поля у нас ничего нет, то ставим туда например плюсик, если в ячейке нолик пусть это будет буква «О» если крестик то буква «Х». Начнем с задания самого поля:

// Игровое поле public static char field = { { "+", "+", "+" }, { "+", "+", "+" }, { "+", "+", "+" } };

Наше игоровое поле представляет собой двумерный символов Field, все значения в котором задаются при его создании. О том, что этот массив двумерный говорят две квадратные скобочки после описания типа char. При этом сначала у нас везде стоят плюсики — т.е. все поле свободно. Следующее, что мы сделаем — это организуем ход пользователя:

// Ход человека public static void HumanMove() throws IOException { int x , y; System.out.println("Enter y (1..3):"); BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); x = Integer.parseInt(br.readLine())-1; System.out.println("Enter x (1..3):"); y = Integer.parseInt(br.readLine())-1; while (field[x][y] == "0" || field[x][y] == "X" || x < 0 || x > 2 || y < 0 || y > 2) { System.out.println("Enter x:"); x = Integer.parseInt(br.readLine())-1; System.out.println("Enter y:"); y = Integer.parseInt(br.readLine())-1; } field[x][y] = "X"; }

Итак, что же здесь происходит? Первая строка — это описание матода, который реализует ход человека. О методах и их описании этом мы поговорим попозже. Во второй строке объявляются две переменные x и y, это будут координаты, куда поставит крестик игрок. Затем, с помощью конструкции System.out.println() мы выводим на экран приглашение ввести координату по оси Y. В следующей строке создается буфферизированный поток br с помошью которого можно считывать из консоли вводимые пользователем строки.

В строке 7 конструкция х=Integer.parseInt(String p) преобразует строковый параметр p в целочисленное значение и это значение мы записываем в переменную х.

Думаю две следующие строки, думаю, понятны по аналогии 🙂 А вот дальше мы проверяем в цикле с предусловием во-первых, не попал ли ход игрока на уже занятое поле, во-вторых, не выходят ли введенные пользователем цифры за границы нашего игрового поля. Если хоть одно из этих условий не выполняется, тогда мы заново просим ввести позицию Y и X. Ну а если все нормально, то цикл не выполнится и мы запишем на игровое поле ход игрока.

Теперь думаем что делать со вторым игроком. Поскольку заморачиваться на стратегии игры мне совсем не хотелось то бот ходит просто случайно. Алгоритм очень прост и код сильно напоминает предидущий кусок:

// Ход компьютера (random-бот) public static void CompMove() { int x = (int) (Math.random() * 3), y = (int) (Math.random() * 3); while (field[x][y] == "0" || field[x][y] == "X") { x = (int) (Math.random() * 3); y = (int) (Math.random() * 3); } field[x][y] = "0"; }

Здесь Math.random() генерирует случайное число в интервале от 0 до 1, затем полученное число мы умножаем на три и округляем до целого. В итоге получаем числа X и Y в итервале от нуля до двух включительно. Проверяем, может ли бот сделать ход на полученные координаты, и если не может, то точно так же генерируем два новых числа и проверяем их. Алгоритм прост и реализация, как мне кажется не должна вызывать вопросов.

Следующий метод определяет остались ли еще на игровом поле свободные клетки.

Public static boolean CanMove() { boolean p = false; for (int i = 0; i < 3; i++) { for (int j = 0; j < 3; j++) { if (field[i][j] == "+") { p = true; break; } } if (p = true) { break; } } return p; }

В переменной p изначально хранится значение false (ложь). То есть сначала считаем, что свободных клеток не осталось. А потом начинаем проверять, есть ли они на самом деле или их нет. Мы проходим по каждой клетке игрового поля (два вложенных друг в друга цикла for) и если () где-то встречаем хоть одну свободную клетку, то записываем в p значение true (истина) и прерываем циклы. Затем при любом раскладе мы возващаем значение p. Получается что если мы встретили хоть одно пустое поле, то мы вернем истину, а если свободных клеточек больше не осталось, то вернем ложь.

Следующий метод просто выводит на экран содержимое игрового поля:

// Вывод игрового поля на экран public static void PrintField() { for (int i = 0; i < 3; i++) { for (int j = 0; j < 3; j++) { System.out.print(field[i][j]); } System.out.println(); } }

godAlex 9 декабря 2013 в 23:09

Нетривиальная реализация крестиков-ноликов под 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, или почти не трогая. Такой был мой план.Идея была хорошая, но в момент перевода под 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-в ряд. Некоторые реализации ИИ работали хорошо на большом поле, но легко проигрывали на маленьком поле, или наоборот.Так как каждая реализация ИИ требовала проверки не только на какую-то работоспособность, но и на возможность победить в игре, то сначала мне приходилось тестировать ИИ вручную - просто играть с ним. Но, это было слишком медленно, и оценка успеха была субъективна, поэтому я нашёл другой способ тестировать ИИ. ИИ был целиком реализован ввиде класса, поэтому была сделана отдельная программа по тестированию ИИ: запускались два разных класса ИИ, которые играли на виртуальном игровом поле, данные по результатам выводились в консоль (кто победил, за сколько ходов). Так же, если было нужно, выводились данные по каждому ходу в текстовом формате (игровая ситуация, что ИИ думает о выгодности каждой клетки).За эталон хорошо играющего противника был взять 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».
Однако, есть ещё объяснения этому: использовались 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, но мне от него большего и не нужно было. Достаточно простой, понятный редактор.Удачный дизайн получился не сразу.

Ещё картинки

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

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

Продвижение

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

Отзывы в Google Play

Говорят, что не нужно беспокоится про одиночные отзывы и оценки в Google Play. Это так - люди разные, разные мнения, кто-то ожидает совсем другое, чем вы думаете. Не читают описание игры, тем самым скачивая то, что кому-то не нужно, или пропускают интересную игру.
Однако, если несколько человек пишут, что «невозможно играть», а то и более подробно описывают «несуществующие» баги («крестик рисуется выше см на 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%. Поэтому можно сделать вывод, что обязательно должен быть английский язык; язык, которым вы хорошо владеете; далее языки по популярности, которые знают ваши знакомые.
На графике зелёным показаны русскоязычные пользователи, остальные две линии - англоязычные. Точка резкого подъёма русскоязычных пользователей - добавление «на двоих» в описание игры.Средняя статистика по использованию языка в разделе «Головоломки» (предоставляется в консоли разработчика Google Play):Как люди попадают на страницу с игрой, кто её скачивает и кто оставляет игру? Это покажет следующая картинка.
Видео
Так как некоторые минусующие писали комментарии примерно такого содержания «можно играть и на бумаге», то я сделал вывод, что многие, кто устанавливают игру не читают её описание. Картинки (скриншоты) плохо объясняют особенность этой игры - рисование. По этому я решил сделать видео. Не для того, чтобы привлечь новых игроков, а чтобы показать геймплей и отсеять тех, кому игра не понравится (чтобы не портили рейтинг игры).Как и предлагалось в документации к google play, я сделал видео, показывающее особенность игры. Старался сделать видео без особых спец эффектов, так как предполагал, что его не много будут смотреть. Но я в 1,5 минут так и не смог урезать 15 минут видео, захваченного с экрана с помощью ffmpeg. А зря.Результат видео был неоднозначный. Приблизительно 2 просмотра в день, 1 просмотр длился 48 секунд (недосматривают). Из этого можно сделать вывод, что с учётом времени, потраченного на перематывании видео лучшая длительность видео 1 минуты. Изменения колличества удалений/установок были незначительны.

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

Общая статистика за год: установок 5113, удалений 4017 (78%), осталось пользователей 1096. За первые 10 дней (первая версия): 56 загрузок, 22 удалений, 34 остались (+3,4 пользователя в день). Такая хорошая статистика из за того, что в первые недели игра обсуждалась на 4PDA.В разные периоды загрузки и удаления колебались равномерно пропорционально. Загрузок и удалений летом менее 10 в день, с января более 20 в день.Хабраэффект будет замерен и выложен. Предположительно будет резкое увеличение загрузок, затем такое же увеличение удалений.UPD: Статья через 2 дня собрала 13000 просмотров, далее расскажу про «хабраэффект».Если коротко: из всех 13000 просмотров заинтересовались игрой и скачали лишь менее 1% человек, зато добавили немало хороших отзывов к игре.Подробнее:Количество загрузок игры за два дня увеличилось на +180 (1,38% от просмотров статьи). В основном были устройства с Android 4.1 и 4.2. Удаления тоже выросли, их было по 50 в дни публикации статьи. В среднем, в эти дни удаляли 43% от всех установок (40% удалений читателей статьи, 70% от других пользователей).Количество пользователей игры за пару дней увеличилось с 1110 до 1230, прирост составил 120 (из них доля людей, читавших статью, оказалась 0,8% от просмотров статьи). Для сравнения, обычный рост за пару дней составляет 3-4 пользователя.Через пару дней после публикации статьи, все показатели стали приближаться к обычным.

Как читатели повлияли на загрузки, в графиках

Установки
Удаления
Прирост пользователей

Заметил, что если некоторых пользователей привлекать к продукту личным общением (ветки в форумах, статьи), то количество комментариев и оценок возрастает. За счёт публикации этой статьи появилось много отзывов в Google Play на игру, и отзывы были положительные, не смотря на критику реально существующих недостатков, которые нужно будет исправить.

Средняя оценка приложения выросла с 2,13 до 3,81.

Просмотров видео прибавилось всего на 7 в день, все просмотры с мобильных устройств. Как ни странно, больше просматривали страничку на моём сайте про игру, чем видео.На e-mail пришли первые два письма от тех, кто пробовал установить игру. До этого приходил только спам.

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

Среды разработки:
  • NetBeans
  • Eclipse (+Android SKD)
Изображения и видео:
  • GIMP - растровый графический редактор
  • Inkscape - векторный редактор
  • OpenShot - видеоредактор
  • ffmpeg - использовал для захвата видео с экрана
Разработка велась в Linux.Все эти программы бесплатны.

Затраты

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

В заключение

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

Теги:

  • gamedev
  • google play
  • Java
  • инди
  • indy games
Добавить метки

Создаем сетевую многопользовательскую игру в "крестики-нолики", используя технологии PHP, XML и комплект инструментальных средств для разработки приложений на платформе Android

Сетевые многопользовательские "крестики-нолики"

Часто используемые сокращения
  • API: Application Programming Interface (прикладной интерфейс программирования)
  • HTTP: Hypertext Transfer Protocol (протокол передачи гипертекста)
  • IP: Internet protocol (Интернет-протокол)
  • SDK: Software Development Kit (комплект инструментальных средств для разработки программного обеспечения)
  • SQL: Structured Query Language (язык структурированных запросов)
  • UI: User Interface (пользовательский интерфейс)
  • XML: Extensible Markup Language (расширяемый язык разметки)

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

С точки зрения разработчика казуальные игры намного проще для разработки, чем графические "стрелялки" от первого лица или спортивные игры. Поэтому разработчику или группе разработчиков легче создать новую оригинальную игру.

В данной статье рассматриваются основы создания казуальной сетевой многопользовательской игры в “крестики-нолики”. Игровым сервером является Web-приложение с XML-интерфейсом, использующее MySQL и PHP. Клиентом является стандартное Android-приложение, работающее на Android-телефонах.

Создание серверной части

Серверная часть использует простую базу данных MySQL, имеющую две таблицы. В показана схема базы данных.

Листинг 1. db.sql
DROP TABLE IF EXISTS games; CREATE TABLE games(id INT NOT NULL AUTO_INCREMENT, primary key (id)); DROP TABLE IF EXISTS moves; CREATE TABLE moves(id INT NOT NULL AUTO_INCREMENT, game INT NOT NULL, x INT NOT NULL, y INT NOT NULL, color INT NOT NULL, primary key (id));

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

Вторая таблица moves содержит отдельные ходы игры и состоит из пяти столбцов. Первый столбец – это уникальный идентификатор хода. Второй столбец – идентификатор игры, которой принадлежит ход. Затем идут позиции x и y хода. Эти значения должны находиться в пределах между 0 и 2, поскольку используется игровое поле три на три. Последнее поле – это "цвет" хода, являющийся целым числом, указывающим X (крестик) или O (нолик).

Для создания базы данных прежде всего запустите программу mysqladmin и с помощью команды mysql выполните сценарий db.sql, как показано ниже:

% mysqladmin --user=root --password=foo create ttt % mysql --user=root --password=foo ttt < db.sql

Данное действие создает новую базу данных ttt, содержащую схему игры в "крестики-нолики".

После создания схемы необходимо создать способ запуска игры. Для этого используется сценарий под названием start.php, приведенный в .

Листинг 2. start.php
prepare($sql); $sth->execute(array()); $qid = $dd->lastInsertId(); $doc = new DOMDocument(); $r = $doc->createElement("game"); $r->setAttribute("id", $qid); $doc->appendChild($r); print $doc->saveXML(); ?>

Сценарий начинается с подключения к базе данных. Затем выполняется выражение INSERT для таблицы games и возвращается сгенерированный идентификатор. После этого создается XML-документ, в тег game добавляется идентификатор и выполняется экспорт XML.

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

$ php start.php $

Итак, у нас есть первая игра. Для просмотра списка игр используется сценарий games.php, приведенный в .

Листинг 3. games.php
prepare($sql); $q->execute(array()); $doc = new DOMDocument(); $r = $doc->createElement("games"); $doc->appendChild($r); foreach ($q->fetchAll() as $row) { $e = $doc->createElement("game"); $e->setAttribute("id", $row["id"]); $r->appendChild($e); } print $doc->saveXML(); ?>

Этот сценарий, как и сценарий start.php, начинается с подключения к базе данных. Затем запрашивается таблица games для просмотра доступных игр. После этого создается новый XML-документ, добавляется тег games, а потом теги game для каждой доступной игры.

После запуска сценария из командной строки должна отобразиться следующая информация:

$ php games.php $

Также этот сценарий можно запустить из Web-браузера.

Прекрасно! Теперь, имея интерфейс games, можно приступить к написанию серверного кода обработки ходов. Этот код начинается с создания вспомогательного сценария show_moves, получающего текущие ходы для данной игры и экспортирующего их в XML. В показан PHP-код для данной вспомогательной функции.

Листинг 4. show_moves.php
prepare($sql); $q->execute(array($game)); $doc = new DOMDocument(); $r = $doc->createElement("moves"); $doc->appendChild($r); foreach ($q->fetchAll() as $row) { $e = $doc->createElement("move"); $e->setAttribute("x", $row["x"]); $e->setAttribute("y", $row["y"]); $e->setAttribute("color", $row["color"]); $r->appendChild($e); } print $doc->saveXML(); } ?>

Сценарий принимает дескриптор базы данных и идентификатор игры. Затем он выполняет SQL-запрос для получения списка ходов. После этого он создает XML-документ с ходами для данной игры.

Эту вспомогательную функцию используют два сценария. Первый из них – это moves.php, возвращающий текущие ходы указанной игры. Этот сценарий приведен в .

Листинг 5. moves.php

Этот простой сценарий подключает код вспомогательной функции, соединяется с базой данных и активизирует функцию show_moves с указанным идентификатором игры. Чтобы протестировать этот код, примените команду curl для активизации сценария на сервере из командной строки:

$ curl "http://localhost/ttt/moves.php?game=1" $

Пока не сделано ни одного хода, поэтому ничего интересного не выводится. Чтобы исправить положение, к серверному интерфейсу нужно добавить последний сценарий. В приведен сценарий move.php.

Листинг 6. move.php
prepare($sql); $sth->execute(array($_REQUEST["game"], $_REQUEST["x"], $_REQUEST["y"])); $sql = "INSERT INTO moves VALUES (0, ?, ?, ?, ?)"; $sth = $dbh->prepare($sql); $sth->execute(array($_REQUEST["game"], $_REQUEST["x"], $_REQUEST["y"], $_REQUEST["color"])); show_moves($dbh, $_REQUEST["game"]); ?>

Этот сценарий начинается с подключения вспомогательной функции и соединения с базой данных. Затем выполняются два SQL-выражения. Первое удаляет все ходы, которые могут конфликтовать с ходами, отправляемыми во втором запросе. Второе SQL-выражение вставляет новую строку в таблицу moves для указанного хода. Затем клиенту возвращается список ходов. Это действие устраняет необходимость выполнения клиентом двух запросов при каждом ходе. Пропускная способность недешева, поэтому при любой возможности следует объединять запросы.

Для тестирования можно выполнить ход:

$ curl "http://localhost/ttt/move.php?game=1&x=1&y=2&color=1"

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

Создание Android-интерфейса

Прежде всего установите Android SDK, несколько версий платформы Android и, наконец, Eclipse и плагин Android Eclipse. К счастью, все это хорошо описано на сайте Android (ссылки приведены в разделе ). Подробное описание настройки среды разработки заняло бы целую статью и, вероятно, не одну.

После настройки среды разработки запустите Eclipse и начните новый Android-проект. На экране должна отобразиться информация, приведенная на .

Листинг 11. BoardView.java
package com.jherrington.tictactoe; import android.content.Context; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.Rect; import android.util.AttributeSet; import android.view.MotionEvent; import android.view.View; public class BoardView extends View { private int _color = 1; public void setColor(int c) { _color = c; } public BoardView(Context context) { super(context); GameService.getInstance().startGame(0); } public BoardView(Context context, AttributeSet attrs) { super(context,attrs); GameService.getInstance().startGame(0); } public BoardView(Context context, AttributeSet attrs, int defStyle) { super(context,attrs,defStyle); GameService.getInstance().startGame(0); } public boolean onTouchEvent(MotionEvent event) { if (event.getAction() != MotionEvent.ACTION_UP) return true; int offsetX = getOffsetX(); int offsetY = getOffsetY(); int lineSize = getLineSize(); for(int x = 0; x < 3; x++) { for(int y = 0; y < 3; y++) { Rect r = new Rect((offsetX + (x * lineSize)), (offsetY + (y * lineSize)), ((offsetX + (x * lineSize)) + lineSize), ((offsetY + (y * lineSize)) + lineSize)); if (r.contains((int)event.getX(), (int)event.getY())) { GameService.getInstance().setPosition(0, x, y, _color); invalidate(); return true; } } } return true; } private int getSize() { return (int) ((float) ((getWidth() < getHeight()) ? getWidth() : getHeight()) * 0.8); } private int getOffsetX() { return (getWidth() / 2) - (getSize() / 2); } private int getOffsetY() { return (getHeight() / 2) - (getSize() / 2); } private int getLineSize() { return (getSize() / 3); } protected void onDraw(Canvas canvas) { Paint paint = new Paint(); paint.setAntiAlias(true); paint.setColor(Color.BLACK); canvas.drawRect(0,0,canvas.getWidth(),canvas.getHeight(), paint); int size = getSize(); int offsetX = getOffsetX(); int offsetY = getOffsetY(); int lineSize = getLineSize(); paint.setColor(Color.DKGRAY); paint.setStrokeWidth(5); for(int col = 0; col < 2; col++) { int cx = offsetX + ((col + 1) * lineSize); canvas.drawLine(cx, offsetY, cx, offsetY + size, paint); } for(int row = 0; row < 2; row++) { int cy = offsetY + ((row + 1) * lineSize); canvas.drawLine(offsetX, cy, offsetX + size, cy, paint); } int inset = (int) ((float)lineSize * 0.1); paint.setColor(Color.WHITE); paint.setStyle(Paint.Style.STROKE); paint.setStrokeWidth(10); for(int x = 0; x < 3; x++) { for(int y = 0; y < 3; y++) { Rect r = new Rect((offsetX + (x * lineSize)) + inset, (offsetY + (y * lineSize)) + inset, ((offsetX + (x * lineSize)) + lineSize) - inset, ((offsetY + (y * lineSize)) + lineSize) - inset); if (GameService.getInstance().positions[ x ][ y ] == 1) { canvas.drawCircle((r.right + r.left) / 2, (r.bottom + r.top) / 2, (r.right - r.left) / 2, paint); } if (GameService.getInstance().positions[ x ][ y ] == 2) { canvas.drawLine(r.left, r.top, r.right, r.bottom, paint); canvas.drawLine(r.left, r.bottom, r.right, r.top, paint); } } } } }

Большую часть работы выполняет метод onTouch, который реагирует на нажатия пользователем конкретной клетки игрового поля, и метод onDraw, который рисует игровое поле, используя Android-механизм прорисовки.

Метод onTouch использует функции sizing для вычисления прямоугольника позиции каждой клетки. Затем он вызывает для прямоугольника метод contains, чтобы определить, нажал ли пользователь клетку. Если да, активизируется запрос сервиса game для выполнения хода.

Функция onDraw использует функции sizing как для прорисовки линий игрового поля, так и для прорисовки всех поставленных крестиков и ноликов. Синглтон GameServer используется для массива positions, в котором сохраняется текущее состояние каждого квадрата игрового поля.

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

Листинг 12. UpdateTimer.java
package com.jherrington.tictactoe; import java.util.TimerTask; public class UpdateTimer extends TimerTask { public BoardView boardView; @Override public void run() { GameService.getInstance().startGame(0); boardView.post(new Runnable(){ public void run(){ boardView.invalidate(); } }); } }

Таймер инициализируется классом TicTacToeActivity во время запуска приложения. Таймер реализует polling-механизм. Это не самый эффективный способ взаимодействия между клиентом и сервером, но он является самым простым и надежным. Наиболее эффективный способ – использовать версию 1.1 HTTP-протокола для поддержания соединения в активном состоянии и реализовать отправку сервером обновлений клиенту при совершении хода. Этот подход намного сложнее; он требует поддержки протокола 1.1 и клиентом и сервером и имеет ограничения на количество соединений. Рассмотрение этого подхода выходит за рамки данной статьи. Для простых демонстрационных игр, подобных нашей, отлично подходит polling-механизм.

После написания исходного кода можно протестировать приложение. Для этого нужно запустить эмулятор. После запуска на экране должна отобразиться информация, приведенная на рисунке 3.

Рисунок 3. Запуск Android-эмулятора
рисунке 5 .

В зависимости от состояния вашего сервера вы либо увидите ходы, либо нет. В нашем случае игра еще не начиналась. Кнопки Play X и Play O находятся над игровым полем, расположенном в центре экрана. Нажмите кнопку Play X , а затем центральный квадрат. Отобразится экран, аналогичный приведенному на .

Можно играть и крестиками, и ноликами. Приложение подключается к серверу для сохранения состояния игры в общедоступном месте. А благодаря таймеру update любой игрок может видеть ходы, сделанные другим игроком.

Заключение

Является ли эта игра законченной? Конечно же, нет. Отсутствует проверка условия победы, игроки могут повторно занимать одни и те же поля, отсутствует контроль очередности ходов. Но основные технологические блоки присутствуют: игровой сервер с доступным для всех игроков сохраненным состоянием игры и стандартное графическое приложение на мобильном устройстве, подключающееся к игровому серверу для предоставления интерфейса игры. Вы можете использовать эту игру в качестве отправной точки для своего собственного игрового приложения и сделать его таким, каким пожелаете. Просто помните, что игра должна быть интересной и увлекательной, и вы, возможно, создадите конкурента Words With Friends или многопользовательский вариант "Angry Birds".