[利用 Python 快速制作游戏字库](一)基础篇+分割字库图片

标签: 利用 python 制作 | 发表时间:2011-10-21 22:44 | 作者:(author unknown) cong
出处:http://simple-is-better.com/

前言

其实早就打算写相关文章的,但因为抽不出时间还是一直没动笔。最近又想起来,然后翻开近半年前的草稿一看,居然一字未动。现在回想起来,当初定的目标似乎太遥远了,所以我打算把原来准备一次写完的内容拆成一小段一小段来写。这样有空就写一小段,似乎更实际一点。

在开始读这篇心得前,本人假定你对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_d.png'%img_idx) #保存当前图片
            img_out = Image.new("RGBA",(max_w,max_h)) #新建另一张图片
            img_idx += 1 #图片序号+1
        cx = 0 #x坐标归零
    #从原图复制指定字符到新图片(这里特意分为两步以便看得更清楚,实际只需一步)
    box = img_in.crop((x,y,x+w,y+ch)) #复制原图的指定区域
    img_out.paste(box,(cx,cy)) #粘贴到新图的当前位置
    #这里可以插入保存字符信息的代码
    #…
    #位置向前移动一个字符
    cx += w
#结束后保存未保存的图片
img_out.save('s_d.png'%img_idx)

 

 

这里所用的 crop 方法需要提供一个“box”参数,这个参数实际是一个列表,里面需要依序提供矩形的左上顶点和右下顶点的x、y坐标,然后该方法返回一个image对象,里面保存着截取的内容,相当于复制图片中的选定区域。然后再用 paste 方法粘贴到指定位置。更多信息请参考PIL文档的imgae模块部分:http://www.pythonware.com/library/pil/handbook/image.htm

以上只是最基本的字库图片分割方法。有时实际要求要复杂得多,比如我要每个字符上下左右都留4像素的边要如何做呢?对此,我想我是没义务多做解释的是吧?

将图片转换为DDS格式

DDS是许多DX游戏常用的纹理格式。NVIDIA Texture Tools 可以将多数格式的图片转换为DDS格式。虽然python也有办法保存DDS格式,但目前的脚本压缩速度都实在太慢。

转换DDS主要用到的是nvcompress这个工具。用法如下:

$ nvcompress [options] infile [outfile]

详细参数表参考“NVIDIA_Texture_Tools_README.txt”。

这里介绍一下这个工具与脚本结合的方法。这是一个命令行工具,所以最简单的方法就是直接命令行调用。

以分割图片的代码为例:

import os #导入os库
……
#新建函数SaveDDS
def SaveDDS():
    img_out.save('tmp.png') #保存一个临时文件
    os.system(r'..\nvtt\nvcompress.exe -nocuda -nomips -bc3 tmp.png s_d.dds'%img_idx) #通过命令行调用nvcompress,注意路径
    os.remove('tmp.png') #最后删除临时文件
……
#在需要保存图片的位置直接调用这个函数
SaveDDS()

这里保存的是一个普通的带透明层的dds图片。而不同游戏又有不同要求,可能需要你多加注意。

写在后面的话

之所以要使用脚本,其目的主要是为了快速实现我们的想法,方便地开发我们需要的工具。而执行效率并不是我们的主要目的,这点一定要记住。如果能兼顾效率的话当然最好,但如果为了追求效率而减慢了开发速度那就失去了使用脚本的意义了。

# 来源:Your life just a MOD


在微博上关注: 新浪, 腾讯   投稿

最新招聘

更多>>

相关 [利用 python 制作] 推荐:

[利用 Python 快速制作游戏字库](一)基础篇+分割字库图片

- cong - python.cn(jobs, news)
其实早就打算写相关文章的,但因为抽不出时间还是一直没动笔. 最近又想起来,然后翻开近半年前的草稿一看,居然一字未动. 现在回想起来,当初定的目标似乎太遥远了,所以我打算把原来准备一次写完的内容拆成一小段一小段来写. 这样有空就写一小段,似乎更实际一点. 在开始读这篇心得前,本人假定你对Python语言已有足够的了解,并已能写一些小程序;清楚游戏字库特别是图片类字库的原理、构造、作用,已做过或有充分准备可以制作一种字库.

利用python打造自己的人脸识别系统 - 简书

- -
正像著名物理学家,理查德•费曼说的一样,如果要真正理解一个东西,我们必须要能够把它创造出来. 动手去做,永远比被动地听有用,我就是这么想并这么实践的. 本文介绍了我自己动手做的一种基于卷积神经网络的人脸识别系统,以python为语言基础,综合应用了keras、opencv、numpy、sklearn等多种技术.

Python 3 利用 Dlib 实现摄像头实时人脸识别 - coneypo - 博客园

- -
  利用 Python 开发,借助 Dlib 库捕获摄像头中的人脸,提取人脸特征,通过计算特征值之间的欧氏距离,来和预存的人脸特征进行对比,判断是否匹配,达到人脸识别的目的;.   可以从摄像头中抠取人脸图片存储到本地,然后提取构建预设人脸特征;.   根据抠取的 / 已有的同一个人多张人脸图片提取 128D 特征值,然后计算该人的 128D 特征均值;.

dropbox讲python

- chuang - Initiative
dropbox定制优化CPython虚拟机,自己搞了个malloc调度算法. 那个 !!!111cos(0). 期待这次PyCon China 2011.

Python调试

- - 企业架构 - ITeye博客
原文地址: http://blog.csdn.net/xuyuefei1988/article/details/19399137. 1、下面网上收罗的资料初学者应该够用了,但对比IBM的Python 代码调试技巧:. IBM:包括 pdb 模块、利用 PyDev 和 Eclipse 集成进行调试、PyCharm 以及 Debug 日志进行调试:.

Python WSGI 初探

- - 坚实的幻想
在构建 Web 应用时,通常会有 Web Server 和 Application Server 两种角色. 其中 Web Server 主要负责接受来自用户的请求,解析 HTTP 协议,并将请求转发给 Application Server,Application Server 主要负责处理用户的请求,并将处理的结果返回给 Web Server,最终 Web Server 将结果返回给用户.

Python实现逻辑回归(Logistic Regression in Python)

- - 神刀安全网
Logistic Regression in Python ,作了中文翻译,并相应补充了一些内容. 本文并不研究逻辑回归具体算法实现,而是使用了一些算法库,旨在帮助需要用Python来做逻辑回归的训练和预测的读者快速上手. 逻辑回归是一项可用于预测二分类结果(binary outcome)的统计技术,广泛应用于金融、医学、犯罪学和其他社会科学中.

python 下载文件

- Eric - python相关的python 教程和python 下载你可以在老王python里寻觅
之前给大家分享的python 多线程抓取网页,我觉的大家看了以后,应该会对python 抓取网页有个很好的认识,不过这个只能用python 来抓取到网页的源代码,如果你想用做python 下载文件的话,上面的可能就不适合你了,最近我在用python 做文件下载的时候就遇到这个问题了,不过最终得以解决,为了让大家以后碰过这个问题有更好的解决办法,我把代码发出来:.

python代码调试

- - 阿里古古
【转自: http://blog.csdn.net/luckeryin/article/details/4477233】. 本文讨论在没有方便的IDE工具可用的情况下,使用pdb调试python程序. 例如,有模拟税收计算的程序:. debug_demo函数计算4500的入账所需的税收. 在需要插入断点的地方,加入红色部分代码:如果_DEBUG值为True,则在该处开始调试(加入_DEBUG的原因是为了方便打开/关闭调试).

python编程规范

- - 互联网 - ITeye博客
@FileName: @Author:xx@ic.net.cn @Create date: @description:用一行文字概述模块或脚本,用句号结尾. 不影响编码的效率,不与大众习惯冲突.. 使代码的逻辑更清晰,更易于理解..   *所有的 Python 脚本文件都应在文件头标上如下标识或其兼容格式的标识.