GBA探索日记(二) OBJ及OAM OBJ就是指的一个个的精灵,或者说就是GBA中的小块小块的图片.它其实跟BG差不多.只是它要小一些.它也算个图层.比如GBA中的RPG游戏就喜欢把一个个的人物用OBJ来实现.这样方便控制他们的位置.当然,从GBA探索日记(-)中也可以看到BG的控制其实也是很方便的.而OBJ和BG的差别就是大小.
设置显示模式 如果你的程序里要用OBJ那么, 在设置显示模式的时候需要把DISP_OBJ_ON和DISP_OBJ_CHAR_1D_MAP加进去 *(vu16 *)REG_DISPCNT = DISP_MODE_1 | DISP_OBJ_ON | DISP_OBJ_CHAR_1D_MAP; #define DISP_OBJ_CHAR_1D_MAP 0x0040 // OBJ Character Data 1D Mapping #define DISP_OBJ_ON 0x1000 // OBJ ON
下面就直接来看看OBJ在GBA中怎么实现的. 首先要说明的就是OBJ在GBA中显示也是由一个个8x8的小图块拼成,跟BG一样. 共有下面12种大小. 0000: 8 x 8 1000: 8 x 16 0001: 16 x 16 1001: 8 x 32 0010: 32 x 32 1010: 16 x 32 0011: 64 x 64 1011: 32 x 64 0100: 16 x 8 0101: 32 x 8 0110: 32 x 16 0111: 64 x 32 OBJ的数据分三部分. 1.Character 它跟BG的数据格式是一样的,都是由8x8的小图块的图像数据.而且你也可以通过转换BG的工具来转换OBJ的Character.所以说BG和OBJ的数据结构基本是一样的.不过所有的OBJ总共的Character不能超过32K. 256色的OBJ能有512个Characters,每个Characters占8*8=64bytes.而16色的Characters能有1024个.每个Character占8*8/2=32Bytes.似乎比一个BG的256个Character多?但是BG可以有多个,不同的BG可以有不同的Character,而所有OBJ只能共用这512个Charater. 2.Palette 调色板就不用说了,它跟BG一样,支持一个256色调色板或16个16色的调色板. 3.OAM OAM就是OBJ Attribure Memory的缩写.它就是控制OBJ属性的数据部分.比如OBJ的位置,大小等数据部分就放在这里.每个OBJ的OAM占8个字节,OAM是从内存中07000000h到070003FFh,共1024=8*128,所以OAM总共能有128个.
如何创建一个OBJ呢? 前面我就说过通过转换BG的工具也可以用于转换OBJ. 以前转换BG我建议使用的是AgbLib里的bmp2map.exe,同样,转换OBJ也同样用bmp2map.exe这个工具. 比如我把Character Data放到sprite_gfx这个数组里,把Palette放到sprite_pal里面.(OBJ中没有Map Data,你的转换时可以把Map data也转换进来,只不过后面用不着) 然后在程序里写入:
DmaArrayCopy(3, sprite_gfx,OBJ_MODE1_VRAM,32); DmaArrayCopy(3, sprite_pal,OBJ_PLTT,32);
把sprite_gfx,sprite_pal拷贝到指定内存中去. 这里又设计到OBJ_MODE1_VRAM和OBJ_PLTT两个内存地址 还是看看AgbLib中是怎么定义它们的吧. #define PLTT 0x05000000 // Palette RAM #define PLTT_END (PLTT + 0x400) #define BG_PLTT (PLTT + 0x0) // BG Palette RAM #define BG_PLTT_END (PLTT + 0x200) #define OBJ_PLTT (PLTT + 0x200) // OBJ Palette RAM #define OBJ_PLTT_END (PLTT + 0x400) #define VRAM 0x06000000 // VRAM #define VRAM_END (VRAM + 0x18000) #define BG_VRAM (VRAM + 0x0) // BG Character/Screen RAM #define OBJ_MODE0_VRAM (VRAM + 0x10000) // OBJ Character RAM #define OBJ_MODE1_VRAM (VRAM + 0x10000) #define OBJ_MODE2_VRAM (VRAM + 0x10000) #define OBJ_MODE3_VRAM (VRAM + 0x14000) #define OBJ_MODE4_VRAM (VRAM + 0x14000) #define OBJ_MODE5_VRAM (VRAM + 0x14000) #define OBJ_VRAM_END (VRAM + 0x18000) 这里我把BG中的地址也包含近来了,你可以对比一下BG和OBJ中的地址.
然后就是设置OAM(OBJ Attribure Memory). 这里先看看官方文档中是怎么说这个OAM的数据结构的.

特别指出的是上面表格中Character Name是指这个OBJ的起始Character的引索
我将介绍两中方法来设置.
第一种方法很直观,也很简单.这是我在网上看到最多的. 首先定义了数据结构 typedef struct tagOAMEntry { u16 attribute0; u16 attribute1; u16 attribute2; u16 attribute3; }OAMEntry, *pOAMEntry; //attribute0 #define ROTATION_FLAG 0x100 #define SIZE_DOUBLE 0x200 #define MODE_NORMAL 0x0 #define MODE_TRANSPARENT 0x400 #define MODE_WINDOWED 0x800 #define MOSAIC 0x1000 #define COLOR_16 0x0000 #define COLOR_256 0x2000 #define SQUARE 0x0 #define TALL 0x4000 #define WIDE 0x8000
//attribute1 #define ROTDATA(n) ((n)<<9) #define HORIZONTAL_FLIP 0x1000 #define VERTICAL_FLIP 0x2000 #define SIZE_8 0x0 #define SIZE_16 0x4000 #define SIZE_32 0x8000 #define SIZE_64 0xC000
//atrribute2 #define PRIORITY(n) ((n)<<10) #define PALETTE(n) ((n)<<12)
然后写入代码: OAMEntry sprites[128]; // 这里把总共128个OBJ都定义了,不过我们现在只使用第一个 void InitializeSprites() { u16 loop; for(loop = 0; loop < 128; loop++) { sprites[loop].attribute0 = 160; //y to > 159 sprites[loop].attribute1 = 240; //x to > 239 } } void CopyOAM() { u16 loop; u16* temp; u16* _OAM = (u16*)0x7000000; temp = (u16*)sprites; for(loop = 0; loop < 128*4; loop++) { _OAM[loop] = temp[loop]; } }
u16 spriteY=100; u16 spriteX=100; InitializeSprites(); sprites[0].attribute0 = COLOR_256 | SQUARE | spriteY; sprites[0].attribute1 = SIZE_16 | spriteX; sprites[0].attribute2 = 0 | PRIORITY(0);
由于这个OBJ的Character是从OBJ_MODE1_VRAM的首地址开始写入的,所以它的Character Name就应该是0.所以sprites[0].attribute2 = 0 | PRIORITY(0);
由于GBA中16色的OBJ和256色的OBJ中对于Characters的寻址方式是一样的.那么无论是16色的还是256色的OBJ,它们的起始Characters都应该是OBJ的VRAM+Character Name*32(虽然256色下的Character占64Bytes).
比如看看下面的例子:
如果传输256色的sprite的Character Data到OBJ_MODE1_VRAM+N*32的地址 DmaArrayCopy(3,sprite_gfx,OBJ_MODE1_VRAM+N*32,32); 那么它对应的的attribute2中的Character Name应该是N. sprites[0].attribute2 = N; N的范围是0-1023
对于attribute3,我们现在暂时不用.
接下来就是把sprites写到OAM中 要在程序的主循环中不断更新OAM while(1) { // 等待图像缓冲同步 WaitSync(); CopyOAM(); }
第二种方法是AgbLib里面使用的方法. 比如我要显示一个是OBJ 就直接定义个 vu32 bg_oam_buffer[2]; 然后同样要写Character和Palette数据进去 DmaArrayCopy(3, sprite_gfx,OBJ_MODE1_VRAM,32); DmaArrayCopy(3, sprite_pal,OBJ_PLTT,32); 设置OAM的时候就这样写 bg_oam_buffer[0] = OAM_COLOR_256 | OAM_SQUARE |OAM_SIZE_16x16 | (100) << OAM_H_POS_SHIFT | (72) << OAM_V_POS_SHIFT; bg_oam_buffer[1] = 0x0400; 下面给出AgbLib中对于上面的宏的定义: #define OAM_V_POS_SHIFT 0 #define OAM_H_POS_SHIFT 16
#define OAM_COLOR_16 0x00000000 // Select 16 #define OAM_COLOR_256 0x00002000 // Select 256 colors
#define OAM_SIZE_8x8 0x00000000 // OBJ 8 x 8 dot #define OAM_SIZE_16x16 0x40000000 // OBJ 16 x 16 dot #define OAM_SIZE_32x32 0x80000000 // OBJ 32 x 32 dot #define OAM_SIZE_64x64 0xc0000000 // OBJ 64 x 64 dot #define OAM_SIZE_16x8 0x00004000 // OBJ 16 x 8 dot #define OAM_SIZE_32x8 0x40004000 // OBJ 32 x 8 dot #define OAM_SIZE_32x16 0x80004000 // OBJ 32 x 16 dot #define OAM_SIZE_64x32 0xc0004000 // OBJ 64 x 32 dot #define OAM_SIZE_8x16 0x00008000 // OBJ 8 x 16 dot #define OAM_SIZE_8x32 0x40008000 // OBJ 8 x 32 dot #define OAM_SIZE_16x32 0x80008000 // OBJ 16 x 32 dot #define OAM_SIZE_32x64 0xc0008000 // OBJ 32 x 64 dot 你可以看出. bg_oam_buffer[0]其实就是第一种方法的attribute0,attribute1的集合. bg_oam_buffer[1]就是第一种方法的attribute2,attribute3的集合. 然后同样地在程序住循环里不断更新写入OAM while(1) { DmaCopy(3, bg_oam_buffer, OAM, 8 , 32); } 这是OAM的定义: #define OAM 0x07000000 // OAM #define OAM_END (OAM + 0x400)

|