[利用 Python 快速制作游戏字库](一)基础篇+分割字库图片
原始出处: Your life just a MOD
前言
其实早就打算写相关文章的,但因为抽不出时间还是一直没动笔。最近又想起来,然后翻开近半年前的草稿一看,居然一字未动。现在回想起来,当初定的目标似乎太遥远了,所以我打算把原来准备一次写完的内容拆成一小段一小段来写。这样有空就写一小段,似乎更实际一点。
在开始读这篇心得前,本人假定你对Python语言已有足够的了解,并已能写一些小程序;清楚游戏字库特别是图片类字库的原理、构造、作用,已做过或有充分准备可以制作一种字库。对于文章中出现的一些库和模块的功能,我会进行一定程度的讲解,更多需要你自行查阅。
准备
1、你需要安装python 2.7.x(目前我使用2.7.2),可以在这里找到:http://python.org/getit/
2、安装PIL(python的一个第三方图像处理库),当前最新版是1.1.7,点此下载,最新版本在此查看:http://www.pythonware.com/products/pil/
3、NVIDIA Texture Tools,可以将其他格式图片转换为dds格式,而且效率较高,还支持很多选项。但不单独提供,这是我提取出来的版本。点此下载
4、[可选]本人制作的字库生成器,你也可以使用其他类似软件代替,但本篇中提到字库生成器都以此为准。点此下载
5、urfFontReader(Urf字库生成器字体读取脚本)。很多例子都会用到,点此下载(记得点download)
转换bmp至png格式
字库生成的方法参考字库生成器的文章,这里不再详述。
由于字库生成器生成的bmp图片不方便接下来的操作,而且文件相对较大,所以推荐转换为png格式保存。png格式可以包含透明层,体积也很小,而且读取速度不慢。假设生成的图片为“1.bmp”,然后看下面的代码:
import Image #导入PIL的基本处理模块
#打开bmp文件并转换为RGBA模式
bmp = Image.open('1.bmp').convert('RGBA')
#用一个列表保存像素数据
rgba = []
#将所有像素用简单公式转换为灰度作为透明层,底图使用白色
for pix in bmp.getdata():
rgba.append((255, 255 ,255 ,int( pix[0]*0.299 + pix[1]*0.587 + pix[2]*0.114 )))
#保存修改
bmp.putdata(rgba)
bmp.save('1.png')
以上代码可以稍加修改,然后将此脚本作为一个命令行工具使用。
读取字体数据
我提供的urfFontReader可以很方便的读出保存在bin文件中的字体信息。这样就可以将相关数据转换为游戏需要的格式。下面通过分析该脚本的代码,初步了解二进制数据的处理方法。
#从struct模块引入必要函数
from struct import pack,unpack
def ReadUrfFontInfo(fontname):
#以二进制模式打开文件并读取
fl=open(fontname+'.bin','rb')
#解开文件头中的数据
img_w,img_h,nums,ch,stp1,stp2=unpack('6i',fl.read(24))
#解开所有字符数据
data = unpack('='+'H4i'*nums,fl.read(-1)) #+’='使之采用标准数据大小
#用一个字典保存字符数据,方便使用
db={}
#转换成字典
for i in xrange(nums):
c,w,h,x,y = data[i*5:i*5+5]
db[c]=(w,h,x,y)
#返回数据
return (img_w,img_h,nums,ch),db
整个脚本主要使用了struct模块的upack函数来解构二进制数据。因为python并不能直接处理二进制数据(虽然可以当作字符串来处理,当其他数据类型就另当别论了)。upack函数的第一个参数用来描述数据结构,第二个参数是以字符串形式保存的数据。因为我这个字库当初设计得很简单,所以只用到二中类型,分别是:“H”无符号短整数;“i”有符号整数。与C程序中相应类型对应。
关于struct模块的更详细信息可以在网上搜索相关例程,或者在python控制台用help函数查看内部文档,之后不再详述。
下面是一个从其他脚本调用urfFontReader的实例。
#直接导入所需函数
from urfFontReader import ReadUrfFontInfo
#使用该函数读取字体bin文件,返回值分别存入等号前的变量
(img_w,img_h,nums,ch),font_data = ReadUrfFontInfo(r'mydir\myfont')
#打印图片的宽、高,包含的字符数和字符默认高度
print """image : %dx%d
number of chars : %d
general height : %d"""%(img_w,img_h,nums,ch)
#打印所有字符数据(unicode代码、宽、高、坐标x、y)
for char in font_data:
w,h,x,y=font_data[char]
print "X"%char,x,y,w,h
转换数据为游戏所需格式
字库生成器为了通用性,所以设计上是输出部分关键数据,然后根据具体游戏再转换为对应格式。
以下以相对较为简单的Chrome4引擎为例。该引擎的字库使用纯文本定义,每个字符需要6个相关参数。具体如下:
Char( unicode值, 实际宽度, 左顶点x坐标, 左顶点y坐标, 右下顶点x坐标, 右下顶点y坐标)
根据上面的定义,有些数据在读取后可以直接使用,而有些则需要进行计算。因此得到以下代码:
from urfFontReader import ReadUrfFontInfo
(img_w,img_h,nums,ch),font_data = ReadUrfFontInfo(r'1')
#创建一个文件用于写入输出文件
fm = open('mid_28.fm','w')
#先写入游戏字库需要的基本信息
fm.write('''Name("mid")
MapWidth(%d)
MapHeight(%d)
FontHeight(%d)
'''%(img_w,img_h,ch))
#遍历所有数据,写入每个字符的定义
for char,(w,h,x,y) in font_data.iteritems():
fm.write("Char(%d, %d, %d, %d, %d, %d)\n"%(char, w, x, y, x+w, y+ch))
由于字典类型是没有顺序的,所以输出的文件会有点乱。但稍加改动就可以按unicode代码的顺序输出数据。
#先将所有项的列表保存到一个变量
data = font_data.items()
#对这个列表排序
data.sort()
#再通过排序后的列表输出
for char,(w,h,x,y) in data:
fm.write("Char(%d, %d, %d, %d, %d, %d)\n"%(char, w, x, y, x+w, y+ch))
以上介绍了一个相对简单的,保存为游戏可用格式的例子。在以后的文章中我会提供更多更复杂的例子。
分割字库图片
有很多情况需要分割字库图片。比如对于一些老显卡,可能不支持2048x2048以上的纹理大小,某些游戏只支持固定大小纹理等等。分割字库的思路很简单。先打开一个字库,然后将字符挨个复制到新大小的图片中,当装不下时就保存当前图片再新建另外一张图片。借助PIL库,很容易完成这个任务。
import Image
from urfFontReader import ReadUrfFontInfo
(img_w,img_h,nums,ch),font_data = ReadUrfFontInfo(r'1')
#设置分割成多大
max_w = 512 #目标宽度
max_h = 512 #目标高度
#打开我们的字库图片
img_in = Image.open('1.png')
img_idx = 0 #图片序号/计数器
cx = 0 #在目标图片中的x坐标
cy = 0 #在目标图片中的y坐标
#新建一张图像用于输出
img_out = Image.new("RGBA",(max_w,max_h))
#遍历所有数据进行分割
for char,(w,h,x,y) in font_data.iteritems():
#如果下一个字符粘贴后超出目标宽度则切换到下一行
if cx + w > max_w:
cy += ch #换行
#如果余下高度不足容纳一个字符则新建另一张图片
if cy + ch > max_h:
cy = 0 #y坐标归零
img_out.save('s
补充:Web开发 , Python ,