- 精华
- 3
- 帖子
- 11434
- 威望
- 14 点
- 积分
- 12724 点
- 种子
- 14 点
- 注册时间
- 2004-2-3
- 最后登录
- 2024-11-12
|
楼主 |
发表于 2010-3-22 22:09 · 北京
|
显示全部楼层
[B]章节 6. 精灵图是什么?如何使用?[/B][/ALIGN]
[B]内容目录[/B][/ALIGN]
何为精灵图像? [/COLOR] [/ALIGN]
OAM介绍[/COLOR] [/ALIGN]
精灵图像显示的硬件之信息 [/COLOR] [/ALIGN]
精灵图在内存中的存储方式[/COLOR] [/ALIGN]
精灵图像的属性[/COLOR] [/ALIGN]
更新OAM[/COLOR] [/ALIGN]
OAM初始化[/COLOR] [/ALIGN]
旋转精灵图 [/COLOR] [/ALIGN]
显示隐藏的精灵图[/COLOR] [/ALIGN]
移动精灵图[/COLOR] [/ALIGN]
设定精灵图的优先级[/COLOR] [/ALIGN]
精灵图的使用[/COLOR] [/ALIGN]
为精灵图像设置VRAM显存 [/COLOR] [/ALIGN]
精灵图像的寻址方式 [/COLOR] [/ALIGN]
精灵图像的读入 [/COLOR] [/ALIGN]
断言是什么?[/COLOR] [/ALIGN]
精灵图的显示[/COLOR] [/ALIGN]
编译[/COLOR][U][/U][/ALIGN]
[B]何为精灵图像?[/B][/ALIGN]
精灵图像并不是什么多么神奇的东西。它是由程序生成的一系列2D图像的动画或者单幅图片。任天堂DS主机有专门的精灵图像处理硬件。这对于2D图像的显示非常有帮助。很多游戏主机没有2D核心,所有的精灵图以及其他2D图像都必须通过3D的方式绘制很多2D方块图来实现。[/ALIGN]
[B]OAM[/B][B]介绍[/B][/ALIGN]
精灵图是由OAM来进行管理的。管理精灵图是一项十分巨大的工程,大部分的工作都无须我们来进行。想想看可能觉得它很神奇但实际并非如此。OAM全称为对象属性存储器(Object Attribute Memory),是内存当中我们用于控制精灵图变化的内存空间。OAM配合SpriteEntry与SpriteRotation来管理精灵图的属性。[/ALIGN]
[B]精灵图像显示的硬件之信息[/B][/ALIGN]
对于任天堂DS主机,我们有总共128个精灵图可以使用。其中只有32个精灵图可以进行仿射变化(旋转、缩放、歪斜等等)。我们在每一个引擎中使用1024个不同地址的块状图来组成精灵图。可以使用块状图来呈现16色或者一组256色的精灵图。使用256色的块状图组成的精灵图是仅使用16色的精灵图的体积的2倍。另一个使用16色精灵图的优势就是可以使用16种不同的色调。当使用256色的精灵图时,每一个精灵图(包括块状图)都必须使用相同的色调。而当使用16色的精灵图时,可以采用不同的色调来进行配图,即使无论是256色还是16色都要使用到相同的块状图数据。游戏当中经常使用这一技巧来表现外形一样但是颜色不同的敌人,虽然块状图一样,但是色调并不尽相同。[/ALIGN]
[/ALIGN]
[B]精灵图在内存中的存储方式[/B][/ALIGN]
精灵图的分辨率为***像素,这就是之前提到的块状图。当要在屏幕中显示的时候,主机首先会把这些块状图像拼图一样无缝地拼在一起。在给精灵图分块的时候可以有两种方式,分别是1D和2D。在2D方式下,精灵图的内存是将每一块块状图最后拼成一个大的图像的形式来存储的。而在1D方式下,精灵图是以线性方式,在[U]图表6.1中可以看到内存排列方式。[/U][/ALIGN]
转换过程和背景图的转换过程极为相似。我们做了几个grit的规则文件来规定grit的转换方式,最后将生成一个.o以及一个头文件。grit规则文件和图像文件也在gfx的文件夹下。[/ALIGN]
[B]图表6.1 下面的文档显示了块状图显示方式的内存数据情况。 [/B][/ALIGN]
const u16 data[] = {[/ALIGN]
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,[/ALIGN]
0x0F0F, 0x0F0F, 0x0F0F, 0x0F0F, 0x0F0F, 0x0F0F, 0x0F0F, 0x0F0F,[/ALIGN]
0x2020, 0x2020, 0x2020, 0x2020, 0x2020, 0x2020, 0x2020, 0x2020,[/ALIGN]
0x2F2F, 0x2F2F, 0x2F2F, 0x2F2F, 0x2F2F, 0x2F2F, 0x2F2F, 0x2F2F,[/ALIGN]
0x4040, 0x4040, 0x4040, 0x4040, 0x4040, 0x4040, 0x4040, 0x4040,[/ALIGN]
0x4F4F, 0x4F4F, 0x4F4F, 0x4F4F, 0x4F4F, 0x4F4F, 0x4F4F, 0x4F4F,[/ALIGN]
0x6060, 0x6060, 0x6060, 0x6060, 0x6060, 0x6060, 0x6060, 0x6060,[/ALIGN]
0x6F6F, 0x6F6F, 0x6F6F, 0x6F6F, 0x6F6F, 0x6F6F, 0x6F6F, 0x6F6F,[/ALIGN]
0x8080, 0x8080, 0x8080, 0x8080, 0x8080, 0x8080, 0x8080, 0x8080,[/ALIGN]
0x8F8F, 0x8F8F, 0x8F8F, 0x8F8F, 0x8F8F, 0x8F8F, 0x8F8F, 0x8F8F,[/ALIGN]
0xA0A0, 0xA0A0, 0xA0A0, 0xA0A0, 0xA0A0, 0xA0A0, 0xA0A0, 0xA0A0,[/ALIGN]
0xAFAF, 0xAFAF, 0xAFAF, 0xAFAF, 0xAFAF, 0xAFAF, 0xAFAF, 0xAFAF,[/ALIGN]
0xC0C0, 0xC0C0, 0xC0C0, 0xC0C0, 0xC0C0, 0xC0C0, 0xC0C0, 0xC0C0,[/ALIGN]
0xCFCF, 0xCFCF, 0xCFCF, 0xCFCF, 0xCFCF, 0xCFCF, 0xCFCF, 0xCFCF,[/ALIGN]
0xE0E0, 0xE0E0, 0xE0E0, 0xE0E0, 0xE0E0, 0xE0E0, 0xE0E0, 0xE0E0,[/ALIGN]
0xEFEF, 0xEFEF, 0xEFEF, 0xEFEF, 0xEFEF, 0xEFEF, 0xEFEF, 0xEFEF};[/ALIGN]
const u16 data[] = {[/ALIGN]
0x0000, 0x0000, 0x0000, 0x0000, 0x0F0F, 0x0F0F, 0x0F0F, 0x0F0F,[/ALIGN]
0x2020, 0x2020, 0x2020, 0x2020, 0x2F2F, 0x2F2F, 0x2F2F, 0x2F2F,[/ALIGN]
0x4040, 0x4040, 0x4040, 0x4040, 0x4F4F, 0x4F4F, 0x4F4F, 0x4F4F,[/ALIGN]
0x6060, 0x6060, 0x6060, 0x6060, 0x6F6F, 0x6F6F, 0x6F6F, 0x6F6F,[/ALIGN]
0x0000, 0x0000, 0x0000, 0x0000, 0x0F0F, 0x0F0F, 0x0F0F, 0x0F0F,[/ALIGN]
0x2020, 0x2020, 0x2020, 0x2020, 0x2F2F, 0x2F2F, 0x2F2F, 0x2F2F,[/ALIGN]
0x4040, 0x4040, 0x4040, 0x4040, 0x4F4F, 0x4F4F, 0x4F4F, 0x4F4F,[/ALIGN]
0x6060, 0x6060, 0x6060, 0x6060, 0x6F6F, 0x6F6F, 0x6F6F, 0x6F6F,[/ALIGN]
0x8080, 0x8080, 0x8080, 0x8080, 0x8F8F, 0x8F8F, 0x8F8F, 0x8F8F,[/ALIGN]
0xA0A0, 0xA0A0, 0xA0A0, 0xA0A0, 0xAFAF, 0xAFAF, 0xAFAF, 0xAFAF,[/ALIGN]
0xC0C0, 0xC0C0, 0xC0C0, 0xC0C0, 0xCFCF, 0xCFCF, 0xCFCF, 0xCFCF,[/ALIGN]
0xE0E0, 0xE0E0, 0xE0E0, 0xE0E0, 0xEFEF, 0xEFEF, 0xEFEF, 0xEFEF,[/ALIGN]
0x8080, 0x8080, 0x8080, 0x8080, 0x8F8F, 0x8F8F, 0x8F8F, 0x8F8F,[/ALIGN]
0xA0A0, 0xA0A0, 0xA0A0, 0xA0A0, 0xAFAF, 0xAFAF, 0xAFAF, 0xAFAF,[/ALIGN]
0xC0C0, 0xC0C0, 0xC0C0, 0xC0C0, 0xCFCF, 0xCFCF, 0xCFCF, 0xCFCF,[/ALIGN]
0xE0E0, 0xE0E0, 0xE0E0, 0xE0E0, 0xEFEF, 0xEFEF, 0xEFEF, 0xEFEF};[/ALIGN]
[/ALIGN]
[B]精灵图像的属性[/B][/ALIGN]
精灵图有三种不同的属性设置。根据设置的不同,像素图可以进行旋转、翻转、马赛克风格以及各种有趣的效果。每一种属性都适用于很多种形式,但是需要设置一系列的位操作才能够实现我们想要实现的效果。因此也就不受libnds提供给我们的SpriteEntry单元的限制了。在多数情况下,我们要用到这个单元来实现我们想要的精灵图,但是由于现在有了这样几个特殊的设置,也就需要一些改动了。[/ALIGN]
接下来我们会讲到如何使用位操作来更新、初始化、旋转,libnds提供了的一些功能比如可以实现精灵图移动什么的。那么就让我们在这样的情况下来写几个函数试试看吧。[/ALIGN]
在文档中提供了以个头文件,目录为code/chapter_6-sprites/include/sprites.h。其中包含了精灵图使用时涉及到的一些函数声明。下面我们将在一个新的工程文件sprites.cpp中使用着每一个函数来实现精灵图。首先要新建一个源代码文件,名称修改为sprites.cpp,然后放进你的源文件目录下。[/ALIGN]
[B]更新OAM [/B][/ALIGN]
不用直接在OAM上进行修改,我们要复制OAM然后使用updateOAM来拷贝内存中每一帧的内容。这么做的原因是OAM本身除了vblank期间以外都是只读的。在拷贝OAM的备份进入到原本的OAM之前,需要经过vblank。[/ALIGN]
更新OAM非常直接。只要把我们的OAM备份拷贝到真正的OAM即可。OAM备份有可能会发生卡死在缓存中无法操作的情况,这时我们需要首先刷新本地内存(需要DMA操作)来确保DMA识别到的是正确的数据。在这之后如果还是卡死的话,就要告诉OAM立即进行查找OAM表然后创建一个关于每一份精灵图的新信息。[/ALIGN]
void updateOAM(OAMTable * oam) {[/ALIGN]
DC_FlushAll();[/ALIGN]
dmaCopyHalfWords(SPRITE_DMA_CHANNEL,[/ALIGN]
oam->oamBuffer,[/ALIGN]
OAM,[/ALIGN]
SPRITE_COUNT * sizeof(SpriteEntry));[/ALIGN]
}[/ALIGN]
[/ALIGN]
[B]OAM [/B][B]初始化[/B][/ALIGN]
在进行OAM初始化时首先要做的就是清除OAM当中所有的精灵图信息。之后,要调用之前写的updateOAM函数。[/ALIGN]
void iniOAMTable(OAMTable * oam) {[/ALIGN]
/*对DS当中全部128个精灵图进行删除,清除已有属性。防止显示时出现遗留下来的数据,并提供一个干净的工作内存区域。*/[/ALIGN]
for (int i = 0; i [/ALIGN]
oam->oamBuffer .attribute[0] = ATTR0_DISABLED;[/ALIGN]
oam->oamBuffer .attribute[1] = 0;[/ALIGN]
oam->oamBuffer .attribute[2] = 0;[/ALIGN]
}[/ALIGN]
for (int i = 0; i [/ALIGN]
/*如果你认真观察一下,可以看到这就是之前用过的仿射变换矩阵。如同在背景图中使用时的那样,将其定义为确切的矩阵。*/[/ALIGN]
oam->matrixBuffer .hdx = 1 [/ALIGN]
oam->matrixBuffer .hdy = 0;[/ALIGN]
oam->matrixBuffer .vdx = 0;[/ALIGN]
oam->matrixBuffer .vdy = 1 [/ALIGN]
}[/ALIGN]
updateOAM(oam);[/ALIGN]
}[/ALIGN]
[/ALIGN]
[B]旋转精灵图[/B][/ALIGN]
现在来试着进行精灵图的旋转操作吧。这相比我们之前做过的东西会有一点难,但是不要怕,还是很有趣的。首先不用针对作出每一个精灵图旋转时的位置,这一点是非常好的。但是要调试出真正想要实现的效果的话,要做出一系列必要的调试工作。[/ALIGN]
libnds中的sin和cos函数是利用查找表来实现的,总共可以实现32768度。人们通常使用360度来描述度数或者弧度。在这里我们要使用的角度也是这32768中的其中一部分,所以不得不将我们习惯的360度制转换成32768度制。 [/ALIGN]
需要根据之前用到的仿射变化矩阵来制作一个变换方式。精灵图的仿射变化矩阵和背景图仿射变化矩阵有些许的不同。如果你有线性代数的基础的话,我建议你读一读下面的硬件信息:[U] http://www.coranac.com/tonc/text/affine.htm[/COLOR][/U]. [/ALIGN]
void rotateSprite(SpriteRotation * spriteRotation, int angle) {[/ALIGN]
s16 s = sinLerp(angle) >> 4;[/ALIGN]
s16 c = cosLerp(angle) >> 4;[/ALIGN]
[/ALIGN]
spriteRotation->hdx = c;[/ALIGN]
spriteRotation->hdy = s;[/ALIGN]
spriteRotation->vdx = -s;[/ALIGN]
spriteRotation->vdy = c;[/ALIGN]
}[/ALIGN]
[/ALIGN]
[B]显示隐藏的精灵图 [/B][/ALIGN]
直到现在,我们还没有使用过这个libnds中神奇的SpriteEntry单元。在很多案例中我们都可以利用它来替我们实现位操作。在显示一张隐藏的精灵图时,由于NDS主机的特异性,我们还是需要自己来进行位操作:如果是仿射的隐藏精灵图,那么它的位数(位9的精灵图、属性0)是普通精灵图(位8的精灵图、属性0)位数的2倍。一步一步跟着下面的操作来走,我们将建立一个函数来实现显示各种隐藏精灵图。[/ALIGN]
void setSpriteVisibility(SpriteEntry * spriteEntry, bool hidden, bool affine,[/ALIGN]
bool doubleBound) {[/ALIGN]
if (hidden) {[/ALIGN]
/*[/ALIGN]
*使精灵图精灵图不可视。[/ALIGN]
*仿***灵图无法隐藏。我们必须将其转换为非仿***灵图。并且要设置位8清除位9。由于这个位是多余的,所以最好节省这一位以提高操作速度。[/ALIGN]
*/[/ALIGN]
spriteEntry->isRotateScale = false; //关闭位9[/ALIGN]
spriteEntry->isHidden = true; // 开启位8[/ALIGN]
} else {[/ALIGN]
/*使精灵图可见*/[/ALIGN]
if (affine) {[/ALIGN]
/*再一次的,要记住仿***灵图不能被隐藏,这是为了保证能够显示出完整的仿***灵图。我们还需要允许精灵图属性中的2倍边界标识符可读。如果不这么做,精灵图隐藏函数就不能很好的达成效果以及无法完整地回复2倍大小的精灵图。[/ALIGN]
*由于要显示仿***灵图,所以这里要开启位9。[/ALIGN]
*/[/ALIGN]
spriteEntry->isRotateScale = true;[/ALIGN]
[/ALIGN]
/*双倍边界标识符只有在精灵图是仿***灵图的时候才有效。在其他用途中,它将作为精灵图可见标识符来使用。这里我们需要一个双倍边界大小的精灵图,所以要开启位8。*/[/ALIGN]
spriteEntry->isSizeDouble = doubleBound;[/ALIGN]
} else {[/ALIGN]
/*这时位9(仿射标识符)已经准备好,所以我们不需要进行清除。然而位8(精灵图可见标识符)需要清除。*/[/ALIGN]
spriteEntry->isHidden = false;[/ALIGN]
}[/ALIGN]
}[/ALIGN]
}[/ALIGN]
[/ALIGN]
[B]移动精灵图[/B][/ALIGN]
接下来要进行的工作才是最有意思的部分。在硬件中移动精灵图不用担心速度、缓冲什么的,一切都很简单。要移动精灵图,我们只需要改变SpriteEntry的X和Y。这里我们不需要过多地考虑基本的资源安排。libnds的SpriteEntry单元已经提供了编译器给我们,并且给出了图像在进行数据操作时最好的编译方法;编译器已经替我们完成了位操作。如此简单,以至于我们甚至不需要来写任何的函数。只需要记住以下几点;如果一定想要自己编写一个函数来进行操作的话,那么我推荐各位最好使用内联函数。[/ALIGN]
/*移动精灵图的语句*/[/ALIGN]
spriteEntry->x = 0;[/ALIGN]
spriteEntry->y = 0;[/ALIGN]
[/ALIGN]
[B]设置精灵图的优先级[/B][/ALIGN]
当处理多层精灵图的时候这项工作就是我们必须要做的了。下面我们将讨论一下如何设置精灵图的优先级。[/ALIGN]
比如一幅作为背景图的精灵图,需要优先级来决定是覆盖在其他精灵图上面还是在下面。与背景图优先级相同的精灵图会显示在其上方。低优先级数字的精灵图会在其他精灵图的上方(数字越低优先级越高)。每个图像引擎都有四个优先级可供选择,与背景图的优先级类似。[/ALIGN]
要设置精灵图优先级,我们只需要适当地设置SpriteEntry中的4个优先级数字即可,具体为多少取决于我们想要的效果:OBJPRIORITY_0, OBJPRIORITY_1, OBJPRIORITY_2, or OBJPRIORITY_3. 下面的代码显示出一个具体设置优先级时的语句。[/ALIGN]
spriteEntry->priority = OBJPRIORITY_3;[/ALIGN]
[/ALIGN]
[B]精灵图的使用[/B][/ALIGN]
既然我们的sprites.cpp文件已经完成了,那么就让我们开始学习下精灵图在内存中的存储、读取之类的吧。把sprites.cpp文件放入源文件目录,打开main.cpp文件。[/ALIGN]
[B]为精灵图设置VRAM显存[/B][/ALIGN]
我们需要在VRAM中开辟一片空间来存放我们的精灵图数据。因为我们要在主图像引擎中使用这些精灵图,所以我们就可以使用VRAM bank E来存放精灵图。VRAM bank E 要比其他VRAM bank都小一些,只有64KB存储空间。然而,用来存储1024个独立的16色块状图来说是完全足够的。[/ALIGN]
在initVideo中,我们需要划分VRAM bank E来用于精灵图的操作。然后我们需要告知主引擎激活我们需要的块状图。在这里我们要使用1D的块状精灵图。修改后的initVideo函数在下面给出。[/ALIGN]
void initVideo() {[/ALIGN]
/*[/ALIGN]
* 定义VRAM在上屏和下屏显示背景图片。[/ALIGN]
* [/ALIGN]
* vramSetMainBanks 函数带有四个定义,[/ALIGN]
* 每一个分别对应一个主VRAM bank。[/ALIGN]
* 我们可以用它来快速分配每一个VRAM bank控制寄存器的值。 *[/ALIGN]
* 我们为上屏背景内存将bank划分 A 和 B 两部分 。[/ALIGN]
* 总共有256KB大小,对于16位图像来说完全够用了。[/ALIGN]
*[/ALIGN]
* 定义bank C 为下屏背景内存。[/ALIGN]
*[/ALIGN]
* 定义bank D为LCD。[/ALIGN]
* 这么做是因为有时我们可能需要一个临时空间。[/ALIGN]
*[/ALIGN]
* 划分bank E 作为主屏上的精灵图内存,也就是对象内存。 */[/ALIGN]
vramSetMainBanks(VRAM_A_MAIN_BG_0x06000000,[/ALIGN]
VRAM_B_MAIN_BG_0x06020000,[/ALIGN]
VRAM_C_SUB_BG_0x06200000,[/ALIGN]
VRAM_D_LCD);[/ALIGN]
[/ALIGN]
vramSetBankE(VRAM_E_MAIN_SPRITE);[/ALIGN]
[/ALIGN]
/* 设置上屏的视频模式 */[/ALIGN]
videoSetMode(MODE_5_2D | // 设置图形模式为第五模式[/ALIGN]
DISPLAY_BG2_ACTIVE | // 开启BG2[/ALIGN]
DISPLAY_BG3_ACTIVE | // 开启BG3[/ALIGN]
DISPLAY_SPR_ACTIVE | // Enable sprites for display[/ALIGN]
DISPLAY_SPR_1D // Enable 1D tiled sprites[/ALIGN]
);[/ALIGN]
[/ALIGN]
/* 设置下屏的视频模式 */[/ALIGN]
videoSetModeSub(MODE_5_2D | // 设置图形模式为第五模式[/ALIGN]
DISPLAY_BG3_ACTIVE); // 开启BG3[/ALIGN]
}[/ALIGN]
[/ALIGN]
[B]精灵图像的寻址方式 [/B][/ALIGN]
如同GBA编程当中一样,我们将使用同样的内存排列(范围)。块状图VRAM地址必须以32byte来进行排列。如果你觉得这个内存空间太小也是没有办法的,因为当使用256色块状图的时候你不能使用全部1024个块状图地址。你可以在这里查看一下如何使用其他排列方式。 http://nocash.emubase.de/gbatek.htm#dsvideoobjs[/COLOR]. 之后需要设置REG_DISPCNT (通过 videoSetMode) 一个在 libnds/include/nds/arm9/video.h 中与DISPLAY_SPR_1D_SIZE_XXX (默认的GBA编程方式中所使用的值DISPLAY_SPR_1D_SIZE_32)相似的值. [/ALIGN]
计算出地址,然后把块状图传输进去,这里我们只需要搞懂两件事:在内存排列中,我们想要分配数据的块状图编号。使用Martin Korth的GBATEK中的"TileVramAddress = TileNumber * BoundaryValue"以及libnds的SPRITE_GFX定义我们可以计算出任何块状图的地址。[/ALIGN]
static const int BOUNDARY_VALUE = 32; /*这是默认的范围值[/ALIGN]
* (可以在 REG_DISPCNT中设置) */[/ALIGN]
static const int OFFSET_MULTIPLIER = BOUNDARY_VALUE /[/ALIGN]
sizeof(SPRITE_GFX[0]);[/ALIGN]
uint16 * tileVramAddress = &SPRITE_GFX[shuttle->gfxIndex *[/ALIGN]
OFFSET_MULTIPLIER];[/ALIGN]
[/ALIGN]
W通常我们都会一次拷贝多个块状图进VRAM。幸运的是当使用grit转换至精灵图的时候,它会自动告知我们块状数据的大小。既然知道了大小,那么我们就可以使用dmaCopyHalfwords(使用byte长度来进行拷贝)往VRAM中拷贝数据。我们也可以从数据大小上计算出一张图中使用了多少块状图,然后分成若干份进行传输。在我们的例子中,使用了16色的块状图,一个块状图(***像素)将占用32bytes。[/ALIGN]
[B]精灵图像的读入[/B][/ALIGN]
现在来看看精灵图是怎么运作的吧。首先我们要读入橘黄色飞船的图像以及月球的图像。建立一个新的名为initSprites的函数。将其放在initBackgrounds函数的后面。确定其中包含了orangeShuttle.h以及moon.h。它们包含了grit生成的我们的精灵图的信息。[/ALIGN]
我也新建了一个叫做“SpriteInfo”的结构类型。这个结构中包含了SpriteEntry中不是很明确声明的精灵图的信息。待一会儿我们将使用它来帮助我们管理精灵图的信息。[/ALIGN]
[B]步骤 6.1 创造一张精灵图[/B][/ALIGN]
首先我们要为精灵图填充SpriteInfo信息。每一张精灵图都有其独自的SpriteInfo结构。我们要做的第一件事是声明一个OAM ID给精灵图。这个数字将帮助我们跟踪精灵图是跟哪一个OAM相关联。同时我们也要用它来计算其他的东西。
声明宽度、高度、以及精灵图的角度。
选择精灵图关联的OAM入口。
设置属性0。
设置属性1。
设置属性2。
向VRAM拷贝块状图数据。
向VRAM拷贝调色板数据。
再根据下面的代码来写initSprites函数。[/ALIGN]
void initSprites(OAMTable * oam, SpriteInfo *spriteInfo) {[/ALIGN]
/* 定义一些精灵图设置中的具体内容。使用这些函数来计算一个有效的块状图或调色板内存的索引。[/ALIGN]
* OFFSET_MULTIPLIER基于下面的来自GBATEK中的方程式进行计算。( http://nocash.emubase.de/gbatek.htm#dsvideoobjs):[/ALIGN]
* TileVramAddress = TileNumber * BoundaryValue[/ALIGN]
* 由于 SPRITE_GFX 是16进制数, the compiler will increment the address[/ALIGN]
* 编译器将会为SPRITE_GFX增加一个每2进1的序列地址。(编译器自动计算)[/ALIGN]
*/[/ALIGN]
static const int BYTES_PER_16_COLOR_TILE = 32;[/ALIGN]
static const int COLORS_PER_PALETTE = 16;[/ALIGN]
static const int BOUNDARY_VALUE = 32; /* 默认的范围值[/ALIGN]
* (可在REG_DISPCNT中设定) */[/ALIGN]
static const int OFFSET_MULTIPLIER = BOUNDARY_VALUE /[/ALIGN]
sizeof(SPRITE_GFX[0]);[/ALIGN]
[/ALIGN]
/* 跟踪可用块状图*/[/ALIGN]
int nextAvailableTileIdx = 0;[/ALIGN]
[/ALIGN]
/* 创造一个飞船精灵图 */[/ALIGN]
static const int SHUTTLE_OAM_ID = 0;[/ALIGN]
assert(SHUTTLE_OAM_ID [/ALIGN]
SpriteInfo * shuttleInfo = &spriteInfo[SHUTTLE_OAM_ID];[/ALIGN]
SpriteEntry * shuttle = &oam->oamBuffer[SHUTTLE_OAM_ID];[/ALIGN]
|
|