Первая игра под PSP
Автор: Alexander Sakharoff
Прошло две недели, как я купил себе PSP, и вот – первая игра готова. Конечно же это Тетрис. На примере него я покажу, как можно быстро сделать простую игрушку. Для крупных коммерческих проектов этот опыт нисколько не поможет, даже наоборот, окажется вредным, но для пробы пера вполне покатит.
Подготовка к работе.
Для начала надо раздобыть компилятор и SDK. Я пользовался набором с www.devkitpro.org (компилятор gcc и собранные библиотеки PSP homebrew SDK) и svn://svn.ps2dev.org/psp/ (отсюда я взял psp-порт MikMod).
Для запуска кода потребуется PSP с прошивкой 1.5 и программа KXploit (которая патчит готовый EBOOT.PBP) либо No-KXploit (резидент для PSP, с ним патчить не придется).
Makefile для сборки проекта берем из любого примера к SDK, и вписываем необходимые нам данные:
# данные для браузера PSP # иконка игры, PNG 144x80 PSP_EBOOT_ICON = ICON0.PNG # фоновая картинка, PNG 480x272 PSP_EBOOT_PIC1 = PIC1.PNG # список объектников, требуемых для компиляции OBJS = main.o font.o sound.o savegame.o game.o graphic.o # список необходимых библиотек LIBS = -lpspgu -lmikmod -lmmio -lpspaudiolib -lpspaudio
Главный модуль программы.
В главном модуле надо вписать служебную информацию:
// имя программы, атрибуты, номер версии (major и minor) PSP_MODULE_INFO("Tetris", 0, 1, 1); // программа работает в user-mode, использование векторного сопроцессора разрешено PSP_MAIN_THREAD_ATTR(THREAD_ATTR_USER | THREAD_ATTR_VFPU);
И добавить необходимые обработчики:
// обработчик выхода из программы (вызывается системой по нажатию Home – Yes) int exit_callback(int arg1, int arg2, void *common); // регистрация обработчика выхода int CallbackThread(SceSize args, void *argp); // создание callback-thread'а, вызывается из функции main int SetupCallbacks(void);
Возможно эти действия можно упростить, но пока я в них не вникал, а просто передрал из примеров SDK.
В самой функции main я вызываю SetupCallbacks, инициализирую графику, звук и ввод, загружаю необходимые ресурсы и вхожу в бесконечный игровой цикл. Выход из программы осуществляется, как это принято, нажатием кнопки Home.
Ввод.
Инициализуется ввод вызовом двух функций :
// устанавливаем частоту опроса состояния sceCtrlSetSamplingCycle(0); // выбираем тип опроса – цифровой или аналоговый sceCtrlSetSamplingMode(PSP_CTRL_MODE_ANALOG);
Получать данные от контроллера можно двумя способами – непосредственным или буферизованным.
// непосредственный опрос состояния (аналог - GetDeviceState в DirectInput) int sceCtrlReadBufferPositive(SceCtrlData *pad_data, int count); // чтение данных из буфера ввода (аналог - GetDeviceData в DirectInput) int sceCtrlReadLatch(SceCtrlLatch *latch_data);
Я использовал буферизованный ввод.
Загрузка-восстановление.
Нам это потребуется, чтобы иметь постоянную таблицу рекордов. Все сохраненные данные (а также дополнительный контент, аддоны) для игр на PSP хранятся в папке PSP/SAVEDATA на memory stick для того, чтобы из браузера PSP можно было просмотреть список сохраненок и, при желании удалить. Для работы с сохраненками используются функции sceUtilitySavedata* и структура SceUtilitySavedataParam. Код для работы с этим всем находится в файле savegame.cpp, опять же, по большей части, подранным из примеров PSP SDK. Разобраться в нем труда не составит.
Звук.
Со звуком я разбираться пока не стал, вместо этого прикрутил MikMod. Для коммерческого проекта это, конечно, ни в какие ворота не лезет, но в первом проекте мне было лень копаться в премудростях PSP-шного звука. В следующем проекте я это исправлю.
Графика.
Графический API весьма похож на OpenGL (а еще больше на Glide, царство ему небесное), так что разобраться с ним труда не составит. Хотя, конечно, чтобы эффективно его использовать, придется долго учиться.
Инициализацию графики и действия выполняемые в начале и конце каждого кадра можно посмотреть в graphic.cpp, вывод текста – в font.cpp, а загрузку текстуры с палитрой и отрисовку спрайта – в game.cpp.
Быстро пройдемся по основным функциям sceGu*:
// Инициализация графики sceGuInit(); // Завершение работы с графикой sceGuTerm(); // Начало работы с новый дисплей-листом sceGuStart(GU_DIRECT, list); // лист должен быть выровненным на границу 16 байт // Установка формата пикселей и размера фреймбуфера // (обычно SCR_WIDTH=480, SCR_HEIGHT=272, BUF_WIDTH=512, PIXEL_SIZE=2 или 4) sceGuDrawBuffer(GU_PSM_5650, (void*)0, BUF_WIDTH); // Установка размеров экрана // Пусть FRAME_SIZE = BUF_WIDTH * SCR_HEIGHT * PIXEL_SIZE sceGuDispBuffer(SCR_WIDTH, SCR_HEIGHT, (void*) FRAME_SIZE, BUF_WIDTH); // Установка размеров экрана sceGuDepthBuffer((void*)(FRAME_SIZE*2), BUF_WIDTH); // Установка смещения. Пространство рендеринга на PSP – 4096х4096, мы рисуем в центре sceGuOffset(2048 - (SCR_WIDTH/2), 2048 - (SCR_HEIGHT/2)); // Установка окна - координаты центра и размеры sceGuViewport(2048, 2048, SCR_WIDTH, SCR_HEIGHT); // Пространство значений глубины sceGuDepthRange(65535, 0); // Закончить текущий дисплей-лист sceGuFinish(); // Ждать конца отрисовки дисплей-листа sceGuSync(0, 0); // Ждать вертикальной синхронизации sceDisplayWaitVblankStart(); // Поменять местами видимый и рабочий буферы sceGuSwapBuffers(); // Включить(GU_TRUE) или выключить(GU_FALSE) дисплей sceGuDisplay(GU_TRUE); // Выбор формата палитры sceGuClutMode(GU_PSM_4444, 0, 0xff, 0); // Загрузка палитры sceGuClutLoad((PALETTE_ENTRIES/8), pPalette); // Установка форматы текстуры sceGuTexMode(GU_PSM_T8, 0, 0, 0); // Установка текущей текстуры sceGuTexImage(TEX_LOD, TEX_WIDTH, TEX_HEIGHT, TEXBUF_WIDTH, pTexture); // Выбор типа фильтрации текстуры (анизотропки нету:) sceGuTexFilter(GU_LINEAR, GU_LINEAR); // Отрисовать набор примитивов (индексированных или нет) sceGuDrawArray(PRIMITIVE_TYPE, VERTEX_FORMAT, VERTEX_COUNT, pIndices, pVertices);
Заключение.
Выкладываю в SVN полные исходники своего Тетриса, своих копирайтов я там не ставил, ибо большая часть кода написана не мной. :) Пользуйтесь ими, как пожелаете.
12 июня 2006 (Обновление: 24 сен 2006)
Комментарии [2]