Привет, Хабр. Эта статья должна была быть написана еще зимой, но по объективным причинам пишу ее сейчас.
Решил я как-то год назад заняться ГеймДевом. Поздновато, конечно, но история не об этом. Сразу решил, что надо писать для мобильных устройств. Направление перспективное и для меня оказалось очень интересным.
Поскольку я программлю на С++, значит, надо подобрать кроссплатформенную среду разработки. Выбор пал на Qt. Сейчас точно не скажу, почему именно она, но выбор был сделан, да и история не об этом.
Достал книгу на русском, Внимательно прочитал половину, сделал детскую игру «Быки и Коровы». Здесь не о чем говорить, это был тестовый проект.
Почитал еще, обнаружил, что у Qt очень хорошая документация на родном сайте и в ней легко найти описание необходимых объектов. И это не может радовать.
Теперь стал вопрос о том, чтобы сделать проект немного посерьёзней. Везде советуют начинающим браться сначала за что-то простое, так сказать попробовать, научиться на своих ошибках и т.д. Я уже начал подготавливаться, чтобы написать старую и всем знакомую Lines, но вот тут стала необходимость для одного проекта написать что-нибудь на Android. И так родилась идея написать платформер. И вот, собственно говоря, история как раз об этом.
Решено было писать на С++, хотелось посмотреть, насколько адекватно оно будет потом работать на Android.
И сразу определился, что писать буду с использованием графической библиотеки Qt, потому что по ней достаточно много хорошего мануала, как говорилось раньше. Я не собираюсь описывать процесс создания платформера, этого добра в мировой сети достаточно на разных языках.
Начал писать это чудо в конце сентября на Qt 5.2, знаю, что уже была 5.3, но лень было заниматься обновлением. В начале ноября всё уже было готово, но по определённым причинам, мероприятие, для которого игра писалась, не состоялось и было перенесено на весну. Выкладывать для мира проект раньше времени нельзя. Было достаточно времени поработать над мелкими доработками и перенести её в Qt 5.4.
Кратко процесс реализации описал, теперь, собственно говоря, с какими проблемами я столкнулся во время разработки и переходом от одной версии к другой.
Графическая библиотека:
Работа со звуком
Тачскрин
Что еще
Еще добавил перевод на английский, но там проблем не возникло, и инфы по интернационализации приложений в Qt достаточно.
Итоги
Игра была написана и успешно запущенна в проекте. Свои первоначальные предназначения она выполнила — мне набраться опыта в геймдеве и стать одним из заданий в городском квесте. Теперь она будет выполнять своё второстепенное предназначение — быть частью моего портфолио.
Спасибо Вам, за то, что Вы это читали.
Решил я как-то год назад заняться ГеймДевом. Поздновато, конечно, но история не об этом. Сразу решил, что надо писать для мобильных устройств. Направление перспективное и для меня оказалось очень интересным.
Поскольку я программлю на С++, значит, надо подобрать кроссплатформенную среду разработки. Выбор пал на Qt. Сейчас точно не скажу, почему именно она, но выбор был сделан, да и история не об этом.
Достал книгу на русском, Внимательно прочитал половину, сделал детскую игру «Быки и Коровы». Здесь не о чем говорить, это был тестовый проект.
Почитал еще, обнаружил, что у Qt очень хорошая документация на родном сайте и в ней легко найти описание необходимых объектов. И это не может радовать.
Теперь стал вопрос о том, чтобы сделать проект немного посерьёзней. Везде советуют начинающим браться сначала за что-то простое, так сказать попробовать, научиться на своих ошибках и т.д. Я уже начал подготавливаться, чтобы написать старую и всем знакомую Lines, но вот тут стала необходимость для одного проекта написать что-нибудь на Android. И так родилась идея написать платформер. И вот, собственно говоря, история как раз об этом.
Решено было писать на С++, хотелось посмотреть, насколько адекватно оно будет потом работать на Android.
И сразу определился, что писать буду с использованием графической библиотеки Qt, потому что по ней достаточно много хорошего мануала, как говорилось раньше. Я не собираюсь описывать процесс создания платформера, этого добра в мировой сети достаточно на разных языках.
Начал писать это чудо в конце сентября на Qt 5.2, знаю, что уже была 5.3, но лень было заниматься обновлением. В начале ноября всё уже было готово, но по определённым причинам, мероприятие, для которого игра писалась, не состоялось и было перенесено на весну. Выкладывать для мира проект раньше времени нельзя. Было достаточно времени поработать над мелкими доработками и перенести её в Qt 5.4.
Кратко процесс реализации описал, теперь, собственно говоря, с какими проблемами я столкнулся во время разработки и переходом от одной версии к другой.
Графическая библиотека:
Графическая библиотека
Начнём с косяков.
Нативная библиотека Qt кушает очень много памяти. Пользовался компонентом QImage. При загрузке в память картинки размером 780 байт объём используемой памяти увеличивался не на пару килобайт, а на пару десятков, а иногда и сотен килобайт. Данный факт меня не очень радовал, а иногда раздражал.
Чтобы как-то уменьшить объемы используемой памяти, все графические элементы поместил в один файл. Но это было связанно еще и с удобством использования. Объем используемой памяти сразу уменьшился на 10М.
Второе, что бросилось в глаза, Это насколько сильно грузится процессор. Не 100%, но более 50%. Может, для игр это и нормально, но дальше было интересней.
При выходе Qt 5.4 компания-разработчик утверждала, что они улучшили графическую библиотеку.
Я подтверждаю, что-то они улучшили :). При переносе на 5.4 стало еще меньше использоваться оперативной памяти и процессор уже загружен был менее, чем на 50%. Эти все параметры смотрел в Windows, как дела обстоят на андроиде — не в курсе.
В процессе обнаружился еще один интересный факт с Зумом.
Стандартный размер экрана для моей игры — 540х960. Он единственный. При уменьшении картинки проблем никаких не возникало, даже начинало работать чуть пошустрее. А вот при увеличении были выявлены некоторые проблемы.
Во-первых — гладкого масштабирования не получилось, процесс отрисовки сразу увеличивался в 20-30 раз. Но от этого можно и отказаться, не такой уж это серьёзный проект, где надо много внимания уделять графике. Но для себя отметил, что надо готовить картинки для разных разрешений экрана.
Вторая проблема заключалась в следующем. Как ранее говорилось, все графические элементы были помещены в один файл. Надо было просто указать область, которую перерисовать на игровой экран. При увеличении данной области почему-то захватывался на 1 пиксель больше указанного.
Решением проблемы является то, что между элементами надо оставлять немного свободного места в 1-2 пикселя. Или сами элементы делать чуть меньшего размера, чем сама область. У меня получился 2-й вариант.
И последнее, что мне не понравилось. Это отсутствия возможности работать с картинками с маленькой цветовой гаммой (16 и 256). В QIMage реализовано якобы 256 бит, но каждый бит надо прописать заранее.
В остальном библиотека меня порадовала. Удобно с ней работать, поддерживает разные форматы картинок, можно сохранить полученное изображение на диск. Разнообразный размер инструментов
Нативная библиотека Qt кушает очень много памяти. Пользовался компонентом QImage. При загрузке в память картинки размером 780 байт объём используемой памяти увеличивался не на пару килобайт, а на пару десятков, а иногда и сотен килобайт. Данный факт меня не очень радовал, а иногда раздражал.
Чтобы как-то уменьшить объемы используемой памяти, все графические элементы поместил в один файл. Но это было связанно еще и с удобством использования. Объем используемой памяти сразу уменьшился на 10М.
Второе, что бросилось в глаза, Это насколько сильно грузится процессор. Не 100%, но более 50%. Может, для игр это и нормально, но дальше было интересней.
При выходе Qt 5.4 компания-разработчик утверждала, что они улучшили графическую библиотеку.
Я подтверждаю, что-то они улучшили :). При переносе на 5.4 стало еще меньше использоваться оперативной памяти и процессор уже загружен был менее, чем на 50%. Эти все параметры смотрел в Windows, как дела обстоят на андроиде — не в курсе.
В процессе обнаружился еще один интересный факт с Зумом.
Стандартный размер экрана для моей игры — 540х960. Он единственный. При уменьшении картинки проблем никаких не возникало, даже начинало работать чуть пошустрее. А вот при увеличении были выявлены некоторые проблемы.
Во-первых — гладкого масштабирования не получилось, процесс отрисовки сразу увеличивался в 20-30 раз. Но от этого можно и отказаться, не такой уж это серьёзный проект, где надо много внимания уделять графике. Но для себя отметил, что надо готовить картинки для разных разрешений экрана.
Вторая проблема заключалась в следующем. Как ранее говорилось, все графические элементы были помещены в один файл. Надо было просто указать область, которую перерисовать на игровой экран. При увеличении данной области почему-то захватывался на 1 пиксель больше указанного.
Решением проблемы является то, что между элементами надо оставлять немного свободного места в 1-2 пикселя. Или сами элементы делать чуть меньшего размера, чем сама область. У меня получился 2-й вариант.
И последнее, что мне не понравилось. Это отсутствия возможности работать с картинками с маленькой цветовой гаммой (16 и 256). В QIMage реализовано якобы 256 бит, но каждый бит надо прописать заранее.
В остальном библиотека меня порадовала. Удобно с ней работать, поддерживает разные форматы картинок, можно сохранить полученное изображение на диск. Разнообразный размер инструментов
Работа со звуком
Работа со звуком
В книге описана работа с мультимедиа для версии 4.8. Но с тех пор всё поменялось. И компоненты, и работа с ними. Немного гугления и чтения документации привели к успеху. В итоге оказалось всё не так сложно. Описывать подробно не стану, на сегодня этой инфы достаточно на форумах и документация есть. Но если кому лень разбираться, пишите, отвечу.
Собственно говоря, с какими проблемами столкнулся. Файлы WAV больших размеров не терпят. Больших — имеется в виду дольше 3-х сек. MP3-файлы тоже не особо жалует. Более 5MB не получилось подключить. И не хотело собираться, когда итоговый размер медиа был более 10MB. Может, это ограничение как-то и регулируется, но я не нашел, как. Я уменьшил качество аудио MP3 файлов и всё пошло.
В остальном компоненты по управлению звуками работали достойно. Можно управлять уровнем громкости, MUTE'ировать звук без остановки аудио. Пускать музыку/звуки на фоне, не останавливая основной поток воспроизведения. Только если вы запустили в отдельном потоке звук, то он будет звучать, пока не закончится, и остановить принудительно его нельзя.
Еще столкнулся с проблемой в 5.2 — не работали внешние кнопки регуляции уровня звука. Пришлось допиливать Activity. О том, как я это делал, на Хабре статью написал. В версии 5.4 уже всё работает, ничего допиливать не надо.
Собственно говоря, с какими проблемами столкнулся. Файлы WAV больших размеров не терпят. Больших — имеется в виду дольше 3-х сек. MP3-файлы тоже не особо жалует. Более 5MB не получилось подключить. И не хотело собираться, когда итоговый размер медиа был более 10MB. Может, это ограничение как-то и регулируется, но я не нашел, как. Я уменьшил качество аудио MP3 файлов и всё пошло.
В остальном компоненты по управлению звуками работали достойно. Можно управлять уровнем громкости, MUTE'ировать звук без остановки аудио. Пускать музыку/звуки на фоне, не останавливая основной поток воспроизведения. Только если вы запустили в отдельном потоке звук, то он будет звучать, пока не закончится, и остановить принудительно его нельзя.
Еще столкнулся с проблемой в 5.2 — не работали внешние кнопки регуляции уровня звука. Пришлось допиливать Activity. О том, как я это делал, на Хабре статью написал. В версии 5.4 уже всё работает, ничего допиливать не надо.
Тачскрин
Тачскрин
Вот тут у меня самое большое недоразумение случилось. А суть заключается в следующем.
В версии 5.2 я изучил подробно работу с событиями тачскрина. Есть контейнер, который хранит координаты и состояние всех «пальцев» на экране. Есть события, которые реагируют на первое прикосновение (первый «палец»), событие на изменение (передвижение «пальцев» или изменение их количества на экране), и событие, которое сообщает о завершении тача (когда все «пальцы» убираются с экрана).
По сути, если у Вас в приложении будет использоваться мультитач, то события мышки можно даже и не обрабатывать. Всё прекрасно работает на событиях Тачскрина. Но по определённым надобностям мне пришлось обрабатывать и события мышки. Впоследствии это мне и помогло разобраться в проблемах.
Всё изменилось с версией 5.4. Когда я собрал на новом Qt проект и сел протестировать новый функционал, каково было моё удивление, когда обнаружил, что управление полетело. А произошло следующее.
Зачем-то разработчики Q, поменяли логику работы событий тачскрина и мышки! Теперь это работает как-то по-чудному. При первом касании «пальца», генерируются события MouseButtonPress и TouchBegin. Но потом, когда генерируется событие MouseMove а TouchUpdate не генерируется. Но в контейнере тача точка остаётся, но координата и состояние ее не меняется. И когда генерируется событие MouseButtonRelease, событие TouchEnd не генерируется и получается, что виртуально у вас всегда на экране «висит» один «палец».
Эта проблема решается следующим образом: при событии TouchBegin копируете контейнер состояния в свой, а при MouseButtonRelease очищаете его до следующего TouchBegin.
После того как на экране появляется второй «палец», генерация событий мышки отключается. И после этого, сколько бы вы пальцев ни ставили на экран или сколько бы ни оставляли, всегда генерируются события TouchUpdate. Когда все «пальцы» убираются с экрана, генерируется событие TouchEnd, а MouseButtonRelease нет. Проблем можно избежать, отреагировав на событие TouchEnd так же, как и на MouseButtonRelease. Но дело в том, что при следующей генерации TouchBegin события, MouseButtonPress не генерируется и вы будете дальше работать с событиями тачскрина. Вот тут-то и затаилась самая большая проблема.
MouseButtonPress не будет генерироваться до тех пор, пока не будет события MouseButtonRelease. Если MouseButtonRelease вызывать искусственно, то всё равно ничего не получится. Нужно поставить на экран и убрать только один «палец», после этого системно будет генерироваться MouseButtonRelease.
Эту проблему решал следующим образом. Вместе с TouchBegin искусственно вызывал событие MouseButtonPress. В функции обработке этого события вначале проверял состояние глобальной переменной, если она false, тогда ей присваивалось true и выполнялся код функции, если же она true значит, мы только что обрабатывали это событие, и просто выходим из функции. При TouchEnd или MouseButtonRelease этой переменной присваивался false.
Если бы в 5.4 ничего не поменялось в работе тачскрина, то этой части текста здесь могло бы и не быть. А так я очень остался недоволен. Раз в «пятилетку» баг выскакивает в работе тачскрина, но не могу его воспроизвести и как следствие — понять, почему он возникает. Как только это случится, то обязательно здесь отпишусь.
В версии 5.2 я изучил подробно работу с событиями тачскрина. Есть контейнер, который хранит координаты и состояние всех «пальцев» на экране. Есть события, которые реагируют на первое прикосновение (первый «палец»), событие на изменение (передвижение «пальцев» или изменение их количества на экране), и событие, которое сообщает о завершении тача (когда все «пальцы» убираются с экрана).
По сути, если у Вас в приложении будет использоваться мультитач, то события мышки можно даже и не обрабатывать. Всё прекрасно работает на событиях Тачскрина. Но по определённым надобностям мне пришлось обрабатывать и события мышки. Впоследствии это мне и помогло разобраться в проблемах.
Всё изменилось с версией 5.4. Когда я собрал на новом Qt проект и сел протестировать новый функционал, каково было моё удивление, когда обнаружил, что управление полетело. А произошло следующее.
Зачем-то разработчики Q, поменяли логику работы событий тачскрина и мышки! Теперь это работает как-то по-чудному. При первом касании «пальца», генерируются события MouseButtonPress и TouchBegin. Но потом, когда генерируется событие MouseMove а TouchUpdate не генерируется. Но в контейнере тача точка остаётся, но координата и состояние ее не меняется. И когда генерируется событие MouseButtonRelease, событие TouchEnd не генерируется и получается, что виртуально у вас всегда на экране «висит» один «палец».
Эта проблема решается следующим образом: при событии TouchBegin копируете контейнер состояния в свой, а при MouseButtonRelease очищаете его до следующего TouchBegin.
После того как на экране появляется второй «палец», генерация событий мышки отключается. И после этого, сколько бы вы пальцев ни ставили на экран или сколько бы ни оставляли, всегда генерируются события TouchUpdate. Когда все «пальцы» убираются с экрана, генерируется событие TouchEnd, а MouseButtonRelease нет. Проблем можно избежать, отреагировав на событие TouchEnd так же, как и на MouseButtonRelease. Но дело в том, что при следующей генерации TouchBegin события, MouseButtonPress не генерируется и вы будете дальше работать с событиями тачскрина. Вот тут-то и затаилась самая большая проблема.
MouseButtonPress не будет генерироваться до тех пор, пока не будет события MouseButtonRelease. Если MouseButtonRelease вызывать искусственно, то всё равно ничего не получится. Нужно поставить на экран и убрать только один «палец», после этого системно будет генерироваться MouseButtonRelease.
Эту проблему решал следующим образом. Вместе с TouchBegin искусственно вызывал событие MouseButtonPress. В функции обработке этого события вначале проверял состояние глобальной переменной, если она false, тогда ей присваивалось true и выполнялся код функции, если же она true значит, мы только что обрабатывали это событие, и просто выходим из функции. При TouchEnd или MouseButtonRelease этой переменной присваивался false.
Если бы в 5.4 ничего не поменялось в работе тачскрина, то этой части текста здесь могло бы и не быть. А так я очень остался недоволен. Раз в «пятилетку» баг выскакивает в работе тачскрина, но не могу его воспроизвести и как следствие — понять, почему он возникает. Как только это случится, то обязательно здесь отпишусь.
Что еще
Еще добавил перевод на английский, но там проблем не возникло, и инфы по интернационализации приложений в Qt достаточно.
Итоги
Игра была написана и успешно запущенна в проекте. Свои первоначальные предназначения она выполнила — мне набраться опыта в геймдеве и стать одним из заданий в городском квесте. Теперь она будет выполнять своё второстепенное предназначение — быть частью моего портфолио.
Спасибо Вам, за то, что Вы это читали.