Пишем игру "Жизнь" под Android используя C++ и Qt за час.

Всем привет! Недавно видел на пабликах типа ТП посты мол "делаем игру жизнь на java за час", "делаем игру жизнь на python за час", вот решил сделать пост, делаем игру жизнь под андроид используя C++ и библиотеки Qt за час.

Важно отметить что игра занимает ~100 строчек кода(исключая h файлы, символы переноса и т.п.), а также то, что созданная игра будет работать и под винду, и под андроид, и под линукс, и под ios и под mac os x. 

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

И так что у нас должно быть:

1. GUI. 

Поле с N*M клеток, кнопка "Новая игра", кнопка "Начать эволюцию". Поле забито прямогуольниками, изначально все пустые. При нажатии на клетку она окрашивается в черный или белый цвет, в зависимости от того какой цвет был там раньше.

2. Логика. Мы должны иметь двумерный массив(используем QT'шный вектор), в котором 0 отмечена мертвая клетка а единицей живая клетка. У меня была цель создать игру примерно за час, и поэтому этот 0 и 1 соответствуют черному и белому цветам многоугольников. Соответственно у нас есть массив с указателями на наши прямоугольники. Это не хорошо, ибо логика должна быть отделена от GUI, но для уменьшения размера кода - решил сделать так.

Должны быть реализованы функции подсчета соседей для каждой из клеток, зная положение клетки по X и Y. После каждого хода, мы рассчитываем количество соседей для каждой из клеток, и в зависимости от состояния клетки и количества её соседей добавляем клетку в массив(вектор), для изменения её состояния. Если за 1 ход не было добавлено ни 1 клетки, то игра заканчивается, выводим пользователю количество ходов


Если вы не знакомы с наследованием и библиотекой STD в C++, то вы являетесь новичком в C++ и возможно код покажется вам запутанным. К сожалению не хотелось бы писать про все это в этом посте, ибо для новичков информации в интернете - тонны, а переписывать еще раз это - просто не зачем. Если вы с этим знакомы, но незнакомы с сигнально-слотовой системой Qt тоже порекомендую прочитать over9000 материалов по этой теме. Если потребуются пояснения я отвечу в комментах.

И так, как выглядит GUI:

Пишем игру "Жизнь" под Android используя C++ и Qt за час. Программирование, C++, Курсы программирования, Qt, Длиннопост

Скачиваем Qt Creator с официального сайта Qt(qt.io), выбираем бесплатный вариант для GPL приложений. Запускаемся, создаем новый проект, используя QWidget, с графической формой

Пишем игру "Жизнь" под Android используя C++ и Qt за час. Программирование, C++, Курсы программирования, Qt, Длиннопост

Переходим к созданию. Переходим в режим дизайн и добавляем на форму "Graphics View" и 2 Push Button. Примерно вот так как на следующей картинке, и кликая правой кнопкой мыши по форме выбираем (Скомпоновать по сетке)

Пишем игру "Жизнь" под Android используя C++ и Qt за час. Программирование, C++, Курсы программирования, Qt, Длиннопост

GUI готово! Теперь нам нужно перейти к непосредственно программированию.

QGraphicsView - это виджет, который может отображать графические элементы. QGraphicsView отображает элементы, который расположены на QScene. На QScene мы размешаем элементы в декартовой системе координат, а с помощью QGrahicsView мы её отображаем(можно поворачивать, увеличивать масштаб и т.п.).

Элементы бывают разных типов. Есть простые - QGraphicsRectItem - это многоугольник, который к слову нам и понадобиться. Есть QGraphicsPixMapItem который отобразит нам изображение. Есть  QGraphicsLineItem - это линия. И другие. 


И так, первая задача. Разместить на сцене N*M QGraphicsRectItem'ов - наших клеток. Мертвой клеткой будет считаться клетка, с Kletka->brush().color() == Qt::white живой Kletka-> brush().color() == Qt::black. И запихнуть все указатели на QGraphicsRectItem в отдельный двумерный вектор, чтобы мы могли с ними работать.

Однако, для этой задачи нам не подойдет "чистый" QGraphicsRectItem. Нужно будет создать класс наследник, и в нем реализовать свою обработку нажатия на клетку. Ведь в самом начале игры нам нужно будет выставить первичные мертвые и живые клетки на поле.

Т.е. по нажатию на наш прямоугольник, он должен окрашиваться в требуемый цвет. А у чистого QGraphicsRectItem обработка данного события пустая.

Создадим класс LifeRect основанный на QGraphicsRectItrem., и переопределим в нем mousePressEvent

Пишем игру "Жизнь" под Android используя C++ и Qt за час. Программирование, C++, Курсы программирования, Qt, Длиннопост

Теперь займемся реализацией. Конструктор будет использовать конструктор с такими же параметрами как у QGraphicsRectItem(позиция по x, позиция по y, широта, высота, родитель). В событии mousePressEvent будет вызываться функция changeColor.

Реализация liferect.c

Пишем игру "Жизнь" под Android используя C++ и Qt за час. Программирование, C++, Курсы программирования, Qt, Длиннопост

И так, переходим дальше. Работаем с нашим виджетом(формой) -  widget.c. В этом классе реализуется логика игры.

Что нам необходимо?

1. Таймер. Каждые 0.1 секунды будет происходить 1 ход.

2. Слоты нажатия на кнопку "Старт" и "Новая игра" соответственно.

3. Размеры поля - X и Y клеток. Принимаются через конструктор.

4. Переменная для хранения количества итераций(для вывода при завершении игры)

5. Двумерный вектор(используется QVector, а не std::vector) который хранит указатели на созданные нами LifeRect'ы. К его элементам мы будем обращаться, и будем представлять живое и мертвое состояние по его цвету, как я уже и писал ранее. Если среди читателей будут новички C++ в комментах могу помочь. Просто представляйте что это массив.

6. Функция для новой игры(обнуление поля, обнуления количества итераций).

7. Функция подсчета живых соседей рядом.


И так, начнем. Собственно вот заголовочный файл:

Пишем игру "Жизнь" под Android используя C++ и Qt за час. Программирование, C++, Курсы программирования, Qt, Длиннопост

В конструкторе присвоим начальные данные(x,y из входных, 0 итераций, укажем то что сцена пока-что пустая, родительский элемент отсутствует. Проинициализируем таймер, но запускать его не будем

Пишем игру "Жизнь" под Android используя C++ и Qt за час. Программирование, C++, Курсы программирования, Qt, Длиннопост

Создать слот on_pushButton нужно нажав правой кнопкой по кнопке на форме, выбрать перейти к слоту, и выбрать сигнал clicked()

Пишем игру "Жизнь" под Android используя C++ и Qt за час. Программирование, C++, Курсы программирования, Qt, Длиннопост

Дальше - интереснее. И так, по нажатию на кнопку новая игра, мы должны нарисовать графическую сцену, с X*Y мертвыми многоугольниками, и наполнить двумерный вектор указателей на многоугольники ими же. Чтобы потом,когда игрок заполнит поле и нажмет на  кнопку "СТАРТ" запускается таймер, по каждому тику таймера обходить этот вектор, вычислить количество живых соседей каждой клетки, и перерисовать требуемые.

Постарался максимально прокомментировать код. 

Пишем игру "Жизнь" под Android используя C++ и Qt за час. Программирование, C++, Курсы программирования, Qt, Длиннопост

К слову говоря, позиции осей X и Y в графической сцене таковы:

Пишем игру "Жизнь" под Android используя C++ и Qt за час. Программирование, C++, Курсы программирования, Qt, Длиннопост

И так, мы прорисовали свои элементы, и уже можем потыкать и поменять их цвета, но пока-что не можем ничего запустить. Нужно реализовать функцию обработки каждого хода. И написать что по 2 кнопке мы запускаем таймер.

Пишем игру "Жизнь" под Android используя C++ и Qt за час. Программирование, C++, Курсы программирования, Qt, Длиннопост

Осталось лишь только реализовать функцию подсчета соседей. Думаю, в ней проблем не будет. Мы просто смотрим на цвета наших 8 соседей(если вылезли за границы то перемещаемся в начало или конец соответственно) и выдаем на выход их количество

Пишем игру "Жизнь" под Android используя C++ и Qt за час. Программирование, C++, Курсы программирования, Qt, Длиннопост

Все готово! Запускаем и играем на Linux, Windows или вашем Mac.

Что потребуется(если вкратце) для запуска на Android?

1. Пакеты Android SDK и Android NDK(для C++).

2. Устройство андроид или AVD.

3. Apache Ant.

Вы должны сконфигурировать ваш Qt Creator, в таком виде(ваши пути), и добавить в проектах сборку Android. Потребуется скачать в AVD драйвера USB, и API требуемых версий Android. Затем уже попробовать установить приложение на телефон/эмулятор. Я могу помочь, или привести ссылки где это подробно разбирается в комментах

Пишем игру "Жизнь" под Android используя C++ и Qt за час. Программирование, C++, Курсы программирования, Qt, Длиннопост

Пробуем на стареньком LG Optimus One с Android 2.3.3. - все работает как часы и на старых андроидфонах. К слову говоря можете попробывать как все выглядет в реальности(перед созданием) по ссылке - https://play.google.com/store/apps/details?id=com.mousemove.... (без рекламы и прочего, как собрал так и залил)

Пишем игру "Жизнь" под Android используя C++ и Qt за час. Программирование, C++, Курсы программирования, Qt, Длиннопост

Исходники - https://cloud.mail.ru/public/AsLq/4y4hufCQ7