搜索
亲,你还没有登录哦,马上登录。如果还没有帐号,请立即注册X
查看: 2465|回复: 6

GBA游戏制作菜鸟入门篇

[复制链接]
  • TA的每日心情
    别闹
    2013-12-11 16:57
  • 签到天数: 129 天

    [LV.7]王国居民III

    发表于 2011-9-13 09:21:20 | 显示全部楼层 |阅读模式
    今天怎么样也要把它转过来,虽然那些工具都下载不了了,可能时间久了吧,
    3 C% r, o2 Y7 V( e9 s. p; K/ p下面为转载内容:1 V% c5 M/ g  F; d. i
    [转帖]GBA游戏制作菜鸟入门篇
    8 Q  R9 |# f) K3 q3 Z" ]
    此文章由混沌星辰雷之精灵撰写
    & ]& q4 ~6 `( T2 I& X原文链接:http://gba.gamego.cn/dispbbs.asp ... ID=10024&page=1
      O) G7 r3 c& p——————————————————————————————————————————
    $ s- s& k( x. ~' h) d% V9 i/ E' V转帖目的:希望大家对GBA游戏热爱的同时,也来了解下GBA开发过程,其实很简单,只要用心,你也可以做出好的GAME
    4 f0 a* z$ K7 m. I          目前转的是所有菜鸟入门篇,如果回了这个入门篇,做一个类似逆转类型的游戏也就比较轻松了~
    6 R' ^' G3 i7 p0 i* K# v) `          很简单,  大家试试
    * L) H* }9 l2 Q$ D0 I——————————————————————————————————————————, h! F+ ]  z, l8 f0 g* V/ ?
    最近见到论坛上有新手帖我的教程,这本来是一件好事,但是想不到那位兄弟竟然忘了帖图!我觉得鄙人教程虽然不算是什么好东西,但是图片,还算得上是画龙点睛之笔。那兄弟的帖子我不能编辑,我只能在这里重新帖一遍教程,并尽量编辑成最容易阅读的格式,放在这里供大家参考吧!
    4 {* e6 O& X2 j- p. A% T' r& ?
    : ~3 @% t- l: m8 b下面是正文了:
    / F) x3 v0 G# C1 e' _7 E" x
      C9 y" S5 i9 H4 h1 J* TGBA游戏编程菜鸟入门篇(一)——简介及开发工具$ \. k1 l% H" v7 K/ G9 b3 Z$ k
    : v% }+ u1 y! K
    玩GBA游戏编程的时候,发现GBA编程不难。自己总结了一些心得,写成教程,贴在这里吧!
    + D$ {% D2 z. W7 ?7 t/ _. t; B: U* l; E0 ~$ Y
    本来这是要投稿给《掌机迷》的,但是人家迟迟没有回音,我不耐烦了,放在这里免费供大家浏览。
    5 h  R9 P/ _. E" U
    : l, v& x; w  d4 E5 ]9 U编程的时候发现,GBA编程涉及很多GBA硬件方面的知识,也和微机原理有着千丝万缕的联系。我前些日子曾经入门过GBA游戏汉化,当时不知道或者懵懂的东西,现在差不多都豁然开朗了。如果你有汉化游戏的欲望,又对微机原理略微了解一些,而苦于自己是菜鸟一直徘徊在汉化门口,那么你不妨看看我的教程,相信你会有所发现。如果你对游戏开发颇有兴趣,又略微学过一些C语言,希望看到自己在GBA上面的作品,而看到天书一般的汇编代码吓得六神无主,那么你也不妨看看我的教程,相信你会很快找回自信……
    " T# E, P% K. R% L' o% `/ G3 L1 w& d" U. U5 j6 R
    好,闲话少说,开始!
    ; Y( ^( A8 I+ l% v- N% S, X) @) O* q+ M8 d& H
    首先声明一下,本人水平很低,低到什么程度呢?我没有学过C语言;我只学过3年的VB。因此,我个人对C语言并不是很了解。如果我的教程中出现可笑的低级错误,请各位海涵……另外,也请编程达人多多指正,雷之精灵在这里感谢各位!+ Y7 f: W" x9 W7 N# S

    + K4 A  y. e7 l5 |  x0 t, @$ c许多人认为编程很难。没错!编程是很难。但是,所谓世上无难事,兴趣是最好的老师。只要你有兴趣,有持之以恒的决心,那么就不难。我在论坛上见到不少朋友非常喜欢玩游戏,也曾梦想着自己编写自己的游戏;但是看到涉足编程领域困难重重就一直在门口徘徊。如果你也和他们一样,那么请放下包袱,尝试着走进来;或许你会发现,编程领域也不是像你想象的那样……  Z3 Z$ N: e6 f  I' c. j/ P, ~

      J: G! K3 W2 l% {3 \关于在GBA上面编程,现在网上的达人们似乎都认为它要比在PC上面编程简单一些。我不持这样的观点。毕竟,在GBA上面编程,迄今为止还没有像VB、VC之类的所谓“所见即所得”的面向对象的开发工具;一切都要靠汇编和C语言……如果你接触过汇编或者C语言,那么,上手就很容易了!# o( }3 d3 P$ n8 M  U* l
    3 s+ g+ }, O" w) P) H; k$ `+ X
    好了,闲话少说,开始吧!由于我不知道大家对于计算机语言的了解情况,在这里,我就假定你是已经稍微接触过C语言,但是绝对没有深入研究过,大约也就只能写出“求三个数的最大值”之类的小程序;对于微机原理处于懵懂状态,能看得懂16进制,知道所谓“高字节数据存放在内存高地址,低字节数据存放在内存低地址”的菜鸟……门槛不高吧?
    2 m$ ~. n$ C  B
    , Z$ ]. [1 V% j由于使用C语言要比使用汇编效率高,所以,在这里我建议大家使用C语言。我这里,也将以C语言来讲述。7 T7 {8 z- H; y' c- |

    4 d" k2 N$ C* l6 L工欲善其事,必先利其器。必要的工具,我们必须具备:/ z0 ^2 e6 \: E" i5 j

    ' G6 Q& [' g: w8 \9 a7 p& {( @1.函数库:这里,我强烈推荐使用“GsLIB”。这是一种高效率的GBA函数库,作者是国内开发达人黄炎中。GsLIB将GBA的各种功能进行封装,形成标准库函数。使用的时候直接调用。现在可以下载到的版本有2.6 ARMSDT版本、2.6 GNUPRO版本和2.7 GNUPRO版本。在我的教程里面,使用2.6 ARMSDT版本。不同的版本有着一些细小的差异。GsLIB可以在黄炎中达人的主页http://gsgba.yeah.net直接下载到。完全免费!如果你打算以我的教程为指导,那么请下载2.6 ARMSDT版本。
    8 P- @! ]; z& J8 ?& m4 c
    ! \7 D0 B  k1 m2.开发环境和编译器:有两种编译器可以选择,就是ARMSDT和GNUPRO。其中,ARMSDT是ARM公司的商业软件,要想使用得要交钱的;而GNUPRO是免费软件,任何人都可以直接下载到。不过,GBA的CPU是ARM公司的产品,所以使用ARMSDT开发效率高。而GNUPRO据说是有BUG,开发效率也比ARMSDT低,生成的ROM体积也稍微大一些……无论如何,由于我们使用的GsLIB是ARMSDT版本,所以必须使用ARMSDT。但是这个软件,网上似乎很难下载到……我在这里也没法提供下载(软件安装包接近30M,附件方式放不上来)不过,最近我有发现D版的ARMSDT,是ARMSDT2.50版本,试用版,在这里如果只是当作GBA编译器的话可以正常使用。在搜索引擎里面找一下,应该可以找到吧……如果实在找不到,论坛的群里有共享。但是,群里的ARMSDT,有不能用的先例……(6lay大人:(阴沉着脸)你说谁?)! f2 U% ]. s) ~7 f/ V. v
    2 u: K4 f! W+ k* g$ _+ e0 Y% s
    3.写程序的工具:既然是C语言,那么,在VC里面写就当然是最好!要是没有,你也完全可以在“记事本”或者“写字板”里面写代码,写完之后保存成后缀名为.c的文件。不过,这两个Windows自带的工具都有一些缺点,比如不能显示行号,不能自动缩进,函数名也不能彩色显示之类的。在这里我就要极力推荐UltraEdit了!这是个16进制编辑器,也可以用来写文本文件。它的功能之强大,你用了就知道了!这个软件,网上多得满天飞。估计你任意找个下载软件的网站都能下载到……* F2 N  A/ I/ Z

    ; d( A9 `0 }* o& P, m4.测试工具:写完程序,经过编译链接之后,生成的文件就是我们想要的ROM了。它的后缀名是.gba……这个,我不说你也知道用什么打开吧!在这里我要说明的是,VBA的版本不要太低,因为有些功能在低版本的VBA里面还没有开放。另外,记住要用英文版,中文版的话,和教程里面会出现不一致,你也许会不知所措。' e' w; h0 Y  E

    # [. Z: B/ V% H$ g  s" j0 O$ z7 g5.调试工具:你写出来的ROM出现BUG甚至错误是必然的。调试是一项很关键的任务。这里需要一个强大的反汇编模拟器。我推荐的是No$GBA。这也是商业软件,据说正式版需要2000美元……但是我们可以很得意的找到破解版……在稍微大一点的模拟网站或论坛里面应该可以找得到。不过,据说现在在网上流传的No$GBA,声音没有破解,因此我们编译出来的ROM,用这个模拟器打开,如果有背景音乐的话会出现噪音。不过这也无所谓,菜鸟教程中声音方面基本不需要调试……就算是要调试,也可以在VBA里面调试……
    ' E* J: [7 K4 C% Q! Y
    2 Q' b& j. C; m0 a& ~# z7 r6.组合精灵编辑器:这是GsLIB的辅助工具之一,不知为什么黄达人没有把它和函数库放在一起。你需要单独下载。下载地址也是http://gsgba.yeah.net。或者到TGB论坛去下载:http://www.tgb.name/index.php?act=Attach&type=post&id=573
    . z* N( D+ K. ^" v
    9 x1 i( x0 _( X4 M: j+ O% J6 M3 d剩下的差不多就是辅助工具了……
    5 x$ _2 ]* {. f& h
    + X8 j: t9 p/ {9 p% d  }1.PhotoShop:基本上PS就是为了处理图片的调色盘才被我加进去的。游戏中最常用的就是图片,而在GBA里面,我们需要关心图片的调色盘。PS可以很轻松的处理调色盘。……你不知道什么叫做调色盘?不急不急,用到的时候我自然会讲解!PhotoShop的下载地址不用我说吧?找不到的话去附近卖光盘的商店转转,100%有卖的!
    . P" G1 I6 s6 G, o0 k. e
    7 @/ e; V3 b* F* b0 l3 J2.ModPlug:如果你想要在你的游戏中加入背景音乐或者是真人语音,那么就少不了这个软件!GBA不能直接支持MID,虽然现在有个所谓的“MP2K”可以使GBA播放MID但非常不幸的是我们的编译器ARMSDT不支持MP2K!所以这条路就走不通了……幸运的是GBA支持MOD。什么是MOD?我也不知道……反正你就认为这是一种类似于MID的音乐格式就行了……到时候我会详细讲述如何用ModPlug制作MOD,如何在GBA上使用MOD的……这个软件也是外国人写的,界面是全英文,好像还没有汉化版,凑合用吧!下载地址我不说了,去搜索引擎里面搜索一下,一定会找到的。
    7 S; ^8 L, [) _+ e
    ! O+ O, ^  ^' A9 O/ Z需要的工具大概就这么多了。当然,系统自带的工具也是少不了的!“画图”、“计算器”之类的,随手就到!
    5 m" Z; E. n+ y8 }' i
    6 r" \: N+ I. Y那么,今天就到这里。从下次开始,我们就要正式进入编程。下次要讲的内容是:在GBA上面显示文字!理论课内容是“中断请求和响应”。# W) n2 S# |! S
    5 L7 C1 m% d$ j

    ; N& H! }- l3 V2 C
    # s2 Y1 N- [3 e" TGBA游戏编程菜鸟入门篇(二)——中断请求及显示文字
    % m3 b' x% ]3 `
    5 x$ n6 p0 }2 C- o5 m9 `嘿嘿……大家好!又见面了!上次教程中提到的必备工具,有没有准备好啊?有准备的,那就开始吧!
    " x9 O$ I8 A; t; Z
    + s% O: }3 {& y! VGBA的显示屏分辨率大家都知道吧!240*160。就是说,宽240像素,高160像素。
    : Q3 n. O; ?6 K. D  u. s. o. z5 r# e8 [, Q
    GBA上有6种显示模式,分别命名为MODE0到MODE5。这六种模式中,MODE0、1、2很相似,被称为TILE方式;MODE3、4、5也很相似,被称为位图方式。
    : d8 w: z* K% S/ x" U6 y1 w& T  ]# }
    2 [. N1 l9 `/ o8 c( L/ \# N什么叫做“TILE方式”呢?简单地说,GBA上面的图像,是用一块一块的小方块拼成的。这种小方块叫做“TILE”,大小是8像素*8像素。在MODE0至MODE2中显示图片,就以这种方式显示。你想不想直观的看一下什么是TILE?找一个规模稍微大一点的GBA汉化网站或论坛,搜索一下叫做“TLP”的软件。下载下来之后,用它打开任意一个GBA的ROM。注意必须是GBA的ROM,也就是说不要压缩。打开之后,你会看到一大片花花绿绿的什么东西。这些花花绿绿的东西,被网格分开。其中每一小格都是8*8像素。这一个小格,就是一个TILE。5 L3 ]$ V* {9 `, u* S/ v$ H# x- v. V  q
    7 w$ p+ \& l6 Y0 R$ t

    3 d) v# s1 s& H0 N- t$ N
    2 @" F0 Z3 K% U  u那么“位图方式”就很简单了。就是所谓的“点阵图像”。MODE3至MODE5下面以这种方式显示图像。! G/ c3 D" N; i3 o
    % O" c6 K+ _; m) w3 B/ E2 p
    但是MODE4和MODE5还和MODE3有点不同……MODE4和MODE5采用了一种叫做“双缓冲”技术,使得显示高速的图像不会出现闪烁。双缓冲,形象地说,就像两张叠在一起的图片,上面那张你能看的到,下面那张被上面的挡住看不到。当你看到了上面那张之后,下面那张翻上来,上面那张翻到下面换成另一张图片。这样你就看到了刚才在下面现在又翻到了上面的那张图片。然后再将这张图片翻到下面,把那张换过的图片再翻上来,同时再将翻下去的换图片……反正就像这绕口令一样,看到上面的图片的同时将下面的图片更新,然后翻转,使你能看到下面那张图片;你看到翻上来的图片的同时更新下面的图片……再翻转……" T% F8 x6 I; J! y

    2 Z/ W" F: t# X. Y那么,在哪种方式下可以显示文字呢?哪种都可以!但是,不同的方式下显示文字的函数有点不同。我们先试着写个GBA的DEMO来显示文字。: k. a' \! Z9 F+ y2 m
    7 }: B, v' h$ r& v
    在哪种方式下显示呢?TILE吧。游戏经常用这种模式显示文字。! E( l! v/ q$ d6 W8 O( F$ k. a

    $ G- E  a# _6 u  v8 f& y$ ~所有的软件安装好了吗?还没有?得得的!我来!, d) K+ E/ [- U- F8 D# M* S

    & q8 n( m' S* V首先安装UltraEdit。方便起见简称UE。UE是准绿色软件,双击安装文件,你随意设置安装路径。安装完之后桌面上就有快捷方式了。如果你下载的版本是试用版,去网上找注册码,或者掏腰包注册。两种途径随你选择。
    & ]% O0 f( Q! K3 e5 N3 [: X$ ~
    9 ~& D7 B4 ]; L. K% J& I然后安装ARMSDT。双击setup.exe进行安装,自己随意选择路径,然后你可能会看到下面这个对话框。
    ) h' f6 Q( d7 I' k4 ?5 F  a
    , h$ X% n" l7 Z( P! R: E
    1 n8 g5 i" x: X! Z
    ; n7 x$ Y) x! L8 _
    4 q( m, E& Y1 c
    # a$ }8 A) W, \' ^" _+ k; N  f3 V; Q  g
    4 }2 w6 n+ Y- w# W+ i
    全都不选。点击Next。然后可能会弹出一个错误对话框。9 J% N8 x- M9 W  b- d' n" m2 ^
    , J$ H7 |8 q8 _3 Z; z
    不要管它,继续。(这个对话框在有的系统上不会出现。系统是WinXP的时候不会弹出来,但Win2000会弹出来。不过这也不是绝对的,我同学的电脑,XP系统,也会弹出来……反正就是,管它弹出还是不弹出,继续就行了……)
    ( D7 k" D1 G' d3 X# g) t- Z5 j  q/ O7 {( h7 J' ^1 n
    然后小等一下就搞定了。安装结束的时候可能会询问你是否要建立快捷方式,不要!建立了也没用!ARMSDT仅仅是一个编译器,除了编译我们用不着它的其他任何功能!这里我假设你是把ARMSDT安装到了D:\\ARMSDT文件夹下面。安装正确的话,文件夹下面会出现文件夹“BIN”、“LIB”、“INCLUDE”、“TEMPLATE”和文件“DeIsL1.isu”、“MFC42.DLL”、“MSVCRT.DLL”、“README.TXT”、“UNINST.DLL”。3 y; t9 v, `; v  @- ~4 d! S3 l1 h

    9 M. E7 m3 A4 l6 K1 J然后就是函数库GsLIB了。这是绿色软件,解压缩到任意文件夹。这里我假设你是把GsLIB解压缩到了D:\\GsLIB文件夹下面。解压正确的话,文件夹下面会出现文件夹“DOC”、“INCLUDE”、“LIB”、“SAMPLES”、“SYSTEM”、“TOOLS”和文件“cgo.exe”“gsgo.bat”。: d9 V" }' I3 Z, T' V3 h
    . E. z( T! ~0 o) ^7 |: u
    这里我有必要解释一下GsLIB的这些文件和文件夹:
    # N& Q& `+ c# u# C# h9 Y* h1 s+ ?7 x1 q& ~2 H9 x. |% t
    首先是DOC文件夹。里面是GsLIB的帮助文件。非常详细!唯一有点缺憾的是解释的不是很明白。无论如何,这里是学习GsLIB的天堂。以后在我的教程里面,如果出现了你没有见过的函数,就到这里面找吧。
    5 H* M6 F# t, h' {7 j* |0 \$ _# v% v+ y# v8 o9 _, B  x( Z" b
    再者是Include文件夹。里面是库函数。如果你打算看更低层的东西例如寄存器和内存单元的分配以及命名,那么这里面或许有你想要找的。! A# b* Q8 t2 Q% w% a( b1 [; Q
    3 [& |# f+ f0 g9 R
    然后是Lib文件夹。我不知道这里面的那个文件有什么用……( F9 J: h+ O' N6 n2 h, G# j5 ]
    5 T: L; I! _2 L) P
    SAMPLES文件夹!这是最令人激动的文件夹。里面有不少的例子,基本上都很简单!你一定要看!以后我们写程序,就在这个文件夹里面写。# ^! C) G* ~7 a7 V) x3 B9 ^7 m; J
    , Y5 `0 k; [: C3 C' m* w# l
    SYSTEM文件夹。这是GsLIB的系统文件夹,我们会用得到的。
    * x+ S- R/ d2 q/ C0 J! D- z. _: D
    - A6 n& }/ @" ~6 v- n% CTools文件夹。这里面是开发游戏的时候,有可能用得到的工具。
    1 ~' }5 M6 V7 @9 L9 Q5 p# w  O" a! G8 F' x
    然后是文件:cgo.exe。这个文件不用管它。$ ?7 I. ]4 g% @, }+ p+ v2 O

    - K$ t# O( e, X6 S1 u- P% ZGsgo.bat。批处理文件。相当重要的文件!你马上就知道,编译程序的时候,就靠它了。* ]. c7 q  L% J" r5 W

    1 P, ?# ~* |0 l. t' j行了,继续!
    ' v! q' C) Y* y" O/ i3 I. I/ X
    0 N$ K$ H) c. f) I4 R6 @模拟器们,都是绿色软件,随你安装吧!
    7 r0 _" v2 k! l$ I
    - W2 N$ @2 y3 W5 _% c5 z! o5 `6 i那几个辅助工具,我就不讲解了。想必你们都会了!- _7 j' Q" d+ s: a* i6 {
      T- I  Y6 v6 ~6 a/ z, E8 E* ~6 }
    哦!差点忘了!你有下载“组合精灵编辑器”吧!这是GsLIB的专用工具,将它解压缩到GsLIB\\Tools文件夹里面。2 R' V, h( v3 {2 S
    ' ~1 O6 }0 i5 W, c" u
    先进入GsLIB\\system文件夹,复制“makefile”文件。
    : K  `% @1 \3 g& x: d; m, \5 e2 B  @4 d
    然后退出来进入GsLIB\\samples文件夹,新建一个文件夹。假设你建立的文件夹叫做“Temp”。6 O/ l6 d* @1 z5 P2 w# v
    ' }5 ^; J6 `, F9 {# }/ q, t
    进入Temp文件夹,粘贴,这个时候刚才复制的“makefile”就粘贴到了这里。6 o+ S2 X, ]" X5 h' {

    5 R# G& `4 v9 I3 D& @0 }" g9 o再新建一个文本文件,取名叫“main.c”。记住后缀名必须是“.c”!这个时候文本文件的图标会变成UE图标的样子。双击,UE自动将这个文件打开了。
    . |! J: D, ?: o1 M4 [& P* I* P8 \5 i5 o" g+ W1 X
    然后,开始写代码吧!
      Q" h# k# K: Y* f' L* s1 U$ L5 G2 h2 M5 G+ @- u! x
    从现在开始,我会详细解释所有的代码。里面涉及的函数,在GsLIB\\DOC文件夹里面的帮助文件里有详细的说明。9 e9 }6 X. w9 K6 q

    , Q: N% c  ~0 `) z#include <GsGBA.h>//这是载入GsLIB函数库。以后你写的所有程序,第一句必须加上这个包含。
    1 q) U5 h$ J# c9 y4 [
    9 I/ w' a, F1 z#include <GsText.h>//要想显示文字,必须载入字库。以后你写的程序如果有需要显示汉字,记住加上这个包含。. b( O4 _9 C& z8 Y& Z: e
    ' D7 B5 C" A% q9 y2 D
    const IntrFuncp IntrTable[14]=
    - e/ q5 [* K! k6 D" y{: v3 F- ^) W* N7 F
       dummy,dummy,dummy,dummy,9 {5 S2 W  P$ [( e
       dummy,dummy,dummy,dummy,! e9 V2 R% b. p  J: C% E5 o8 [
       dummy,dummy,dummy,dummy,
    - ?/ T" t9 W9 K   dummy,dummy
    : I0 o2 g4 N& x! x, [9 i};//这是中断向量表。下面我会简单讲解中断,现在你就记住要这样写就行了。
    : `# Q, e& Z7 p. n
    " J3 L- m; x3 N9 i# F6 V1 ^int AgbMain()//主函数。注意这和标准C不一样!标准C中主函数是Main();,而这里是AgbMain();。
    $ v# E7 |* L  Q' s  n. b- r$ s' T' H( E, n0 i
    {  ~: ]+ _' E8 z& n
    # [3 K6 a2 {! [
      GsSetMode( MODE_0);//设置模式函数。这里将模式设置为MODE0,TILE模式。
    8 f2 k7 c) R- Q0 r
    : w: b; b0 ^/ {/ a6 F" Y# o  D  BGPaletteMem[1]=0xffff;//这是设置调色盘。BGPaletteMem[ ]可以看作是一个数组。这句就是说,将16进制数0xffff写入数组BGPaletteMem的第1号元素中。以后,调色盘中中的第1号颜色就是它了。6 j1 B" s5 @! j& Q
    5 j0 {/ E- M. h
      GsInitBG();//初始化BG。这是一个很重要的函数。只要在TILE模式下,就要涉及一个“BG”问题。只要使用BG,则必须在使用之前初始化BG。关于BG的详细内容,下一次的教程《GBA游戏编程菜鸟入门篇3——TILE模式及显示图片》中我将以大篇幅详细讲解;再下一次的教程《GBA游戏编程菜鸟入门篇4——多层BG及TILE模式下的多层BG》我将以更大篇幅讲解。现在,可以说,掌握了BG,就掌握了GBA游戏编程的三分之一……
    ) [$ {; ~+ E$ E% b* ~4 |$ Z+ P& S
    7 g/ e9 E( k  J  GsSetBg(0,Gs_BG_TEXT_SIZE_256x256,Gs_BG_COLOR16,0,21,0);//设置BG。如果想对BG进行什么操作的话,则必须在初始化BG之后对其进行设置。根据帮助文件,可以得知这是设置BG0,大小是256*256,TEXT属性,16色,TILE数据在第0号区域,MAP数据在第21号区域,不允许马赛克特效。现在你或许对这些参数为什么要这样设置感到疑惑,不过,下一次的教程你就明白了。如果下一次的教程你还没明白,那么再下一次的教程你就能明白。再次强调,BG是很重要的,掌握了BG,就掌握了GBA编程的三分之一……熟练运用BG,你就可以自由自在地在GBA上操纵图片显示。# s' _( n) v- C0 B: K' ?; ]) I8 e

    - I  u2 Y. |# P* m; ?  GsTileTextInit(0,3,21,0);//初始化文字显示。如果你想在GBA的某个BG上显示文字,则必须对该BG进行初始化文字显示。根据帮助文件,这是将BG0进行初始化,MAP数据在第21号区域,使用第0组调色盘。; a" _! y8 T1 I+ @$ }
      [8 _; P4 `" v- Q2 D6 p# C
      GsSetBgState(0,ON);( }$ g+ ^6 Z, O: ~6 J  m

    * O$ Y) v# V( o' ?4 Y- u  GsSetBgState(1,OFF);
    " w! M9 @) Q! h+ c* M  [7 v% \5 z! U$ p; N9 W
      GsSetBgState(2,OFF);
    ! Z. h3 l/ n7 @' m+ S
    2 h/ a$ K% u- `7 W5 Z: B8 _+ Y) m  GsSetBgState(3,OFF);//打开BG0,关闭BG1~3。在MODE0模式下,BG有4层。在这里我们只用到了BG0,并用于显示文字。所以,将BG0打开,其他BG,关闭就行了。/ e- U- d2 k% o2 u# h
      GsTileTextOut(20,40,"我爱TGB,我爱烧录地带!",1);//显示文字啦!这就是传说中的显示文字的函数。根据帮助文件,这是在GBA屏幕上的(20,40)坐标处开始显示“我爱TGB,我爱烧录地带!”这几个字,颜色是调色盘中第1号颜色。在程序的开始,我们已经定义了调色盘中的第一号元素是0xffff,这里就使用这种颜色。( u+ V, A& w; R" E# p8 a
    ) q$ y3 a- A; }8 |8 T7 `
    while(1)//开始循环。0 l2 h: b# R( A# u

    % c$ B! l' b0 I+ q# A{
    , D( r! o& z* P' l
    8 Q- P! i1 ?: i0 n2 Z3 C8 n    GsWaitSync();//等待同步。这是什么意思呢?就是说等待发生VBLANK中断。每当GBA显示完成一幅图片之后,就发生一次VBLANK中断。也就是说,当发生中断之后,屏幕上的图片已经显示完了。
    # H( R, b. _. i) C8 f+ i- P}//GBA的ROM就是一个永不停止的循环,在这里进行循环,就是显示完文字之后,等待同步。一旦等到了同步,文字也显示完了。于是屏幕上就在坐标(20,40)处显示出了“我爱TGB,我爱烧录地带!”这几个字。颜色为0xffff,就是白色。- E8 O! t  c  _  ~+ n
    return 0;//函数值返回主函数。由于主函数是int类型而不是void类型,所以会有返回值。/ @4 y+ Q) @& B+ ^
    }& ?' I  Y! x8 w0 c9 ^

    % w6 G; X- t6 |2 K" ^1 t就这样,写完了。保存。很难吗?去掉那些注释看看,很简单!$ }6 I2 t8 O. g) N

    * l: e1 q% w' G程序写完了,编译。: Y5 D% U' x$ ~/ i% a0 g6 D$ w

    5 `3 U% K, Z; a退到GsLIB文件夹下面,双击gsgo.bat文件。这个时候会弹出来一个DOS窗口。4 [6 u! b- e  s  |. h+ [

    6 J' N. t/ l1 [  ~; S" X2 F! o+ o: ~+ f' ~7 [3 S, E) T2 ~% `

      ~0 `. k' Q' S* ]8 n; t) b! Y; w" ~: K4 S9 V1 r$ L9 A" J& M7 Z5 d

    7 a& x; Y% X2 V4 Y6 f
    1 D! K5 D9 o0 j: _8 _键入“cd samples\\Temp”回车,进入你新建的那个文件夹。: ~" {7 S" b  x, ^4 F

    / ]) d' k8 Q  ]8 r然后键入“make”。make在这里是编译的意思。一点回车,窗口立即向下刷屏。
    + L# m7 \. ^4 |- [: k0 s! Q0 ?3 G: T3 w* E' b% u8 b
    这就说明编译链接成功。注意看下面有句话“ARM Linker:finished, 0 informational, 1 warning and 0 error messages.”这里标明了你的程序里面的警告错误。这个时候再进入GsLIB\\samples\\Temp文件夹看看,是不是多了几个文件?其中有个叫做“main.bin”的文件,那就是ROM啦!把它的后缀名改成.gba,然后用VBA打开看看吧!. w, W1 F6 I  R4 B: V
    ! \( {  s, r) T, v3 a

    7 Q" Z/ Z/ A9 T# t* z) u( k7 E& h: X$ J4 E7 Q7 F& N

    ! ?, h2 X" m; D" |至于“ARM Linker:finished, 0 informational, 1 warning and 0 error messages.”这句话,我们需要关心那个warning和error。如果出现的是“1 warning and 0 error messages”,就是说一个警告,没有错误。这一个警告是什么呢?你看看多出来的文件里面,是不是有一个“log.txt”呢?这就是记录warning的文件。打开看看,大意就是在Windows环境下,生成的文件未必能正常运行。这是当然的啦!GBA的ROM是运行在GBA平台的……又不是Windows平台!而模拟器,只是在Windows平台上模拟了一个GBA平台而已……如果出现的warning不止一个,那就说明你的程序里面有未定义的函数什么的,编译没通过……而要是出现error,那就说明程序有错误,无法编译了!这两种情况一旦出现,仔细检查程序,改正遗漏错误什么的……) h4 @% X4 `% U6 Y

      m. W3 M$ f1 m, o1 l3 M5 s我刚才说到make这个命令。这个命令在DOS里面是没有的,只有你安装了ARMSDT,make命令才可以使用。在这里make是用来编译程序,生成ROM的。还有两个命令,我也有必要说一下:就是make clean和make vba这两个命令。make clean是清除所有文件,包括ROM,但是保留程序文件main.c。当你的程序里面有新添加资源的时候,编译之前最好用这个命令清除一下然后再编译;因为有时候程序有添加新资源但是其他一些编译产生的中间文件比如o文件、s文件什么的还是保持没有添加资源的状态,这个时候编译出来的ROM会出错。make vba,就是编译完成之后立即调用VBA载入ROM察看。不过遗憾的是它调用的VBA是SDL版本,这个版本没有图形界面,很难操作,不推荐使用。1 q; }8 |8 e  C7 V+ u8 p3 g

    : t( k: Y/ t9 L; h最后,该讲解中断了。' I  R+ W8 _3 X; c& ?$ c9 E( K

    ( ~5 [% N. W! K4 g中断是CPU为了协调外设和它之间速度和反应的差异才设立的。CPU的运行速率非常高,相比之下外设就相当慢了。假如CPU想访问键盘,而这个时候你并没有按键,难道CPU就一直等着你按键吗?当然不会这样!这个时候CPU继续它的工作,当你有按键的时候,键盘向CPU发出按键中断请求,也就是请求CPU中断程序,处理你按下的按键。当CPU收到键盘的中断申请时,暂停程序,转去处理键盘。处理完键盘之后,CPU再回到刚才中断的程序处,继续运行……
    0 j9 v1 R% s/ W6 e8 j' g0 A/ s6 W* }
    GBA的部件中,能发出中断的部件很多,那么,这么多中断,CPU怎么判断到底是谁发出的中断请求呢?中断向量表!中断向量表中有14个中断可以受到你的控制。这14个中断,依次是:6 b1 Q. C1 K. T/ V3 A6 x0 Q

    4 v% @5 V- e" `VBLANK中断、HBLANK中断、VCOUNTER中断、TIMER0中断、TIMER1中断、TIMER2中断、TIMER3中断、串行通信(Serial Communicating)中断、DMA0中断、DMA1中断、DMA2中断、DMA3中断、按键(Key)中断还有卡带(Cartrage)中断。
    6 g) u2 A( Y9 H, f2 t3 C5 ?7 d  Q6 I
    4 l/ T+ x  S& D! J  W/ v这14个中断中,常用的只有两个:VBLANK中断和DMA0中断。你会看到以后我的程序中只用一个VBLANK中断就完全够了。关于DMA0中断我一般不用,估计只会在讲DMA的时候才会用到吧……
    # ?- {: G4 r. g; l" C- o
    1 U4 E: A" ^& `1 M怎么使用中断呢?如果你的程序很简单,不需要中断,那么就像上面写的一样,14个中断全部填“dummy”空函数,或者干脆不填,留个分号在那里就行;如果你想让程序响应VBLANK中断,那就在中断向量表中VBLANK的位置替换成你的中断服务子程序。具体怎样做呢?下次我就给出讲解和例子!下次要讲的内容:在GBA上显示图片!理论课内容是TILE模式详解。下一次的教程中,我们就详细看看TILE模式,并对BG进行初步解释。一般的GBA游戏中,TILE模式下的MODE0是最常用的。学好了TILE模式,或者说,学好了MODE0的使用,你基本上就可以自由操纵GBA了。位图模式,相信你可以独立学习……% m; i& z. W' J: `3 m

    $ `) H9 i- b5 a7 L- v- Z6 K
    ' l1 S9 m! I* d. a9 F  D# V- Y* {$ B' g  {

    , m# R$ e$ g$ u2 P; Y: a
    8 M( d4 F0 @( l, Y- i  i* C
  • TA的每日心情
    别闹
    2013-12-11 16:57
  • 签到天数: 129 天

    [LV.7]王国居民III

     楼主| 发表于 2011-9-13 09:22:30 | 显示全部楼层
    GBA游戏编程菜鸟入门篇(三)——TILE模式及显示图片
    3 y4 d1 r! h& A+ f; R# C* T6 Y
    5 X" H7 y& C3 f1 G今天我们来看一下GBA怎么显示图片。
    / _* m) r! l& j3 A" ]& i8 Z7 Q* L  [% d2 F" e* \4 H' A
    还记得TILE模式吧!GBA有两种显示模式——TILE模式和位图模式。其中MODE0、1、2属于TILE模式,MODE3、4、5属于位图模式。
    / H! I" c- F' L! `, x  S6 o/ N
    6 O0 I0 X4 f# h* C3 T那么,一幅图,或者精确地说,位图,包含哪些数据呢?TILE、MAP、PALETTE。) R4 \: P; W6 R5 |3 `8 d+ z1 F
      _9 s% {, j( c# j" u
    TILE:就是前面曾经说过的,8*8像素的小方块。整张图片就是由这样一块一块的方块拼成的。) c" Y1 D; {$ z* W4 b

    , x( J. q* b3 O" `2 HMAP:一块一块的小方块拼成了图片,那么,哪块小方块在图片的什么位置,怎么确定呢?MAP数据就用来确定TILE在整张图片中的坐标。还包括其他一些数据这里我暂时不做涉及。
    3 T; k- p6 ~9 _7 |8 h( l! f4 R! X6 W2 t% X! j; x2 B
    PALETTE:调色盘。就是图片中所有用到的颜色。GBA支持的调色盘有3种:16色(4位)、256色(8位)和32768色(15位)。可见,对于真彩的24位位图(1600多万色),GBA是不支持的。不过,对于人类来说,恐怕没有谁能分辨出来32768色和24位色的区别吧!所以,支持这三种调色盘,可以说足够了。
    1 ]9 N0 j9 v2 a' G5 O4 m3 l; `$ t  V- a. X/ Q" _- \% O

    * q" u& E, `5 C" ]
    3 V/ j8 U9 Z- p  e
    $ Q/ q8 L0 z! p& z" a那么,你就会明白了,一张256*256像素的图片,包含(256/8)*(256/8)=1024个TILE。这1024个TILE,形成了一个TILE数组。4 N  A* P! Z* b+ s; X' l, Y
    $ O% d, I* m! c2 M) `- L
    那么,确定了图片的三个重要数据,GBA怎么显示这张图片呢?图层的概念你听说过吧!为了使图像富有立体感,GBA上面应用了图层的技术。你在GBA上面看到的图片,那就是图层叠加技术产生的效果。比如下面这张图:% y, `4 B- h! a
    : O' _" l* Q( V+ ~8 a
    $ W5 q0 V3 a, ]! u& V. e) x
    , E( K3 S- j% B1 B1 d$ O- j
    9 m, u+ P) D8 P( D
    3 g( M4 a- p3 z" }7 S" U6 b1 f! c
    它是怎么做出来的呢?它是由下面这几个图层叠加出来的!1 V% L2 z/ ~0 A3 A8 a8 J: X6 a, R
    ; i' u( A) w: r' ^7 m1 Z) d* {1 O& W* a

    . d; r8 o: J# d4 t1 F- K: ]1 ~( o7 V1 T" m) U8 R

    - v. v! T6 H& D  T4 x) _
    4 n3 O7 r% h$ ?( J# c+ c$ W  E7 J4 n" S
    假如我规定,在这里黑色是透明色,就是说如果图片中有某部分是黑色,那么在GBA上这部分就呈现透明状态不显示。这样的话将这几幅图叠在一起,显示出来的就是上面那幅图。当然,叠加是有顺序的。从上面那张完整的图中能看出来,“口袋怪兽”这一层应该是最上层,因为它下面的几层被它覆盖了一部分。烈空座这层应该是最底层,因为它似乎哪层都没能覆盖……
    8 U. a8 q4 t2 y- |" r& Z: b
    , t' _4 w' ^! U) ~(众人:你等等!)雷精灵:有什么疑问吗?(众人:你截的图是不是少了一幅啊?那“绿宝石”“PUSH START BUTTON”和公司版权那几个字,属于哪一层?)
    ; u5 o" h* b9 x9 p& v. r4 v; ]- N! ^% r3 t' r  H* s0 B1 m2 E% V
    雷精灵:我正要解释这个问题:那几个字,不是图层。它们是“精灵”。它们处于图层的最上方。有关精灵这部分,以后我会详细讲解。精灵的出现,堪称游戏中的一大革命。掌握了这部分,你就能作出很漂亮的动画效果了!
    % z; T( Y! u3 h9 z
    3 |) l, B( x2 E, h" e* F4 GGBA有几个图层可用呢?4个。分别被称为BG0、BG1、BG2和BG3。这4个图层,GBA本身并没有规定谁是高层谁是底层,而是完全由你规定。每一个图层,你都可以随意操纵,例如图层面积、使用的调色盘、特效设置等等。
    9 t3 M/ \" h0 v) `& M+ N4 Z- v' }4 l7 c& A
    有了图层的概念,那么下面的就很容易理解了。
    6 ~' J* X9 b3 X. ]7 y4 |6 c
    ' Z) [  ]9 J# y, ETILE方式下,你知道,有三种模式。游戏中最常用的是MODE0,因此我重点说明MODE0。其他几个MODE,因为都是TILE方式,大同小异你自己完全可以理解!) C& @# k5 P$ O  L& ?$ M# t' y
    3 Z4 I* e8 B. S
    MODE0,具体是什么样的呢?
    ) b! u, ?" g  @# O7 x
    ' [0 G% \9 v6 |1 @MODE0由4个非旋转(TEXT)图层组成。是普通游戏里用的最多的模式。硬件上支持的大小有这么几种:256*256、256*512、512*256和512*512。你会发现这几种大小都超过了GBA的显示区域。惊讶?别,GBA硬件最大支持1024*1024大小的图片;而在GsLIB的支持下,理论上可以支持无限大的图片。要这么大的BG有什么用?一般的RPG里面的地图,不会就只有一个GBA屏幕那么大吧!这个时候GBA通过滚动BG来显示整张图片。这种技术叫做“卷轴”。
    , b' |  Z0 u: a; c$ B% a/ u! |$ F
    & u0 L: d2 r  ~- I, nMODE0支持两种调色盘:16色(4位)和256色(8位)。也就是说,MODE0下面的这4个图层,可以都是16色的,也可以都是256色的,也可以是既有16色也有256色的。$ |9 G6 c1 w+ }$ a/ L4 z
    ; R4 E. f+ A, q* U6 j0 ^
    MODE0不支持图层旋转。旋转,不用多解释吧!你要是想做出图层旋转特效,不要使用MODE0。具体一些……《马里奥赛车》玩过吧!杯赛刚开始的时候,伴随着背景音乐,画面由远拉近,同时镜头由正对选手们的方向转向背对选手们的方向。那些选手是“精灵”我们暂时不讨论;背景,那就是“旋转”!不过,这个“旋转”,还不是真正的图层旋转,这实际上是传说中的“MODE7”。关于这方面的技术我也不是很了解……现在我们先放一下,继续回到MODE0。
    - F# q& p0 F( _$ I( t, C! x  l6 Z4 E1 W9 K2 H
    嗯……就这么多啦。其他几种MODE,我给个表格比较。
    ' p$ l1 r+ d% l  F
    * d0 e2 W  [# ?4 e% h8 i8 N" U( G
    + L2 _3 R; a( X2 l6 E
    # V6 r9 \9 J5 ?" f. a, ?4 t; j4 p* Z* q& B1 ]
    / z- ^4 i$ ?6 R$ K

      H( B4 r$ {3 g. t明白了这些理论,下面这个程序你就不难理解了。我们现在就写个在MODE0下显示图片的DEMO。这一次,我们将让CPU能够响应键盘操作。什么效果呢?还记得MODE0下面BG的大小均超过GBA屏幕大小吗?我们在键盘上按方向键,对BG进行卷轴。
    ) I3 Z3 T+ l; ]; M% v' W" ]+ j6 d2 v# X
    和上次一样,首先,在GsLIB\\samples\\文件夹下面新建一个文件夹;把GsLIB\\system文件夹里面的“makefile”复制到新文件夹里面;新建一个叫做“main.c”的文本文件,用UE打开……准备工作就完成了。我这里还是假设你新建的文件夹叫做temp。- u/ _4 X1 {0 n  f8 p! `

    - ~9 |) [) `3 F让GBA显示什么图片呢?新建一个位图,起名叫“BG.bmp”,然后用Windows的“画图”打开,将图片的大小设置成256*256像素,然后你随便画点什么。保存。' K& B. b3 c+ W

    9 Z) D( r# V9 {) H8 x
    + s: _5 v$ M2 \2 X
    - K& f, }. D7 O# B' v# d$ D/ O& w9 G
    然后用PS将这个位图转成16色的。/ R+ ^3 e9 I) W# W" z; I
    6 f9 [2 U7 u! [+ \
    (众人:你等等!)雷精灵:有什么疑问吗?(众人:转成16色?为什么要转成16色?)雷精灵:还记得MODE0支持的两种调色盘吗?这次我们让GBA在MODE0下面显示16色位图。(众人:那么,假如说我的位图颜色很丰富,转成16色岂不就会掉色很严重?)) Q$ _% o/ `+ ^; S/ |2 l
    3 G& }2 g& s5 t* z
    雷精灵:没错!16色对于高画质的的图片的确很不适合,因为颜色太少,会使画质严重下降。这个时候你就用256色。但是256色依然是颜色太少,如果你对画质要求非常高,比如是想做CG特写之类的,那MODE0就无能为力了。这个时候你就用位图方式下的MODE3吧!不过,256色一般情况下也应该足够了。你可以用PS将一个真彩的位图转成15位位图另存,然后再转成8位位图另存,你会发现不仔细看基本上也没多少差别……更何况是在GBA的屏幕上,也不怎么能看得出来……: {5 b' i" j5 V5 f% A, W
    ( J8 j' s$ d2 X8 G
    好,图片准备好了。这张位图,大小是256*256像素,颜色为16色。格式为bmp。
    8 W7 q, q1 M/ J; L3 d5 |# }, u; Y! `. b, j3 f. N
    但是GBA并不能直接识别位图,我们需要把位图转成三种数据——TILE数据、MAP数据和PALETTE数据。
    6 j$ Z1 n! |1 C. l) z& {( S2 T2 D8 K
    双击GsLIB文件夹下的“gsgo.bat”,弹出DOS窗口,键入“cd samples\\temp”,进入你新建的那个文件夹。(众人:还没写程序编译干什么?)雷精灵:谁说要编译了?
    5 o+ _& p' t+ O  t5 ^% N. ~1 U0 T7 k5 j  ?: V6 n
    键入下面的命令:“gfx2gba -fsrc -c16 -t8 -mm -pBG.pal BG.bmp”,点回车之后,文件夹下面多出三个文件来,分别是“BG.pal.c”“BG.map.c”和“BG.raw.c”。这几个文件,想必你一眼就能看出来是什么吧!还不知道?用UE打开这些文件,你就完全明白了。
    $ D$ p0 g# m7 C) t+ O8 G/ O9 ^
    " P, K  I+ _; u2 e: \0 v(众人:那个命令是干什么的?)雷精灵:gfx2gba,这是GBA用的一个工具。它可以将一张位图转化成位图的三个数据,并保存成C语言数组。关于这个工具的详细介绍,GsLIB的帮助文件里面有很详细的说明。不过,请注意一下,这次我们转化的图片,大小是256*256的。这不是凭空规定的,也不是因为MODE0下面的BG支持的那四种大小规定的,而是下面我们使用的传递MAP数据的函数决定的。关于这个问题,我想留到下一次《GBA游戏编程菜鸟入门篇4——多层BG及TILE模式下的多层BG》的时候再讲。
    ' K+ F9 s, G  `2 |% G, o0 c8 x/ R& K/ |5 S4 A
    然后写程序了。
    % {' |$ `" i/ h! ?/ H6 l( Z5 C% t( E
    - Q3 |6 a3 D3 W4 f( x' u: O1 e#include <GsGBA.h>//不用多解释了吧?由于这次用不着显示文字,所以就不用包含<GsText.h>了。 % Q' ~, U: o  |/ @

    ; n( G, u& H9 b0 G; G8 r' ^#include "BG.map.c"
    7 k( y( q: Y% H: n9 I
    & P7 z2 W  d! c+ Q: s2 R& ~0 |#include "BG.raw.c"
    + c  K9 l+ Z2 ?! Y! V3 F
    9 p7 X7 r+ I) I. j+ u#include "BG.pal.c"//这三个包含,就是把你那张位图的所有数据包含到程序里了。 ' z2 T$ j$ x- J4 x$ k$ E2 ~
    . g& ]' @5 f& R6 {; L0 r
    int x;int y;//这里定义了两个变量。这两个变量是后面对BG进行卷轴操作的偏移量。
    5 b7 c: `5 G  `9 ~$ bvoid Vblank();//在这里先声明一下VBLANK中断服务程序  n  [  _& V, ]
      t! y  C9 e$ i$ Z7 E' e1 K
    const IntrFuncp IntrTable[14]={
    # w( D/ t$ c  {( t9 K% i6 h" h% w& e( _' b5 A
    Vblank,dummy,dummy,dummy,dummy,dummy,dummy,dummy,dummy,dummy,dummy,dummy,dummy,dummy0 q6 A/ Q' G5 }' w5 u2 g
    * T8 n% |! m" ~; L% E- D
    };//注意看这个中断向量表。VBLANK的中断位置,被一个函数Vblank所占用。这个时候,假如CPU响应中断的话,每当VBLANK中断到来,都会去执行一次Vblank函数。
    , N/ V& ~$ F- [% v2 C0 _; T3 J4 qint AgbMain()//主函数了 ) s5 c6 w4 F' t* E
    {7 F9 ^8 v- w9 o& b% {% F$ r7 v8 ?
    ( z) A8 X7 ^' q) m4 P
      GsOpenIRQ();//这是初始化所有中断,并打开开VBlank中断, Key中断,DMA0 ,DMA1 ,DMA2 ,DMA3中断请求。以后如果出现这些中断,CPU就可以响应。
    - D7 T5 a9 r5 J$ _/ d* w% ^  GsSetMode(MODE_0);//这是设置模式。这次使用MODE0来显示图片。 8 \$ }/ ?' E6 S7 z" B" ]( G: ?5 C
    % p6 z1 z2 x* ~6 Q6 f6 W
      GsInitBG();//初始化BG。只要是使用MAP,必须初始化BG。
    ; J; @5 F0 w# N" n" T* A1 e
    * x( B3 W+ Z  ]% m4 [" }: c9 v  GsLoadBgPal16(BG_Palette,0);//装载图片的调色盘到GBA的调色盘内存的0号区域。在MODE0下面GBA的调色盘最大为256色。这256色调色盘,又可以分成16组,每组16色。这次使用的,就是这256色调色盘中的第一组,也就是调色盘0号区域。根据前面的表格,在MODE0下面,调色盘可以是1个256色的调色盘,或者是16个16色的调色盘。由于我们的图片是16色的,因此就用那16个16色的调色盘中的第一个就行了。关于调色盘,下次教程我会详细解释!% A  p" E) l- J" T( _$ }# o
      GsLoadBgMap(BG_Map,sizeof(BG_Map),31);//装载图片的MAP数据到Char Base第31号区域。GBA图像内存,除去调色盘内存和OAM内存(这个OAM内存你现在暂时不要管它)被划分为4个块,共32个区域,也就是每个块有8个区域。 这每一个块,被称为一个Char Base。 ' m0 q7 m% n7 }5 v+ L
      GsLoadBgTile(BG_Tiles,sizeof(BG_Tiles),0);//装载图片的TILE数据到第0号Char Base。 1 g2 |3 W) h4 o, Q% a% m+ r- [3 p
    2 Y  |( s/ K; Y% _9 }! N
      GsSetBg(0,Gs_BG_TEXT_SIZE_256x256,Gs_BG_COLOR16,0,31,0);//设置BG。我们将使用BG0来显示图片。这个BG,是TEXT属性,大小是256*256,颜色是16色,其中MAP数据在BG的31号区域,TILE数据在BG的第0号Char Base,禁止该BG的马赛克特效。
    . Y; U7 l0 [0 L5 p6 [) B  GsBgPriority(0,0);//设置BG的优先级。BG0优先级最高。也就是,处于所有BG的上层。
    - E0 f8 o9 k( i' ~" i  Y, s0 s* b; Z9 V) d2 n7 h9 U
      GsSetBgState(0,ON);//设置BG状态。将BG0打开。由于GBA默认所有BG在初始化之后都是打开状态,所以这一步要不要无所谓。 - M: x% S* i+ i4 W. f

    $ v  @& k& b2 u# w  while(1) " c9 E, D# R* u! W! {  r$ |! Y

    ) r! c) m& W) T9 P) P  {
    # L* V& a  e& |# W4 @5 V+ y2 ~. p3 n4 l! C
      GsWaitSync();//等待同步。
    - k# `9 V7 X2 l7 g
    & z) a  s, F1 X+ @8 N/ a4 E  }
    & E9 [2 d4 b2 C* P) \
    8 L- w2 V; c  K# L6 P1 w* |' m4 F  return 0;//返回。
    0 t2 i: X9 i! b6 K9 i5 u: T- e. E- B+ r2 k+ ]" o9 m% l
    }4 d% `. g5 }6 S; B6 i% H

    1 \8 Y9 ~7 b8 }% S" `) Wvoid Vblank()//VBLANK中断服务。每当中断到来的时候都会执行这个函数。 ) D! _( ?) Z' Q. m

    % l- L8 M. D9 ?# C( U: D{& A, N1 l# p% o; ?, P6 C: q

    / h; i7 ]2 g$ Y& N! t  KeyRead();//读键盘函数。这个函数会得到你所按下的按键。
    7 {* C8 k+ p! ]5 A; k9 [7 P
    $ H2 K7 c0 @$ n# f9 q/ I. @) P$ B  if (Cont & L_KEY)x--;//如果检测到键盘左键连续按下,则变量x自减。
    + Q3 c0 G7 @' o. {/ W
    ( g5 y* m7 Q5 e- V% i  if (Cont & R_KEY)x++;//不用解释了吧? # k& w' |# N7 G

    2 T& c( \4 o1 y7 u- x  if (Cont & U_KEY)y-- ;; ^: F* l1 a1 l( C$ t% @

    ' h9 m1 A* w) m+ y' w  if (Cont & D_KEY)y++ ;
    5 z2 ]) t. T) f+ h/ r  T3 V1 `+ t* T0 M% d4 d3 b( D
      GsBgOffset(0,x,y);//对BG进行卷轴操作。 . g% B. t! Y/ v( S; L+ k" s5 l

    0 D/ B. V; U9 x; `4 n3 I9 V}: F1 ~4 V% P" d, H9 }! q
    . n, H% B7 k; I6 F9 v6 Q  A
    行了,程序完成。编译吧。$ S7 j7 H0 E) H1 b, s8 W) q1 F" t5 O

    / X% b" F6 P9 S9 }. T. @6 B& z
    $ K0 R9 a) B- Y8 V+ |; M  [0 {
    2 l7 w) r0 t. T5 j% {7 {# |& p+ G/ Q2 Q9 h8 J; \0 o
    ; q) o1 V4 P3 ?- S$ Y
    能够看到,屏幕上出现了你的图片,按下键盘的方向键,图片就开始卷轴。
    * t/ K! c! c" g# F6 `" x" s) ?* k% g( w* b
    我们来分析一下程序的运行状况:首先GBA打开了中断。从此以后,只要有中断产生,GBA就可以响应了。然后GBA将图片的所有数据装载到内存里。再然后,GBA设置BG0,并使图片在BG0上显示。再然后,程序进入死循环,每循环一次,等待一次同步,而每等待一次同步,图片就显示一次。于是GBA屏幕上就连续不断的显示出了那张图片。然后,假设VBLANK中断发生了。这个时候GBA执行中断服务。首先查询键盘。如果这个时候你没有按键,那么两个变量就没有变化。卷轴偏移为0,也就是不偏移。中断完成之后GBA又回到死循环里,继续显示图片。假如又发生了一次中断,而恰巧这个时候你有按键,于是GBA检测到按键,并得到你按下的按键值。假如是左键,那么x变量自减,于是图片向左偏移一个像素。连续按键,于是图片就连续卷轴……卷轴完成之后,GBA又回到死循环,继续显示图片……
    - C; d5 h$ q$ P' P$ }% I# d( B, g1 U" G) R8 C3 o
    OK!这个图片是正常显示了,而我们也可以对它进行键盘操作了。希望你能明白这个程序。下次的教程,我们来看一下多层BG共存;就像上面那张口袋妖怪绿宝石的标题画面一样。掌握了多层BG技术,你可以做出非常漂亮的游戏标题!还有,你有玩过《逆转裁判》吧!对那种图层结构组成感觉如何?下次的教程,我就教你“屏幕上一个漂亮的背景图层+屏幕下方的对话框+对话框里面的文字”,也就是,三个BG同时使用!这三个BG中,一个图层是用来显示256色背景图片的;另一个图层是16色对话框的;还有一个是用来显示文字的……下次的内容:多层BG!理论课内容:TILE模式下的多层BG。另外,下次将要涉及程序调试;这是一个很令人惊讶和振奋的工作,因为你能看到以前屏幕上乱七八糟的突然间就显示出一幅美丽的图片……别忘了准备好你的模拟器们!
    # m' R% W! H# C# v6 P' @. X" B* M) l" ^- |% C
    GBA游戏编程菜鸟入门篇(四)——多层BG及TILE模式下的多层BG
    ( M! s  j3 C+ T! g! }6 X: ?4 J% ^( P; P9 e, p2 X3 Z
    废话少说,开始今天的教程。为了方便说明多层BG这个概念,顺利操作多层BG,我们这次改变一下学习顺序……+ c$ D6 G5 P6 h! q1 Q

    % c, v& g. c4 q' b4 P7 U" v首先打开上次你编译好的ROM,就是那个显示一张图片的ROM。' H+ _, D3 x# K2 d  ]6 a/ o, S. V. P% D# m
    7 }6 ~& d$ N" a4 F/ P$ c

    4 o' H. W- N/ ~  G& W/ ]  ~; h: }# N; ?0 O& K4 c- z2 R1 U% x& `
      v, ?" a8 P, I' ?; B
    ) G$ F. d4 }% l2 E
    然后菜单-Tools-Map Viewer,这个时候出现了一个窗口。+ x6 `7 K, t1 ]. g) M
    8 ~% m# H9 C: @+ ]; ~

    : K* g; @, y6 [5 z" U- v
    % i& Y" T7 ]& C- H6 ]/ ~8 L" @, S' }& z. N$ f, @* D+ u
    这个窗口,可谓调试游戏的巨大帮手之一。我们仔细看一下,这个窗口有什么作用。
    0 w+ B9 S4 M$ c3 R5 W/ q
    2 ?" @8 O9 m( N& D' _首先,左上角的“Frame”框架:这个框架是灰化状态,就是说,当前不可用。那么这个框架到底有什么用呢?还记得位图模式吗?还记得“双缓冲”吗?没错!这里直接反映双缓冲!Frame0,就是双缓冲的上层;Frame1,当然就是下层。在我们这个程序里面,使用的是TILE模式,不存在双缓冲,当然这个框架是灰化状态……& ~( W  ?  k3 i3 ~4 C
    - [& e) _# s' d8 x1 |$ ~" |$ N3 j
    紧挨着它下面的“Background”框架:这就是所谓“BG”了!我们知道,在位图模式下不存在BG的概念,只有TILE模式下有BG。而MODE0下面可用的BG有4层,分别是BG0到BG3。这里就直接反映出了这4层BG。在我们这个程序里面,是使用了BG0用来显示图片,所以BG0有图片。你切换到BG1到BG3看看,都是“空白”吧!7 L# z9 M( y# Q3 w) B' S% k
    / o# p* ?$ {; E
    在下面两个复选框:分别是拉伸和自动更新。一般,拉伸选项没有很大的作用。不过,那个自动更新,确实有一定作用!在以后的程序里面,可以很方便的跟踪BG。到时候,我们会涉及的……
    - t7 B- F9 G6 n7 W1 [, D
    6 {6 H3 a7 _5 B+ J# |- G+ [2 o再下面,是一个TILE。当你的鼠标点击右边的BG预览窗口的时候,这里就放大鼠标点到的TILE。一般来说,作用不大。3 O4 I6 ~4 _# y! g; q

    5 J4 Z2 \" t3 v+ }( J最重要的,就是中间那几个数据了!
    1 J9 ?# A5 P# b0 N
    2 E( ~' @8 j) _" ^) d2 uMODE:直接反映程序当前使用的MODE。我们这个程序使用MODE0,所以……' k; v$ \9 s8 i( `' H  Y. H6 d/ e
    + M) ?* ^0 k2 w6 Y/ b3 d1 U" [
    MAP:这是图片MAP数据存放地址。根据我们这个程序,可以知道MAP数据存放在第31号区域,它的地址就是0x0600f800。关于这个16进制数到底怎么和区域号换算,一会儿你就会很直观地看到了!& ?6 U/ l9 x2 ^/ e, E3 q4 E( W& E
    # d! V' X& X% N( U$ C1 y2 y3 i/ B
    CHAR:毫无疑问了,这是TILE数据的存放地址。我们的程序里面,使用第0块Char Base来存放TILE数据。它的地址是0x06000000。同样,这个16进制数也和Char Base有换算关系。一会儿你也会很直观地看到。
    ! K9 R" o- t& p# `/ `6 H4 {) W. D& b& M  {& w' [3 _7 ]
    SIZE:大小。程序里面设置BG的时候,规定BG是“Gs_BG_TEXT_SIZE_256x256”,所以这里显示256*256。0 s) i3 r+ v- Y/ [1 R0 }
    7 T, g* @4 G8 _/ l: Z
    COLOR:颜色。16色一点不错!程序里就是这样设置的。/ o* e0 j4 }7 Y% b# o

    + I/ j( U; I+ f7 CPRIORITY:优先级。优先级从0到3依次降低,就是说优先级为0的话,可以遮挡其他优先级的BG。程序里面就是这样设定的。6 E( ^7 C' t  X+ L
    & i% O: m% g+ d! T; j& E% d
    MOSAIC:马赛克。如果你设置BG的时候,允许该层BG使用马赛克特效,那么这里就会出现“1”。我们的程序里面不允许马赛克特效,所以这里显示为0。关于马赛克,我先说两句:马赛克属于BG的一种特效。GBA硬件支持4种特效,分别是马赛克特效、透明特效(ALPHA特效)、窗口特效和FADE特效。完全学会TILE模式之后,你就可以初步控制BG了。这个时候,来点锦上添花的特效那是再好不过的了!到我讲完TILE模式和多层BG的时候,我们就深入特效……' W/ ~. l5 k4 D% M# j+ \7 F
    9 W7 O) q2 \" ^8 y3 y
    OVERFLOW:溢出。这个数据,我到现在还不知道它有什么用……请达人不吝赐教!
    9 w8 B) w" ^) K6 x4 ~/ p/ _( b
    + S5 i  V# \. s5 M* C- {  c再往下三个数据,当你的鼠标点击右边的BG预览窗口的时候,左下方出现TILE的放大图的时候,这里显示这个TILE的详细数据。由于我们有一个更强大的反汇编模拟器,而那个模拟器在这方面的功能实在是强大,所以我一般忽略这里……
    + E: k3 G  s  |2 ~$ `" l3 Q% A+ I3 J: f8 n; z
    最后,那个小黑窗口……装饰吧……?
    ) [  _1 w  I( E; ^* o% M: k4 x( D) e4 c
    嗯……差不多了,MapView窗口就这么多东西。然后是Tools-Tile Viewer。
    ; V/ X, Y* B! ^1 J5 ]
    8 B; F& Y$ K, D
    # |3 W/ P3 @7 c, ]
      q4 Y# U: Q2 Q. K# J% s: n; Q& N5 o/ a

    & p9 g+ I1 }" p& O7 a  F  X- U# ]4 s这是察看TILE数据的天堂。我们继续。- H. e' q. u2 v5 n* I

    1 J! b# K& x! z# z2 r! `( I# BColor框架:我不用解释吧?如果你的图片是16色,那么选中“16”;如果是256色的,就选中“256”。选错了,右边的窗口预览就乱得不像样。
    4 A! `0 I# h2 `, h  i+ x0 `  |* S2 ~0 [2 e* V
    (众人:那么,32768色呢?)雷精灵:哼哼……TILE模式下会出现15位色吗?(众人:谁说TILE模式了?位图的MODE3、MODE5不就支持15位色吗?)雷精灵:位图模式下这个框架就会灰化……+ j0 a2 C7 k, `7 U$ p

    ( q: C8 w( }# K  zCHAR BASE:这是最需要关注的!你会看到,这里有五个单选框,实际上,前4个代表了BG的4个Char Base,最后一个是精灵RAM,又称OAM。现在,我们需要重点关注前4个。
    2 n& K% a" K* P, x& `: j; t  Q9 i
    在我们的程序里面有关TILE数据地函数是这样设置的:GsLoadBgTile(BG_Tiles,sizeof(BG_Tiles),0);GsSetBg(0,Gs_BG_TEXT_SIZE_256x256,Gs_BG_COLOR16,0,31,0);。可以看出,TILE数据被保存在显存第0块Char Bsae。因此,选中最上面的“0x6000000”,右边的预览窗口出现了图片。不过,图片是支离破碎的。至于我们它为什么会这样先不管。4 p# @3 z- g8 z5 a

    & L" A( \% d3 z6 s' @0 B9 d5 i现在你修改这两个函数成这样:GsLoadBgTile(BG_Tiles,sizeof(BG_Tiles),1);GsSetBg(0,Gs_BG_TEXT_SIZE_256x256,Gs_BG_COLOR16,1,31,0);。明显,这次是使用第1块来存放TILE数据。这个时候编译出来的ROM,选中第一个0x6000000”时,图片出现在窗口下方,而选中下面一个“0x6004000”,图片像没修改的时候一样出现了。
    8 }# h  ?; d# W3 f% B, ?5 M1 |+ R% S, e
    ' [; k; S8 m0 c5 @( V& D0 ]这个时候,你选中“0x6000000”,用鼠标点击一下图片的第一个TILE,中间的“ADDRESS”数据出现了“0x6004000”!看到了吗?这就是第1块的地址啊!
    2 q$ w+ Z; b7 v& @4 d8 x3 C, }* e# X+ V' o7 ]3 R2 r# w3 ?
    综上所述,程序使用哪个块存放TILE,你就选中哪个单选框。于是支离破碎的图片就显示在右边的预览窗口中。并且是顶头显示。如果你选中的是别的单选框,要么图片不显示,要么TILE数据上面出现了“空白”……
    # h3 X4 {) u8 M7 H* w& i- y& c% K9 o! W# B3 m% U
    于是,有人就试着使用第3块了。察看的时候,预览窗口出现了这样的景象。
    2 _5 L* K, Q- P* A$ g  @
    6 I2 ~9 |9 z8 u/ E5 p  X9 W# |6 t) X7 ]& G) y. t
    + {' e4 b! |9 k' F1 h6 D( Q6 y

    * g3 N; K2 s" v! l预览窗口最下面出现了一些奇怪的竖道……这是什么?切换成256色,还是那样。“0x6010000”里面呢?黑的……' d2 t' z. Q$ V) k9 r) m
    ! H$ H- e. y+ c% g5 U8 L
    (众人:雷精灵!这是什么?)雷精灵:这是MAP数据!- f4 O# ~# x4 s3 M
    7 |( v! h7 I/ ^& ^9 A. \
    看看程序,MAP数据是存放在第31号区域的。我前面说过,一个块,可以分成8个区域。那么BG可用的是4个块,分别为0到3块;那么就有32个区域,分别为0到31号区域。所以,在第3块的最低层,那里就是第31个区域,MAP数据就在这里面呢……+ P' T9 _/ P. |# S3 i/ b
    4 @! U( g. w9 Z7 Q+ J' r4 B! G
    MAP数据的存放位置是受你控制的,因此可以存放在任意一个区域中,只要是不和TILE数据冲突就行!* x& [, Q6 G, l: a/ S7 h* w
    0 D5 X; I1 `6 O  z0 g
    (众人:那么,冲突会有什么效果呢?)雷精灵:试试呀!我们来改一下程序:GsLoadBgMap(BG_Map,sizeof(BG_Map),0);GsLoadBgTile(BG_Tiles,sizeof(BG_Tiles),0);GsSetBg(0,Gs_BG_TEXT_SIZE_256x256,Gs_BG_COLOR16,0,0,0);。这样就是把TILE保存到0块Char Base,把MAP数据保存到第0号区域。编译,看看吧!
    ' i" R3 r; @& \8 l4 N6 s/ k( B; k0 r/ A% a& h9 X* T! F3 b
    看到了吧!图片花了……
      E2 ^3 t+ C7 z4 v" _8 ^6 X2 G6 O4 L: {$ m* E  W/ g9 F3 K
    所以,当要显示图片的时候,千万记住不要让TILE数据和MAP数据冲突!这一条,是实现多层BG的基础。+ s% a' B7 l% ?: u

    1 x7 r7 A9 M) c2 Z好!下一个,调色盘察看窗口。Tools-Palette Viewer。5 {( ]$ I. K$ ~7 g6 P, [. a! p
    , Q( ^/ B. A& l* ]8 Z, U% k& |

      p$ S. \' B3 Z7 F3 a- q6 E# w' `& }5 R+ k; Q, F% P4 a9 V; W0 s8 x1 R
    / c' T# K! g* T1 A3 G
    左方,明显是察看BG的调色盘的。右方,是察看精灵调色盘的。我们的程序中没有使用精灵,所以精灵调色盘就是空的。
    1 Z, K0 f0 i. e. E* V. T
    0 i6 B0 c  C) f7 [BG调色盘中,可以看到是16*16方阵,共256个小格。每一格代表一种颜色。可见,这里最多显示256种颜色。如果是位图模式,那么这里就全是空的。
    3 |* q* O* H$ B  ], d
    ( i1 i  q' ~2 x; V' |9 C这256种颜色,可以分成16组;每一行为一组,分别称为第0号调色盘到第15号调色盘。在MODE0下GBA可以使用16个16色的调色盘,或者1个256色调色盘。这里很重要,请务必记住。
    ! \: ~; y/ f! r
    0 \2 m6 D4 ]: {8 Q) Z9 h那么,回过头来看我们的程序:我们这张图片,是16色的,程序中也设置BG的颜色属性是16色。所以GBA就从调色盘中腾出一组空间来,用于存放图片的调色盘数据。腾出来的是哪组调色盘呢?你设置的:GsLoadBgPal16(BG_Palette,0);——第0组!所以,调色盘察看窗口中,第一行调色盘,也就是第0组调色盘,出现了图片的调色盘数据。  p0 d+ l1 t- n% T

    % }- |: F. _1 O! R0 ^那么,能不能用别的组?可以!这16组中的任意一组都行,随你的便!你可以改一下程序看看,效果完全不变!唯一变化的,就是调色盘察看窗口中图片调色盘的存放位置……
    " B5 g3 o' G+ z* G) a5 L+ S
    $ y: g: P) O! A  J, A$ ~$ J(众人:为什么第0组调色盘似乎没填满的样子?)雷精灵:说是16色,实际上不到16色……你用来转化位图成C语言数组的那个工具——gfx2gba,就是你键入的那一串命令,自动优化了调色盘。没有用到的颜色,就扔了……
    ! w2 J# `( g& u* Q2 p
    + j: N1 R+ {2 p1 N关于调色盘,我必须说明一点!对于16色的调色盘,你知道的,共有16组。这每一组的第一个颜色,都是“透明色”!也就是说,对于16色调色盘,最左边这一竖排,都是“透明色”。什么是透明色?就是说,这种颜色在GBA上面不会显示出来。你可以看一下察看窗口,第一种颜色是绿色,0x03e0。这种颜色在GBA上面没有被显示出来。我们的程序中,用于显示图片的是BG0,其他几个BG,颜色都是绿色——都被透明了!所以,只显示出来BG0……这个时候,想必你也明白了,在第一个DEMO中,就是显示文字的那个DEMO,我为什么把颜色装载进调色盘数组的第1号而不是第0号……
    ( _. u. c# k2 D3 b: g& |5 w. a4 ], G0 a( \4 p% \
    我刚才说,16色的调色盘,第一种颜色是透明色;其实,256色调色盘也是第一色为透明色。只不过,因为256色调色盘可以容纳16个16色的调色盘,所以对于整个调色盘来说,16色的调色盘可以有16个透明色而256色的只能有一个透明色。2 @  K5 ^/ j5 c! a  z5 q7 t, w

    ) f$ R$ k5 Y2 e2 A5 J0 i以上所说的非常重要。你可以看看教程3中那张《口袋妖怪》的片头截图:它是采用了3层BG。根据高优先级的BG可以遮挡低优先级的BG这一原则,那么可以知道“口袋妖怪”这层BG优先级最高,以此类推。但是,如果简单的把三层BG叠加起来,那么很明显只能显示出“口袋妖怪”这一层。因为这一层的黑色,把下面的BG遮挡了!所以,可以推断,在这里黑色为透明色!同样,云雾那层BG,黑色也是透明色。至于最后烈空座这层,无法推断谁是透明色……* o5 B( n0 `2 `) I: M
    $ x4 h1 |9 v$ ]( R" ~
    (众人:那么,我要是自己做了一张图片,想规定某某颜色为透明色,应该怎么做呢?)雷精灵:呵呵呵……有请PhotoShop!4 h/ X8 B& w% U& |9 F& r( x; s$ F

    * N$ {1 u# M1 E# J6 y还是我们上次做例子的那张图片。用PS打开。由于截图太大我不放上去了。: c) h5 T/ H5 |: n  a  J1 n. a% r

    $ j- F3 X. ?+ w& m' @" [4 I然后,菜单-图像-模式。这个时候,后面出现了一级子菜单。
    5 ^; Q. J" x9 l2 _, m6 r' T7 @/ G+ Z) l& g7 w' M
    注意看那个“索引颜色”和“RGB颜色”。如果这个时候默认选中的是“RGB颜色”你就选择“索引颜色”。
    2 r6 S- u; R; u; V8 l) Y* F1 D  J+ {+ H  n7 S$ Y- h9 A/ B1 `4 l
    弹出一个窗口。看到了吗?这里更明显的显示出了图片颜色数确实不到16色。
    / H9 _7 Q4 x# ]# x$ I9 |$ t( x9 |! Z
    不过我们关心的不是这个。“调板”选择“局部(可**)”中的任意一个,然后颜色数你就可以自己规定了。填上15。记住一定不要填16!否则这张图片无法保存成4位位图!
    $ ]# V8 a6 G+ ~( _
    4 A/ I) k2 b: p然后点“强制”下拉框,选择“自定”。如果本来就是“自定”你就再选一次。
    - ^( D5 a% `7 ]! U1 B
    6 c1 n; d& _- o- T这个时候弹出来一个颜色窗口。$ P9 Q$ H3 `( `
    / i5 ?0 r7 {% x) f) m5 a
    点第一格颜色。一定要点第一格颜色!因为GBA调色盘规定第一种颜色是透明色,而我们正是要选择透明色……
    9 i1 @) d* u8 c0 k3 f+ W: k5 n4 L$ Z1 L& v; @3 q- @0 \
    然后出现“拾色器”窗口。扔在那里不要管它!把鼠标移动到你那张图片上。这个时候,鼠标变成了吸管。
    3 Y7 U! j$ t, q0 e: [% }5 A9 g& H
    你想让哪种颜色为透明色,就用吸管去吸那种颜色就行了。吸完之后,“拾色器”选定了你选择的颜色。' J# q# b# U3 B( ~
    8 t/ ^# f6 p7 l1 F
    然后一路“确定”就行了。最后“另存为”4位位图就搞定了。  {% N9 c  |1 y$ x$ G: V( g* @% t
    ) ]% o3 ?1 p; N: C9 J
    那么,256色位图呢?一样的方法。自己规定颜色数的时候就随你便了,只要不大于256就可以。填256色行吗?行!8 J/ u: q7 w- H1 W+ ?
    0 ?/ K0 D7 j2 m6 n
    然后就完全一样了。保存的时候,保存为8位位图。
    8 R+ K! x- W. g6 ^5 [7 \* V# J* D  M3 s2 W4 p. a' @4 t3 H5 V5 S+ n
    图片搞定了,转化,编译吧!2 I5 ]5 _# t- z1 Y# Z0 M
    8 l5 ?1 ~/ e9 V$ \5 f
    嗯?没变?别胡扯!你看看调色盘!第0号颜色,是不是变成了你选择的那种颜色?2 J; c; t, @" [& {5 [. Q0 }
    ' X$ F1 g/ X( l+ O
    (众人:那么,怎么显示出来没有变化啊?)雷精灵:我问你,如果这种颜色成为透明色,那么图片中涂有这种颜色的区域,显示出来会有什么变化?(众人:透明呗!)雷精灵:对!确实是透明!那么,BG0的某块区域透明了,会有什么后果?(众人:如果BG0优先级最高的话,其他的BG会在透明的区域显示出来。)雷精灵:你这不是明白了吗!你看看BG1到BG3。(众人:噢噢噢!原来是这样!)雷精灵:就是这样。如果其他几个BG没有用到的话,它们自动使用透明色。但是由于没有给它们定义调色盘,所以对它们而言,调色盘第0号颜色就不是透明色。所以,它们几个BG,显示出来了。但是对于你用到的BG,该透明的区域确实透明了!只不过,后面的BG颜色是透明色,并且通过透明的区域显示了出来……0 k5 X# Q7 X( T" {* V
    / l2 R$ q/ d3 ?+ l$ h
    那么,既然都明白了,我们来做个显示多层BG的DEMO!这一次,我一步一步的写源代码。
    + p7 {! k) p! C4 }0 c* q4 g, v& n! }! f3 u, W6 }( {
    (众人:不是还有几个窗口没讲吗?还有一个OAM Viewer……)雷精灵:我知道。那个OAM Viewer,是用来察看精灵的。但是反汇编模拟器No$GBA在这点做得极为出色,所以你可以抛弃VBA的OAM Viewer。其他几个比如IO Viewer什么的,一般用不到。, `: y# S  j1 k

    - ^# z) K4 A. Q3 v. `嗯……首先是图片资源。自己找图吧。
    " W! }; a6 u2 B
    * S: _+ I( c. e2 A, \首先我们需要的是一个256色的背景图片,256*256像素。我在这里给它命名为BG.bmp。
    , |4 ]5 {0 l$ o: l( c9 G
    8 @! ]8 E3 U! Z# s这张背景图片有点特殊要求:还记得MODE0下面的调色盘,最大为256色吧!那么,我们这次的DEMO,是要同时显示这张256色背景图片和另一张16色的对话框图片,还有位于对话框里面的文字。这就出现了一个矛盾:如果将256色图片的调色盘装载到调色盘内存里面,那么,那个16色图片的调色盘,装载到哪里呢?调色盘最大256色,已经没有空间留给16色调色盘了;更何况,还有文字层,至少需要2种颜色(透明色和文字颜色);如果你想要文字描边效果那还得需要一种颜色;一般的RPG里面,为了突出某些重要的剧情物品或者重要对话,有些文字反白显示或者高亮度显示或者红色显示什么的……这又得需要若干颜色。5 y3 R; J/ W# T2 \

    ( H. Y/ Q  q; S1 o  [2 j以最坏的情况考虑,对话框图片是16色,那么就需要一组调色盘,文字颜色,也给它分配16色!(15种颜色,显示文字绝对够用了……)那么,那张256色图片,留给它的调色盘的空间,只有224色!
    " E8 r& P/ ]7 z; J! f3 ?1 [1 B4 d  |
    所以,你找到的这张图片,如果颜色超过224色,请注意要减色!否则,对话框图片的调色盘和文字调色盘将会覆盖你的256色图片调色盘的一部分,导致显示出来的图片出现颜色异常!/ F$ ~+ f8 D- a

    ' p4 {0 i% j$ v& J9 K/ _减色也很简单。还是有请PhotoShop,还是像选择透明色一样,只不过到了填色的时候,填224就行了。由于是背景图片,也就是它的BG优先级最低了,所以没必要选择透明色。
    , X9 @9 o  ]/ c6 }, c; R* e+ ]$ R1 L$ [3 {2 f7 x
    - D# E+ l. b, n1 \) N
    8 j& {4 O8 d# U' b$ U, |/ S( u: L0 f

    ; t( e3 t+ q- {4 P, l+ O- o( [$ D( c5 T

    # h4 r6 H+ y: {" n* e- x; k* g这张图是我从GBC的《心跳回忆》里面截出来的。由于GBC窗口小,所以做了加工,将它拉大到240*160,就是GBA屏幕大小。然后再拉大到256*256,以适应程序需要。图片截出来的时候,是真彩色(24位色),然后用PS减色,减到224色了。
    0 h5 x9 `) _( z5 t$ _/ E
    5 v! S7 o3 h) d转成C语言数组。由于这次的图片是256色,转成数组的时候,命令有点变化。根据gfx2gba的说明,这次的命令是:gfx2gba -fsrc -t8 -c256 -pBG.pal BG.bmp。
    ' P- `; z3 {5 o: x! n% }1 ]  c5 a# s, v/ j5 H
    然后文件夹里面多出了BG.PAL.C、BG.MAP.C和BG.RAW.C这几个文件。
    ) q' {% j+ l( X8 O* L% p* j
    7 l8 E! B" j+ U2 O7 N2 T然后就是16色的对话框图片了。依然,16色,256*256像素。起名为Frame.bmp。. V8 c! a$ P& M+ O- d+ U

    + y2 Z0 J0 |" m9 A记住这张图片要选择透明色。然后转成数组。* Y5 c! G' L, w% l! ?) S- A

    4 v8 C+ V6 |3 R, d/ V. p! E. _
    $ u, }( w2 d  W+ R! |# @  w
    1 U6 A* H; |2 d. z+ `* H3 U8 c. D/ K2 S. d. I$ b& t6 w! `! x
    * B+ p8 s; m, R1 j
    ; T: C' H1 {7 |% I! S0 K1 b7 _
    这张图是我从HuangYZ大人写的《心跳回忆GBA》的DEMO里面截出来的。最近我一直在写烧录地带的《心跳回忆》,当时为了能够在游戏对话中显示姓名所以对那张图修改了一下使对话框左上角突出一块。这次直接拿来用了。在这张图片里面黑色被我设置为透明色。; a5 S5 P0 Q& g$ Y
    7 u8 _* _/ A0 Q5 X( H
    好,写程序。2 t# \; _' Q: Q
    - o' R& n0 g5 l
    首先最好规划一下:我打算,让BG0显示256色背景图片,Char Base使用第0块;BG1显示16色对话框图片,CharBase使用第1块;BG2显示文字,Char Base使用第2块;至于Char Base第3块,存放这几个BG的MAP数据……优先级毫无疑问,BG3不使用,BG2最高,BG1次之,BG0最低。
    / e. k; q  f. ~5 q, j# I' [# G' c/ Q, v! h4 T' {2 ]4 v% n
    1 E* J+ ]' H+ h$ N8 L
    #include <GsGBA.h># N0 D6 }4 r& H) K

    ) }, f, I. Y" |7 J$ G9 R#include <GsTEXT.h>1 p; |" f; V- Z
      C: b7 i2 t& a
    #include "BG.pal.c"1 W/ U5 V/ v* o1 q

    , C* s# g' K8 z, @#include "BG.map.c"
    ; q" M$ H: ]8 p! O5 {8 f& e$ k/ |% n9 O8 s0 `0 ?
    #include "BG.raw.c"
    , F5 e  |4 L- e- ^- k
    4 L2 _& `4 d( k5 S0 g#include "Frame.pal.c"3 R( b% p7 o( w- R& L

    ) o# m2 N6 R  I2 |0 p#include "Frame.map.c"
    ! G, h. F2 K) ]2 N4 S2 G9 W' C
    ( c% y/ B. I; \, \8 N#include "Frame.raw.c"//你也有感觉到,这一堆包含实在是太让人头疼了。确实,一张图片就有三个包含,以后出现的DEMO,万一出现更多的图片,这个包含,岂不是要写很长……所以,我们最好把这三个文件合并。最简单的方法,就是用DOS下面的COPY命令,把三个文件组合到一起。不过,每次处理完一张图片就要打一次COPY命令,也有够麻烦的。最好的方法是写一个小程序(当然这是运行在Windows环境下面的程序),每当生成完那几个数据文件就自动将这三个文件组合。我偷懒了一下,写了个批处理,也自动完成了这样的任务……
    7 u3 I- x6 x" C$ ^) h& }  c% x; \/ A$ R( [/ i
    const IntrFuncp IntrTable[14]={dummy,dummy,dummy,dummy,dummy,dummy,dummy,dummy,dummy,dummy,dummy,dummy,dummy,dummy,0 r& w5 i! E+ G2 F4 c: [$ i
    };//中断向量表。这次,仅仅是显示而已,不对它进行操作,不设中断。
    0 R( X! U' |0 u9 z8 f5 I5 z7 T/ W4 O0 }
    int AgbMain()
    / a) @$ f! O6 d' V+ k6 l& B4 I0 q6 z5 T5 B0 [
    {
    0 k' q  J. c  h) R2 L8 B# N% o7 d) Y& k: E8 i
    GsSetMode(MODE_0); 3 k& m9 ]; ]6 e: c. U7 ~

    ! f" G& m, k2 ~4 E( r. p! TGsInitBG(); - k* y* f8 @- p

    6 B# N0 V' ]7 }# s. C3 \3 DGsLoadBgPal(BG_Palette,224);//载入256色调色盘。这个函数的参数请参阅帮助文件。我前面说过,最坏的情况下载入256色调色盘时最多载入224色,所以这里调色盘载入字节数设为224。
    . C8 M# R4 F( x8 y4 Z! k1 O' f  X  L6 W" ^1 d( [
    GsLoadBgMap(BG_Map,sizeof(BG_Map),31);//还是一样,将256色背景图片的MAP数据装载到第31号区域。
    8 v6 E& T6 n- A! S
    ' A  u/ c1 t; o) q1 l2 \6 yGsLoadBgTile(BG_Tiles,sizeof(BG_Tiles),0);//还是一样,将256色背景图片的TILE数据装载到Char Base第0块。
    7 q8 a# k( n% U6 R6 V. `
    & u! t: N! [; G& |; M; eGsSetBg(0,Gs_BG_TEXT_SIZE_256x256,Gs_BG_COLOR256,0,31,0);//设置BG0:TEXT属性,大小是256*256,颜色是256色,其中MAP数据在BG的31号区域,TILE数据在BG的第0块,禁止该BG的马赛克特效。
    ( m# R5 S' e8 O- I$ v  X  a# |1 b; \! z' N
    GsBgPriority(0,3);//设置BG0的优先级。最低。
    # i3 d! K' {! m, t6 C# h2 o" P( m9 a/ k4 O! I- A
    GsSetBgState(0,ON);//打开BG0。写到这里,如果就此停止,再写上死循环、等待同步和函数返回,那么编译之后,就应该显示出那个256色的背景了……不过我们可不能就此停止,后面还有两个BG呢…… 0 _) F, K2 U9 ~8 D
    - x  u" K; B" i! ~
    GsLoadBgPalEx(Frame_Palette,224,16);//这个函数是实现多层BG调色盘的关键。假如,我直接载入BG1的调色盘,那么它将默认从调色盘0号位置开始存放。这样做的后果就是将BG0的调色盘覆盖了一部分!导致BG0那张256色的图片颜色不正常。所以这里必须要用这个函数!根据帮助文件,可以得知Frame图片的调色盘将从调色盘内存第224号开始存放,存放16种颜色。这正好和上面那张256色的调色盘相接起来,既不浪费空间又不导致冲突……
    ( T- y- `% Z8 M, k" I# i9 H, f, ?( ^/ e  v- X" @
    GsLoadBgMapSelectPal(Frame_Map,sizeof(Frame_Map),30,14);//这个函数是实现多层BG MAP的关键。既然Frame图片的调色盘是从第224号位置开始存放的,那么对于MAP数据,也应该从第224号位置(也就是第14组调色盘)读取调色盘……(众人:MAP数据与调色盘数据有什么关系啊?)雷精灵:怪我怪我,我当时没有说清楚,MAP数据里面保存TILE数据的坐标,还有一些调色盘的索引数据,TILE的形状和翻转数据什么的……这个不重要,所以我当时没讲……% H8 _. u* a5 m9 s
    ; h( P, \7 H% h) u2 Z2 I
    GsLoadBgTile(Frame_Tiles,sizeof(Frame_Tiles),1);//这个不需要变动函数,直接将16色图片的TILE数据装载到Char Base第1块就行了……- D. e& {  h7 |9 c& N2 U( |' y5 X

    . m# s* K# x7 h8 g4 cGsSetBg(1,Gs_BG_TEXT_SIZE_256x256,Gs_BG_COLOR16,1,30,0);//设置BG1:TEXT属性,大小是256*256,颜色是16色,其中MAP数据在BG的30号区域,TILE数据在BG的第1块,禁止该BG的马赛克特效。6 y, V$ o- b3 F6 {! U$ W7 X! q

    " c7 u6 L! O! S* `8 r. j' mGsBgPriority(1,2);//设置BG1的优先级。次低。
    ) w( d+ h* L- G( I& f7 L3 J/ H9 i% s; f4 y+ p. P4 W
    GsSetBgState(1,ON);//打开BG1。接着是文字层了……1 H; H2 E, Z2 d* [- N4 ?

    , F1 _" J  u' I. ^( Y" ABGPaletteMem[241]=0x00ff;1 X; w. t" m0 x7 B- l8 t9 Q

    " ]3 S4 @4 k% q; d6 V4 ]7 D$ G* @GsSetBg(2,Gs_BG_TEXT_SIZE_256x256,Gs_BG_COLOR16,2,29,0);//设置BG2:TEXT属性,大小是256*256,颜色是16色,其中MAP数据在BG的29号区域,TILE数据在BG的第2块,禁止该BG的马赛克特效。( c) r, ]; p  A, l1 ^6 D
    7 k+ L9 @2 `  r- Z+ u1 t3 ~9 s
    GsTileTextInit(2,2,29,15);//初始化文字层。根据帮助文件,被初始化的文字层是BG2,MAP数据将被保存到第29号区域,使用第15组调色盘。而那个强烈推荐为3的参数,被我改了!我为什么要改呢?你想一下:这个参数是CHAR BASE号,既然文字层使用的CHAR BASE是第2块,那么这个参数当然不能再用3,而只能用2了!不信?你改成3看看!8 H- L" Z% \) [0 T

    " {* H; k/ v6 |1 zGsTileTextOut(20,110,"我爱TGB,我爱烧录地带!",241);//……: `/ Y2 Q9 C& f) ]1 E& s* h
    " v! o* K+ U8 X
    GsBgPriority(2,0);//设置BG2的优先级。最高。3 ~; a1 H) \+ w/ A6 `  k, C

    5 Y3 }& v7 t: r0 C0 P3 qGsSetBgState(2,ON);//打开BG2。
    * u" I' J  [$ ^& a: N$ x  o8 S2 j0 `" r1 U9 p
    GsSetBgState(3,OFF);//关闭BG3。没用到的图层,最好把它关了!$ |' o- |' }! K/ F+ N  o$ `

    ; M6 \0 B4 A: y" [& qwhile(1)
    * }8 V' h9 r+ C1 ]3 G{$ k* V0 [, `. S
    GsWaitSync();& L/ e! r6 B1 p
    }//死循环里依然是等待同步。/ Z/ P$ A! O5 D! N2 Q+ @
    return 0;/ B, a% ^4 C4 h/ a/ J9 H+ Z
    }' H0 k$ J9 _2 G. W
    : \: A* J0 }1 R- S1 P2 n
    程序结束。虽然看起来很多但是分成块之后就一点都不难了!编译看看结果吧!( @  P" m3 B8 _6 t  ^4 v

    8 M: @2 g* y' d. h$ g# v
    ' l7 T3 E6 a8 }6 _/ W' T7 t+ F5 V. l) u  r% D/ {$ m

      W: J% |( r" k5 h! i/ \. U& J& y
    ; ^: b. a' r. e% ^- F) w" ^5 _5 k. j0 ^
    2 v" @% |& ^5 S" Q  S* `

    # S+ h- p- F3 |$ E- c( s$ m表面上看,确实相当不错!美中不足的是,那一行文字,有点偏上!最好能把它拉下来,放到对话框里面!这个问题怎么解决呢?我先不说答案,大伙讨论讨论,应该怎么做?
    9 C* h5 h" n! y0 }( f  C& p4 l% e  Y& O
    (one of 众人:简单之极!文字函数中,不是有控制显示坐标的变量吗?例子中文字的y坐标是110,那么,增加y坐标,文字不就降下来了么!others:嗯……嗯……说的有道理!)雷精灵:呵呵……我不信!你试试!
    : Q5 X5 V- b* U, A' j3 P% N7 W8 K$ O7 s0 N5 h
    (the one who just expressed his ideas:嗯?怎么会这样?文字被砍掉一半!others:是呀是呀!我稍微降得多了一些,文字就全没了!)雷精灵:再仔细看看文字显示函数!1 A8 G4 H8 S& |# j3 b
    # x5 h3 b& l2 E6 X
    只见帮助文件上赫然写着:“参数:int x,y : 坐标 注意,y必须在0~120之内”!
    # ]8 R+ a9 O, }1 _3 ?+ o3 q7 P+ B- I
    0 K' v7 `6 u0 O3 Q2 ]5 h(众人:那怎么办?文字拉不下去啊?)雷精灵:呵呵……只拉文字,确实是拉不下去……但是……
    * {; d* K  Y. M
    7 f2 m& t0 B2 V: A( o$ t(another one:……但是……如果连整个BG都一起往下拉的话……)雷精灵:对!就是这样!对文字层进行卷轴!: x8 D  n0 o! V5 s# R/ K
    - }- `3 F" T, }$ b6 \- P
    将这个函数添加到显示文字之前:GsBgOffset(2,0,-10);然后看效果……/ I( M( M. m* E( a+ S! l  m# C% T
    $ c4 q" h0 N9 q' |; D
    ; M# @0 Z# g8 t3 M

    1 \+ n1 K; }% J5 w2 [! g) J) }( m, b) K
    好,这次的DEMO就差不多完成了。如果你对上面的代码还是不甚了解的话,建议你再从教程2重新阅读一下。如果你彻底明白了,那么,我们来玩一个游戏:找不同!你看DEMO中背景图片,和上面那张源背景图片有没有什么不一样的地方……找到的话,你就看一下TILE VIEWER。注意要仔细看看CHAR BASE!如果你发现了什么,那么,你就有能力继续看下一篇教程了——《GBA游戏编程菜鸟入门篇5——Char Base冲突》……下一次的教程,我们来彻底根除这次的DEMO中出现的问题。理论课内容也是Char Base冲突。

    ) s, Q; @. z5 v1 [9 `2 z! H( B* H
    ! e" U: ^  w) X$ F2 t$ k
    0 P& R+ y; b( L# p0 m
    % ~- i' Q. R( o" x  i/ V
  • TA的每日心情
    别闹
    2013-12-11 16:57
  • 签到天数: 129 天

    [LV.7]王国居民III

     楼主| 发表于 2011-9-13 09:23:29 | 显示全部楼层
    GBA游戏编程菜鸟入门篇(五)——Char Base冲突
    % _2 \, i- X6 Z: o. ?& u. G& ~1 \  N/ F5 S. ?
    上次的教程实在太长,我的手,打字都快打抽筋了……帖到论坛上的时候甚至发生了“发帖内容不得超过15K”这样的警告!我不得不把教程分成两次帖上去。但是这同时引起了代码错误,所有颜色都丢失了。这是没办法的事情,唯一解决的途径,就是让管理员将论坛限制放宽一些……扯远了……这次的教程很简单,内容也少,不过,不是很容易理解……大家做好心理准备!1 n8 v- N5 ^- ^' @3 `- y* }& }/ m
    ) c4 V, e5 e5 x, i
    想必聪明眼尖的朋友一定已经发现了,上次生成的那个DEMO中,256色背景图片,楼房窗口处和源图片不同。察看TILE VIEWER,发现原本用来存放Frame对话框图片的CHAR BASE,出现了其他数据!
    4 }1 E* p3 Y* K' s/ R1 W4 I4 D. u* J4 t6 k* s  ?, d) O/ n

    6 Y+ C2 |: X6 y$ E
    # K; `. _% a$ c' t2 [2 R9 J# w6 S3 {9 y

    * _8 B' v- {9 B  H/ \8 x6 j
    + x# @) i5 N2 E5 W# }. @用16色察看,只能依稀辨别出来,最前面几个TILE还有点像Frame的某些TILE,后面一堆TILE,就不知道是什么了……用256色察看,竟然发现这是256色背景图片的下半部分!
      a' A- A: B1 P3 a8 q+ H  v& R: f: _3 A
    这是怎么回事?
    / r7 v; m2 K9 d) ^) b# a- N
      A( Y' O! L+ u6 x; o# a7 ^/ J, A回过头看看程序:背景图片是存放在0号CHAR BASE,Frame存放在1号Char Base。存放在0号的256色的背景,怎么会跑到1号里去?0 u! K3 Z& ~& D3 I8 @% S9 B3 w
    9 s+ y3 M7 x  N- D
    (众人:0号Char Base不够存放背景图片,导致溢出的部分跑到了1号里面去啦?)' A* r) M6 ~8 `7 X
    " v  W  W- V1 L
    雷精灵:强啊!你怎么知道的?
    * |  F5 D8 l8 H3 K. T2 F3 n; s  Z3 O; ?! ]
    (众人:把两个图片的TILE文件打开,明显BG的TILE远大于Frame嘛!而每块Char Base的空间只有0x6004000-0x6000000=0x4000这么大,万一BG的TILE大于Char Base的容量,那很明显多出来的数据会顺序存到下面的Char Base里面去啊!)
    . }' b/ L+ h4 ]* Z( Z; H" S  r* {- p; ]1 C1 W5 r" N9 f
    雷精灵:Exactly!那么,怎么解决这个问题?
    ! A% U/ S1 I3 E/ `$ d2 E* ^3 s1 c* q. |" V' n  k5 J8 K6 x
    (众人:……)7 j& @- G2 f6 J4 y" i) o

    + v4 T3 |6 h  n雷精灵:(得意地笑)最简单的方法,就是不用1号Char Base存放Frame图片了。根据程序,0号已经用来存放BG图片,1号被占用了一部分不能使用,2号存放文字,那只能用3号!
    ; P  M6 s  u9 A- j$ b# H  x. R0 C; X/ }
    (众人:慢着!3号里面有这3个图层的MAP数据!你也不能用!)
    0 L- D  d( Y0 q# n9 ]: ^- a
    " }6 N( n0 m) s: ]9 Z9 ^3 a/ t雷精灵:没错。但是MAP数据很小,我让MAP数据搬家,腾出空间来存放Frame图片的TILE。
    1 |' ]* z: c, v  @$ [( L9 X% n2 z0 a1 s: u, T+ b; b; X+ D
    (众人:那么,MAP朝哪里存放?)
    0 V5 `: O  w9 K' k/ u
    9 x8 g9 }( s. @  Z雷精灵:1号Char Base不是被占用了一部分么?把那没被占用的部分拿出来存放Map数据吧!
    1 W1 U) P% r- k$ j! s$ ~' H2 b, @& K( l* R2 f) u9 R0 v9 m
    (众人:(怀疑的目光)行么?)
      T5 K" i0 E7 r$ p8 G. J5 i: l0 q3 T+ Y* ], i0 b( b/ J/ ~3 p) G
    雷精灵:试试吧!+ K6 d5 y3 J+ S& B% X3 o# ]
    6 q9 N7 b, P# `  \# F
    从主函数开始吧!9 W4 h/ ]6 L0 ~! p4 `* d
    6 G; i( f3 ?- f) q
    int AgbMain()  M  |) V8 V# d) d% }
      M* k1 N8 B9 @1 B8 n1 s- [
    {7 R5 x) _) d3 _. D! G! C$ r
    8 e. c$ o; M3 v! L# g7 f  C' @
    GsSetMode(MODE_0);
    1 a7 M/ o7 D$ _1 h
    ; t& p/ e+ [0 j, q/ QGsInitBG();
    , s6 {( `, p  |, J/ t3 p6 \! R: y. H$ M5 Q
    GsLoadBgPal(BG_Palette,224);//与调色盘无关,不用管它。 - K4 n3 b& E/ e: q& I
    , O' D8 b! V* Z9 g) l, V1 R/ F
    GsLoadBgMap(BG_Map,sizeof(BG_Map),15);//这次将256色背景图片的MAP数据装载到第15号区域。 为什么是第15号?你想……一块CharBase容量为0x4000;一块CharBase有8个MAP区域,那么,每个MAP区域容量就有0x4000/8= 0x0800。你看看在1号CharBase中,哪块区域还没有被占用?(0x6007800-0x6000000)/0x0800=0xf=15!那个数据0x6007800怎么得到的?打开Tile Viewer,选中256色,切换到1号Char Base,看到图片下面的“空白”了吧!鼠标点上空白区域的第一个Tile,看看它的地址……
    $ c$ G" r, Q5 G: q3 o1 ZGsLoadBgTile(BG_Tiles,sizeof(BG_Tiles),0);//还是一样,将256色背景图片的TILE数据装载到Char Base第0块。 * d) Y  h; y5 L8 T4 P- S

    3 h2 w; b& }* B8 d3 {GsSetBg(0,Gs_BG_TEXT_SIZE_256x256,Gs_BG_COLOR256,0,15,0);//设置BG0:TEXT属性,大小是256*256,颜色是256色,其中MAP数据在BG的15号区域,TILE数据在BG的第0块,禁止该BG的马赛克特效。
    & J: z* Y$ b5 C$ U2 Q
    5 e* o! y: I. i4 BGsBgPriority(0,3);4 H" [, l/ M1 @5 ^
      J1 @" M  {& s9 R
    GsSetBgState(0,ON);% e! u5 P$ I% A$ }  v

    1 K2 v$ u! X0 x* b' M1 w  A4 UGsLoadBgPalEx(Frame_Palette,224,16);
    4 w! ]! |  ]9 _' X1 k& R( o3 E. F
    GsLoadBgMapSelectPal(Frame_Map,sizeof(Frame_Map),16,14);//那这里,明显就存放在16号得了!
    : l+ r0 M6 W& \7 L" Z9 s1 C6 N# J2 I. A2 i' q; M) _9 ]! u
    GsLoadBgTile(Frame_Tiles,sizeof(Frame_Tiles),3);//这个不需要变动函数,直接将16色图片的TILE数据装载到Char Base第3块就行了……
    7 ]0 o. T) ~+ d; A  QGsSetBg(1,Gs_BG_TEXT_SIZE_256x256,Gs_BG_COLOR16,3,16,0);//设置BG1:TEXT属性,大小是256*256,颜色是16色,其中MAP数据在BG的16号区域,TILE数据在BG的第1块,禁止该BG的马赛克特效。
    9 g+ H& f& O; |# h1 M# `" a5 B
    1 J( d/ a( D9 AGsBgPriority(1,2);//设置BG1的优先级。次低。
    7 M( v. ]: u$ _( R* a1 L3 q+ Q: {' S3 h
    GsSetBgState(1,ON);//打开BG1。接着是文字层了……
    7 {, r* l" _- L7 `0 H6 _
    . z/ ?/ R8 g; A: |( Q# Z( I& @BGPaletteMem[241]=0x00ff;
    " @$ A) U% _" \& x/ H% }
    # }2 N: `3 w/ {; ^8 p; ]9 [- MGsSetBg(2,Gs_BG_TEXT_SIZE_256x256,Gs_BG_COLOR16,2,17,0);//设置BG2:TEXT属性,大小是256*256,颜色是16色,其中MAP数据在BG的17号区域,TILE数据在BG的第2块,禁止该BG的马赛克特效。5 N5 d9 ^# q& U3 d, _; {
    $ a1 g( J6 y% U
    GsTileTextInit(2,2,17,15);//那这里也是一样了。1 S# C8 M- q$ K1 W9 }) a2 G
      v: _  h: v  E& Q: _
    GsTileTextOut(20,110,"我爱TGB,我爱烧录地带!",241);//……, l& Z0 U7 x6 N2 Y, \5 `

    ! c/ b7 n2 L6 rGsBgPriority(2,0);//设置BG2的优先级。最高。
    8 o2 Z& C  a- c/ H0 q0 B7 O5 Z; ~5 {/ b
    GsSetBgState(2,ON);//打开BG2。
    6 a+ q, j1 U4 g. G; c- M6 L4 u5 R9 a- o$ l- p
    GsSetBgState(3,OFF);//关闭BG3。没用到的图层,最好把它关了!
    : G8 D, E8 `6 C7 A# [
    8 C3 c' c5 m: l5 p7 x: o修改完了,试试看吧!3 I1 Y* A, v- |. j& s7 a. V

    5 m7 d' O8 o9 Q! r1 d
    ) }6 @: o0 k2 w. D$ h& k. e0 L* ]+ S/ e$ _5 F* M6 z5 q
    ! R  d$ F2 o/ H" K! A8 h
    : i; B2 i# u/ k( G( d
    什么!!!2 t2 R$ I) F9 l6 i

    ! V$ m4 J$ ?1 n2 m' x7 w哎呀呀……疏忽了!看来1号CharBase没剩下多少空间,把BG0的MAP载入的时候,还可以容纳,但是这个时候已经满了,BG1和BG1的MAP,跑到2号CharBase里面去了……
    & a# t3 v0 N: H2 ^
    & g5 g4 B  q6 @: N9 ]% R没戏了,MAP还是放回31、30和29号区域吧!好心没得好报!8 @7 Y: Z, n% {8 d& t3 T1 L
    8 }3 a- w9 C2 ?

      O0 S- V; `7 X' m8 M6 l2 f  m
    - ?% L/ }2 V! M2 b/ l
      o; M" }- K$ r( }5 X2 g8 U1 z$ O. q( ?
    终于行了……有够累!
    4 s" S, L: z+ T7 O9 n& I9 |, n
    $ }% f$ _9 `8 v' k从这个DEMO,我们能看出来,在作多层BG操作的时候,请多加留意!对于比较大的图片,你必须充分考虑到这图片的TILE是不是会超出一个CharBase所能容纳的范围!如果超过了,那么它的下一个CharBase就会被占用一部分,从而导致这个CharBase不能再容纳其他BG的TILE数据……当然,可以将其占用之后剩下来的空间作为MAP数据的存放空间,但是,你仍然需要注意剩下来的空间是不是足够放MAP数据……对于2层BG来说,基本上不会遇到这种情况,即使这两个BG都是256色。不可质疑这仅仅是“基本上”,无论如何你需要留心!相比之下3层BG,尤其是其中有一层BG是256色,这种情况出现的几率就会大大增加……如果是4层BG,仅仅是“如果”,那么这种情况出现的几率就是100%。这个时候请务必精打细算,仔细寻找可以容纳MAP数据,而又不会造成冲突的空间。你必须耐心的计算,到底哪里可以容纳MAP数据,到底哪里可以存放TILE。实在必要的时候,穷举法也不失为一种方法……
    / B5 e( a0 ^% r9 H2 y, A
    ! g0 X5 g4 S, B但是,通过最近自己写的DEMO,我发现,某些图片,如果比较精细,颜色数较多,那么有一定几率会出现:这张图片的TILE数据甚至2个CharBase都不能容纳!这个时候千万不要进行大于2层BG的多层操作!否则,你会很沮丧的发现根本没地方容纳另一层BG的数据!这个时候你面临的就是,牺牲图片精细度和牺牲表现效果这两个抉择……不用我说,这两个抉择都是很痛苦的……
    # Z% G% x9 b/ P7 B. j9 v: V0 z) A6 @, S8 {" v5 U$ K% O& Z
    嗯……差不多就这些了。到现在为止,如果你能完全理解上面几篇教程中的思想,那么你就掌握了GBA编程的三分之一。诸位不要说我罗嗦,再次强调一遍,TILE模式下的BG技术很重要……想进入GBA编程,请一定要掌握这些内容!
    ' Y9 f8 g- {6 u! d# C3 a# [
    7 _% s! H) R) t% e. d0 Y那么,难点已经过去了,下一次我们来点轻松的——特效!在特效的配合下,BG就会变得多姿多彩!关于特效的调试,基本上不需要。你只需要通过VBA,看看效果是否符合你的想法就可以了。另外,我推荐的两种模拟器,在表现特效方面有一些明显的不同。具体地说,No$GBA表现马赛克特效的时候,是将这种特效作为水波特效处理的;表现FADE特效的时候BG和精灵不一样。由于在GBA机器上,特效的效果是和VBA一样的,而且VBA远比No$GBA普及,所以你就以VBA为准吧。下一次的内容:硬件特效!理论课内容:GBA的BG特效。
    7 w: V% P# V, f5 X% V
    . D4 \8 H4 S9 F* v: H) s- b$ `7 @* z7 E1 A1 u- I6 C
    2 Y5 l; V4 U3 t7 E
    * s0 w* G* g4 ^* p
    0 i2 E& F% G$ B! a& j) O* n
    ! @  U; ~8 X7 Y$ [3 t" v8 W+ X; [

    , a# |9 c  h1 IGBA游戏编程菜鸟入门篇(六)——GBA的BG特效及硬件特效
    0 e( l/ n, w! r, w6 {, G# H
    9 k$ H& m  E1 O特效,不是BG的专利,精灵也有特效。不过,我们暂时不讨论精灵特效。到了《GBA游戏编程中级进阶篇》的时候,我会详细讲解精灵,当然精灵特效也会讲解……
    + T# E3 d' w( O) o7 w) `; C2 W0 X3 [( c4 T* [2 d
    对于GBA来说,硬件直接支持的特效有4种:马赛克(Mosaic)特效、透明特效(Alpha)、窗口特效(Window)和明暗特效(Fade)。而通过相关算法,又能实现若干软件特效例如烟花特效、水波特效、颜色滚动特效之类的。其中有一个比较出名,就是伪3D特效。GBA超级大作、人世间最强大的GBA游戏、不到长城非好汉不玩这游戏就白活的经典、人类智慧最美丽的结晶、(后面省略形容词10000字)的游戏《黄金太阳》,将这种特效发挥的淋漓尽致、入木三分、(后面省略副词10000个)……! Z9 k# {, p0 s/ N( z: r0 B4 R
    7 D$ Z# N* E, X
    这一次,我们先看看BG的硬件特效。至于软件特效,因为这要涉及对TILE和MAP甚至调色盘的数据操作,而这种数据操作还不是一般的位操作或者循环就能完成的,所以我打算在《GBA游戏编程高级技术篇》中讲述……
    5 j* m3 ~7 u, i4 m. u! `4 L
    8 M: l9 [. q7 `1 ~$ B* r首先是马赛克特效。这种特效在现在的GBA游戏里面很常见。例如《口袋妖怪》,当口袋妖怪中毒之后,主角在大地图上行走的时候,每走4步画面就马赛克一下然后迅速恢复正常同时伴随着中毒掉血的效果音。再例如《恶魔城-晓月圆舞曲》中,游戏刚开始的时候主角来须苍真醒来,发现弥那小姐坐在他身边,阿鲁卡多站在右边。苍真醒来的过程,画面就是慢慢由模糊变成清晰的马赛克过程……还有几个GBA的Hgame,里面的马赛克特效实在是令人不爽……(众人:这小子有够无耻……)
    3 p5 K/ I/ V5 h* i: M3 ~" a0 d4 o/ x, ?  i; l! V5 H1 [
    马赛克特效的原理是什么呢?这不是很重要,但是了解一下也不坏。假如GBA硬件不支持马赛克特效,你知道它的原理后也能自己写出相应的算法,实现软件马赛克特效。1 w& H$ W- |; D( ?) h& M- X$ ^
    1 o4 X8 j/ C; }4 o3 @8 ]9 @
    规定图片的左上角坐标为(0,0),向右横坐标增加,向下纵坐标增加。假如要做双轴3倍的马赛克特效,那么,横坐标和纵坐标均为3的倍数的坐标点例如(0,0)、(3,0)、(6,3)、(6,6)之类的坐标点,其左方和下方两个坐标点处的像素,均变成横坐标和纵坐标均为3的倍数的像素。我画个图表示一下:
    6 J( g8 P7 {9 L
    # w( }" N5 F# y2 F8 b! c& r  R
    + C& D7 X# ]) J8 ~* @  R2 W9 t+ h5 _& f

    2 g" _/ \( s7 _
    2 [% c8 z" v" X7 A6 [
    * B+ z. H' w# j2 ~1 @涂有同一种颜色的像素,均变成它的右上角那个点的像素。这样,图片就形成了一块一块的明显的方块。
    9 J: ?. `8 S. z1 g/ t1 ?  t6 I, I2 g% x% _5 L5 b4 g2 }  z$ a
    那么,同样的,要做x轴4倍y轴5倍的马赛克特效,就是横坐标为4的倍数,纵坐标为5的倍数的点,像素值变成它的右上角那一点的像素值……可见,控制马赛克化面积的变量就是那个倍数。在GBA里面,横坐标和纵坐标的倍数都可以由你控制。
    ; j0 x/ G% b! ~4 b3 I% U% |/ @( d$ H- V
    那我们就做个DEMO看看效果吧!% A, q* f) z3 O( K0 w
    : M  j  c1 L9 B1 n; N
    就拿写单层BG的那个例子吧。我先照抄程序,到需要修改的地方,我会加上注释。+ {) _" V; i8 w3 y
    # g3 ~0 ?) q3 }; j. b0 P
    #include <GsGBA.h>
    - F9 v: J! n. i1 V& j* Q- Z# f4 U
    + o* B% d) w, p5 L! L5 N1 c+ w#include "BG.map.c" 8 K& \1 K# C$ |

    & c8 C/ K, f+ B8 s6 D#include "BG.raw.c"
      ?/ \9 ?* Y8 ~& O  n) x
    ; B& m; w" s7 ~. T#include "BG.pal.c" ) A5 Q) Y9 s( B9 Q  J7 H" [

    7 V: t3 L6 o; S0 {( l. ?int x;int y;//这里定义了两个变量。这两个变量是后面对BG进行卷轴操作的偏移量。
    , V, l: v; k( w" m+ h( W/ q) d5 K$ o! U8 C: }8 {4 c2 J5 z* m
    int mos=0;//这个变量是用于马赛克特效渐变的变量。$ N5 @0 z* g* `( K; y0 \, k
    % o" k( r5 u* J6 ?  \: W
    void Vblank();
    % K0 z1 G/ G: m
    + R6 K- F, p9 G6 L$ M2 c  {const IntrFuncp IntrTable[14]={$ N5 Z% @1 m( }& p7 x1 `; O
    % O0 {8 F  x1 _+ o5 V, N. `' ?
    Vblank,dummy,dummy,dummy,dummy,dummy,dummy,dummy,dummy,dummy,dummy,dummy,dummy,dummy5 Z" S, i3 D* g$ e" X, Q& g4 d% K) C
    ' K0 _8 u" c  S$ B
    };
    4 Z' y& y, H) `5 N2 G' b% ?
    . j9 D" b9 i1 m9 K# ?- ~) h  k) S% Oint AgbMain() 1 F% ~5 c2 P; U: a. q
    {7 f" W( D; u( Y' ?/ x1 Y
    . j* @$ J3 i: S0 Z; n+ L
    GsOpenIRQ();
    - V* U( D7 E% ?  R; |! {/ ?" f) L, M2 w/ h8 Y8 w# K6 d& P
    GsSetMode(MODE_0);
    0 I9 @: L7 [( q- Q+ w7 g+ M! I3 `7 d; j; B: y' A
    GsInitBG(); * S1 A6 b5 ?2 B/ E! s
    - X$ k" V, e6 n3 E
    GsLoadBgPal16(BG_Palette,0);5 I% K% N& r6 `! [

    ! P0 u: @/ z# j8 HGsLoadBgMap(BG_Map,sizeof(BG_Map),31);4 Z9 V$ X! f" V: u/ `

    ; Y& f3 \1 R9 JGsLoadBgTile(BG_Tiles,sizeof(BG_Tiles),0);
    , ~) ?: F, b" [4 ]% u9 y
    . n6 P9 @! _! W- h0 _' w1 MGsSetBg(0,Gs_BG_TEXT_SIZE_256x256,Gs_BG_COLOR16,0,31,1);//最后一个参数是控制马赛克特效的,打开它。8 s) O; z+ z3 o' f

    & R$ L# ]  j' t# qGsSetEffect(BLD_BG0_1ST,BLD_BG0_2ND);//对于硬件特效,设置完BG之后一定要记住打开特效支持!否则后面无论你怎么写,都是没有效果的。这次只针对BG0做特效,因此这个函数的两个参数均选择BG0。如果有两层或者两层以上BG做特效,相应的参数就要小修改一下。. q7 z- K* t, t5 T: B, q$ x9 x
    2 L4 [4 V- z" `) ~% _' f7 H8 o! Q
    GsBgPriority(0,0);   b9 ]: ]$ [: U' K0 f( z
    * m1 z. O' ~. ?' I* _8 }. e* V
    GsSetBgState(0,ON);
    & t* I- I  x/ k0 n
    0 v* @3 x& E0 P. {7 qwhile(1) : v) R" p6 ]8 y$ e# s

    8 {" V' [7 X6 }! B{
    / {# I5 G& r$ d& C* n! p2 T" i( L6 r* b, h( w& G( K
    GsWaitSync();   n+ ]2 I2 {4 Z

    % H6 |2 Y+ I- q7 B0 q1 \} 9 u" \! z, S' ?/ ?3 z

      ?, v& B9 ]4 X$ o" ?% G7 q+ Mreturn 0;
    + U( B1 X, s7 ?9 l9 W3 \5 O2 {! n1 V% D6 {; t$ }
    }. r' Y& V/ T* k
    * {/ S% B/ ^0 u/ e& j4 I" p
    void Vblank() 9 ~' `/ F4 D) l5 {0 d. j
    9 F: F6 I+ ~8 t' Q
    {1 Y  f, v* [- @* j1 h' Q( J$ W" Q
    9 Q" v2 {9 w' M+ Y- ~
    KeyRead();
    $ ~8 k4 Y: ~5 k9 t8 y. J  U8 h# c2 g- A' I
    if (Cont & L_KEY)x--;
    / S2 w! o0 m$ s( \2 O+ e  E  l+ I: a2 v3 u& L# w$ E
    if (Cont & R_KEY)x++;
      t5 C. V2 I$ ]) d& t3 r# k/ {' ~7 O4 ?' b6 d
    if (Cont & U_KEY)y--;' k% c4 I/ \. S! d; f
    3 {6 B9 n: w$ T4 W0 R) l+ \- Z3 f
    if (Cont & D_KEY)y++;$ o# q7 N5 {7 y2 h* p5 T

    8 ?. p; N* u! }, U# NGsBgOffset(0,x,y);5 w0 f5 g! P" a' h# i9 C5 L

    . M  @7 ^, |/ q  ^% nif (Trg & L_BUTTON)mos++;//当按下L键的时候,马赛克变量自增。这里没有用连续按键。因为连续按键的话,马赛克变化太快……
    4 X+ j4 |( \; m
    4 _  M0 p! k. T; q' B/ }- n: P9 o9 s: Fif (Trg & R_BUTTON)mos--;
    ) t7 E& Z, p9 K1 M
    8 u9 }+ I. E8 H% Z; ~) nGsSetMosaic(mos,mos,0,0);//同样,这里要对键盘操纵的马赛克变量做处理。我们这次的DEMO没有用到精灵,所以后面两个参数均为0。
    4 q0 K% E/ e, ]5 q: ~
    % A* r( A: B0 k: b2 P$ ^. e}/ N$ F7 Q9 o% k8 @/ E: m4 [* a" i# _
    6 f: C5 }6 y" u0 N( y5 O$ X& u  J
    这是效果。
    - u! o  T$ C" j$ k0 m+ S3 [" U" Z$ ~& H# t' A
    ! h1 A& R* o! t5 I% _5 }

    7 P* l* |7 S' r! x% R" _* v
    & \: w" X5 L8 r* \
    " f8 m$ U0 ?8 r  i( ~注意,马赛克特效属于破坏性特效操作,就是说,如果进行马赛克特效之前不对图片的数据进行备份的话,就不能恢复了!这一点,相信大多数朋友深有感触……例如一张H图片,重要部位被马赛克处理了,那么想恢复原状就是不可能的。(众人:(鄙视的目光)只有你才深有感触吧……)幸运的是在GBA里面是有备份的,而且这个备份操作是自动的,不用我们操心……
    ) i2 v9 w$ u" J8 y- w0 i5 e
    ( i8 o& l& {3 c有关马赛克特效暂时说这么多。其实灵活运用马赛克特效可以作出一些很出色的软件特效,例如水波特效。有关这方面的内容需要涉及物理方面的牛顿定律以及数学方面的正弦函数和随机函数,应该说比较深奥。到了《GBA游戏编程高级技术篇》里我会简单介绍这些软件特效的算法。
    / l; Y  w# i7 u  L
    ! Z- m' H' x$ W3 e* u% C接着是FADE特效。这在GBA游戏里面也是很常见。例如游戏开头的LOGO。我本人就很喜欢在游戏刚开始的时候显示我的LOGO,然后用FADE特效将LOGO变黑,然后慢慢变亮,停顿几秒钟之后再慢慢变黑……
    ) c' c6 J% o" Y0 {. H; @5 M! m( @& v" ]3 }
    来一个DEMO吧!还是用上面那个例子。6 e4 I. O) h2 m, B0 D, W

    : e: M7 g$ O8 h% P! l7 w#include <GsGBA.h> " U, ?- }3 ?% L9 W: r" A- u1 `. i- x

    : [8 b- P  _& O! @#include "BG.map.c"
    5 H; L! B, A+ G' o0 S" o) E  }  K: W. D  \; w
    #include "BG.raw.c"
    $ @2 A) f& L4 J; ?: o8 J& y8 s+ T" b. W" L5 y, a
    #include "BG.pal.c" 7 j' ]( W) p. H# Y
    3 \+ g1 [( Z2 D7 H8 h2 Q* Y; ?
    int x;int y;//这里定义了两个变量。这两个变量是后面对BG进行卷轴操作的偏移量。
    9 p7 `; M  n! z4 e# s  \# Y0 o8 S4 A+ P  E: P
    int brightness=0;//这个变量是用于FADE特效渐变的变量。; C' a! X" W0 \/ Y% W1 f

    8 u% x' Y9 k" I4 `. A. b) ^void Vblank();
    + q* c; Z4 T/ s' B0 O: {7 }
    5 \7 {" e  t! H9 V% l4 }. I! wconst IntrFuncp IntrTable[14]={
    7 q% \/ Z1 J5 L3 [" h1 B( E4 M3 k" u9 S
    Vblank,dummy,dummy,dummy,dummy,dummy,dummy,dummy,dummy,dummy,dummy,dummy,dummy,dummy
    . Q* z: S! h( p2 j; V3 V
    - B/ \6 m0 S0 x1 r; T6 [: g; h};' ]- p: w2 ^) A" A- ]( |
    / j, f# L" T8 W: M  h
    int AgbMain()
    : R* b6 G& d: `1 h" f{1 w- I7 @8 ^( f
    9 @9 L1 Z0 |/ g* w: f2 M
    GsOpenIRQ();
    6 Z; q$ W/ |4 u1 n" N* w! ]4 Q+ N" O" X( Z+ ~$ C
    GsSetMode(MODE_0);
    4 C8 h* p! v! k! a1 }
    4 w9 H4 V- i2 d- t3 l" R- iGsInitBG();
    - w, s! v0 x. M& G
    ' |  M+ w$ G$ q/ i( H. wGsLoadBgPal16(BG_Palette,0);' }5 V# R/ D* z

    2 V/ Q3 Y$ K7 Q1 qGsLoadBgMap(BG_Map,sizeof(BG_Map),31);" K& b4 I1 @# C7 Y# P7 M

    / J: q& _) @# q4 ^& a$ E3 `GsLoadBgTile(BG_Tiles,sizeof(BG_Tiles),0);
    4 ~2 m6 v' g& P# i9 B
    7 e) w4 t' Q$ B$ u/ ^( ^GsSetBg(0,Gs_BG_TEXT_SIZE_256x256,Gs_BG_COLOR16,0,31,0);//最后一个参数是控制马赛克特效的,这次不需要马赛克了,关闭它。% m$ H4 q, h; n! F6 a2 H
    ) ~: z5 x4 p3 w- @9 m4 m# Y; Z2 \
    GsSetEffect(BLD_BG0_1ST,BLD_BG0_2ND);//同样,设置完BG之后一定要记住打开特效支持!这次还是只针对BG0做特效,因此这个函数的两个参数均选择BG0。( r% }' j* \; b6 w. e

    8 B+ S% M+ N3 R7 Z. A- @GsBgPriority(0,0); % Q# r& M9 t2 \' i+ P7 M# O1 K, o
    2 c! f7 r6 ~8 [. p' k
    GsSetBgState(0,ON);
    ) N$ r) v; R3 C# s7 V- @- G& X" O$ z6 f" [1 k# m/ Q8 c
    while(1) " z' I% W' k6 Z8 ^) I7 U

    2 ~2 h$ r! B# z! F" ]' {{  T7 t1 q  |' D: V7 d( j# Z
    ( S$ l# {6 N/ Y! \7 Q7 w+ J; P8 K
    GsWaitSync();
    4 y2 x( I! i- Q1 r* G; x* h; F* ~2 ~0 T$ O
    }
    * Y; w3 Y( ~# ^- Q) L' l7 Y8 [5 E, i7 s0 c4 i. a- v
    return 0;
    ) [' r/ \$ S* `2 C( C% w1 N7 H3 W9 V% J% d: M
    }
    1 h# P/ N7 J- M4 U+ c' Z. A1 g! W5 f2 J( [, y1 V. O/ L
    void Vblank() . R( y( o% E0 }; M1 F
    0 O$ K# s5 e1 ^8 ]& S
    {# r7 g% C$ S5 f
    - J4 g1 y' U; J+ Q' G1 p1 O
    KeyRead(); ( x3 J: ^- J8 T* I: `4 ^

    7 Z1 [8 @. }: d. nif (Cont & L_KEY)x--; 2 _; \" G; [/ t

    9 D+ s2 G' B* {5 Fif (Cont & R_KEY)x++; 7 M" ?$ l. x7 ~' v! m0 s9 J. f
    " d1 i+ |8 M$ x6 |  m; ^" ^
    if (Cont & U_KEY)y--;/ \/ Y8 G, O; C( c) ^1 N
    * [2 F# j1 m! h6 v5 U
    if (Cont & D_KEY)y++;* V# g" i- T' N: W
    2 K+ r; b$ l1 Z
    GsBgOffset(0,x,y);
    ; _0 z0 _. _: x$ E! l( T8 Q" e# {% |" e/ b
    if (Trg & L_BUTTON)brightness++;//当按下L键的时候,亮度变量自增。这里没有用连续按键。也是因为连续按键的话,亮度变化太快……$ S' R* ?  s# \# p

    + s7 @$ p. f5 p! U3 Hif (Trg & R_BUTTON)brightness--; - I9 |- N, K) A
    - k. W7 x1 ]" e, P" h
    GsSetBright(brightness);//这里就直接控制亮度了。要注意的是,帮助文件中规定的亮度参数是s16类型,也就是允许负数。但是即使你把这个变量定义为无符号变量程序照样正确。原因很简单我就不说了。另外帮助文件中规定参数范围为-15~+15,其实超过这个范围也无所谓……你可以试一试!
    * e: O2 ~9 W' K8 L9 F9 X
    ! q) Y2 P; A/ u) J! m}4 ^" t+ ]/ m8 M( n4 J" ]- F4 L

    - Y- L) X9 j) ~$ K& `  \效果图:% p/ @& n' S3 S! u; A% S3 r5 B

    & e( d- C' D% C# S) Z  O
    9 O' I( }4 S+ b8 {- r; E  w4 Y! J
    , D( m/ U: C; L) E
    * @( S$ Z/ m9 L% i) O5 t1 h# Z  f5 m9 B0 \
    FADE特效也是破坏性特效操作,不过你仍然不用操心……嘿嘿……
    0 G/ V& y- D7 }) \
    6 r" h: @$ A' z5 f7 k8 \利用FADE特效,可以很方便的做出黑天特效。GBA超级大作、人世间最强大的GBA游戏、不到长城非好汉不玩这游戏就白活的经典、人类智慧最美丽的结晶、(后面省略形容词10000字)的游戏《黄金太阳2-失落的时代》中,有一个这样的剧情:主角们旅行到一个狼人村庄,刚走近村庄的时候,天暗了下来,然后迅速天黑了。(很令我奇怪,无论我走了多长时间总是到了村子口天就黑,即使刚从客栈里面爬起来……这几位兄弟时间掐的还真准!)这里的特效就是FADE特效了……当然,也可以做出刺眼的光照特效。仅次于GBA超级大作、人世间最强大的GBA游戏、不到长城非好汉不玩这游戏就白活的经典、人类智慧最美丽的结晶、(后面省略形容词10000字)的游戏《黄金太阳》的游戏《口袋妖怪》中,神兽古拉顿觉醒之后,整个芳缘地区开始出现烈日天气。那种刺眼的阳光,就是FADE特效……
    % i- p% p( b1 e" F8 g. L$ U/ A' M4 y0 q: n& S8 L9 n  r  _8 Y  s& [
    然后是窗口特效。顾名思义,窗口特效就是BG上面出现一个区域,限制了BG的显示范围。我孤陋寡闻,玩过的GBA游戏很少,迄今为止只发现了一个游戏使用了BG窗口特效——《RPG制造》。游戏制作中有个选项,就是游戏结束后出现STAFF表,是以什么方式出现。有一个选项是背景图片被挤到屏幕左方,右方空出来的区域,显示STAFF。这个显示过程就是窗口特效。
    ; H+ A6 t0 G0 `1 H. U, T  j) w% I
    4 g* {; I* V5 L- v3 aGBA硬件共有三个窗口可用:窗口0、窗口1和OBJ窗口。其中窗口0和窗口1效果相同,呈现为屏幕上的一块矩形区域。OBJ窗口可以算是OBJ的一种特效,它只能配合OBJ使用。一旦OBJ使用了OBJ窗口,其显示就会变成一个轮廓。关于精灵窗口特效,最经典的例子就是GBA超级大作、人世间最强大的GBA游戏、不到长城非好汉不玩这游戏就白活的经典、人类智慧最美丽的结晶、(后面省略形容词10000字)的游戏《黄金太阳》中,角色们在大地图上使用“Psynergy”(好像是被翻译为“精神力”的吧?),其身体就变成了一个轮廓……还有,在阴影里面使用Psynergy“阴影”,身体也是变成轮廓,而且很明显……
    , f. a3 I. f0 z; }. W
    * z$ J$ M$ @5 J7 {) T0 K我们也来一个窗口特效的DEMO。
    4 `6 T6 M2 L- h# l
    ; u2 c3 E7 }8 `  [! }#include <GsGBA.h>
    9 Z' I; Q" v/ y6 X* @; U7 |- r0 S5 c' i1 K7 \! [/ g& r) @1 L1 t
    #include "BG.map.c"
    ( ]  r9 p. G1 e" }( z, ]" S: e$ g( a
    #include "BG.raw.c" 2 [2 E9 `) |3 V" I4 h" r( q- d- N: v

    / c: W9 D. d7 H6 O* S- R1 D/ J#include "BG.pal.c"
    6 Q  P6 j; |$ Q) O7 t2 |& a
    8 m% F. R. B. c  _' f7 Q% q. r+ G6 A#include "Blue.map.c" 6 L. I. V1 Q9 Y
    5 E+ ^6 h: d  V4 k/ f6 M4 k
    int winx=0;! t8 x5 I/ F' d& c. N: P1 O
    # Z3 |1 r: [) t4 x2 j7 K
    int winy=0;//这2个变量是用于控制窗口大小的变量。' @$ P7 `+ F" o  A

    / x9 P, S9 m) k! c  d4 ~void Vblank();; a+ q0 V7 S/ }6 K+ F9 K

    ! i4 {* u. O" [4 m# w7 ?" Bconst IntrFuncp IntrTable[14]={1 R$ C( S. G* e* c- G# r

    . K+ }7 T+ x6 `3 F9 `, X# S5 |Vblank,dummy,dummy,dummy,dummy,dummy,dummy,dummy,dummy,dummy,dummy,dummy,dummy,dummy
    # v" }! a8 {  N8 m0 c- U" B
    4 Q% _: G/ T. _" J1 ~0 m) Q6 n! ?( e, M};! l0 M8 B3 ^3 Z; J+ t
    0 N5 C& ^7 ]. t6 y" f
    int AgbMain()
    + Q) v4 s4 k, e( `: p) u  I{( b" K/ ^$ O) w: |( a1 ^5 t

      D; I/ w3 [9 l3 X% _GsOpenIRQ();
    ; {1 D4 {- Y- f7 X5 M+ `, q
    ' I! {  [/ ^: K/ i2 Q0 j+ T% ~GsSetMode(MODE_0);
    % K7 s1 e" L& ]7 e! p# u+ L& @  a; `0 c  n( @5 L: Q2 `
    GsInitBG(); . D2 `5 ?3 G9 x

    - ?! P( Y  Y; j- Q& ?6 @) U# T# fGsLoadBgPal16(BG_Palette,0);
    : `$ @; Z* Q9 O% l4 L' F" h$ V1 y
    ; d: e& ?6 `8 Q7 zGsLoadBgMap(BG_Map,sizeof(BG_Map),31);7 w4 C% t! ~/ ^! K- ]

    % l) W  Q2 [% w  C* ^8 w) mGsLoadBgTile(BG_Tiles,sizeof(BG_Tiles),0);# j, }0 @% O7 w' M. ^

    . \9 @1 x' W# M5 b1 x" W$ `6 nGsSetBg(0,Gs_BG_TEXT_SIZE_256x256,Gs_BG_COLOR16,0,31,0);
    ! k+ s0 }) A8 y8 ^: Y3 n. r6 n) v9 o
    GsBgPriority(0,0); 9 p2 c0 t5 f9 s9 w/ q
    ; s* E  G! H! j2 @9 H& _9 Q
    GsSetBgState(0,ON); 6 C6 @" d  |- }7 Y. l: Q

    4 D; q* e  e- J9 o0 Q: K& V% DGsSetEffect(BLD_BG0_1ST,BLD_BG0_2ND);//同样。
    ( t# `; Q3 M3 `4 e" o+ L5 K/ A7 Q  N
    GsSetWinState(0,1);* C: u7 B8 _; K

      l) x# S% j: AGsSetWinState(1,0);//设置窗口状态。我们没有用到窗口1,只用到了窗口0,所以激活窗口0,关闭窗口1就可以了。
    0 k' k. v- `1 S( ?
    ' h* i! l; e( Dwhile(1)
    ( ], o, U/ i/ V6 F) F5 u6 @2 |/ }4 i
    0 ?: d& `* v: G* B{5 o( A* E0 Z5 e( Y- P% q

    1 |5 d) @, H( x( e4 |3 G  m! O1 \! XGsWaitSync(); 6 v+ a7 ]3 }* W4 \5 I8 T
    7 O- N. J4 ^1 {$ S
    } 4 }" R# e3 s5 M2 X( k7 E

    & a% `5 k& T6 M( breturn 0;
    : n! v) l) M7 |  ?+ J; S5 K- Q! [/ t
    }# ^6 n/ h. ?7 G- i- F; F1 f
    + q3 S4 B% c: j1 i5 F) q
    void Vblank() - g7 c  _/ r$ v" H7 S
    " H9 L2 S9 y. h+ |" Z& H$ Z5 D
    {
    1 p' j. ]! M5 p# \2 P, n% w  e: x; p* X+ p  p+ C- H* @5 S
    KeyRead();1 F5 t% G% V  u7 X6 D
    0 U9 ?; y# B( I, o# ^, L3 w7 {
    if (Cont & L_BUTTON){winx++;winy++;}//这会有什么效果,你能猜出来吗?" V2 m+ R3 h+ N" F( X, Y
    ! h! @2 b  g8 S2 k3 o- i' s6 O4 X
    if (Cont & R_BUTTON){winx--;winy--;}
    & O' K- W, P. J7 W. E( m" U6 ~% w
    GsCreateWin(0,120-winx,80-winy,120+winx,80+winy,WIN_BG1_ON,WIN_ALL_ON,ON,ON);//创建窗口。使用窗口0,大小由我*纵,窗口里面显示BG1,窗口外面全部显示,允许窗口特效。BG1我们没有用到,所以就是“空白”。
    3 N) ]* h+ l, c- ?' n* ~4 o; U$ s" ?4 m* N
    }
    $ C3 N8 z! K& c% _" t7 b, _9 Q- ]; i. O+ y7 f5 I) }
    这是效果。- l+ S2 h3 j. I! M

    , v) z6 v, [* B  D! V
    $ N+ `# X2 P' u$ \
    " Y4 K7 L7 |1 q6 i5 s; J0 c3 N' p9 L% Z+ F4 V3 t. \
    * {( n& f" S# o. N) q: H
    窗口特效不是破坏性操作,从宏观上看,其实窗口就是一个“虚拟”的BG,这个BG盖住了真实BG的一部分……形成了像上面的那种效果。
    8 {, q5 m; x; s- k+ V  v- C
    ; o# q8 {' V8 q8 Y0 D% D8 n在GBA中,存在两个窗口,就是窗口0和窗口1。理论上讲这两个窗口都可以由你操纵。不过,或许是我的分析有误吧,我只能操纵窗口0,窗口1操纵的时候,总是出现异常的状况,所以,有关窗口1的资料,我暂时无法提供……召唤达人中……如果你要使用窗口1,请慎重!6 U7 ?) K  Z" Z$ w

    4 x/ U4 S$ a/ m+ d8 M" o# s$ ~最后,剩下Alpha特效了。大家都知道“三原色”——红绿蓝。这三种颜色通过不同的比例混合,可以做出各种各样的颜色来。GBA硬件可以完成这种颜色混合。这种特效,就叫做Alpha特效。; O2 V4 ?1 t9 ^3 }

    3 ?% T6 i! N' E; c6 l这种特效在GBA游戏里面不怎么常见,我只见过一次——还是在GBA超级大作、人世间最强大的GBA游戏、不到长城非好汉不玩这游戏就白活的经典、人类智慧最美丽的结晶、(后面省略形容词10000字)的游戏《黄金太阳》中,片头黄金太阳升起的时候,背景的阿鲁法山在阳光的照射下显得颇为壮观……阳光穿过浓厚的云层射下来,形成美丽的光环。这个光环,就是活用Alpha特效做出来的软件特效——光晕特效。我个人认为这是所有游戏里面最令我陶醉的一幕画面!这个特效,把这一景观做绝了!# _. X9 C0 g0 I+ o' f
    7 L) T2 b- @( g9 `# G

    1 Y( Q; m9 z3 s7 x9 B" V: C( n% E
    9 I7 V% v* Q* G, J6 Y1 Y' n1 l, F0 a( W1 |" v- Y* L- U- n$ v- a/ P

    - |4 _8 `2 U4 M! Y6 p: w2 X关于这个硬件特效,我不想写例子了,我觉得,大家参考samples文件夹里面的DEMO,自己试着写一个就可以了。还是那一句话,用的好的话,这个特效是很炫的!
    . W! U+ d6 r/ j3 L7 o% D
    " l2 Y3 }/ E1 q* J: n0 L' d到现在为止,有关BG的内容就全部讲完了。不过,这并不是说BG就这点东西,更深更好用的函数还有呢!下一次,将是《GBA游戏编程菜鸟入门篇》的最后一次教程了。学完这最后一次教程,你就不是菜鸟了!因此在中级教程里面的例子,我不加注释的部分你应该能够看懂。如果你还是看不懂的话我劝你还应该再从头看一遍。下一次的教程,GBA上面的音乐播放!学会这一部分,配合特效和多层BG,做出来的效果不用我多说什么吧……嗯……还有一点小小的事情:如果你有点五音不全或者对音乐的鉴赏能力很差,那么你就不要学下一次的教程了。因为下一次的教程要涉及自己写MOD,这是和写MID非常类似的工作,如果你连MID都不会写,那么MOD你也未必能写出来……
    2 r) }6 t/ y$ [  X3 @; `* W* N4 x; \4 B( Z7 U* m# l  b$ |  o; o
    GBA游戏编程菜鸟入门篇(七)——GBA音乐播放
    / N1 h3 W2 m  ~$ g( G1 ~) e! O- f/ O) w+ L& n, w/ o& C: Y
    + v3 d/ F2 i2 y5 u* Q
    GBA上面一共有6个声道,分别为SOUND1~4,和DirectSound A/B。这6个声道硬件上到底是怎样播放声音的,这个问题实在是复杂,官方开发包里面的帮助文件和寄存器映射表我基本上是完全看不明白。不过,GsLIB中提供了一个非常好的工具——MOD播放器!这个工具,使得我们可以非常容易的在GBA上面播放声音,无论是背景音乐还是效果音或者真人语音……不过,会有一点点的失真。" c1 f* W8 s  x0 m3 b: P
      o) c. ^2 V$ w9 H* _& A
    在GBA上面播放音乐有两种途径,第一种是使用GsLIB2.6 ARMSDT版本提供的工具——ModPlayer——来播放MOD;第二种就是只有GNUPRO版本的GsLIB才能实现的,使用MP2K播放MID。ModPlayer有种种缺点,但是由于使用简单,我们还暂时不能将它抛弃;而MP2K才是终极之道,官方游戏,大多数都是使用MP2K的……如果你掌握了ARMSDT版本的GsLIB的使用,那么我强烈建议你转向GNUPRO版本!因为两种版本中的函数基本上都是一模一样,相信你会很快习惯!
    2 i: t7 T( }: s7 C2 y* B5 L0 ?- Q: A0 m
    5 s7 B1 \: s! o6 K/ Q4 x首先简单解释一下所谓MOD。MOD是一种音乐格式,和MID类似,只不过自带采样数据。帮助文件里面就是这样解释的。详细的资料,我也不知道,你要是有兴趣,可以到网上找一下相关的资料。在我们这里,了解这么多就够用了。4 H" D3 K# H6 S# R. s3 ^. Y

    ! B6 K9 e, k: M  F既然MOD自带采样数据,那么MOD实际上就可以用于播放WAV,只要你的采样数据是你所需要的WAV……因此,如果你想在游戏中加入真人语音或者效果音,只需要录制好你需要的声音成WAV格式,然后在ModPlug里面导入你做好的WAV成采样数据,然后……
    3 J0 ~2 L) j$ a9 s& P+ ~" q# j2 d$ i7 X' a; i: ?
    首先说明一下,制作MOD的软件应该很多,但是我只找到这一个;不过这个软件也的确强,虽然做出来的MOD有点失真,但是这很可能是由于我对软件生疏,制作的时候造成的……不能不说这软件真的很好……由于我也是刚刚使用这个软件,操作的时候出现的错误在所难免,请各位见谅,雷精灵也就只能给各位提供这些资料了……当然,如果您很擅长使用这个软件,那么还请您多多赐教!
    3 o4 y. y% B% n$ V  a- [; p, `/ W6 A* V4 p" u. c
    哼哼……我们来实际操作一下!. M8 ]; A( a$ E. m  F0 ~9 B* M; z# y
    . ?$ G) z. F$ Y4 b: R
    首先是ModPlug的界面……. s1 U/ c, i$ x  C9 H3 I/ Q  G
    / t. K# `' ?* K- S; n* v5 m
    ( i( @9 y8 ~; p/ P
    这个时候你可以用它打开一个MOD文件,熟悉一下每个部分的功能和作用……关于MOD文件,samples文件夹里面有两首很好听的MOD……& R' G& S7 H' @
    1 |, M0 H1 s! X0 A9 v2 p2 k
    自己录制一点语音吧!我从游戏里面截取了一点声音并转成了WAV格式。这是PS《生化危机3》钟楼里面的八音盒音乐,很爽……不过要注意一下,转成的WAV,最好不要是立体声,我不敢保证立体声的采样数据能不能做出MOD来……* J! Y8 c) U; m5 X* E1 U

    2 T2 S' f+ ]) e  p0 _* A点击这里下载《生化危机3》钟楼八音盒WAV音乐:http://www.tgb.name/index.php?act=Attach&type=post&id=516
    * G2 R' t- B/ E. b, _, h$ H8 }7 [6 D2 A* K4 k
    然后打开ModPlug,新建一个文件。) X, M) p. Y2 }* L' }% d
    ! F* p8 g% b' b
    然后主窗口里面出现了5个选项卡,分别是General、Patterns、Samples、Instruments和Comment。大家的英语一定都比我强,我就不翻译了……
    , r8 z4 z" W/ m$ t& E) b' }7 r# j
    1 A4 e$ y% e/ \& t+ A点击General选项卡使之前置,最右边是声音的幅值显示器。这个东西大家一定见过!那两路显示,代表声音的两个声道。
    : e' o* U! l0 f% R6 N4 L1 S) X4 v, I% w8 L& Z
    关键是它的旁边那个“Change”按钮。点击,弹出来一个窗口。把Channel数改成4。这样会减小MOD的体积,而且由于是在GBA上使用,4个Channel也完全足够了。
    , R5 T. j& o  U" Q0 q; J
    / Q; y' ^. [% k- N/ y/ r+ |然后点击Samples选项卡,这个时候采样数据前置。* [6 x" c; b) n9 z

    ! n( F  r9 c/ c. R
    1 A0 m: g7 ]+ @9 v9 m2 T0 d+ K
    8 a2 b/ u3 _/ L) |
    ( b6 `7 I+ ]' z9 v1 X$ B直接导入采样数据,也就是你准备好的WAV文件。导入完成之后下面的波形窗口就会出现采样数据的波形。点击播放按钮会播放采样数据。
    1 O# ]$ @( x# |
    6 A- f6 i$ e4 H$ i0 m6 c$ F下一个步骤非常关键!请务必照着教程做!8 y2 A' F" G7 e
    - v9 Y0 Y; c* ^3 I! Z
    2 `; e2 H& \# H2 e5 [  O" u

      n9 Y  @8 z4 s4 n) S, V4 b5 M* I# W9 q+ f- ~( u5 Q

      @, N; J7 Q# l7 |点击两次那个“Downsample”按钮。这个时候下面的波形会有些许的变化。  n. @# _) @& K, c2 P) C0 Q8 ]8 e
    $ c% I" ^( E8 E  s
    然后点击Patterns选项卡使之前置。准备写音符。4 J  A) i! n2 C
    5 G  t' A8 N4 ]5 q2 P; {' z8 t8 i
    双击第一个Channel的第一个音符,弹出音符属性对话框。音符选择“C5”,采样数据选择第一个,就是你刚刚导入的采样数据。由于我导入的采样数据比较长,我新建了一个Pattern,这样可以有效的防止声音被掐掉尾巴。4 u* i0 I  x7 v7 [5 J" ^

    * `+ w) @7 J0 l' k6 N* {然后直接另存为MOD格式。$ X+ b1 p- E; n6 p6 {

    8 d5 t1 W+ g7 E关闭,再打开,听一下效果吧!虽然音调有点低沉,速度变慢,有点失真,不过效果还是颇不错的……如果你想做成立体声,就把另外几个Channel也填上音符C5。
    8 |! q$ K5 ^/ u( ]
    5 f! l/ {$ T4 E* _- B: b" g如果你对这个失真很在意,那么我建议你先用变声软件处理一下你的采样数据,然后再导入……
    $ b& V0 x" T( ~. @' D" z
    + ~* E) G) r% ^0 w9 E: P& N9 m- v7 z那么,MOD有了,开始写程序吧!
    ( _5 m1 D9 s) D# ~
    $ M& R( Y5 E; z/ F% z(众人:嘿嘿……这还用得着你写?光看帮助文件和samples里面的例子就能够写出来了……)
    ; F& A$ I3 G1 `0 S, c5 \- S4 t8 e2 y! `/ K/ s. S
    雷精灵:(尴尬)啊……那……好吧……我就把我写的DEMO放上去得了!
    / }1 Y. C" K% b: k+ Y. c/ {% m9 Z
      P5 G; s0 u) B) Z) n. b& M点击这里下载DEMO:http://www.tgb.name/index.php?act=Attach&type=post&id=518+ f  y: w/ E+ F# ]+ o7 I

    : x) q. p- \! Y就像这样,GBA可以播放音乐了。只要更换你的采样数据,就可以做出各种声音的MOD。如果你导入两个或者更多采样,写音符的时候在其他Channel使用别的采样,那么,效果就不用我说吧!如果在其他Channel使用相同的采样数据,但是有点延迟,那么就可以做出回声效果……只要你能想得到,就不怕你做不到!
    9 A6 d& }7 L6 k
    & j+ X8 Y5 Z" }/ |# W8 _同样,如果直接使用现成的MOD音乐,就可以当作是背景音乐;录制效果音作为采样,那么做出来的就可以当作是效果音;录制语音,那么就可以实现播放真人语音……有没有人放一首歌上去?诸位不要笑,完全可能!
    ) u6 U/ A6 o4 x2 @4 U) r  L2 s9 L5 j! y7 [; k1 ]- V
    到这里,初级教程就完全结束了。现在的你,已经不是菜鸟了!毕竟,能够自由的操纵BG,随心所欲的制造特效,还顺带着加上背景音乐,已经很了不起了!下一次教程,我们就涉足更深的内容——精灵使用!精灵技术是游戏中的一项革命技术,掌握了精灵使用,你就掌握了GBA游戏编程的另外三分之一!   C% `5 w- [8 K2 ^! F+ v6 T- `
    6 O* b& m  m$ f/ v% c6 F
    GBA游戏编程菜鸟入门篇(特别篇)——制作一个MINI游戏
    / S8 l4 k7 {/ Z, l; i, b7 _
    2 C: M5 [" n8 O5 `- X$ O  c
    初级教程中,我们掌握了TILE模式下BG的简单内容,学会了使用多层BG,能够使用简单的硬件特效,也可以进行音乐播放。这次的特别篇里面,我们将尽量多的使用我们学过的所有知识,制作一个MINI的游戏!
    $ N1 O# B: |$ Y0 e: W) s) _
    * \. G1 P! M' S% t2 O* A一般的游戏中,哪怕是很小的游戏,“精灵”技术也是必不可少的。不过,我们还没有学到精灵技术,还暂时不能使用精灵。但是,极其简单的游戏中,如果活动物体很少,并且不需要进行大幅度的变动,那么也可以用BG代替精灵。这次我们要做的游戏中,就用BG代替精灵。/ ^# ^! H* ~6 x" {2 {8 o
    / k' Y. s' M1 n# I. ^. E% G
    做个什么游戏呢?HGAME怎么样?
    2 h& f. @! g( W( i0 h$ v' g+ U* R1 ?9 G/ ?5 m5 I# ^5 a
    (众人:(怒)你个色狼!)
    8 A  H0 c" a  X0 e, }' N: E$ C, ^
    . x& f* A5 U2 r4 X雷精灵:大家不要误会呀!HGAME一般注重情节和CG,活动物体和精灵一般不多。所以,这个时候选择HGAME,应该是合适的……
    4 C' A! S' E/ d9 N4 y+ N5 `- y/ |/ H8 k$ o" R3 H- G7 g, R- j7 ~' I
    我最近在网上看到了一个挺有趣的HFLASH,大概的情节就是,游戏随机出现一个数字,然后下面的秒表计时。如果你在恰当的时间按下按钮,使当前秒表显示的数字和给定的数字相同,那么游戏里面的女孩就脱一件衣服……直到你把女孩脱光……相反,如果你没有准确的在恰当的时机按下按钮,那么女孩就穿一件衣服……若干次失败后,就GAME OVER啦……* i( a" }; t/ N) A  a( M6 `

    ) |. U6 n5 c8 C0 T  \: B: c如果要把这个游戏在GBA上实现,应该怎么做呢?' l) T  G/ o3 U1 _+ J$ N- S
    / Q! y& S3 a! k' a9 ]
    1.随机出现的数字,可以用随机函数确定,数字,以文字的方式出现,可以用MODE0下面的文字函数实现。不过,我这里最好还是先说一下,关于这个数字,最好还是用精灵的形式实现。因为用精灵的话,你可以将文字做得很漂亮……不仅如此,精灵实在是太灵活了!用好了精灵,效果真的会相当出色的……
    5 G+ d: E6 b/ ]$ B$ U9 F
    ! \1 |; d1 P. B) v2.秒表,因为频率是固定的,所以可以用中断实现。VBLANK中断,它的频率就是固定的,非常接近60Hz。秒表显示的文字,当然也是用文字函数。
      d, `% H7 \  ~; j9 W" Q$ n3 u% u& r
    3.按键响应,也要使用中断。检测到按键的时候,暂停秒表运行,判断两个数值,然后分支。
    0 `6 u1 Z7 Q# ~7 w! o: X8 s. G2 S: f- G4 I. i  |" G, P, V
    4.女孩的图片作为背景,根据分支确定背景的显示和替换。
    3 l0 C- @+ w& n/ V. M7 p: q+ ^, i/ e) o) S# w/ ^6 E" f( H
    嗯……大体就这么多了。开始吧!
    * O) U+ `$ m9 r; {* N# _
    % J" r* I! U0 O' H首先准备图片。这个游戏的所有资源,在教程的最后我均提供压缩包下载。
    3 ?3 I5 N; o/ x0 U  G5 [3 i! G& S5 O! p
    CG,BMP格式,256*256像素,256色。因为处于BG最低层,不设透明色。& ~7 N1 Z9 Q5 J9 d& I
    0 w  E4 k9 b/ E0 q; z8 L
    文字层1,用于显示随机出现的数字,还有一些对话什么的。调色盘使用16色。
    ( d" Z3 d" M+ V/ \8 F" c# J! @- x: I
    ! T! [7 q$ L0 W. Y- ?( ]文字层2,专门用于显示秒表数字。调色盘也是使用16色。7 M* a2 V* v+ d5 A
    ' z6 a6 P" C- f: n6 }6 Q
    为了出现比较好的效果,加上游戏片头。也算是为自己宣传了吧!游戏片头使用256色图片,256*256像素,使用FADE特效。0 ^/ J$ a! t1 o

    7 v& `' O: Z. p1 H' C游戏标题也加上吧!使用马赛克特效。3 o& x1 E+ g) O7 m# `) v+ L
      e! B1 ~# X: m* k
    背景音乐,毫无疑问得要啦!格式为MOD。
    0 d4 _& d3 H3 k+ {) m# s6 g+ s; G$ c, W) K
    程序开始。包含头文件,包含资源文件。9 H" l" I! `7 Z% o1 @
    3 F8 g6 i$ J! m9 U/ k3 N
    主程序,打开中断,然后直接进入死循环。所有的工作,都留给中断去做。: S) s! _. p8 K4 ^

    * @- F$ N/ i& k由于中断要处理各种不同的函数,所以需要在程序中变换中断服务程序。有两种方法可以实现:函数指针和分支。
    . f# r5 W. ^7 H- M/ y/ b% N( {) s* U" A
    函数指针方面,我没有学过,所以我也没办法讲解了。我就用分支来处理这种情况吧。顺便一说,用分支并不是一个好方法,如果游戏比较复杂,那么分支将会非常庞大;到时候恐怕连你自己都不明白到底是怎么回事了……) @' \6 D, V& x$ m' L7 K% Y+ K2 b$ k

    ( F' a8 w: H0 A, c( [9 P  g所谓分支,就是通过一个游戏流程变量(我一般称之为“剧情变量”)来控制游戏当前状况。这个变量为全局变量,初值为0。中断服务程序就是通过这个变量的值,来选择不同的中断服务。就拿咱们这次的游戏为例,游戏刚开始,显然要显示你的LOGO,因此在中断服务中,case 0的时候就是载入LOGO的数据,然后等待同步。中断完成之后顺便让剧情变量增值一下,那么下一次中断来到的时候,执行的就是case 1的服务。很明显这个服务程序就该是FADE特效了……. n8 J5 Z0 Q( B  u1 d

    & y9 |; x8 Q* r+ ~0 V1 d& X% e代码中出现了一些有趣的技巧,如果你不明白,不妨把你不明白的地方屏蔽掉,重新编译看看出现了什么效果,或许你会知道我为什么会这样做。6 [+ a4 P: Y9 Z" ?
    5 p( K3 A: j: ]8 g4 u! S/ C
    游戏中出现了一个明显的问题,就是背景音乐。背景音乐在标题菜单中播放效果非常好,但是一旦游戏开始,数字开始变化的时候,音乐明显出现“卡”的现象。这是怎么回事呢?这与我们写程序的方式有关系。我们的主程序中,什么都没有,所有的工作,都是交给了中断去做。中断,顾名思义就是中断主程序,转向服务程序。但是音乐正在播放,这个时候中断主程序而转向服务程序,音乐自然也被中断了。不过,由于服务程序非常短小,所以中断服务被迅速执行完毕然后退出来继续执行主程序,也就是继续播放音乐,所以,从宏观上看,就是音乐出现了一个短暂的中断,从而出现了“卡”的现象。
    3 _/ ~* }. M: m  {: |' J/ G& V9 n! r( e& x
    那么怎么解决这个问题呢?没有别的办法,重写程序,让主程序不要吃闲饭,尽量少占用中断,非得占用中断的话,尽量缩短中断的长度。如果非得大量占用中断的话,那就只有抛弃MOD PLAYER,使用MP2K了!
    1 G5 b* G5 q% A0 v4 {- k* R& f1 Q
    程序没有优化,因此冗余代码很多。你要是有能力,优化一下会减小ROM体积。程序中还存在一些预留的入口,利用好了,可以做出“秘籍”、“隐藏要素”!当然,你也可以继续扩大程序,加上其它的功能。比如纪录通关的按键次数,然后用存档函数保存数据;新写一个界面,做成排行榜,然后利用排序函数做出排名……毛主席说过:人有多大胆,地有多大产!虽然这话有点唯心主义,但是可以确定的是,只要你有想法,只要你有能力,没有你实现不了的东西……
    , V" T, T  d" F$ w! B) P
    $ M; s* s& ~' ]4 @* k, A: k
    9 j* N4 D+ u2 O# ]! x" ~: H! i' ^7 @

    6 G5 Z( \9 V2 q6 H1 G. @
  • TA的每日心情
    激动
    2013-12-13 22:59
  • 签到天数: 346 天

    [LV.8]以国为家I

    发表于 2011-9-13 11:50:15 | 显示全部楼层
    太长了!顶HOHO!
  • TA的每日心情
    开心
    2021-1-29 11:50
  • 签到天数: 104 天

    [LV.6]王国居民II

    发表于 2011-9-19 18:45:14 | 显示全部楼层
    就是太长了
  • TA的每日心情
    淡定
    2014-1-31 21:15
  • 签到天数: 435 天

    [LV.9]以国为家II

    发表于 2011-9-20 23:04:53 | 显示全部楼层
    {:soso__5124458142922744482_4:}难道汇编就是这个的应用版?
  • TA的每日心情
    淡定
    2014-1-20 22:59
  • 签到天数: 156 天

    [LV.7]王国居民III

    发表于 2012-1-27 14:04:55 | 显示全部楼层
    叉烧包······0 x2 s5 \  n$ {/ a* e  B3 I! _
    没图真晕啊······
    您需要登录后才可以回帖 登录 | 加入王国!

    本版积分规则

    关于我们|联系我们|口袋王国|Poke The Kingdom.

    GMT+8, 2025-11-3 02:51 , Processed in 0.136551 second(s), 18 queries .

    Powered by PokeTK V5

    © 2011-2021 Poke The Kingdom.

    返回顶部