TA的每日心情 | 别闹 2013-12-11 16:57 |
|---|
签到天数: 129 天 [LV.7]王国居民III
|

楼主 |
发表于 2011-9-13 09:23:29
|
显示全部楼层
GBA游戏编程菜鸟入门篇(五)——Char Base冲突
' f0 `/ x! _* x) |" w
9 Z5 b7 O6 ]# I4 V6 k2 ~% A上次的教程实在太长,我的手,打字都快打抽筋了……帖到论坛上的时候甚至发生了“发帖内容不得超过15K”这样的警告!我不得不把教程分成两次帖上去。但是这同时引起了代码错误,所有颜色都丢失了。这是没办法的事情,唯一解决的途径,就是让管理员将论坛限制放宽一些……扯远了……这次的教程很简单,内容也少,不过,不是很容易理解……大家做好心理准备!8 x+ M3 m3 P+ {; D( Y
" @/ K! _9 W, M8 z; Y) a想必聪明眼尖的朋友一定已经发现了,上次生成的那个DEMO中,256色背景图片,楼房窗口处和源图片不同。察看TILE VIEWER,发现原本用来存放Frame对话框图片的CHAR BASE,出现了其他数据!
9 W. @3 W+ F* l
5 ^% |# l5 o$ Y/ d9 r) j j4 g* D& j
0 {# ]: z8 W! \ 4 Y: Y& r) ]; {# {- C! {: v7 a
; e' c' K5 i5 ^* k/ X
9 r j) ^- o$ L& O; L. w" l1 A6 v' k- J# h; e; G
用16色察看,只能依稀辨别出来,最前面几个TILE还有点像Frame的某些TILE,后面一堆TILE,就不知道是什么了……用256色察看,竟然发现这是256色背景图片的下半部分!
5 u( J& h' a7 `" W: g7 A) ~
. [, C, b; h; K C9 C/ T这是怎么回事?5 \7 I7 C I( l& i
y- G8 \$ v, d$ p: U, ]0 G3 L
回过头看看程序:背景图片是存放在0号CHAR BASE,Frame存放在1号Char Base。存放在0号的256色的背景,怎么会跑到1号里去?
2 ~5 P7 A* v/ O, [% I- g. t- `' c/ F1 f- g+ j4 H& r
(众人:0号Char Base不够存放背景图片,导致溢出的部分跑到了1号里面去啦?)5 B0 g/ B! C) h9 w
, S* x5 `# K Y9 Q: n0 ^+ k9 R
雷精灵:强啊!你怎么知道的?
; f% L4 N6 w! y" R: T# k
2 S" |0 V% p* r, B! r1 F0 K+ Y(众人:把两个图片的TILE文件打开,明显BG的TILE远大于Frame嘛!而每块Char Base的空间只有0x6004000-0x6000000=0x4000这么大,万一BG的TILE大于Char Base的容量,那很明显多出来的数据会顺序存到下面的Char Base里面去啊!)6 D" n4 v7 q3 f
7 R# ^7 i1 Q6 ]( w" I6 p
雷精灵:Exactly!那么,怎么解决这个问题?9 X0 s# q# O, m' d" @6 ]
4 L3 h9 }! [; B2 q/ Z5 m
(众人:……)
! d8 b% u0 o% U) g2 ^
; g6 r( e# I( L. w8 o雷精灵:(得意地笑)最简单的方法,就是不用1号Char Base存放Frame图片了。根据程序,0号已经用来存放BG图片,1号被占用了一部分不能使用,2号存放文字,那只能用3号!6 m, z3 c5 `( b0 ` X! \' t
, @; k: | |! x
(众人:慢着!3号里面有这3个图层的MAP数据!你也不能用!)
& X8 X$ J( y/ ]* E6 o+ d
* d* v7 u! y( s/ J5 e$ f雷精灵:没错。但是MAP数据很小,我让MAP数据搬家,腾出空间来存放Frame图片的TILE。
2 Z7 z2 A2 j" r; ~* o8 d% A5 M2 i _: @, _5 T! H7 ^4 c' ~& p
(众人:那么,MAP朝哪里存放?)
4 r* G/ m5 a3 E: M D- e' x. `; _! B7 h" }7 V
雷精灵:1号Char Base不是被占用了一部分么?把那没被占用的部分拿出来存放Map数据吧!; A3 G5 Y3 {1 q/ q: ?1 x
; C! b7 w2 ?& O2 d0 G(众人:(怀疑的目光)行么?)
4 C- i2 x% A) L$ C; t% ?
1 V, y' Q0 g$ O; d3 k+ R/ z1 q雷精灵:试试吧!; V) V% c' I/ ?8 Y
. L2 e: ?3 p2 Q$ a8 M( S0 ^; V
从主函数开始吧!
# b+ a% A( }0 G8 J: k* ]. E8 A
' r3 k& o" \+ s6 U0 O, C9 f( lint AgbMain()
f* N+ f7 E% U/ {( o$ m ^" N9 Y3 ^, F5 j# ~+ o! L2 J0 |
{
" {( D s) D2 q0 D! |! {/ r5 ~8 S$ p- S" C* Q
GsSetMode(MODE_0);
, d1 ^3 a4 y F' J: \. D j M+ r5 c0 I8 V8 Y+ L# U
GsInitBG();
. \9 v9 {9 M; i* H+ A. j. h4 z: u& _7 }% K
GsLoadBgPal(BG_Palette,224);//与调色盘无关,不用管它。 % b$ b: O3 N+ f3 _
& L9 i% d Q1 e k/ O+ K) C$ ^4 OGsLoadBgMap(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,看看它的地址……
7 n4 Z. m' E, P3 I: pGsLoadBgTile(BG_Tiles,sizeof(BG_Tiles),0);//还是一样,将256色背景图片的TILE数据装载到Char Base第0块。 " W! R4 o/ s/ d+ c
$ c4 \3 I# f$ }* A+ s
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的马赛克特效。
5 o' P" W" b1 A" \4 M2 G
' c3 S) M3 X% R+ D6 E+ J/ XGsBgPriority(0,3);
+ Y. K. l" f4 H: k9 @ ~: _* u% R6 T# X, j, r) G5 z
GsSetBgState(0,ON);
$ P S. K2 V2 u* M, D2 |6 A8 T- r, M7 v$ t2 \. U
GsLoadBgPalEx(Frame_Palette,224,16);
# u7 ]% Y+ |3 I' t2 {% }' n+ y C9 I+ x# E
GsLoadBgMapSelectPal(Frame_Map,sizeof(Frame_Map),16,14);//那这里,明显就存放在16号得了!
+ ?7 ]7 j+ ^' y6 t) D* p: }3 n8 A+ [6 @/ k" @! l6 Z" \8 d
GsLoadBgTile(Frame_Tiles,sizeof(Frame_Tiles),3);//这个不需要变动函数,直接将16色图片的TILE数据装载到Char Base第3块就行了……
& q) E5 i! j) b7 j8 q* jGsSetBg(1,Gs_BG_TEXT_SIZE_256x256,Gs_BG_COLOR16,3,16,0);//设置BG1:TEXT属性,大小是256*256,颜色是16色,其中MAP数据在BG的16号区域,TILE数据在BG的第1块,禁止该BG的马赛克特效。
: I9 C- z% f! V9 w: q5 ]& O
! A; b9 _" `- i1 ^4 ~GsBgPriority(1,2);//设置BG1的优先级。次低。
G7 W% M+ K' p1 q/ }' k8 E+ N0 l3 A$ `9 @, p* k+ Q
GsSetBgState(1,ON);//打开BG1。接着是文字层了……6 Z+ m8 f* \8 T& P9 N3 ?# u
+ m4 C6 [" t S+ ]9 Q+ m" r
BGPaletteMem[241]=0x00ff;8 P8 E" X0 q9 w, K2 y) u
% l7 ]& T# a: m+ @$ x) w# ?
GsSetBg(2,Gs_BG_TEXT_SIZE_256x256,Gs_BG_COLOR16,2,17,0);//设置BG2:TEXT属性,大小是256*256,颜色是16色,其中MAP数据在BG的17号区域,TILE数据在BG的第2块,禁止该BG的马赛克特效。7 s2 f; S, N, \
" N1 @) Y; \+ l& M2 T
GsTileTextInit(2,2,17,15);//那这里也是一样了。2 ?. m' C S$ l5 a1 H/ n! v; n
% ?2 |3 N- \- U& D }+ A, e3 Y
GsTileTextOut(20,110,"我爱TGB,我爱烧录地带!",241);//……
, N( f% I6 A' X% q4 c) o$ B
7 ?4 V! b: x) q3 |1 k1 LGsBgPriority(2,0);//设置BG2的优先级。最高。3 n) y/ }/ N X. A" H% X
+ a0 ?! @6 ^! r- |) G, uGsSetBgState(2,ON);//打开BG2。$ G1 t! T9 _2 ]# R
2 j" g' q/ Q# a& g* S/ J3 K
GsSetBgState(3,OFF);//关闭BG3。没用到的图层,最好把它关了!" w8 A9 n, _- K. Q) S
" F0 M0 `2 S/ h8 h& W8 P8 F) G4 D$ _修改完了,试试看吧!% f6 u+ H& L2 b1 t2 p( F- z& L
; w$ ~1 t( t% u5 Y5 ^# C
, e. \4 L/ r2 \& e; r% ?+ L

: E: O% y7 t% h7 |2 N) L0 f* }7 t2 A' o, x8 u/ z, ]8 m
$ I5 ^3 Y1 [3 [( P; t, E什么!!!
- n& ^5 ?9 Q! k- k( @" j/ M$ L; c9 a5 c; {5 u: U" V
哎呀呀……疏忽了!看来1号CharBase没剩下多少空间,把BG0的MAP载入的时候,还可以容纳,但是这个时候已经满了,BG1和BG1的MAP,跑到2号CharBase里面去了……' h& c5 g6 {* u! U
1 n9 r/ K }- a( a6 G
没戏了,MAP还是放回31、30和29号区域吧!好心没得好报!
! L( l v$ [* K& a! A6 ^/ ^( ^
4 p8 g$ C) ~$ o- P+ s# }- m
" A4 Y' |9 R" F . d7 M: S( O. m- A+ R
2 v3 g: n" }; U7 W; I5 t6 a" _2 ]
1 \) m. B7 {7 J2 k) W; ~. y
终于行了……有够累!
2 M) A. w9 ^& m% j% m W8 h+ S
2 s! y& f: _3 _! u& W2 j从这个DEMO,我们能看出来,在作多层BG操作的时候,请多加留意!对于比较大的图片,你必须充分考虑到这图片的TILE是不是会超出一个CharBase所能容纳的范围!如果超过了,那么它的下一个CharBase就会被占用一部分,从而导致这个CharBase不能再容纳其他BG的TILE数据……当然,可以将其占用之后剩下来的空间作为MAP数据的存放空间,但是,你仍然需要注意剩下来的空间是不是足够放MAP数据……对于2层BG来说,基本上不会遇到这种情况,即使这两个BG都是256色。不可质疑这仅仅是“基本上”,无论如何你需要留心!相比之下3层BG,尤其是其中有一层BG是256色,这种情况出现的几率就会大大增加……如果是4层BG,仅仅是“如果”,那么这种情况出现的几率就是100%。这个时候请务必精打细算,仔细寻找可以容纳MAP数据,而又不会造成冲突的空间。你必须耐心的计算,到底哪里可以容纳MAP数据,到底哪里可以存放TILE。实在必要的时候,穷举法也不失为一种方法……5 V# n5 S8 `) u
8 J0 G; t; J# d# H但是,通过最近自己写的DEMO,我发现,某些图片,如果比较精细,颜色数较多,那么有一定几率会出现:这张图片的TILE数据甚至2个CharBase都不能容纳!这个时候千万不要进行大于2层BG的多层操作!否则,你会很沮丧的发现根本没地方容纳另一层BG的数据!这个时候你面临的就是,牺牲图片精细度和牺牲表现效果这两个抉择……不用我说,这两个抉择都是很痛苦的……: J1 @0 p8 L/ f. U: f
2 S, t9 v5 q0 k8 |% ^
嗯……差不多就这些了。到现在为止,如果你能完全理解上面几篇教程中的思想,那么你就掌握了GBA编程的三分之一。诸位不要说我罗嗦,再次强调一遍,TILE模式下的BG技术很重要……想进入GBA编程,请一定要掌握这些内容!8 B5 s& z8 t7 [6 O6 _; e
8 \7 j H( O1 P$ K( a! Z* w
那么,难点已经过去了,下一次我们来点轻松的——特效!在特效的配合下,BG就会变得多姿多彩!关于特效的调试,基本上不需要。你只需要通过VBA,看看效果是否符合你的想法就可以了。另外,我推荐的两种模拟器,在表现特效方面有一些明显的不同。具体地说,No$GBA表现马赛克特效的时候,是将这种特效作为水波特效处理的;表现FADE特效的时候BG和精灵不一样。由于在GBA机器上,特效的效果是和VBA一样的,而且VBA远比No$GBA普及,所以你就以VBA为准吧。下一次的内容:硬件特效!理论课内容:GBA的BG特效。
* s( H( j( f8 W @9 o7 S) T* k9 c) U% X" [3 |
7 d" x" T2 G9 X6 K$ U5 T
; \* d" U$ z( Z! \
7 e9 f: n; ?# C9 {
, m8 S0 s7 i2 z Q* F( x" ?% b4 D/ u9 R& b0 v
3 t; \# M9 y- f C8 g
GBA游戏编程菜鸟入门篇(六)——GBA的BG特效及硬件特效
) `! {/ b) g1 h8 H3 Q- x" M. W' g, z
特效,不是BG的专利,精灵也有特效。不过,我们暂时不讨论精灵特效。到了《GBA游戏编程中级进阶篇》的时候,我会详细讲解精灵,当然精灵特效也会讲解……3 R* {) ?' S r+ G
$ e2 o3 t$ y' K1 ^4 K( J, ~
对于GBA来说,硬件直接支持的特效有4种:马赛克(Mosaic)特效、透明特效(Alpha)、窗口特效(Window)和明暗特效(Fade)。而通过相关算法,又能实现若干软件特效例如烟花特效、水波特效、颜色滚动特效之类的。其中有一个比较出名,就是伪3D特效。GBA超级大作、人世间最强大的GBA游戏、不到长城非好汉不玩这游戏就白活的经典、人类智慧最美丽的结晶、(后面省略形容词10000字)的游戏《黄金太阳》,将这种特效发挥的淋漓尽致、入木三分、(后面省略副词10000个)……
5 c7 O# p- N. b1 s9 w7 d
1 E9 C! e' o( p: c1 @/ _; J这一次,我们先看看BG的硬件特效。至于软件特效,因为这要涉及对TILE和MAP甚至调色盘的数据操作,而这种数据操作还不是一般的位操作或者循环就能完成的,所以我打算在《GBA游戏编程高级技术篇》中讲述……- J. U) u- r2 |9 Q
- E- d) K6 |5 V7 N首先是马赛克特效。这种特效在现在的GBA游戏里面很常见。例如《口袋妖怪》,当口袋妖怪中毒之后,主角在大地图上行走的时候,每走4步画面就马赛克一下然后迅速恢复正常同时伴随着中毒掉血的效果音。再例如《恶魔城-晓月圆舞曲》中,游戏刚开始的时候主角来须苍真醒来,发现弥那小姐坐在他身边,阿鲁卡多站在右边。苍真醒来的过程,画面就是慢慢由模糊变成清晰的马赛克过程……还有几个GBA的Hgame,里面的马赛克特效实在是令人不爽……(众人:这小子有够无耻……)
, J4 h( S1 S) g( X4 d( @) x) O* w% |' Q8 ~2 n0 u6 T: @
马赛克特效的原理是什么呢?这不是很重要,但是了解一下也不坏。假如GBA硬件不支持马赛克特效,你知道它的原理后也能自己写出相应的算法,实现软件马赛克特效。
3 V5 c; k' c1 c8 w! y0 K* |9 h: k
规定图片的左上角坐标为(0,0),向右横坐标增加,向下纵坐标增加。假如要做双轴3倍的马赛克特效,那么,横坐标和纵坐标均为3的倍数的坐标点例如(0,0)、(3,0)、(6,3)、(6,6)之类的坐标点,其左方和下方两个坐标点处的像素,均变成横坐标和纵坐标均为3的倍数的像素。我画个图表示一下:6 x" R/ _5 | K
" Q0 U) G- r) P0 q9 v2 B
4 y z6 _2 D; Y1 v$ W
: P/ Q7 ?' u ^6 L& [0 N
+ A9 v8 C, f+ d- f: I
# L o$ w8 I: f0 C, c7 \
6 e" {; E( r0 W& Y/ Z8 c! ?涂有同一种颜色的像素,均变成它的右上角那个点的像素。这样,图片就形成了一块一块的明显的方块。$ q/ Q+ o+ Z' K0 h3 A+ q
1 G" k! Q4 J+ s0 x4 i. L0 E( D- ~那么,同样的,要做x轴4倍y轴5倍的马赛克特效,就是横坐标为4的倍数,纵坐标为5的倍数的点,像素值变成它的右上角那一点的像素值……可见,控制马赛克化面积的变量就是那个倍数。在GBA里面,横坐标和纵坐标的倍数都可以由你控制。
( e( M1 x i( ^) F$ a& a6 S6 x1 i5 |. w/ G/ G
那我们就做个DEMO看看效果吧!0 |2 ]. S, z# r8 l1 y' M+ g- h
( F0 R; W( x9 l, ~' r, v% l1 V4 M就拿写单层BG的那个例子吧。我先照抄程序,到需要修改的地方,我会加上注释。
& ]6 V4 g" j# X M2 {) u5 _& M- i% T* E
#include <GsGBA.h> 3 a7 s* \5 n2 q$ {$ h
" u+ h1 ^2 u% A; Y7 D: n) g& |
#include "BG.map.c"
- L( T3 g0 ]% W; T/ E& W
9 S* B4 Y1 `) ?, B+ ~/ k: F#include "BG.raw.c" 3 c3 S& N. M9 Y. U+ u( _# G
: {) j9 L2 X* V
#include "BG.pal.c"
( ]. E2 w6 m' p. Z, K5 @& R: X7 L% A4 i& v0 N) f
int x;int y;//这里定义了两个变量。这两个变量是后面对BG进行卷轴操作的偏移量。0 I5 a' b0 Z! h4 W
1 f/ P/ ]5 [/ g4 k1 c! z
int mos=0;//这个变量是用于马赛克特效渐变的变量。
$ i" G2 v+ c5 j) t; {0 b
3 J4 g6 t1 b; y4 q6 l7 cvoid Vblank();0 z$ M9 W5 }. b
6 T0 n; S2 B+ C/ Q, l1 u u3 Y/ R% w
const IntrFuncp IntrTable[14]={* a3 ]/ ~# b" ^6 U; U& N
# F! a* t9 A! l% _
Vblank,dummy,dummy,dummy,dummy,dummy,dummy,dummy,dummy,dummy,dummy,dummy,dummy,dummy
( Y3 b( L- c2 ?8 f' _6 t$ c# T$ E& ?: H( v/ ?
};( A0 W- Y6 d- X, O z
: {+ q, M: a; l% c
int AgbMain()
9 O' t: v+ }( F{
: i1 T- c0 C1 R8 [5 L. G/ `, W) F$ h& J! [
GsOpenIRQ();# A! M1 Y+ [3 ~0 i a6 e
$ p6 b- O: o9 Q( C" _* QGsSetMode(MODE_0);
. h/ r. r# c9 k2 W: s+ Z: v2 ~8 @1 N/ x$ W9 N0 `
GsInitBG(); : E, _( G' R7 {1 V" u
% f- T0 T; R" O6 _' }GsLoadBgPal16(BG_Palette,0);
" g4 w+ p! X8 c' D' e1 Y+ C4 m$ `+ a7 L$ e
GsLoadBgMap(BG_Map,sizeof(BG_Map),31);
# U& C0 A* ?# N' d0 E2 w
* g Q! J8 m. ?3 jGsLoadBgTile(BG_Tiles,sizeof(BG_Tiles),0);
- J1 G9 b2 ?: {, W$ a" ]+ W9 e$ n) D4 Y& {* R. F0 g
GsSetBg(0,Gs_BG_TEXT_SIZE_256x256,Gs_BG_COLOR16,0,31,1);//最后一个参数是控制马赛克特效的,打开它。* c" n) I. T. Z
& V& a/ d. U8 ?! _' ~% O2 @1 k% yGsSetEffect(BLD_BG0_1ST,BLD_BG0_2ND);//对于硬件特效,设置完BG之后一定要记住打开特效支持!否则后面无论你怎么写,都是没有效果的。这次只针对BG0做特效,因此这个函数的两个参数均选择BG0。如果有两层或者两层以上BG做特效,相应的参数就要小修改一下。9 Z% L; n& x: G, W( R1 U# n/ F
& n6 j7 A0 i7 ` u4 O2 P
GsBgPriority(0,0); 5 I A# P' R0 s# z# d$ g& m8 H
/ f3 ^7 {0 m1 J9 R6 T, M6 NGsSetBgState(0,ON); * ~3 n' x5 S* h) |
" s1 x6 d% G, u+ Q: `' U
while(1) ; `6 [# F- t0 }& Z; M* B
. u F4 O1 Z9 N% n
{2 R' m8 q1 \0 n) k: I6 z3 R! @
& U; R* a9 r! T0 R3 C* XGsWaitSync();
9 N2 K/ j& [ M2 u6 j2 @* R" O2 A+ y) u9 X% w/ Q# x
} 3 k$ b" p Y9 Y: k- Y; m
& f2 q$ ]0 p) Q* Y! y" X$ ~9 Zreturn 0;
) |! [5 m2 S+ v+ X* ~8 z7 K7 I& g/ E% y' r5 K; O+ F( n; \
}
; T, \& l9 S6 @; f6 w3 v7 e! ]' U. H8 P, ~
void Vblank()
2 r( G; m) t; ?' g! o! @4 O& [+ J1 C* h8 q6 a% U. X
{
7 c. z( X- Y! X) [3 O) _
q! F( a/ X7 hKeyRead(); 1 f6 \) N* M8 U8 A+ Y' C5 n, E
4 \1 u3 f! f# { C" w* Q
if (Cont & L_KEY)x--;
; e6 x. ~$ _" |5 N/ V" B& b. }
4 M4 k2 Z$ a' `6 B9 Q; e9 ?5 oif (Cont & R_KEY)x++;
: _$ P7 r/ B. Y5 z; s# p0 c$ R2 D. R2 c+ J& ~
if (Cont & U_KEY)y--;
, q5 c8 C% r' _% X: d# L8 _
2 M! F8 E5 n7 A `$ Q! y7 A( Eif (Cont & D_KEY)y++;$ A" R1 S( M; B' k* L$ C, V+ l
: J$ ?$ h' Y: c$ m( ]. v B: u7 F
GsBgOffset(0,x,y);
; Z1 F g0 l0 }! i9 Z7 t2 F0 y W0 m* z9 d% e1 C( [
if (Trg & L_BUTTON)mos++;//当按下L键的时候,马赛克变量自增。这里没有用连续按键。因为连续按键的话,马赛克变化太快……
- d$ [7 q5 C% ]1 t9 D/ r# \0 }' h: Y5 n8 b1 j
if (Trg & R_BUTTON)mos--;
- [. r9 g) f1 B- [. N: f( a! n1 T0 B# c
GsSetMosaic(mos,mos,0,0);//同样,这里要对键盘操纵的马赛克变量做处理。我们这次的DEMO没有用到精灵,所以后面两个参数均为0。
0 \* r: U! z7 g) V2 I$ u8 ~ W1 A: R" u8 I" m6 ~
}
- H2 V6 Z% y3 v K9 @* [4 D' w, [7 ~ I; }. `1 N2 j
这是效果。- o$ S! S/ `# O: H# ~0 F. `
0 G/ f7 u* U6 r3 F, U: q( U5 i: Q

! B" {% l* e5 o% c% A( T
) ?# \( i8 Z7 _- h: A8 J/ B( L3 h. ?2 G2 t# ^" k
注意,马赛克特效属于破坏性特效操作,就是说,如果进行马赛克特效之前不对图片的数据进行备份的话,就不能恢复了!这一点,相信大多数朋友深有感触……例如一张H图片,重要部位被马赛克处理了,那么想恢复原状就是不可能的。(众人:(鄙视的目光)只有你才深有感触吧……)幸运的是在GBA里面是有备份的,而且这个备份操作是自动的,不用我们操心……. c; U! j2 Z, d- c4 L
& W+ ?* d9 r% P" m/ b8 Y' E! t7 \5 S有关马赛克特效暂时说这么多。其实灵活运用马赛克特效可以作出一些很出色的软件特效,例如水波特效。有关这方面的内容需要涉及物理方面的牛顿定律以及数学方面的正弦函数和随机函数,应该说比较深奥。到了《GBA游戏编程高级技术篇》里我会简单介绍这些软件特效的算法。" G& f1 c0 ?! Q( ^! g# O, \
) e' B0 C p, P9 B9 b% c$ l接着是FADE特效。这在GBA游戏里面也是很常见。例如游戏开头的LOGO。我本人就很喜欢在游戏刚开始的时候显示我的LOGO,然后用FADE特效将LOGO变黑,然后慢慢变亮,停顿几秒钟之后再慢慢变黑……, m% F- ?& P3 W2 l H @9 m
; N- @* H) g4 }. b! H" ~来一个DEMO吧!还是用上面那个例子。- E# x9 \% `% \* ~
5 D+ R% E; k" W" d0 ]! V#include <GsGBA.h> 6 N! J; `4 P2 d% |
* ~/ _4 k" U4 ]5 O3 _#include "BG.map.c" 8 c0 |% h8 p8 `# E% \6 E, ]& z% R
3 ]6 f$ c* p3 s& c( ^#include "BG.raw.c" 4 b9 X8 u3 T7 A$ N' W
, t5 \. e! n* \/ Z
#include "BG.pal.c"
5 w. i: m, z( T* K# k4 R+ w$ \ ^) O7 N5 a3 D0 `# h
int x;int y;//这里定义了两个变量。这两个变量是后面对BG进行卷轴操作的偏移量。
b- \+ q# z. `: a8 R9 y A4 N+ Z' H9 Y$ |$ |
int brightness=0;//这个变量是用于FADE特效渐变的变量。
+ N) R4 N) v4 z
. q5 c' R8 F7 M% hvoid Vblank();
) A, U* E2 \2 k& G: ]9 r; b* m! b' |4 ?) L8 f1 x& I/ ~$ `: D' s
const IntrFuncp IntrTable[14]={
; u8 e+ p0 m' D. ?1 s9 u4 l C2 N$ A, G7 C2 `! o7 N- }
Vblank,dummy,dummy,dummy,dummy,dummy,dummy,dummy,dummy,dummy,dummy,dummy,dummy,dummy
4 B. A2 X+ f! z: o/ D+ q& q+ w7 @1 F8 ^- k, U
};/ y4 Q; F+ `! k3 F) F( {( K
9 v5 q3 M6 E. s0 L; k
int AgbMain() 4 e# A& x T& N
{
4 C% f8 l) x4 P
( V' I& p: t3 ^- @9 {2 bGsOpenIRQ();
9 @9 x2 v- W0 d& g6 O, ]( s* A* B/ Z& I: ^6 U8 @0 l
GsSetMode(MODE_0);
* U3 T5 F, D! u3 ^; Y& P% U4 N. K% v4 M! {* U0 a! \6 a
GsInitBG();
1 B6 P' _. p' I2 T8 k' f M U1 h' m% @5 z
GsLoadBgPal16(BG_Palette,0);
" h" v- O% q; e: J# a0 H# a. m$ ^7 p9 j3 r9 g; r
GsLoadBgMap(BG_Map,sizeof(BG_Map),31);
$ _; n9 \& L% G
8 I6 i4 j+ U6 g9 H4 x9 S2 Q9 p: qGsLoadBgTile(BG_Tiles,sizeof(BG_Tiles),0);
; X$ w+ b; A8 A1 _
8 Z# [3 [3 a1 U; O# b) s+ RGsSetBg(0,Gs_BG_TEXT_SIZE_256x256,Gs_BG_COLOR16,0,31,0);//最后一个参数是控制马赛克特效的,这次不需要马赛克了,关闭它。
* \4 H0 F1 @& V% r; ~; r+ k2 o/ r$ f# l c8 w
GsSetEffect(BLD_BG0_1ST,BLD_BG0_2ND);//同样,设置完BG之后一定要记住打开特效支持!这次还是只针对BG0做特效,因此这个函数的两个参数均选择BG0。0 W4 ~3 y) O" n5 W3 A2 N
& G4 n9 c0 g7 l/ {GsBgPriority(0,0);
5 F8 u/ e- L+ ?% h3 b H4 t i+ d/ S; G8 e5 u q* c
GsSetBgState(0,ON); , b8 K& D; v o# E
' h ?2 ~% T; k1 T {while(1) 1 H6 S" O) h5 A) L
& y1 ~0 m" g X( D
{
. S4 Z0 @$ q! J4 w% F- K& u: r& R
GsWaitSync(); + o; P6 _, |, i6 x
' I6 C) Y" x' p, N8 v} 5 g }1 t( u6 s9 i+ j. H
% s: q! ]* r8 e9 l" V- ~- X
return 0; 5 w& f9 V; G0 v" c8 F6 J) J
, ~) X2 {! M; U, B! v5 Q9 F}5 r: Q' J$ l$ H3 T) e' D
- o# Q! C8 y' f* S; `; w( A6 x$ E0 jvoid Vblank()
9 f, w+ g0 U; a' j+ v8 d+ m
7 Q- }( p" e1 F{: _4 I O7 l6 k- x, p
1 q& w! u+ F$ n; _6 c; R
KeyRead();
$ m4 q5 A0 {0 h, ^( w1 X# n, f# p, U5 D6 @( v2 Q. Q4 t9 Y
if (Cont & L_KEY)x--;
8 R8 s p0 ]5 M0 C& _. m, M. j* w7 P
if (Cont & R_KEY)x++; : t' a/ z P5 p" G0 O1 ?. `" ]" i' g
& g% ^6 l; H3 {
if (Cont & U_KEY)y--;
1 b: A! s" l* k( N" a7 B- ~! |5 Y) I. O4 r8 G! S7 T
if (Cont & D_KEY)y++;* ?8 [6 S# m, F, t( ~
+ v1 a1 m2 p! aGsBgOffset(0,x,y);/ T4 v3 o3 I2 H7 E% n! Q4 [8 `
- H2 P1 W4 ?! n' aif (Trg & L_BUTTON)brightness++;//当按下L键的时候,亮度变量自增。这里没有用连续按键。也是因为连续按键的话,亮度变化太快……
8 G C& f/ Y% w
3 q, i- E" G( Uif (Trg & R_BUTTON)brightness--; - a( Y1 C, F- k: n" A6 Z) Z7 U
% D) S, b" m. _$ IGsSetBright(brightness);//这里就直接控制亮度了。要注意的是,帮助文件中规定的亮度参数是s16类型,也就是允许负数。但是即使你把这个变量定义为无符号变量程序照样正确。原因很简单我就不说了。另外帮助文件中规定参数范围为-15~+15,其实超过这个范围也无所谓……你可以试一试!
6 Z9 m- a- Z a1 S+ D. h1 H9 o5 j
# O7 ]% @( X6 x; S7 | \, Q}
5 R; _& w$ y; i) m" A( q) M5 A
; m5 j- d+ R# A$ p1 q效果图:
% S2 e# e& A4 l9 @& O% x# r
) @, o5 h/ @' R
) K: p8 | {* s) ]( ^' ~
4 j$ T3 k( ~' ]/ q5 R2 S) w8 n ]! v" R" q
, B1 n' J3 G& l* \& G2 V
FADE特效也是破坏性特效操作,不过你仍然不用操心……嘿嘿……
" N0 j6 Y5 I! u2 l/ K4 W! q- S3 M4 Z7 l. d0 ^' Y( M
利用FADE特效,可以很方便的做出黑天特效。GBA超级大作、人世间最强大的GBA游戏、不到长城非好汉不玩这游戏就白活的经典、人类智慧最美丽的结晶、(后面省略形容词10000字)的游戏《黄金太阳2-失落的时代》中,有一个这样的剧情:主角们旅行到一个狼人村庄,刚走近村庄的时候,天暗了下来,然后迅速天黑了。(很令我奇怪,无论我走了多长时间总是到了村子口天就黑,即使刚从客栈里面爬起来……这几位兄弟时间掐的还真准!)这里的特效就是FADE特效了……当然,也可以做出刺眼的光照特效。仅次于GBA超级大作、人世间最强大的GBA游戏、不到长城非好汉不玩这游戏就白活的经典、人类智慧最美丽的结晶、(后面省略形容词10000字)的游戏《黄金太阳》的游戏《口袋妖怪》中,神兽古拉顿觉醒之后,整个芳缘地区开始出现烈日天气。那种刺眼的阳光,就是FADE特效……6 E5 _& D/ i2 X0 J! ]# }1 T& f) e- q
8 e$ f' c0 W6 L& h然后是窗口特效。顾名思义,窗口特效就是BG上面出现一个区域,限制了BG的显示范围。我孤陋寡闻,玩过的GBA游戏很少,迄今为止只发现了一个游戏使用了BG窗口特效——《RPG制造》。游戏制作中有个选项,就是游戏结束后出现STAFF表,是以什么方式出现。有一个选项是背景图片被挤到屏幕左方,右方空出来的区域,显示STAFF。这个显示过程就是窗口特效。
/ H, E$ k) R2 ^ }' u* _
$ x/ m& C9 a2 c) `4 FGBA硬件共有三个窗口可用:窗口0、窗口1和OBJ窗口。其中窗口0和窗口1效果相同,呈现为屏幕上的一块矩形区域。OBJ窗口可以算是OBJ的一种特效,它只能配合OBJ使用。一旦OBJ使用了OBJ窗口,其显示就会变成一个轮廓。关于精灵窗口特效,最经典的例子就是GBA超级大作、人世间最强大的GBA游戏、不到长城非好汉不玩这游戏就白活的经典、人类智慧最美丽的结晶、(后面省略形容词10000字)的游戏《黄金太阳》中,角色们在大地图上使用“Psynergy”(好像是被翻译为“精神力”的吧?),其身体就变成了一个轮廓……还有,在阴影里面使用Psynergy“阴影”,身体也是变成轮廓,而且很明显……
5 P, I( Q7 I' u6 U5 Q( K) Q% C+ e& M; v. \+ ~
我们也来一个窗口特效的DEMO。
5 V; k* l. p# J8 F
$ b" D" h' N/ \7 g @3 c#include <GsGBA.h> 9 o" Z+ \" V" X7 \9 {
$ H/ w |* g( C3 ]
#include "BG.map.c"
8 e! _) l; h& _. g4 z; j3 N" t, L7 o) z5 t
#include "BG.raw.c" ) [$ \5 y# W6 f6 F# Q6 h O( C
/ o1 C/ S+ p- w#include "BG.pal.c" 0 e# S; r/ p" d8 h4 e2 j
$ D) g% Z/ I( j$ n
#include "Blue.map.c"
( `* p! b* R2 d3 b. s" }% _( ?2 l# b, P+ ?6 N: b! D% s6 J( h
int winx=0;/ U+ `5 s0 ]' a# U+ z
3 _* y( }2 |! l6 W$ b
int winy=0;//这2个变量是用于控制窗口大小的变量。
4 g; S& q# t @" K' p- |0 S( H- h& m5 Q
void Vblank();
: d0 e5 {5 W4 |( j2 {: I# M
: s% i- H2 _" H# U! @2 k8 Yconst IntrFuncp IntrTable[14]={* A. e% _* y/ \. |
8 S3 g3 _: p, Y4 l! i
Vblank,dummy,dummy,dummy,dummy,dummy,dummy,dummy,dummy,dummy,dummy,dummy,dummy,dummy( ~5 g0 G+ S9 ?0 {- G
. U! i/ E) H1 z9 f
};; m/ d. D7 i5 j7 [9 V
& w) I5 V$ p1 o3 B5 ^3 h) t3 t
int AgbMain()
0 Z: o) Z: a9 N5 y) B6 J% }; S+ q2 m{
( @2 i0 M4 n2 u& ]4 t+ v! K
q+ e' @- D$ |$ ZGsOpenIRQ();
- I4 j+ p- D7 O" @
3 C# Q( R, K. D$ c! w3 L( {GsSetMode(MODE_0);
3 Y% }- e' @" I& T0 r/ \
6 w6 h, Q" k1 r8 |% T+ ]& ]8 H* zGsInitBG(); / R/ W6 |7 V$ e' y. i; z h
9 K* f, Y+ x3 JGsLoadBgPal16(BG_Palette,0);5 R+ @/ ~$ ~' E* O# Z! Z8 q8 K
( A/ `* h9 Y8 z8 k
GsLoadBgMap(BG_Map,sizeof(BG_Map),31);" F* B2 L8 i5 u' j
! j7 A* m6 G7 e" V) D7 ^. eGsLoadBgTile(BG_Tiles,sizeof(BG_Tiles),0);
" ^0 j* n Q9 O$ [) P% r4 t8 J* k$ ?1 p: k' c
GsSetBg(0,Gs_BG_TEXT_SIZE_256x256,Gs_BG_COLOR16,0,31,0);
2 b7 q8 i9 p% {( f) S# D2 o3 x4 M' n! o/ z( j7 y
GsBgPriority(0,0); S- F0 }; i3 C7 r
! q4 x* p% \1 D# ~0 B
GsSetBgState(0,ON);
9 q0 m& C3 K" R% e
1 `) u) h- h' i. d! s+ BGsSetEffect(BLD_BG0_1ST,BLD_BG0_2ND);//同样。
. D+ ^/ q* t+ v2 e/ j! N6 g. J! w, Z j
GsSetWinState(0,1);
4 G# U9 z- h/ S! _8 z) a: h: V' |2 G3 L
GsSetWinState(1,0);//设置窗口状态。我们没有用到窗口1,只用到了窗口0,所以激活窗口0,关闭窗口1就可以了。
& W# Q4 A8 t. w( N S6 G
# B6 r2 G: v( g5 Y* @- Cwhile(1) % |3 |0 h# T& F6 E: m2 X/ _2 C
' A n; H# ^, b& Q" {1 H) g{) G: j7 j$ N$ y: U; `
+ w( n/ s9 d* i2 {+ w7 iGsWaitSync();
9 L8 F) h8 {# S8 i; A# F7 b1 t! U! G; R6 E Z- \$ ^
} ( D/ [$ `1 y7 ^7 H4 G
* F; r! U1 j, N. x8 ~return 0;
F/ p' I: f! a$ Q: a0 p, s* Z5 e% E4 ]6 f% b" m* _
}+ D& o4 X" r. C2 a) y
; B! s9 s8 [! T, H$ p: cvoid Vblank()
@" d- T* q3 G1 u8 _; K8 Z/ z, g3 }
{ d4 Z" g n+ } N
7 C: p4 G3 f0 ]+ N; {# S
KeyRead();
9 W; Q) m$ I1 q$ a+ e' C3 P/ n- I* l0 C" W
if (Cont & L_BUTTON){winx++;winy++;}//这会有什么效果,你能猜出来吗?
* F/ c7 H9 P$ s2 W: m; U, t
2 f$ \0 H& I/ c; B* ]4 l: Vif (Cont & R_BUTTON){winx--;winy--;}
5 i. d5 Y5 ?5 r) A5 o
4 y( K' A0 T7 i: g0 b1 KGsCreateWin(0,120-winx,80-winy,120+winx,80+winy,WIN_BG1_ON,WIN_ALL_ON,ON,ON);//创建窗口。使用窗口0,大小由我*纵,窗口里面显示BG1,窗口外面全部显示,允许窗口特效。BG1我们没有用到,所以就是“空白”。
) G! L+ K8 `( w7 t6 G
8 }1 f+ F* u( u5 ~0 O}6 _* P# E3 ?' X" C0 \/ B
' ^! m& B( d* u& @# i/ ~ i这是效果。
6 \5 e' L* A/ K; D7 |0 r4 A" i! k6 a: W! U' w
" e W: L( p( ^1 V: I
, L0 E9 @9 b1 |& |/ g/ q
1 |; l! F4 ~3 p8 w: {. N7 R' H4 a/ {. Q" F; X) v4 Q
窗口特效不是破坏性操作,从宏观上看,其实窗口就是一个“虚拟”的BG,这个BG盖住了真实BG的一部分……形成了像上面的那种效果。% c# m3 n. v3 e* K( U8 h* P! k
5 |' h9 }2 w" i/ g" x2 ~0 N
在GBA中,存在两个窗口,就是窗口0和窗口1。理论上讲这两个窗口都可以由你操纵。不过,或许是我的分析有误吧,我只能操纵窗口0,窗口1操纵的时候,总是出现异常的状况,所以,有关窗口1的资料,我暂时无法提供……召唤达人中……如果你要使用窗口1,请慎重!
0 f8 ?+ F$ ]9 {' i" V, J9 @" [6 f3 ^2 y) v+ f% @* R! _+ c
最后,剩下Alpha特效了。大家都知道“三原色”——红绿蓝。这三种颜色通过不同的比例混合,可以做出各种各样的颜色来。GBA硬件可以完成这种颜色混合。这种特效,就叫做Alpha特效。
' i* f1 h9 I* ~& o) p/ y6 X) j8 N: F. }9 B
这种特效在GBA游戏里面不怎么常见,我只见过一次——还是在GBA超级大作、人世间最强大的GBA游戏、不到长城非好汉不玩这游戏就白活的经典、人类智慧最美丽的结晶、(后面省略形容词10000字)的游戏《黄金太阳》中,片头黄金太阳升起的时候,背景的阿鲁法山在阳光的照射下显得颇为壮观……阳光穿过浓厚的云层射下来,形成美丽的光环。这个光环,就是活用Alpha特效做出来的软件特效——光晕特效。我个人认为这是所有游戏里面最令我陶醉的一幕画面!这个特效,把这一景观做绝了!" l' J& f& ] H6 O h, D# d- d
+ b$ o# y* @3 O
1 i3 w# ]+ F: o- w8 j S+ F

5 j, ?) ^: I; l5 {! j# R M: j+ Q' X, Q4 \! g1 E1 M
' ?" i0 q" N/ x' R, K关于这个硬件特效,我不想写例子了,我觉得,大家参考samples文件夹里面的DEMO,自己试着写一个就可以了。还是那一句话,用的好的话,这个特效是很炫的!
$ q+ z% `, L2 K6 c) k5 O+ K0 I h0 B; {
到现在为止,有关BG的内容就全部讲完了。不过,这并不是说BG就这点东西,更深更好用的函数还有呢!下一次,将是《GBA游戏编程菜鸟入门篇》的最后一次教程了。学完这最后一次教程,你就不是菜鸟了!因此在中级教程里面的例子,我不加注释的部分你应该能够看懂。如果你还是看不懂的话我劝你还应该再从头看一遍。下一次的教程,GBA上面的音乐播放!学会这一部分,配合特效和多层BG,做出来的效果不用我多说什么吧……嗯……还有一点小小的事情:如果你有点五音不全或者对音乐的鉴赏能力很差,那么你就不要学下一次的教程了。因为下一次的教程要涉及自己写MOD,这是和写MID非常类似的工作,如果你连MID都不会写,那么MOD你也未必能写出来…… H! @9 f1 U' R/ S5 V! s! w
' O. @0 a8 |3 E0 U& y2 M3 h. T
GBA游戏编程菜鸟入门篇(七)——GBA音乐播放
; K6 s* b4 v6 y& k N8 M. l
6 s1 I* ?* |5 D
$ @7 Z; r5 b7 a" L: ]# }4 u7 V6 wGBA上面一共有6个声道,分别为SOUND1~4,和DirectSound A/B。这6个声道硬件上到底是怎样播放声音的,这个问题实在是复杂,官方开发包里面的帮助文件和寄存器映射表我基本上是完全看不明白。不过,GsLIB中提供了一个非常好的工具——MOD播放器!这个工具,使得我们可以非常容易的在GBA上面播放声音,无论是背景音乐还是效果音或者真人语音……不过,会有一点点的失真。
9 {; w1 _3 b0 X! F( ` B+ S- Y; P, ~+ p. {7 X$ c
在GBA上面播放音乐有两种途径,第一种是使用GsLIB2.6 ARMSDT版本提供的工具——ModPlayer——来播放MOD;第二种就是只有GNUPRO版本的GsLIB才能实现的,使用MP2K播放MID。ModPlayer有种种缺点,但是由于使用简单,我们还暂时不能将它抛弃;而MP2K才是终极之道,官方游戏,大多数都是使用MP2K的……如果你掌握了ARMSDT版本的GsLIB的使用,那么我强烈建议你转向GNUPRO版本!因为两种版本中的函数基本上都是一模一样,相信你会很快习惯! I7 Y) m, X, U0 Z" n9 @' T* Z
^2 L4 L4 Z$ T" [3 G/ J6 R
首先简单解释一下所谓MOD。MOD是一种音乐格式,和MID类似,只不过自带采样数据。帮助文件里面就是这样解释的。详细的资料,我也不知道,你要是有兴趣,可以到网上找一下相关的资料。在我们这里,了解这么多就够用了。9 K6 |, }2 I' ~- { Y
* Q, V* c# P; s既然MOD自带采样数据,那么MOD实际上就可以用于播放WAV,只要你的采样数据是你所需要的WAV……因此,如果你想在游戏中加入真人语音或者效果音,只需要录制好你需要的声音成WAV格式,然后在ModPlug里面导入你做好的WAV成采样数据,然后……1 C! g2 A0 E! q# ]) S& [0 ?, q
5 Z4 {5 |/ J" @& ]: Q, Z+ Z$ Y
首先说明一下,制作MOD的软件应该很多,但是我只找到这一个;不过这个软件也的确强,虽然做出来的MOD有点失真,但是这很可能是由于我对软件生疏,制作的时候造成的……不能不说这软件真的很好……由于我也是刚刚使用这个软件,操作的时候出现的错误在所难免,请各位见谅,雷精灵也就只能给各位提供这些资料了……当然,如果您很擅长使用这个软件,那么还请您多多赐教!
" V, |" {( ~) x
# X ~; p+ J, u' H哼哼……我们来实际操作一下!4 o" A( z! s# A4 z. F0 G- D. ~
# V& n: r0 f+ s& t
首先是ModPlug的界面……, X; C: o9 g( {: Q$ I

9 k: y. k, x# b
0 l8 j! l; h h1 V8 p7 E这个时候你可以用它打开一个MOD文件,熟悉一下每个部分的功能和作用……关于MOD文件,samples文件夹里面有两首很好听的MOD……( m* T- \: H8 X0 F M& @" H; z. Q
& ^( X3 K$ I- n" ]4 y: T自己录制一点语音吧!我从游戏里面截取了一点声音并转成了WAV格式。这是PS《生化危机3》钟楼里面的八音盒音乐,很爽……不过要注意一下,转成的WAV,最好不要是立体声,我不敢保证立体声的采样数据能不能做出MOD来……
& n5 G* g: w _# H, U; ^2 W' v8 H& T. ]2 d4 a! b
点击这里下载《生化危机3》钟楼八音盒WAV音乐:http://www.tgb.name/index.php?act=Attach&type=post&id=516
* ?/ R/ }; F" N, m T" m, F8 X5 v t. q9 g' t
然后打开ModPlug,新建一个文件。! c( [$ w2 d0 S3 m
- F0 A8 q& F+ e* J' j
然后主窗口里面出现了5个选项卡,分别是General、Patterns、Samples、Instruments和Comment。大家的英语一定都比我强,我就不翻译了……
( U8 a7 `9 v' W3 N; V g6 S+ N% l& d# O) f2 l4 ` _6 G
点击General选项卡使之前置,最右边是声音的幅值显示器。这个东西大家一定见过!那两路显示,代表声音的两个声道。4 t$ B! C1 d5 |5 W+ F- P2 u* E
$ w+ r4 }$ k$ A) K7 Z! Q& T+ x
关键是它的旁边那个“Change”按钮。点击,弹出来一个窗口。把Channel数改成4。这样会减小MOD的体积,而且由于是在GBA上使用,4个Channel也完全足够了。
( @7 b0 k3 X4 U6 {: b8 y
9 B1 @/ o Q; m" R5 E然后点击Samples选项卡,这个时候采样数据前置。9 n5 {1 R- _6 G8 V$ j1 l
6 [, _0 \6 i6 d9 D; Y
' e' x* M6 o0 k& l+ g2 J3 Z

) `! N2 [1 O. u+ {# r& p3 W2 n" g$ Q" a5 d5 z) a. U/ k# D
直接导入采样数据,也就是你准备好的WAV文件。导入完成之后下面的波形窗口就会出现采样数据的波形。点击播放按钮会播放采样数据。4 F$ u: m4 _# |/ }9 N% v
1 S# y, Q1 A1 B0 u# x3 o$ z下一个步骤非常关键!请务必照着教程做!1 n4 N! D: M/ m# H
; |* B, ]) P+ N+ z& M4 e# X! ^5 K

( v7 T$ a8 h( b" f- c2 A/ ?, H
0 D* q& N7 A+ Y, t+ o! O W2 Q9 B# V# x; A, S! C* w
点击两次那个“Downsample”按钮。这个时候下面的波形会有些许的变化。
* `5 H0 c8 y7 C, Q: E( b0 z, U2 J- B; Y8 v
然后点击Patterns选项卡使之前置。准备写音符。
1 | _2 m/ X( J4 b3 }1 W* ]) A, N: \
3 B0 I. W4 T$ d5 N: e( ~0 H" f双击第一个Channel的第一个音符,弹出音符属性对话框。音符选择“C5”,采样数据选择第一个,就是你刚刚导入的采样数据。由于我导入的采样数据比较长,我新建了一个Pattern,这样可以有效的防止声音被掐掉尾巴。4 h t4 f1 k) s, R2 ~1 t* L! J
* L7 V) ~9 ^! c7 Q4 M5 K: c然后直接另存为MOD格式。
+ [$ Y: w0 ~+ {( F8 |: o; G3 \
3 {6 Q7 n& m4 J. _0 v& q0 f' }关闭,再打开,听一下效果吧!虽然音调有点低沉,速度变慢,有点失真,不过效果还是颇不错的……如果你想做成立体声,就把另外几个Channel也填上音符C5。# |' [6 F' P* Z) p- q' h
) t! p9 U8 {& t; [
如果你对这个失真很在意,那么我建议你先用变声软件处理一下你的采样数据,然后再导入……
/ A' B0 Q8 F. |. K1 }) y1 m: d1 ]/ p5 n- H: X4 b
那么,MOD有了,开始写程序吧!0 ^; z9 `4 m" a
0 t) R* ~7 c3 {9 W- Z2 t- d) b
(众人:嘿嘿……这还用得着你写?光看帮助文件和samples里面的例子就能够写出来了……): t" a% [6 M/ i% F- w& g( e5 y
: N8 Z+ k9 O* H+ E8 g1 F9 A
雷精灵:(尴尬)啊……那……好吧……我就把我写的DEMO放上去得了!
8 V* y1 j' l( W# W0 e* v8 @% K. u+ ~: l! J
点击这里下载DEMO:http://www.tgb.name/index.php?act=Attach&type=post&id=5185 k$ Z! E! B. p
, P% _8 e! X4 g' H5 _就像这样,GBA可以播放音乐了。只要更换你的采样数据,就可以做出各种声音的MOD。如果你导入两个或者更多采样,写音符的时候在其他Channel使用别的采样,那么,效果就不用我说吧!如果在其他Channel使用相同的采样数据,但是有点延迟,那么就可以做出回声效果……只要你能想得到,就不怕你做不到!
( E4 j* q8 [3 t5 |8 ], j! Y9 s/ b3 J, M ]3 G K
同样,如果直接使用现成的MOD音乐,就可以当作是背景音乐;录制效果音作为采样,那么做出来的就可以当作是效果音;录制语音,那么就可以实现播放真人语音……有没有人放一首歌上去?诸位不要笑,完全可能!
, |7 P# M( ~" v% [
$ z& r! K8 H/ t0 g到这里,初级教程就完全结束了。现在的你,已经不是菜鸟了!毕竟,能够自由的操纵BG,随心所欲的制造特效,还顺带着加上背景音乐,已经很了不起了!下一次教程,我们就涉足更深的内容——精灵使用!精灵技术是游戏中的一项革命技术,掌握了精灵使用,你就掌握了GBA游戏编程的另外三分之一! 7 C0 K. Z4 h* P
3 D. x9 {( F, r- g9 aGBA游戏编程菜鸟入门篇(特别篇)——制作一个MINI游戏
: c! B1 Q$ }+ @4 a6 E7 \; f: ]8 u: l1 M
初级教程中,我们掌握了TILE模式下BG的简单内容,学会了使用多层BG,能够使用简单的硬件特效,也可以进行音乐播放。这次的特别篇里面,我们将尽量多的使用我们学过的所有知识,制作一个MINI的游戏!
& D6 x1 Y- D+ `: S: o
7 H5 G8 l8 N6 g9 \& F一般的游戏中,哪怕是很小的游戏,“精灵”技术也是必不可少的。不过,我们还没有学到精灵技术,还暂时不能使用精灵。但是,极其简单的游戏中,如果活动物体很少,并且不需要进行大幅度的变动,那么也可以用BG代替精灵。这次我们要做的游戏中,就用BG代替精灵。1 u) k i! k: B0 [' e; I
- l+ }2 p% K* D9 `# n1 d/ R( U做个什么游戏呢?HGAME怎么样?7 b! R% J: i+ [( q. P- Q0 D i: f
$ L& y* S3 ^# _+ ~# D(众人:(怒)你个色狼!)" T" [4 z% i( t1 ?# `5 |
, m6 C6 ^7 v( J雷精灵:大家不要误会呀!HGAME一般注重情节和CG,活动物体和精灵一般不多。所以,这个时候选择HGAME,应该是合适的……7 v: C3 w* k$ T1 M7 Q- E0 a; ~, R ]
+ H) S( V9 |7 r: T我最近在网上看到了一个挺有趣的HFLASH,大概的情节就是,游戏随机出现一个数字,然后下面的秒表计时。如果你在恰当的时间按下按钮,使当前秒表显示的数字和给定的数字相同,那么游戏里面的女孩就脱一件衣服……直到你把女孩脱光……相反,如果你没有准确的在恰当的时机按下按钮,那么女孩就穿一件衣服……若干次失败后,就GAME OVER啦……) F( U ?. V7 J% ~5 ]
' H- S" S; `; P4 U) f# X* `( [
如果要把这个游戏在GBA上实现,应该怎么做呢?
! [. i: R% s% n7 P' Y" G: C0 @: t: u
1.随机出现的数字,可以用随机函数确定,数字,以文字的方式出现,可以用MODE0下面的文字函数实现。不过,我这里最好还是先说一下,关于这个数字,最好还是用精灵的形式实现。因为用精灵的话,你可以将文字做得很漂亮……不仅如此,精灵实在是太灵活了!用好了精灵,效果真的会相当出色的……' X' a J" I2 E) t' C
8 i \0 _ ]2 J2.秒表,因为频率是固定的,所以可以用中断实现。VBLANK中断,它的频率就是固定的,非常接近60Hz。秒表显示的文字,当然也是用文字函数。1 n4 X7 o$ \! P! K
0 Y+ S8 A- X/ t! m3.按键响应,也要使用中断。检测到按键的时候,暂停秒表运行,判断两个数值,然后分支。
$ {) S5 }4 {0 [7 a3 k0 j1 r
7 E4 L' C7 o5 `2 ]4.女孩的图片作为背景,根据分支确定背景的显示和替换。
0 Z+ J8 R; S3 m$ u, ]7 u4 h3 e
3 ^4 P6 ~! m6 x. b9 u4 k嗯……大体就这么多了。开始吧!
. @4 J! }& L" k/ d$ Q* H3 B, @
首先准备图片。这个游戏的所有资源,在教程的最后我均提供压缩包下载。
7 t5 n+ m1 H L6 v1 _- v, o. ?7 w2 G0 t E7 D; W
CG,BMP格式,256*256像素,256色。因为处于BG最低层,不设透明色。0 Y+ M6 K& ~. n5 u8 H/ M8 E
6 D9 Z% Z4 E& D
文字层1,用于显示随机出现的数字,还有一些对话什么的。调色盘使用16色。
0 A" b1 Y: I. Z' b" s' f- X6 x3 s* |% u+ A/ G4 C0 z
文字层2,专门用于显示秒表数字。调色盘也是使用16色。- Q' q8 X w# p+ t* I/ Z
+ Z6 B+ @0 ]3 ?# x4 A为了出现比较好的效果,加上游戏片头。也算是为自己宣传了吧!游戏片头使用256色图片,256*256像素,使用FADE特效。
( P9 F& K5 b) M% }7 H: }: s" Z5 P/ i( a& E+ z( q1 h& S& U0 \
游戏标题也加上吧!使用马赛克特效。
& { q3 F; t' f5 E) e$ Z! D! b, B
+ Q2 e- [6 C& N1 c' n+ \$ B2 D背景音乐,毫无疑问得要啦!格式为MOD。
, u0 T9 X# V$ ~) @1 r& R
2 v' t% o9 H+ {3 C t程序开始。包含头文件,包含资源文件。& T6 o. v, S) Q, n9 j
' k: h& D" D0 g; F主程序,打开中断,然后直接进入死循环。所有的工作,都留给中断去做。' o+ l0 C) \! s, i8 `, T
" @. E8 H$ E5 o; }9 {: G由于中断要处理各种不同的函数,所以需要在程序中变换中断服务程序。有两种方法可以实现:函数指针和分支。' E0 X5 E2 k4 q5 ]: P+ s
+ j- n' Y/ m/ G# _& J函数指针方面,我没有学过,所以我也没办法讲解了。我就用分支来处理这种情况吧。顺便一说,用分支并不是一个好方法,如果游戏比较复杂,那么分支将会非常庞大;到时候恐怕连你自己都不明白到底是怎么回事了……
! P1 E% w3 b) F* Y' c
0 I2 W6 V7 V8 B- N, D! O所谓分支,就是通过一个游戏流程变量(我一般称之为“剧情变量”)来控制游戏当前状况。这个变量为全局变量,初值为0。中断服务程序就是通过这个变量的值,来选择不同的中断服务。就拿咱们这次的游戏为例,游戏刚开始,显然要显示你的LOGO,因此在中断服务中,case 0的时候就是载入LOGO的数据,然后等待同步。中断完成之后顺便让剧情变量增值一下,那么下一次中断来到的时候,执行的就是case 1的服务。很明显这个服务程序就该是FADE特效了……
! h3 a7 Z& l. W' `5 |- {8 V0 s; @9 ` Y$ P5 |( O6 D. ^- x
代码中出现了一些有趣的技巧,如果你不明白,不妨把你不明白的地方屏蔽掉,重新编译看看出现了什么效果,或许你会知道我为什么会这样做。
8 n" v% ~3 R, `- Y" P4 w T) H: i$ O8 ~; Z* M/ |' d& X& A
游戏中出现了一个明显的问题,就是背景音乐。背景音乐在标题菜单中播放效果非常好,但是一旦游戏开始,数字开始变化的时候,音乐明显出现“卡”的现象。这是怎么回事呢?这与我们写程序的方式有关系。我们的主程序中,什么都没有,所有的工作,都是交给了中断去做。中断,顾名思义就是中断主程序,转向服务程序。但是音乐正在播放,这个时候中断主程序而转向服务程序,音乐自然也被中断了。不过,由于服务程序非常短小,所以中断服务被迅速执行完毕然后退出来继续执行主程序,也就是继续播放音乐,所以,从宏观上看,就是音乐出现了一个短暂的中断,从而出现了“卡”的现象。' p) b7 M( l! f. O9 }
* E( Z: E+ Y8 k7 o6 K7 k9 B那么怎么解决这个问题呢?没有别的办法,重写程序,让主程序不要吃闲饭,尽量少占用中断,非得占用中断的话,尽量缩短中断的长度。如果非得大量占用中断的话,那就只有抛弃MOD PLAYER,使用MP2K了!
2 f# }. N2 x# s" ?" w! E$ q w! L& G* d: Q# `) ?; @& p$ @
程序没有优化,因此冗余代码很多。你要是有能力,优化一下会减小ROM体积。程序中还存在一些预留的入口,利用好了,可以做出“秘籍”、“隐藏要素”!当然,你也可以继续扩大程序,加上其它的功能。比如纪录通关的按键次数,然后用存档函数保存数据;新写一个界面,做成排行榜,然后利用排序函数做出排名……毛主席说过:人有多大胆,地有多大产!虽然这话有点唯心主义,但是可以确定的是,只要你有想法,只要你有能力,没有你实现不了的东西…… |
7 }' Z% T& X: s+ |5 f& @
) q3 K, z7 Z! x% l) x+ Q! T* {9 O7 I1 a7 }+ f! B
B. `3 g# _; h8 Q) m
|
|