Сегодня мобильный телефон перестал быть просто средством связи, окончательно превратившись в мощное многоцелевое устройство. Новые модели позволяют слушать радио и mp3, делать фотографии, снимать видео и, конечно, играть в игры. Интерес к играм для мобильных телефонов растет с каждым годом. В этой статье я покажу Вам, как написать "тетрис" для телефона под управлениемSymbian Series 60. Представленная ниже базовая концепция может с успехом использоваться и при разработке других игр.


Шаг первый. Приложение Hello World

Начнем с простейшего приложения Hello World, которое поставляется вместе с SDK. В Zip архиве в конце статьи содержатся исходники проекта. Sis файл может быть установлен непосредственно на телефон и запущен.

В папке group\ Вы найдете исходные файлы проекта: bld.inf и S60Test.mmp. Файл abld.bat генерируется с помощью программы bldmake. Также в папке находятся файлы проекта Borland C++Builder (Project_Sources.* и S60Test_Sources.*).

Файл group\step1.rss содержит описание ресурсов. В нашем примере он содержит конфигурацию сервисных кнопок (R_AVKON_SOFTKEYS_OPTIONS_EXIT - говорит о том, что команда "Опции" будет привязана к левой кнопке, а "Выход" - к правой) и меню "Опции". Другие ресурсы будут добавлены позже.

Файл group\step1.pkg содержит описание компоновки файла *.sis (SIS это так называемый инсталляционный пакет. Он запускается на телефоне и производит инсталляцию приложения.)

В папках inc\ и src\ содержатся исходные коды программы. Пример Hallo World подробно описан в документации, поставляемой вместе с SDK, поэтому я ограничусь короткими замечаниями.

В отличие от приложений для Windows и Unix, Simbian OS приложение не имеет функции main() с основным циклом внутри. Структура Symbian OS приложения больше походит на DLL библиотеку.

Рассмотрим работу приложения. Оболочка вызывает функцию NewApplication() чтобы получить объект класса CApaApplication. Series 60 приложение возвращает объект, который является экземпляром класса CAknApplication. В нашем примере это CS60TestApplication, объявленный в s60testapplication.cpp. При объявлении должны быть заданы две функции. AppDllUid, которая возвращает UID приложения (Каждое приложение имеет уникальный UID. Обратите внимание на то, что мы используем UID для разработки. Этот UID необходимо изменить, когда вы создадите финальный релиз своего приложения). Вторая функция - CreateDocumentL. Она создает объект класса CApaDocument. В нашем примере это CS60TestDocument, который является потомком CAknDocument. Функция CreateAppUiL этого класса нуждается в переопределении. Эта функция отвечает за интерфейс пользователя.

В нашем примере за него отвечает класс CS60TestAppUi. В функции ConstructL мы вызываем функцию BaseConstructL, которая выполняет инициализацию объекта, в частности загружает системные клавиши и меню из файла ресурсов. Затем мы создаем объект класса CS60TestAppView, который в свою очередь является потомком класса CCoeControl. CCoeControl выполняет рисование управляющих элементов на экране. Наше управление заполнит ClientRect (), т.е. пространство между областью окна состояния и описанием функциональных клавиш. Замена этой функции на ApplicationRect() привело бы к созданию полноэкранного приложения. Вызов AddToStackL приводит к отображению нажатия клавиш.

Объект AppUi возвращает события от меню. Когда пользователь выбирает пункт меню, вызывается HandleCommandL с кодом соответствующей команды (код задается при описании меню в файле ресурсов). Мы создали команды "Exit" и "Hello", которая отображает примечание.

В классе CS60TestAppView мы переопределили лишь одну функцию - Draw. Эта функция вызывается, когда необходимо перерисовать экран. Для заполнения экрана используется ClientRect. Чтобы создать строку, мы используем класс TBuff.

Шаг второй. Добавляем структуру данных

На втором шаге мы добавим необходимые нашей игре структуры данных.

Мы используем два класса TBlock и TGrid. Эти классы не имеет указателей на наши данные. TBlock представляет отдельный блок тетриса (форма строится из четырех кирпичей). TGrid представляет сетку (20x10 блоков) с уже зафиксированными кирпичами. Класс Document содержит поле iGrid, которое включает в себя всю игровую сцену и iCurrBlock, включающее падающий в настоящий момент блок. В iBlockPos содержится позиция падающего блока.

Код, добавленный в этом примере, главным образом занимается манипуляцией битов. Код вполне стандартный. Единственное отличие от простого Windows приложения - использование в Symbian OS класса TFixedArray. Объекты этого класса позволяют получить доступ к данным абсолютно так же, как при работе с массивом. Отличие заключается в том, что внутри класса осуществляется проверка диапазона, то есть если Вы попытаетесь присвоить значение элементу, номер которого превышает размер массива, переполнения буфера и потери памяти не произойдет. Будет спровоцирована исключительная ситуация.

Также на этом шаге было изменено меню клавиши Option.

Шаг 3. добавляем интерфейс пользователя.

На этом шаге мы создадим пользовательский интерфейс. Это позволит проверить работоспособность структур данных. Используя стрелочки, пользователь может управлять блоком. Клавиша OK фиксирует блок. (Замечание: новый блок появляется в самом верху игрового поля. Вам придется несколько раз нажать стрелку вниз, чтобы увидеть его.) Пользователь может вращать блок.

Рассмотрим реализацию. Сначала в CS60TestAppView::Draw мы рисуем игровое поле. Используем методы класса TGrid чтобы получить тип блока, и методы DrawLine и DrawRect, чтобы нарисовать игровое поле. Для управления цветом используются методы SetPenColor (для управления цветом линий) и SetBurushStyle/SetBurshColor (для управления цветом заливки). Описание этих и других методов CWindowGc можно найти в документации SDK.

Нам необходимо запрограммировать реакцию программы на действия пользователя. Каждое нажатие клавиши приводит к генерации события клавиатуры. Это событие посылается в CS60TestAppView (которое добавляется с помощью AddToStackL на верх стека). По умолчанию обработка возвращает EKeyWasNotConsumed. Результат, полученный от CS60TestAppView, посылается к следующему элементу в стеке - CS60TestAppUi, именно здесь мы перехватываем событие помощью HandleKeyEventL и выполняем его обработку.

После того, как в результате действий пользователя были внесены изменения в сетку, экран должен быть перерисован. Перерисовка осуществляется с помощью DrawDeferred в CS60TestAppUi::UpdateBoard.

Правильная реализация должна возвращать EKeyWasConsumed в ответ на события от всех задействованных клавиш. Моя реализация возвращает EKeyWasNotConsumed не зависимо от того, какая клавиша была передана структуре. Я не пытался отсеивать лишние клавиши, поскольку структура обрабатывает системные клавиши и не как не реагирует, на используемые для управления нашей программой.

На этом шаге в меню Option была добавлена строка 'New Game'.

Шаг 4. Добавляем движок

Сейчас наша игра не особо интересна, поскольку пользователь может ставить блоки где ему вздумается. На этом шаге мы добавим в игру движок, который будет заботится о том, чтобы блоки смещались вниз через определенный временной интервал.

Для реализации движка был создан отдельный класс CS60TestEngine. Он является потомком класса CTimer. Этот класс вызывает метод CS60TestEngine::RunL через определенный промежуток времени, который задается с помощью метода After(iInterval), где iInterval - время задержки между вызовами в миллисекундах. Если использовать внутри класса CTimer цикл для организации временной задержки, главный поток приложения окажется блокированным и нажатие клавиши не будет обрабатываться.

CTime - активный объект, поэтому мы должны его инициализировать, поместив с помощью CActiveScheduler::Add(this) в очередь планировщика.

При перезапуске игры мы вызываем метод Cancel, а затем заново устанавливаем интервал задержки.

Внутри метода RunL мы опускаем блок на одну линию вниз. Если опускать блок уже некуда, то мы пробуем фиксировать его и создаем новый. Если фиксировать блок нельзя (если игровая область заполнена) то завершаем игру. Показываем пользователю сообщение и останавливаем работу движка (с помощью переменной iState).

Обратите внимание, строка "Game over" загружается из файла ресурсов. Такой подход очень удобен при переводе игры на различные языки. Для локализации Вам нужно всего лишь перевести файл ресурсов. Указанная строка представляет собой TBUF запись в файле s60test.rss. Эта запись имеет имя r_note_game_over. В процессе компиляции создается файл s60test.rsg, который может быть подключен к C++ программе. В этом файле R_NOTE_GAME_OVER будет определена как ID ресурса. Чтобы получить ее значение, необходимо вызвать


CEikonEnv::Static()->ReadResource(message, R_NOTE_GAME_OVER);

Шаг 5. Полировка игры

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

Первым делом добавим паузу. Сейчас если пользователь переключится на другое приложение или вызовет меню, блоки будут продолжать падать. И когда пользователь решит вернуться к игре, область будет заполнена блоками.

Для реализации паузы создадим две функции: Pause/Unpause. Когда пользователь вызывает меню или переключается на другое приложение, вызываются TechPause/TechUnpause. Если в результате действий пользователя два раза было вызвано TechPause, то для возобновления игры вы должны дважды вызвать TechUnpause.

Само по себе включение и отключение паузы реализуется в DoPause. При включении паузы, мы сохраняем значение текущего интервала и выключаем таймер. После отключения паузы мы вновь устанавливаем таймер.

TechPause/TechUnpause вызываются внутри CS60TestAppView::FocusChanged. Как следует из названия, эта функция вызывается при потери и восстановлении фокуса приложения (то есть когда пользователь вызвал меню или переключился на другое приложение). Pause/Unpause вызывается из CS60TestAppUi::HandleCommandL, когда пользователь выбирает соответствующий пункт в меню Options.

При выборе пользователем пункта меню 'Pause' мы должны поменять название пункта на 'Unpause'. Это осуществляется в методе CS60TestAppUi::DynInitMenuPaneL, который вызывается каждый раз перед отображением меню. Будем проверять состояние движка и выводить соответствующую строку из файла ресурсов.

Давайте выведем на задний план нашего игрового поля картинку. В Symbian OS приложениях изображения хранятся в файлах с расширением *.mbm - Multi BitMap. Эти файлы создаются из *.bmp в процессе компиляции приложения. Добавим блок START BITMAP в *.mmp. Внутри одного *.mmp можно размещать несколько *.bmp файлов.

Мы создадим файл s60test.mbm, который будет содержать одно изображение - tlo.bmp. Приставка c12 перед именем означает, что используется 12-ти битная цветовая схема (4096 цветов). Если Вы хотите использовать 65536 цветов, используйте приставку c16. В процессе компиляции будет создан файл s60test.mbg. Это подключаемый файл с ID *.mbm файлов. В нашей игре используется одно изображение с именем EMbmS60testTlo. Для загрузки изображения используется строка

CEikonEnv::Static()->CreateBitmapL(iPathName, EMbmS60testTlo);

Обратите внимание на путь(\System\Apps\Step5\S60Test.mbm). Он не содержит названия диска. Картинка будет искаться на диске с которого запускается приложение.

Для того, чтобы нарисовать картинку используется функция DrawBitmap. Помимо рисунка будем выводить на экран номер уровня и число набранных очков. Это простой текст, выводимый также, как в шагах 1 и 2.

Шаг 6. AIF файл

Для профессионального вида нашей программе не хватает двух вещей: иконки и названия. Для добавления их в приложение используется AIF (Application Information File) файл.

Нам необходимо создать в нашем ресурсном файле s60TestAif.rss запись AIF_DATA, где указать имя нашего приложения. Именно это имя видит пользователь на экране своего телефона в менеджере приложений.

Нам потребуется четыре рисунка - изображение 44x44, маска размером 44x44 (картинка, указывающая какие части предыдущей картинки являются невидимыми), изображение 44x23, маска 44x23. На AIF файл должна быть ссылка в файле *.mmp. При компиляции в директории приложения будет создан файл step6.aif.

Ну, вот пожалуй и все. Надуюсь Вы не жалеете о потраченном времени. Ссылки на исходники приведены ниже.

Шаг Исходник *.sis Различия
1 step1.zip
step1.sis
-
2 step2.zip
step2.sis
step2.diff.txt
3 step3.zip
step3.sis
step3.diff.txt
4 step4.zip
step4.sis
step4.diff.txt
5 step5.zip
step5.sis
step5.diff.txt
6 step6.zip
step6.sis
step6.diff.txt

Автор оригинала:mikolajz
Перевод:aRix





Наши соцсети

Подписаться Facebook Подписаться Вконтакте Подписаться Twitter Подписаться Google Подписаться Telegram

Популярное

Ссылки

Новости [1] [2] [3]... Android/ iOS/ J2ME[1] [2] [3]) Android / Архив

Рейтинг@Mail.ru Яндекс.Метрика
MobiLab.ru © 2005-2018
При использовании материалов сайта ссылка на www.mobilab.ru обязательна