Наверное, каждый время от времени ностальгирует по былым временам. Как собирали вкладыши от «Турбо», играли в кэпсы и дни напролет проводили за приставками . В этой статье мы будем реинкарнировать популярнейшую игру того времени "Duck hunt”. Писать мы ее будем на чистом QML, под нашу любимую платформу MeeGo Harmattan. В конце будет немного о публикации в магазине Nokia.
Гуру программирования вряд ли откроют для себя что-то новое, а вот те, кто только начинает погружаться в загадочный мир программирования, могут почерпнуть для себя что-то интересное.
В итоге у нас получится вот что:
Собственно, задача выглядит следующим образом: На фоне статичной картинки определенный промежуток времени передвигаются случайным образом анимированные спрайты. При касании экрана – проверяется, есть ли в наличии патроны, и проигрывается звук выстрела. Затем проверяется, находятся ли координаты касания внутри прямоугольника, описанного вокруг передвигающегося спрайта. Если да – аппарат вибрирует, проигрывается звук, анимация спрайта меняется на другую, летят перья и спрайт медленно перемещается вниз экрана (утка убита). При достижении нижней точки – снизу появляется другой спрайт (собака), размахивая подбитой уткой. Если в течение заданного интервала по летающему спрайту так и не попали – передвигаем спрайт за пределы экрана, собака плачет и выпускается новая жертва. Добавим еще вверху экрана скролящийся спрайт с облаками, внизу – панель со статистикой игры и кнопку меню. При нажатии на кнопку отрисовывается панель с кнопками опций. Если в итоге количество пропущенных уток менее трех – стартуем всю сцену сначала, увеличив количество уток и увеличив им скорость перемещения. Если нет – GameOver.
Далее будут рассмотрены только основные моменты создания подобных игр. Подробности можно посмотреть в исходниках, ссылка на GitHub будет в конце статьи.
Про спрайтовую анимацию.
В игре всё построено на спрайтовой анимации, поэтому первым делом создадим новый элемент AnimatedSprite. Чтобы создать новый элемент в QML, достаточно создать файл с одноименным названием, и в нем, используя уже существующие элементы, создать то, что нам надо. Элемент AnimatedSprite будет работать с графическим файлом, в котором все кадры анимации идут последовательно, друг за другом:
Поясню основные моменты нового элемента:
Файл: AnimatedSprite.qml
importQtQuick1.0 Item{ id:spriteAnimation propertyintframesHorizontCount:0 // кол-воизображенийпогоризонтали propertyintframesVerticalCount:0 // кол-во изображений по горизонтали propertyintframesCount:(framesHorizontCount*framesVerticalCount) propertyintcurrentFrame:0 // текущийкадранимации propertystringsourcePath:"" // путьдофаласоспрайтами propertyintanimationSpeed:0 // скоростьанимации propertyintstartFrame:0 // номер начального для текущей анимации кадра propertyintendFrame:0 // номер последнего для текущей анимации кадра …………………………………… …………………………………… …………………………………… height:spriteAnimationImage.height/framesVerticalCount width:spriteAnimationImage.width/framesHorizontCount // вычисляем высоту и ширину кадра в пикселях Image{ // элемент, который отрисовывает текущий кадр анимации id:spriteAnimationImage source:sourcePath x:-((spriteAnimation.currentFrame*spriteAnimation.width)-Math.floor(spriteAnimation.currentFrame/spriteAnimation.framesHorizontCount)*(spriteAnimation.framesHorizontCount*spriteAnimation.width)) y:-(Math.floor(spriteAnimation.currentFrame/spriteAnimation.framesHorizontCount)*spriteAnimation.height) // по этим формулам вычисляем, по каким координатам мы найдем текущий кадр анимации в файле } }
В QML2, кстати, появился стандартный элемент AnimatedSprite но мне еще не довелось его попробовать.
На основе нового элемента AnimatedSprite создаем более конкретные элементы: DogAnimation, FeathersSprite, Clouds, DuckSprite, добавив в них состояния (states) для конкретных анимаций. Поясню на основе DogAnimation.qml:
importQtQuick1.0 Item{
id:dogAnimation …………………………………… …………………………………… …………………………………… states:[ …………………………………… …………………………………… …………………………………… State{ name:"JUMP" PropertyChanges{target:dogAnimation;startFrame:6} PropertyChanges{target:dogAnimation;endFrame:8} …………………………………… // при переходе в состояние "JUMP" объекту будет выставлен начальный кадр анимации, равный 6. Последнийкадр – 8. Т.е. в цикле будут отрисовываться только с 6 по 8 кадры из файла со спрайтами. }, …………………………………… …………………………………… …………………………………… State{ name:"MISS" PropertyChanges{target:dogAnimation;startFrame:9} PropertyChanges{target:dogAnimation;endFrame:10} …………………………………… } ] }