十九、11x11字体的拼字程序
这一部分来看一下如何采用拼字的方法来进行文字的显示。11x11的字体已经可以完美的显示绝大部分的汉字,并且通过拼字在相同的空间里可以多显示一半的文字,可以得到更好的游戏体验。
如果知道游戏中图像显示的原理就很容易理解拼字程序是如何来实现的了。比如这里是一个11x11的“保”字,它实际上是分成了四个Tile,每个Tile有8个字节,对应一个16进制的数值保存在Rom中

如果把这些16进制的数换算成二进制的数就一目了然了。

0对应背景,1对应黑点,所有的黑点就组成了文字。而对于一个11x11的字来说有很多空间是没有利用上的。我们拼字时,就是把每一个字节一分为二(二进制的高四位和低四位),等于把一个字从横向上分成4份,丢掉最后一份空白的,然后每两个字的前三部分重新组合,实现6个Tile显示2个字。
我们先导入合适的字体,字体选择SimSun,大小选择小四,其他设置如下,注意要确保字符在最左侧

下面就是程序大概的内容,主程序WriteMiddle包括4个部分:
WordLocation用于计算出所要显示的字符在字库中的位置。这个和之前的一样,可以直接借用。
接下来Write11x11是用来完成拼字操作的程序。
VRAMWrite是将拼完的字符对应的4个Tile写到VRAM中的程序。
MAPWrite将这些Tile写到MAP上从而可以在屏幕上显示对应的汉字。
注意后两个程序和之前的会稍有区别。
10:4100 WriteMiddle: push hl push de push bc push af call WordLocation call Write11x11 call VRAMWrite pop af pop bc pop de pop hl call MAPWrite ret |
在进行拼字操作时我们需要判断当前操作的是第一个字还是第二个字,我们把这个信息保存在DA02,同时拼字操作需要在WRAM中完成,要在WRAM中找一块足够大的未使用的区域,这里我们选择DA10到DA9F这一块。
10:4230 Write11x11: call WriteWord0 ld a,(DA02) cp a,1 jp z,WriteWord2 call WriteWord1 Write11end: ld hl,DA02 inc (hl) ret WriteWord2: ld hl,DA50 ld de,DA30 call WordMix call WriteWordM jr Write11end |
首先,调用程序WriteWord0把字符的4个Tile从Rom中读写到DA50这里
10:4260 WriteWord0: ld b,40 ld de,DA50 call TileRead ret |
这里面TileRead程序是负责把字符的Tile读出来的,因为要切换Bank,所以这个程序放在Bank 0。字库的Bank号与之前一样,直接将字库对应的编码减去20得到
0:3FA0 TileRead: ld a,(DA01) sub a,20 push hl call 2A21 pop hl call Readhl2de push hl call 2A30 pop hl ret |
Readhl2de程序根据b的循环值从hl读出文本信息到de,这里多个程序都会用到,因为也涉及到在不同的Bank上都会调用这个程序,所以也放在Bank 0
0:3F99 Readhl2de: ldi a,(hl) ld (de),a inc de dec b jr nz,Readhl2de ret |
当判断出是第一个字的时候(DA02初始值为0),就调用程序WriteWord1把字符信息从DA50中读写到DA10,因为第一个字不需要拼字,所以直接放到DA10这里,后面会直接调用程序写入VRAM。完成后会给DA02的值加1
10:4270 WriteWord1: ld b,40 ld de,DA10 ld hl,DA50 call Readhl2de ret |
如果判断出是第二个字的话,就执行WriteWord2对应的程序来进行拼字。
WordMix程序实现拼字,把hl的字符的前4位右移4位到低位和de的字节相加实现拼字,把hl的字符的后4位左移4位保存到hl用于后续操作。将DA50开始的编码与DA30这里的编码进行拼字操作,连续操作40次,刚好把后一个字与前一个字拼完
10:4290 WordMix: ld c,40 MixNext: ld a,(hl) ld b,a srl b srl b srl b srl b ld a,(de) add b ld (de),a ld a,(hl) ld b,a sla b sla b sla b sla b ld (hl),b inc hl inc de dec c jr nz,MixNext ret |
然后调用程序WriteWordM,把拼好的字放到DA10这里准备写入VRAM
10:4280 WriteWordM: ld b,40 ld de,DA10 ld hl,DA30 call Readhl2de ret |
写入VRAM的程序VRAMwrite也和之前略有不同,这里直接从DA10这里写40个字节到VRAM就可以了,写完把de保存到DA04、DA05
10:4300 VRAMwrite: call VRAMaddr ld b,40 ld hl,DA10 judgeVRAM: ld a,(FF00+41) and a,03 cp a,00 jr nz,judgeVRAM ldi a,(hl) ld (de),a inc de dec b jr nz,judgeVRAM ld a,d ld (DA04),a ld a,e ld (DA05),a ret |
计算VRAM中地址的VRAMaddr程序也不一样。因为第二个字的左半部分其实是和第一个字的右半部分重叠的,所以要多一步判断是否为第二个字的步骤。判断出是第二个字之后,VRAM地址要减20
10:4350 VRAMaddr: ld hl,DA04 ldi a,(hl) cp a,0 jr nz,VRAMaddread VRAMreset: ld a,C0 ldd (hl),a ld a,90 ldi (hl),a VRAMaddread: ld d,a ld e,(hl) ld a,(DA02) cp a,2 jr nz,VRAMend ld a,e sub 20 ld e,a jr nc,VRAMend dec d jr VRAMend VRAMend: ld a,d cp a,97 jr z,VRAMreset call VRAMTile ret |
这里的VRAM初始地址设置到了90C0就是为减20留下缓冲。这个游戏里VRAM空间足够大,没什么问题。如果VRAM空间比较小的话,就要另外考虑了。
计算Tile编号的程序VRAMTile还是和之前一样
10:4330 VRAMTile: ld a,d swap a and a,F0 ld b,a ld a,e swap a and a,0F add b ld (DA07),a ret |
接下来是将Tile输出到MAP的程序MAPwrite,,和之前的思路是一样。利用原程序的写Tile方式,de值控制MAP地址,0:3897用于写上半部分,0:389D用于写下半部分,e加1后写字的后半部分。文字的第一个Tile编号保存在DA07,开始导入字库时选择的是垂直重组,所以一个字的四个Tile是按照左上、左下、右上、右下存放在VRAM里的,写到MAP时也按照这个顺序来写
10:4400 MAPwrite: ld a,(DA07) push af call 3897 pop af inc a push af call 389D inc e pop af inc a push af call 3897 pop af inc a call 389D dec b call Reset2to0 ret |
区别是结束时对e值的操作。Reset2to0程序判断当前是第一个字还是第二个字。因为第二个字的左半部分和第一个字是要重叠的,所以如果是第一个字的话e值要减1。如果是第二个字的话,就把DA02归0,表示下一个会是第一个字。
10:4450 Reset2to0: ld a,(DA02) cp a,02 jr nz, ResetEnd xor a ld (DA02),a inc e ResetEnd: dec e ret |
到这里就可以看到,现在游戏中显示的字体就是我们想要的11x11字体拼字的效果。

但还是有些小问题,这是因为换行操作后,程序还是按照之前的字数来计算,实际上此时应该强制设定接下来的是第一个字。所以我们需要在读到1A这个换行操作后,把DA02归0。
通过追踪可以看到程序中读出1A会跳到0:35BF,在这里转出,给DA02赋值00,确保换行后始终是第一个字
0:35BF jp 3FC4 0:3FC4 call ResetDA02 call 3CF7 jp 35C2 |
程序ResetDA02用于将DA02归0, 把这个程序放在Bank 0,这样如果后面其他地方还要用的话可以直接调用
0:3FB3 ResetDA02: push af xor a ld (DA02),a pop af ret |
这样换行后字体就显示正常了。

采用类似的操作,对于等待输入控制符12也做相应修改,程序中读出12会跳到0:3511,在这里转出,将DA02和DA04都归0,确保按A键确认后始终是第一个字,且在VRAM中从头开始写入
0:3511 jp 3FCD 0:3FCD call 3FBA call 1ED1 jp 3514 |
程序ResetDA04用于将DA02,DA04归0
0:3FBA ResetDA04: push af xor a ld (DA04),a ld (DA02),a pop af ret |
到这里我们就完成了11x11拼字的程序,后面就会基于这个版本再去做其他部分的汉化。