GBA 开发简单入门 |
||||||||
一. GBA开发包--DevKitAdv 简介 DevKitAdv 主要包括两部分,一是GCC++编译器,二是 GBA库. GCC++编译器功能和我们常用的VC差不多,只不过少了个编辑源代码的文本编辑器(至少我没发现,我用的是EditPlus,UltraEdit也可以),还有就是--不支持类(class),真是让人头痛,只能用struct来替代.它的作用是把我们写的代码编译成二进制的可执行文件,当然这个可执行文件是相对GBA和GBA模拟器而言的.就象Windows里的EXE文件无法在Mac机上使用是一样的道理; GBA库提供了图像,控制及声音一系列的函数,和GCC++配合使用. 下载地址: http://occultforces.mine.nu/~darkfader/gba/files/devkitadv.zip
|
||||||||
二. DevKitAdv 的安装 set PATH=d:/devkitadv/bin;%PATH% cmd (win98是command) |
||||||||
三. 最简单的 GBA 程序 (t1) // main.c // 一些基本数据类型 typedef unsigned char u8; typedef unsigned short u16; typedef unsigned long u32; #define REG_DISPCNT *(u16*)0x04000000 // 显示寄存器地址 #define VRAM 0x06000000 // 图像缓冲区地址 #define M5_VRAM 0x0600A000 // M5缓冲区地址 #define BACKBUFFER 0x010 // 双缓冲/背缓冲地址 #define PALETTE 0x5000000 // 调色板地址 #define MODE_3 0x03 // 240*160 15bits/单缓冲区 #define MODE_4 0x04 // 240*160 8bits/双缓冲区 #define MODE_5 0x05 // 160*128 15bits/双缓冲区 #define BG2_ENABLE 0x0400 // BG_2 #define (Mode) REG_DISPCNT=(Mode) // 设置显示模式的宏定义 // ----------- 主程序 ------------ int main() { //设置屏幕模式,这里使用MODE_4 SetMode (MODE_4 | BG2_ENABLE); } 1.MODE_5和MODE_3都是16bits,但MODE_3只有单缓冲,制作动画效果肯定没双缓冲好,因此排除MODE_3; 2.MODE_4是8bits,理论上256色对于掌机够用了,虽然16bits真彩的诱惑没有人想抗拒,可MODE_5只有160*128咧,在实际应用中建议还是使用MODE_4. 很简单吧--的确是的,现在要用GCC编译它: gcc -lm -o main.elf main.c objcopy -v -O binary main.elf main.bin
|
||||||||
四. 在MODE_4背景层画图的 GBA 程序 (t2)
1. 把原始256色图像文件转换成 *.h / *.c 的数据文件,我们用的是 < BMP2GBA > ,这里以"image.bmp"为例,转换后我们就得到了一个"image.h"文件; 2. 在程序开头#include "image.h",这样就能在程序中使用"image.h"定义的调色板和图像数据; 3. 在程序中把"image.h"定义的调色板和图像数据写入MODE_4背景层的调色板和图像缓冲区. 另外,GBA还有专为精灵设置的物体层,它的用法和背景层一样,只是功能有点不一样,地址是0x06000000.有关用这里就不详细说了,大家可以把精灵数据直接输出到物体缓冲区就可以了. 下面是源程序: ... ... // 包含图像调色板和数据的头文件 #include "gfx/image.h" // ----------- 全局变量 -------- // 系统调色板 u16* palette_mem=(u16*)PALETTE; // 图像缓冲区 u16* video_buffer=(u16*)VRAM; // ----------- 函数定义 --------- // MODE_4绘图函数 void Draw(u16* src_palette,u16* src_data,u16* dst_palette,u16* dst_data); // ----------- 主程序 ------------ int main() { // 设置屏幕模式,这里使用MODE_4 SetMode (MODE_4 | BG2_ENABLE); // 在背景层画图,Palette和Data是在"image.h"定义的调色板和图像数据数组名 Draw(Palette,Data,palette_mem,video_buffer); } void Draw(u16* src_palette,u16* src_data,u16* dst_palette,u16* dst_data) { int loop,x,y; // 写入目的调色板 for(loop = 0; loop < 256; loop++) dst_palette[loop] = src_palette[loop]; // 写入图像缓冲区 for(x = 0; x < 120; x++) { for(y = 0; y < 160; y++) { dst_data[(y) *120 + (x)]=src_data[(y) *120 + (x)]; } } } 编译后得到main.bin,然后在GBA模拟器里运行,就可以得到这样的结果: 大家看了可能有点失望~是啊,256色用来看PPMM实在... ...下一步我们就使用MODE_5来画16bits真彩的MM! |
||||||||
五. 在MODE_5画图的 GBA 程序 (t3) 在GBA的MODE_5里画一幅图也要经过相似3个步骤,只不过不需要调色板数据: 1. 把原始真彩图像文件转换成 *.h / *.c 的数据文件,我们用的是 < Targa2GBA > ,这里以"image.bmp"(240*160)为例,DOS窗口下进Targa2GBA目录,输入"t2g mode5 image.bmp image.h",转换后我们就得到了一个"image.h"文件; 2. 在程序开头#include "image.h",这样就能在程序中使用"image.h"定义的图像数据; 3. 在程序中把"image.h"定义的图像数据写入图像缓冲区. 下面就是源程序: // 包含图像数据的头文件 #include "gfx/image.h" // ----------- 全局变量 -------- // 图像缓冲区 u16* video_buffer=(u16*)VRAM; // ----------- 函数定义 --------- // MODE_5绘图函数 void Draw(int x,int y,int w,int h,u16 *src_data,u16 *dst_data); // ----------- 主程序 ------------ int main() { // 设置屏幕模式,这里使用MODE_5 SetMode (MODE_5 | BG2_ENABLE); // 在背景层画图,image是在"image.h"定义的图像数据数组名 Draw(0,0,240,160,image,video_buffer); } void Draw(int x,int y,int w,int h,u16 *src_data,u16 *dst_data) { int i,o,idst; // 把源图像数据复制到图像缓冲区的指定地方 idst =(y*160)+x; for (i=0;i |
||||||||
六. 全屏显示的 MODE_5 GBA 程序 (t4) 由于GBA不支持线性的图像变换,因此得到的结果会产生一些马赛克的现象,现在还是附上这个变换函数和最终结果,其实质量还是可以接受的,大家可以试试使用这个新的MODE_5. // 切换到新MODE_5全屏模式,page为缓冲区,原理是把显示寄存器数据X,Y交换,得到128*160的显示,GBA就会全屏显示. void (int page) { u16 *ioreg=(u16*)0x4000000; *ioreg=5+((page&1)>>4)+(1>>10); ioreg[0x10]=0; ioreg[0x11]=256; ioreg[0x12]=128; ioreg[0x13]=0; } int main() { // 设置屏幕模式,这里使用MODE_5 SetMode (MODE_5 | BG2_ENABLE); // 切换模式 SetFlipMode(0); // 在背景层画图,image是在"image.h"定义的图像数据数组名 Draw(0,0,240,160,image,video_buffer); return(0); } Come on,来看看结果: 此时就是新MODE_5下的全屏模式,图像被翻转并放大,因为MODEL_5的分辨率是160*128,而上面的程序画的是240*160的图像,所以有部分就看不到了. 如果要象MODEL_4那样正常显示我们的图像,那么只有对原图像文件做点手脚才行了,下面就是处理新MODEL_5下图像文件的步骤: 1.把原图像顺时针旋转90度; 2.再将图像水平翻转; 3.调整图像大小至 160*128. 这里是示意图:
OK,把步骤3后的图像编译进bin就可以得到: 大家可以看到有轻微的马赛克,但比起256色的效果还是要好一些,到底使用什么模式还得看做的游戏类型,如果是一些经典RPG/ACT/SLG等还是建议用MODE_4.
|
||||||||
七. GBA的双缓冲显示(t5) 大家在做上面MODEL_5的程序时一定会发现图像在闪动(第六节的240*160的MM象被破了相...),而MODEL_4下却比较稳定--这是因为MODEL_5下要处理16bits(实质上是15bits)的图像,数据量比MODEL_4下的8bits大很多,在没使用双缓冲的情况下,图像填充时就会造成闪烁,这就是为什么我们抛弃了MODEL_3的原因... 原理也很简单,图像在背缓冲区里填充好之后再直接输出到前缓冲区显示,程序里就是一个 "等待同步-> 交换缓冲" 的过程: ... ... // ----------- 全局变量 -------- // 图像缓冲区 // ----------- 函数定义 --------- ... ... // 等待缓冲区数据同步 void WaitSync (); // 交换缓冲区内容 void SwapScreen (); // ----------- 主程序 ------------ int main() { // 设置屏幕模式,这里使用MODE_5 SetMode (MODE_5 | BG2_ENABLE); while(1) { // 在背景层画图,image是在"image.h"定义的图像数据数组名 Draw(0,0,240,160,image,video_buffer); WaitSync(); SwapScreen(); } } void WaitSync () { while (*(volatile u16*)0x4000006<160) {}; } void SwapScreen () { if (REG_DISPCNT & BACKBUFFER) { REG_DISPCNT &= ~BACKBUFFER; video_buffer = (u16*) M5_VRAM; } else { REG_DISPCNT |= BACKBUFFER; video_buffer = (u16*) VRAM; } } |
||||||||
八. GBA 的按键输入(t6) 讲了老半天的图像,虽说是对着MM,但大家一定也有点烦了,我们现在就换个方向,来看看GBA的控制. ... ... // 按键控制 #define KEY_A 1 #define KEY_B 2 #define KEY_SELECT 4 #define KEY_START 8 #define KEY_RIGHT 16 #define KEY_LEFT 32 #define KEY_UP 64 #define KEY_DOWN 128 #define KEY_R 256 #define KEY_L 512 volatile u32* KEYS = (volatile u32*)0x04000130; // 包含图像调色板和数据的头文件 #include "gfx/image.h" // ----------- 全局变量 -------- // 图像缓冲区 u16* video_buffer=(u16*)M5_VRAM; // 图像显示坐标 int img_x,img_y; // ----------- 函数定义 --------- // 按键控制 void KeyAction(); ... ... // ----------- 主程序 ------------ int main() { // 设置屏幕模式,这里使用MODE_5 SetMode (MODE_5 | BG2_ENABLE); while(1) { // 处理按键事件 KeyAction(); // 在背景层画图,image是在"image.h"定义的图像数据数组名 Draw(img_x,img_y,96,64,image,video_buffer); WaitSync(); SwapScreen(); } } void KeyAction() { // 上方向键 if(! ( (*KEYS) & KEY_UP) ) { img_y+=5; } // 下方向键 if(! ( (*KEYS) & KEY_DOWN) ) { img_y-=5; } } 简单吧,这个例子响应上下按键来控制图像上下移动.我们没有先清除背缓冲就在里面填充图像数据,所以你可以看到图像移动后,原来位置上的图像仍在背缓冲里,这样可以很清楚的看到双缓冲的工作过程. 什么?太丑了,那好,加一个背景就OK了嘛... ...后一幅是带ALPHA. #include "bg.h" ... ... // 先画背景 ... ... |
||||||||
九. 简单声音输出(t7) Simple is the Best(简洁至上),这里我们使用一个现成的声音模块(Troff Player,by Vova & Serge).这里还要用到一个转换工具< MOD2GBA >,用来把MOD音乐文件转换成GBA的 *.c / *.h 声音数据文件.MOD和MIDI差不多,但支持更多更强的效果. // MOD数据文件 #include "song_data.h" // MOD播放函数文件 #include "modplayer.h" // ----------- 主程序 ------------ int main() { //设置屏幕模式,这里使用MODE_4 SetMode (MODE_4 | BG2_ENABLE); // 初始化声音(声道数,音量) InitSound(2, 7); // 初始化音乐(节拍,循环) InitSong(20000, 0); while(1) { // 更新音乐播放状态 UpdateSong(); } } OK,就这么EZ. |
||||||||
十. 用图块建立可滚动/缩放/旋转的背景(t8) 这一节主要是源程序中注释为主,这里就不详细说明了."gba.h"包含基本宏定义,"maths.h"是sin/cos乘256后的值数组,"main.h"包括了我们定义背景结构及操作背景的函数. 程序中的地图背景是由不同的图块所构成,而这些图块统一紧挨着放在一个图像文件,这样每个图块就会有一个索引号;地图信息只要记录这张地图里共有多少个 单位(图块)以及每个单位对应的图块索引号就OK了,在例子中"gfx/tiles.h"就是图块大本营,而"gfx/level1.h"则是图块索引排列表.地图工具为"map editor beta 4". 背景的滚动/缩放/旋转是通过一系列的简单数学计算,修改GBA系统提供的一些背景属性来完成,因为是由硬件来完成背景的操作(MODE_1),所以速度很快,我还有个MODE_5下直接修改像素点位置来完成旋转的例程,待会儿大家可以比较一下. |
||||||||
十一. MODE_5下的背景旋转(t9) 这个例子通过计算旋转后像素的新位置来完成旋转,原理很简单,可以直接应用到精灵上. // ----------- 主程序 ------------ int main() { // 角度 int i=0; // 设置屏幕模式,这里使用MODE_5 SetMode (MODE_5 | BG2_ENABLE); while(1) { // 在背景层画图,image是在"image.h"定义的图像数据数组名 Draw(0,0,128,128,bg,video_buffer); // 将旋转后的背景数据写入图像缓冲 RotateBG(bg,video_buffer,i++); if (i==360) i=0; } } // 旋转背景 void RotateBG(u16 *src,u16 *dst,int angle) { int x,y,ys,yc,tx,ty; unsigned int srcptr=0,dstptr =0; // 计算旋转后像素的指针 for (y=-80;y<80;y++) { ys = y * Sin [angle]; yc = y * Cos [angle]; for (x=-80;x<80;x++) { tx =(((x*Cos [angle]) - ys))>>7; ty =(((x*Sin [angle]) + yc))>>7; srcptr = (((ty<<7)+tx+(160*80+80)) & 16383); dst [dstptr] = src [srcptr]; dstptr++; } } }
|
||||||||
十二. GBA下的中文显示 呵呵,英文的还没来得及写就先把中文搞掂了~~~~ 这里所做的程序核心技术来自于DOS下的中文显示技术.其实和老外用位图来显示26个英文字母原理大同小异,我们使用16*16点阵字库来完成中文的显示. 大家一定知道在输入法里有一个从没用过区位(内码)输入法,这个输入法使用4个数字来完成中文输入----估计没有谁会用它来和MM聊天.中文字库其实也就可以看成一张很大的位图,上面按一定顺序画满了常用的汉字,而区位(内码)输入法的4个数字代表了一个汉字在字库里所在的区和具体位置. 举个例子,"说"字的区位码是"4321",当我们选择区位输入而且在记事本里输入"4321",输入法程序就会马上到字库里4321区域,然后把4321区域的点阵(位图)数据传给系统显示出来,这样"说"字就出来了. 在GBA里显示中文是一样的原理,但由于GBA没有文件输入/输出函数,我们不得不先把二进制的点阵字库转换成数组存储数据,包括区位码和点阵(位图)数组.这里就不详细说明原理了,我为大家做好了一个 你首先得把你所有的文字做成一个文本文件,比如" zk.txt ",然后执行" hzk2gba zk.txt "来得到" GBAZK.h "文件,这就是GBA可用的字库数组头文件. 这样做的目的是不用把字库里所有的汉字送入GBA,只留下我们用到的汉字,可以很大的节省空间和内存. 接下来的工作就是利用得到的" GBAZK.h "在GBA里写下你想写的话. ... ... #define RGB(r,g,b) ((r)+(g<<5)+(b<<10)) // 设置GBA的色彩 #define MAX 10240 // 最大字符数 // 包含中文字库数据的头文件 #include "gfx/GBAZK.h" // ----------- 全局变量 -------- // 图像缓冲区 u16* video_buffer=(u16*)VRAM; // ----------- 主程序 ------------ int main() { // 设置屏幕模式,这里使用MODE_5 SetMode (MODE_5 | BG2_ENABLE); // 写入字符 DrawText("水银教程",20,20,255,0,0); } //- 写中文字符函数 ---------------- void DrawText(unsigned char *str,int x,int y,int r,int g,int b) { int i,j,k,n; // 字符所在区,位,实际位置 int qu, wei, location; while(*str) { // 得到单字的区位码 qu = *(str++)-0xa0; wei = *(str++)-0xa0; location = qu*94+wei; // 在字库里查找 for(n=0 ;n 就这么多了,字体样式的问题得等下一步解决TrueType字体显示才行,字体大小嘛,大家自己用背景/物体层的缩放或是直接缩放像素的方法试试.至于英文的显示~~~~~ ^_* ,好困噢~~~~ 下回见!!!!! |
||||||||
到这里先告一个段落了,大家可以到www.gbadev.com上了解更多的东西,我这里就不累赘了. |