Отображаем графику в КПК или примитивная игра | Инфа для совсем начинающих



Реп: (34)
Вступление:

Не секрет что поиграв в компьютерные игры на КПК многие думают "а не сделать ли мне свою собственную?", но столкнувшись с теми или иными трудностями бросают это дело. Трудности конечно бывают разные, зачастую и не связанные (или отдаленно связанные) с программированием - например абсолютное неумение нарисовать хотя бы примитивную картинку или сделать простейший звуковой эффект. (Здесь не рассматриваю такой момент когда человек всё это может, но просто наконец осознает нереальный объем работ).
Однако если научить рисовать довольно тяжело и не знаю даже возможно ли (аналогично с музыкой и звуками), то уж преодолеть хотя бы некоторые трудности программирования не так и сложно.
Вот... Собственно поэтому я думаю собрать в данном топике некоторую полезную информацию по изготовлению хотя бы самой что ни на есть простейшей игрушки в плане именно программирования, а именно отображения графики. Возможно кто-то здесь чему-то научится, а кто-то наоборот поможет советами и научит других.

Итак... Начнем делать игру :)

Во-первых на чем будем писать: я выбрал обычный c++ ибо vb просто не знаю, а .net просто не люблю... также я не стал использовать готовые библиотеки работы с графикой, ибо думаю что освоив досконально простые вещи в простой игре можно будет спокойно браться за более продвинутые технологии и соответственно более сложные игры.

Во-вторых - что это будет за игра... Ясное дело игра будет совсем простой, но тетрис это будет уж слишком банально, поэтому возьмем скажем что-то вроде космической леталки-стрелялки - сделать такую вещь на самом деле совсем не сложно с программной точки зрения, а также графику для неё нарисовать довольно просто даже для не слишком искушенных в этом деле.

В третьих - не пинайте за ошибки в коде или в объяснении поскольку я не монстр программирования на КПК, много чего не знаю и это для меня всего лишь хобби в свободное время - лучше объясните обоснованно свою точку зрения, приводите свой код и так далее.
-------------------

Часть 1.
Как хоть что-нибудь отобразить на экране КПК

Я рассмотрю два способа вывода на экран - через gapi и через rawframebuffer. И в том и в другом случае экран как правило представляет собой адресное пространство (фреймбуфер) размером [ширина] x [высота] x [bpp], где bpp - количество бит на пиксель. В обеих случаях левый верхний пиксель имеет смещение = 0 относительно начала фреймбуфера, следующий справа пиксель имеет смещение = 1 х [bpp] и так далее до нижнего правого. Под словами левый верхний я здесь и в дальнейшем буду иметь ввиду "стандартную" ориентацию экрана КПК, то есть кнопками вниз. Если например мы будем проектировать игру с ориентацией "кнопки слева" - то смещение = 0 будет иметь правый верхний пиксель, а смещение = 1 х [bpp] соответственно тот который прямо снизу под ним. Также я всегда буду рассматривать bpp = 16, с маской 565 (поскольку это самое распространенное значение для КПК последних лет, понятное дело бывают и другие значения). То есть один пиксель занимает ровно 16 бит (один word) и интенсивности цветов R, G, B в этих 16-ти битах занимают соответственно 5 старших бит - красный, затем 6 бит - зеленый и последние младшие 5 бит - интенсивность красного.
Исходя из вышеизложенного скажем для того чтобы вывести зеленую точку в левом верхнем углу экрана необходимо по смещению = 0 записать во фреймбуфер число (двоичное) 00000 111111 00000. Или используя код:
---
LPWORD framebuffer = ... (инициализация разная) ...
framebuffer[0] = 0x7E0;
---
Аналогично чтобы вывести точку с координатами (x,y) будет следующий код:
---
LPWORD framebuffer = ... (инициализация разная) ...
framebuffer[y*screen_width + x] = 0x7E0;
---
Здесь - screen_width = ширина экрана фреймбуфера. Именно фреймбуфера а не физического экрана, ибо например на VGA-устройствах фреймбуфер при работе через gapi может запросто иметь размеры 240 x 320.

Теперь о том как инициализировать фреймбуфер и получить адрес его начала.

1) Через библиотеку gapi.
Это довольно старый, но тем не менее использующийся до сих пор способ. Для его работы необходимо наличие библиотеки gx.dll которая является стандартной в системах начиная с windows mobile 2003 (???) и выше.
Сначала необходимо с помощью функции GXOpenDisplay инициализировать работу с данной библиотекой, затем с помощью функции GXGetDisplayProperties получаю информацию о свойствах фреймбуфера экрана соответственно проверяя наличие необходимого режима. Этот код должен выполняться один раз в самом начале программы сразу после создания основного окна, которое у меня тут имеет handle: g_hMainWindow.
---
GXDisplayProperties dispprop;
if (GXOpenDisplay(g_hMainWindow, GX_FULLSCREEN) == 0) {
MessageBox(g_hMainWindow, L"GAPI: Unable to init display.", L"ERROR", MB_OK);
return FALSE;
}
dispprop = GXGetDisplayProperties();

if ((dispprop.cBPP != 16) || (dispprop.cxWidth != 240) || (dispprop.cyHeight != 320))
{
GXCloseDisplay();
MessageBox(g_hMainWindow, L"GAPI: Unable to init 240x320 16bpp mode.", L"ERROR", MB_OK);
return FALSE;
}
---
После успешного выполнения данного куска кода можно собственно начинать отображать графику во фреймбуфер с помощью функций GXBeginDraw и GXEndDraw:
---
LPWORD framebuffer = (LPWORD) GXBeginDraw();
// начинаем "рисовать"
memset(framebuffer, 0xFF, 50*sizeof(WORD)); // верхняя линия в 50 пикселей белым цветом
framebuffer[10*240+20] = 0x7E0; // зеленая точка с координатами (20,10).
// закончили рисование - вызовем функцию завершения
GXEndDraw();
---
Также перед выходом из программы необходимо вызвать функцию GXCloseDisplay чтобы высвободить ресурсы gapi которые мы тут "одолжили" у системы.

2). Через rawframebuffer:
Более современный способ введенный майкрософтом начиная с версий windows mobile 2003SE и выше. Особенно не отличается от gapi разве что ему не нужна библиотека gx.dll, но зато он не будет работать на старых устройствах. А также его большой плюс так это то что на VGA-устройствах с его помощью можно будет выводить именно vga-картинку ибо через gapi мы получим только qvga...

Сначала как обычно - необходима инициализация и проверка параметров фреймбуфера экрана:
---
#define GETRAWFRAMEBUFFER 0x00020001

#define FORMAT_565 1
#define FORMAT_555 2
#define FORMAT_OTHER 3

typedef struct _RawFrameBufferInfo
{
WORD wFormat;
WORD wBPP;
VOID *pFramePointer;
int cxStride;
int cyStride;
int cxPixels;
int cyPixels;
} RawFrameBufferInfo;

RawFrameBufferInfo screen_rfbi;
HDC hdc = GetDC(NULL);
ExtEscape(hdc, GETRAWFRAMEBUFFER, 0, NULL, sizeof(RawFrameBufferInfo), (char *) &screen_rfbi);
ReleaseDC(NULL, hdc);

if ((screen_rfbi.wBPP != 16) || (screen_rfbi.wFormat != FORMAT_565) || (screen_rfbi.cxPixels != 480) || (screen_rfbi.cyPixels != 640))
{
MessageBox(g_hMainWindow, L"RAW: Unable to init 480x640 16bpp mode.", L"ERROR", MB_OK);
return FALSE;
}
---

Далее аналогично - берем ссылку на фреймбуфер из нашей структуры и заполняя его наблюдаем прорисовку всего этого на экране:
---
LPWORD framebuffer = (LPWORD) screen_rfbi.pFramePointer;
// начинаем "рисовать"
memset(framebuffer, 0xFF, 50*sizeof(WORD)); // верхняя линия в 50 пикселей белым цветом
framebuffer[10*480+20] = 0x7E0; // зеленая точка с координатами (20,10).
// закончили рисование - не надо никаких функций вызывать ...
---

Здесь надо добавить что по-хорошему надо использовать значения cxStride и cyStride (а если используем gapi - cbxPitch, cbyPitch в структуре GXDisplayProperties) которые показывают сколько необходимо добавить к адресу во фреймбуфере чтобы перейти на соседнюю точку соответственно по-вертикали и по-горизонтали. И соответственно более верный код рисования точки по координатам (x,y) будет таким:

---
((LPWORD)screen_rfbi.pFramePointer)[(y*screen_rfbi.cyStride+x*screen_rfbi.cxStride)>>1] = 0x7E0;
---

Перед закрытием программы не требуется вызов каких-либо функций завершения работы.
(во всяком случае я не нашел - если кто больше знает - подскажите)

Как сделать чтобы картинка двигалась - вроде довольно просто - стираем всё во фреймбуфере, рисуем картинку... потом опять стираем и опять рисуем картинку но сдвинутую на некоторое количество пикселей. Однако данный способ абсолютно неприемлем! Нельзя сразу рисовать сложную динамическую сцену во фреймбуфер. На экране при движении объектов будет наблюдаться мерцание или дергание. Поэтому чтобы отобразить на экране сложную динамически изменяющуюся картинку самым простым способом будет использовать копию фреймбуфера (такого же размера) в которой мы сначала будем рисовать всю графику, а затем быстро копировать сразу весь подготовленный экран в настоящий фреймбуфер.

Далее - чтобы объекты двигались равномерно и плавно - необходимо производить обновление картинки со стабильным интервалом сколько-то раз в секунду - как показывает практика где-то около 20 и больше уже можно считать нормальной скоростью. Итого - за 1/20 секунды программа должна произвести необходимые действия по обработке объектов игры, подготовить всю графику в копии фреймбуфера и вывести его на экран, затем следующий цикл аналогичных действий и т.д.

Итак... закончили с теорией... Теперь более практически: сделаем простую программу которая будет рисовать примитивный графический объект на экране двигая его с помощью стилуса... Да-да на этом всё - для начала будет достаточно разобраться хотя бы и в этом ибо в последующих версиях будет всё гораздо сложнее и меньшее количество комментариев и объяснений...

Сама структура программы очень простая:

Сначала:
инициализация окон, переменных и прочего, также инициализация графического режима и проверка его параметров.
Далее в цикле по таймеру с постоянным интервалом в 1/20 сек.:
обработка нажатий кнопок и стилуса
отработка логики объектов игры
очищаем копию фреймбуфера
рисуем туда всё что необходимо
копируем фреймбуфер на экран
При выходе:
очищаем, освобождаем память и т.д.

Теперь подумаем о разрешении графики в игре. Большинство КПК имеют разрешение экрана либо 240 х 320 либо 480 х 640. Ну и поскольку программа должна работать на обеих типах КПК - имеем несколько выходов:
1) нарисовать разные картинки графики для разных разрешений и сделать либо две версии программы, либо одну, но в ней при инициализации графики выбирать нужные картинки - получаем просто увеличение объемов работы над программой но зато лучший вид при отображении на тех и других типах устройств.
2) нарисовать всю графику в большом (vga) разрешении и работать с копией фреймбуфера как будто бы на vga-устройстве вне зависимости от реального девайса, но при окончательном выводе копии в актуальный фреймбуфер либо просто её копировать (для vga-устройств), либо копировать с одновременным масштабированием (для qvga) - я пробовал такой способ когда делал простой тетрис - вполне работоспособно хотя конечно vga графика теряет в качестве на qvga устройствах после масштабирования.
3) нарисовать всю графику для qvga разрешения и соответственно на vga устройствах при окончательном выводе масштабировать её - здесь я буду использовать именно этот метод ибо хоть картинка будет терять в качестве на vga-устройствах - но зато мы сильно выиграем в скорости ибо подготовить и прорисовать qvga фреймбуфер в 4 раза быстрее чем vga, также графика в памяти будет занимать в 4 раза меньше места - а ведь скорость для КПК, как и размеры графики/занимаемой памяти это очень и очень важный параметр.

В нижеприведенном исходнике я использовал только gapi метод вывода картинки ибо удобен тем что при выводе через него - на vga-устройствах не требуется самостоятельно масштабировать изображение - gapi изначально предлагает qvga фреймбуфер и сам масштабирует при выводе на экран.

Второй момент - получение координат стилуса в программе. Виндовс передает координаты относительно текущего виртуального разрешения программы. Например если на vga-устройстве в ресурсы программы не включить HI_RES_AWARE - координаты будут передаваться как будто для qvga. Поэтому в своей программе я добавил этот ресурс и поскольку фреймбуфер у меня qvga - переданные мне vga-координаты мыши я просто делю на 2.
Ещё я обнаружил такой момент - когда запускаешь программу из ландшафтного режима - то передаваемые ей координаты могут как отличаться от "портретного запуска", так и не отличаться... Во всяком случае я заметил что при использовании gapi (функции GXOpenDisplay я так подозреваю) - координаты не отличаются, а например если использовать rawframebuffer - координаты будут передаваться соответственно "повернутыми".

Уфффф... эдакое долгое объяснение вышло самых базовых моментов... впрочем ладно - основа положена...

Исходник я компилировал Embedded VC 4.0 c сервис-паком 3 и установленным Pocket PC 2003 SDK.

Прикрепленные файлы

Прикрепленный файлTest0.zip ( 137.41 КБ )


Сообщение отредактировал chamine - 24.01.07, 15:33



Реп: (64)
слетит на некоторых девайсах =)
иногда смещение по Х и У в КПК не соответствует ожиданиям :)
т.е. когда фреймбуфер организован нелинейным методом.
например,если повернуть экран :)

а при инициализации gapi в полный экран окно действительно во весь экран



Реп: (143)
chamine,
Молодец. Очень подробно и разжевано.
Респект тебе в лычку !

tz-lom
Дорогой ты мой tz-lom ! Когдаж ты начнешь писать свои статьи, ты ж мега спец по графике ! Жду не до ждусь, ну или хотя бы свой мега-софт покажи, очень охота посмотреть (хоть на дельфе)!
А то критик из тебя - хоть куда (к стати, не обоснованный критик), а вот девелоперской жилки у тебя пока не видно.



Реп: (21)
Да, интересная статья. Узнал много нового. Хотя для тех кто действительно ничего не знает даже это будет слишком сложно.
Я вот особо не заморачивался как там что рисуется, хотя теоретически конечно знаю. Для программиста что важно - умение пользоватся инструментом, т.е. какой нибудь библиотекой. Сокращает время и позволяет избежать ошибок,. тем более что библиотека соответствующим образом оптимизированна, и полюбому будет иметь выигрыш в скорости. Когдаж DirectX дойдет до WinMobile? :)

Сообщение отредактировал SquirrelRed - 24.01.07, 15:36



Реп: (143)
SquirrelRed @ 24.01.07 05:23:31
Когдаж DirectX дойдет до WinMobile?

Тогда, когда 80-90% КПК в мире будут с графическими ускорителями.
ИМХО : КПК пройдет те же стадии эволюции, что и "большой брат" (но более быстро, т.к. почти все подводные ками на пути этой эыолюции извесны).



Реп: (21)
От себя хочу добавить пояснения:

То что мы видим на экране хранится в области памяти КПК, называемой передним буффером(front buffer), соответственно, если мы хотим изменить изображение на экране - мы должны изменить содержимое переднего буффера.
Можно менять это содержимое напрямую, но это вызовет упомянутый charmine эффект мерцания, также это довольно медленно. Поэтому поступают следующим образом - рисуют новую картинку в области памяти, называемой задним буффером(back buffer), а затем копируют задний буфер на передний. После чего задний буффер очищается для нового рисования.

Задний и передний буфферы по сути являются массивами типа:

Pixel pixels[ширина*высота];
высота и ширина буффера, т.е. изображения в нем хранящегося

В массиве хранятся данные типа Pixel, это по сути целое число, и означает оно цвет. Например 0 - это черный.

у некоторых возникает резонный вопрос, а почему не хранить буффер в виде:

Pixel pixels[ширина][высота];

Так можно было бы обратиться к любому пикселю с координатами x, y следующим образом
pixel[x][y] = цвет;

Однако, первый метод значительно быстрей, и удобнее при выведении картинки. Это связанно с особенностью двумерных массивов, которые по сути являются массивами указателей на массивы (вообщем, это особенность языка C++, советую ознакомится).

Структуры вроде описанной так же называют поверхностями. Соответственно: передний буффер - передняя поверхность, задний - задняя.



Реп: (64)
добавлю:
т.к. массив видеопямяти действительно лучьше пользовать как WORD *pixels[width*height]; то есть изящный способ копирования прямоугольных изображений- memcpy
например нужна нам картинка в точке x,y размерами w,h естественно растр картинки должен быть уже в памяти,адрес видеобуфера запущен итд
и ещё-надо чтоб была известна ширина экрана,и тогда путём нехитрых операций делаем:
memcpy(pixels+x+y*screenx,rastr,w*h);
всё просто =)
тока минус в том,что картинка будет выведена вертикально ну и не на всех режимах работает,зато быстро =)

П.С.
(к стати, не обоснованный критик)

тем,кто неверит что этот код может сбоить-попробуйте повернуть экран в эмуляторе
П.П.С.
если время будет,выложу сырцы с коментариями,а пока тупо нет времени



Реп: (34)
Часть 2.
Организация графических файлов и более совершенный вывод графики.

Итак - потренировавшись выводить примитивные точки/линии, крашенные прямоугольники и т.д., можно переходить к более продвинутым картинкам растровой графики (спрайтам).

Для начала определимся куда и в каком формате будем выводить эти спрайты. Поскольку как известно большинство КПК для хранения одной точки требует 16 бит с цветовой маской 565 (описано в части 1.), а также имея ввиду что всю графику сначала требуется вывести в некий "теневой" фреймбуфер - логично будет выбрать такую структуру этого самого теневого фреймбуфера: одномерный массив из WORD размером 240 х 320 х sizeof(WORD), где по нулевому смещению будет располагаться верхняя левая точка, следующая за ней по горизонтали справа будет иметь смещение 2 (в байтах), а нижестоящая будет иметь смещение 480 (в байтах). Эта структура будет одна для всех типов КПК на которых будет запускаться наша игра вне зависимости от их разрешения или метода вывода теневого фреймбуфера на экран - просто при окончательном выводе теневого фреймбуфера на экран будут использоваться разные функции.
Это позволит нам хранить все спрайты в одном формате. Соответственно формат хранения спрайтов будет такой-же как и формат самого теневого фреймбуфера - а именно одномерный массив из WORD размером [ширина спрайта] x [высота спрайта] x sizeof(WORD). Собственно в этом формате я и буду хранить спрайты на диске только добавив вначале файла ещё два WORD - размеры спрайта: ширина и высота.

Здесь продвинутый юзер подумает: "блин, ведь мы храним только R,G,B цветовые составляющие на одну точку - а где ж е-мое (полу)прозрачность?". Что-ж пока у нас полупрозрачности не будет - ограничимся тем что скажем цвет = 0 (я выбрал такой) будет считаться полностью прозрачным. Относительно реализации полупрозрачности я ещё напишу статью, но пока для нашей простейшей игрушки я её не использовал.
Забегая вперед могу сказать что для полной и относительно быстрой реализации полупрозрачности каждую точку также можно хранить в 16-ти битах, но цветопередачей придется пожертвовать и цветовая маска будет 4-4-4-4 (ARGB) - то есть 4 (верхние) бита на степень прозрачности (alpha channel), и последующие 12 бит на цвета R,G,B. Также теневой фрейбуфер будет содержать точки с маской не 565, а 0444 - то есть 12-ти битный цвет (степень прозрачности во фреймбуфере хранить незачем).

Итого на диске спрайты у меня будут храниться в виде:
0000 WORD - ширина
0002 WORD - высота
0004 WORD - первая точка
... и т.д.

Как сделать данные файлы? А собственно как вам удобно - я например рисовал картинки в GIMP - сохранял их в .png файлах, а затем, используя php-скрипт конвертировал их в данный формат. (этот скрипт внутри прикрепленного файла есть).

Вся графика у меня хранится в этих файлах, вся эта куча файлов должна лежать в определенном каталоге рядом с исполняемым файлом программы. Также все графические файлы загружаются сразу в память при запуске программы... Здесь есть разные пути усовершенствования:
1) Паковать файлы скажем алгоритмом gzip и при считывании распаковывать
2) Считывать не все файлы сразу, а только те которые нужны для определенного уровня игры
3) Считывать определенное число файлов (пока не кончится определенный отведенный для графики кусок памяти) и затем по-необходимости считывать требуемые, одновременно выбрасывая из памяти редко- или последние использованные

Далее ... вывод спрайта в теневой фреймбуфер, а также некоторые функции копирования теневого фреймбуфера в настоящий я написал на arm-ассемблере для ускорения работы.
Как "засунуть" ассемблерную программу в eVC можно почитать скажем тут:
_http://www.pocketmatrix.com/forums/viewtopic.php?t=4063
Довольно большая книжка по ассемблеру:
_http://www.arm.com/miscPDFs/9658.pdf
После использования arm-ассемблера кстати программу можно будет запустить только на реальном устройстве с процессором arm... На эмуляторе вроде как потестировать будет уже нельзя...

Несмотря на то что исходник довольно таки вырос по сравнению с первой частью - в нем по-прежнему нет особенно ничего мега-сложного. Все графические объекты игры (а там их пока всего три - звезды, корабль и выстрелы) описаны своими классами со своими функциями вывода спрайта в теневой фреймбуфер - соответственно основной цикл графики особенно не изменился:
1) очищаем теневой фреймбуфер
2) проходим по всем объектам на игровом поле вызывая для каждого функцию отрисовки спрайта
3) копируем теневой фреймбуфер в настоящий используя преобразование если надо (если например реальный фреймбуфер разрешения 480 х 640, а теневой у меня 240 х 320 - соответственно я его растягиваю)

Скриншот ...
Прикрепленное изображение

В прикрепленном архиве, кроме исходников, в каталоге Release лежит уже скомпилированная программа - этот каталог можно скопировать куда-нибудь на КПК и запустить посмотреть...

Прикрепленные файлы

Прикрепленный файлTest1.zip ( 238.47 КБ )



Реп: (2)
chamine, очень полезные статьи.
В Test1, как мне кажется, есть ошибки - структурная переменная RawFrameBufferInfo уже определена в gx.h. Поэтому компиллятор ругается на ее повторное переопределение в файле gfxdraw.h. Где-то есть ошибка в asm. Как Вы получили ехе - непонятно.



Реп: (34)
Хм ... RawFrameBufferInfo определена в gx.h? Наверное у меня просто старый SDK (Pocket PC 2003 SDK, скачанный ещё в конце 2003 года). Ладно, попробую скачать более новый, посмотрю.
А что за ошибка в asm'e? Просто у меня компилирует вполне нормально ... для проверки например я набрал:
"c:\Program Files\Microsoft eMbedded C++ 4.0\EVC\wce420\bin\armasm.exe" asmdraw.asm asmdraw.obj
А что у тебя за версии armasm кстати? У меня это: ARM Macro Assembler, Version 12.20.9615



Реп: (2)
SDK у меня такой же. Там вот что :
#ifndef GETRAWFRAMEBUFFER
   #define GETRAWFRAMEBUFFER   0x00020001
   typedef struct _RawFrameBufferInfo
   {
    WORD wFormat;
    WORD wBPP;
    VOID *pFramePointer;
    int cxStride;
    int cyStride;
       int cxPixels;
       int cyPixels;
   } RawFrameBufferInfo;

   #define FORMAT_565 1
   #define FORMAT_555 2
   #define FORMAT_OTHER 3
#endif


Версия armasm 14.0.50725.0. У меня студия 2005. Пока детально не смотрел - скачал, попробовал откомпилировать.



Реп: (34)
Странно, но у меня в файле C:\Program Files\Windows CE Tools\wce420\POCKET PC 2003\Include\Armv4\gx.h отсутствует какое-либо упоминание фреймбуфера.
Но проверить его наличие #ifndef действительно полезная штука :rolleyes: , спасибо...



Реп: (2)
chamine @ 24.01.07 23:51:04
Странно, но у меня в файле C:\Program Files\Windows CE Tools\wce420\POCKET PC 2003\Include\Armv4\gx.h отсутствует какое-либо упоминание фреймбуфера...

Тогда понятно. У меня gx.h из другого ресурса:
C:\Program Files\Developer Resources for Windows Mobile 2003 Second Edition\inc\Pocket PC
Кроме того, gx.h входит в пакет студии 2005 - C:\Program Files\Microsoft Visual Studio 8\SmartDevices\SDK\PocketPC2003\Include\gx.h

Ждем Ваших дальнейших статей...

ЗЫ свое промежуточное сообщение удалил - здесь напишу. Все работает замечательно. На ночь глядя не скопировал на кпк директорию с графикой :D

Сообщение отредактировал Silver626 - 25.01.07, 00:35



Реп: (34)
Часть 3.
Примитивная полупрозрачность

Итак... продолжаем графические мучения :D
Сейчас надо будет сказать пару слов о реализации примитивной полупрозрачности спрайтов. Зачем вообще она нужна? А хотя бы для того чтобы более красиво смотрелись выводимые на экран элементы интерфейса (кнопки типа "выход", "опции") и игровые элементы например текущий счет в игре, энергия нашего космического корыта и так далее ибо я их вывожу прямо поверх игрового поля.
Наиболее просто реализовать 50% прозрачность всего спрайта (то есть все точки спрайта выводятся с одинаковой прозрачностью). О хорошей реализации прозрачности (когда любую точку спрайта можно будет вывести с любым коэффициентом прозрачности речь ещё пойдет, но пока рассмотрим такой вот самый простой вариант).

Теперь - что такое полупрозрачность?
Как мы уже знаем - каждая наша точка на экране представлена тремя цветами R,G,B. Допустим на экране уже нарисована точка с коэффициентами R1,G1,B1 и мы хотим поверх неё нарисовать ещё одну полупрозрачную с коэффициентами R2,G2,B2. Действие довольно простое - для 50% прозрачности надо поделить все цветовые коэффициенты на 2 и сложить соответствующие получившиеся значения. То есть результирующая точка будет иметь коэффициенты R = 0.5*R1 + 0.5*R2, G = 0.5*G1 + 0.5*G2, B = 0.5*B1 + 0.5*B2. Всё! Как видите - ничего сложного и если таким образом мы отобразим целый спрайт на экран - он будет выглядеть как раз полупрозрачным.
Осталось теперь это реализовать программно.
У меня теневой буфер экрана как уже говорил имеет цветовую маску 565. Можно конечно выделять из каждой точки по-очереди три цвета, делить их на 2, но это будет нерационально. Немного подумав - можно понять что выделять отдельно цвета не требуется, можно разделить на 2 сразу все 16 бит одной точки а те цветовые биты которые "уползут" за границы - просто убрать. Итого код будет такой:
---
WORD color1 = ...; // цвет исходной точки
WORD color2 = ...; // цвет точки которую мы хотим наложить
// результирующий цвет, 0x7BEF = двоичное 01111 011111 01111
// чтобы убрать верхние биты цветов которые "сползли" при сдвиге
WORD color = ((color1>>1) & 0x7BEF) + ((color2>>1) & 0x7BEF);
---
Или на ассемблере:
---
; по адресу [r0] - исходный цвет (теневой фреймбуфер)
; по адресу [r1] - цвет который хотим наложить
; в r4 наша маска для сдвига - число 0x7BEF
...
ldrh r12, [r1], #2 ; получаем в r12 цвет2 - точка спрайта (одновременно передвигая указатель r1 на след. точку)
tst r12, r12 ; сравниваем цвет2 с нулевым - нулевой цвет у меня полностью прозрачный и я его не рисую
beq label1 ; соответственно если рисовать точку совсем не надо - прыгаем дальше
and r12, r4, r12, LSR #1 ; сдвигаем цвет2 в r12 на 1 (делим на 2) и "вычищаем" верхние биты маской из r4 (0x7BEF)
ldrh r9, [r0] ; аналогичная операция с цветом точки фреймбуфера
and r9, r4, r9, LSR #1
add r12, r12, r9 ; суммируем две точки в r12
strh r12, [r0] ; записываем в теневой фреймбуфер
label1
...
---

"Прокрутив" эти операции в цикле по всему спрайту - получим его вывод наполовину прозрачным.
В моём исходнике это реализовано в ассемблерной процедуре asm_draw_strans(...)
Результат полупрозрачности думаю заметен на скриншоте (вывод надписей) ...

Прикрепленное изображение

Ещё пару слов можно сказать о том как обработать такие моменты когда система в момент работы игры выбрасывает какое-либо окно например на передний план - всё довольно просто - необходимо обрабатывать сообщения WM_ACTIVATE и WM_SETTINGCHANGE и если наше окно стало неактивным например - ставить игру на паузу и прекращать прорисовку графики, соответственно при активизации окна - убирать паузу (или не убирать) и рисовать графику дальше. У меня в исходнике встроена небольшая обработка WM_ACTIVATE, даже как-то худо-бедно работает...

Также в исходнике увеличился код логики самой игры, её понятное дело я объяснять не буду, да и мой код там совсем не супер :nea:

В архиве в каталоге release лежит уже скомпилированная программа - весь каталог (именно весь release) можно записать на КПК и запустить test1.exe.
(Кстати выход из программы - тык стилусом в левый верхний угол экрана).

Прикрепленные файлы

Прикрепленный файлTest1.zip ( 307.47 КБ )


Сообщение отредактировал chamine - 01.02.07, 17:20



Реп: (0)
очень интересно.
будет ли продолжение?



Реп: (64)
+1,очень понятно и юзабельно
ЗЫ
можно кое-где доработать код,снизив кол-во вычислений,например
     // минимальный радиус проверяемого элемента
     __r = (__dx>>1); if ((__dy>>1) < __r) __r = (__dy>>1);
     // расстояние между центрами этих двух элементов
     r2 = sqrt((gem->x-x)*(gem->x-x) + (gem->y-y)*(gem->y-y));
     if (r2 > (r + __r)) continue;
     // нашли столкновение

лучьше написать как
     // минимальный радиус проверяемого элемента
     __r = (__dx>>1); if ((__dy>>1) < __r) __r = (__dy>>1);
     // расстояние между центрами этих двух элементов
     r2 = (gem->x-x)*(gem->x-x) + (gem->y-y)*(gem->y-y);
     if (r2 > (r + __r)*(r+__r)) continue;
     // нашли столкновение

-выигрываем один корень и проигрываем умножение,и всё-равно выигрываем в скорости :)



Реп: (8)
эмм, а как эт все откомпилить?
у меня eVc постоянно ругается:

Прикрепленные изображения
Прикрепленное изображение



Реп: (64)
компилируй не на эмулятор,а на машину Win32 (WCE ARMV4) :)



Реп: (8)
одинаково не компилит:(

даже если ставить pocket pc 2003 device/ Выдает все тоже самое. Библиотеку afx. h уже пытался искать и сувать в Test1 - повторяется все тоже самое:((
что делать то?

Прикрепленные изображения
Прикрепленное изображение



Реп: (64)
смени #include <afx.h>
на #include "afx.h"
afx.h должна быть в папке с сорцом


Полная версия   Текстовая версия

Помощь   Правила

Сейчас: 29.03.24, 01:32