本文将介绍几款推荐的中文字体,并且分享如何尽量减少字体文件的大小。
昨天我尝试试用一款名叫 Love 的游戏引擎,发现它没有内置任何中文字体,因此游戏中的中文字符会出现乱码。此外,它也无法正确加载 Mac操作系统中的字体,于是我把系统目录中的一个字体文件复制到游戏目录下,解决了问题。
但是,这个字体文件太大了, 有 67M。于是我马上想到了,游戏中只用到一小部分字体和字号,那我是不是可以压缩字体文件?结果果然找到了不少工具。另外,我觉得系统默认字体比较单调,就这网上搜索了可免费商用且无需授权的字体,分享给大家。
可免费商用且无需授权的 7 种中文字体
(精简版,只保留 3500 常用字)
来自 Github项目:https://github.com/DeronW/minify-font
字体文件压缩思路
字体文件中,除了只保留常用字之外,还可以进一步压缩:只保留游戏中遇到的字。这里我用到了一个工具:
https://ecomfe.github.io/fontmin/
Fontmin
第一个纯 JavaScript 字体子集化方案
我在项目中处理字体,经过了三步:
1. 选择一款ttf 格式的字体,并把字体文件放在自己游戏项目中的 font 目录
2. 编写一个Python 脚本(extract_chars.py),用于提取代码中遇到的所有字符
#!/usr/bin/env python3# -*- coding: utf-8 -*-"""字符提取工具 - Python版本用于从项目中的Lua文件提取所有字符,并直接打印到标准输出日志信息输出到标准错误,方便重定向输出空白字符(空格、换行、制表符等)会被排除"""
import osimport sysimport codecs
# 颜色设置(用于彩色输出)class Colors: GREEN = '\033[92m' YELLOW = '\033[93m' RED = '\033[91m' END = '\033[0m'
def print_color(text, color): """带颜色打印文本到标准错误""" print(f"{color}{text}{Colors.END}", file=sys.stderr)
def log(text): """输出日志到标准错误""" print(text, file=sys.stderr)
def find_lua_files(directory="src"): """查找指定目录下的所有Lua文件,不包括tools目录""" print_color(f"查找{directory}目录中的Lua文件...", Colors.YELLOW)
lua_files = [] for root, _, files in os.walk(directory): # 跳过tools目录 if "tools" in root.split(os.sep): continue
for file in files: if file.endswith(".lua"): lua_files.append(os.path.join(root, file))
# 如果src目录下没有找到文件,尝试在根目录查找 if len(lua_files) == 0 and directory == "src": print_color("src目录下未找到Lua文件,尝试在项目根目录查找...", Colors.YELLOW) return find_lua_files(".")
# 排序文件列表 lua_files.sort()
print_color(f"在{directory}目录中找到 {len(lua_files)} 个Lua文件", Colors.GREEN)
return lua_files
def read_file(filename): """读取文件内容,处理编码问题""" try: # 尝试用UTF-8读取 with codecs.open(filename, "r", "utf-8") as f: return f.read() except UnicodeDecodeError: try: # 尝试用GBK读取(针对中文Windows系统) with codecs.open(filename, "r", "gbk") as f: return f.read() except: try: # 尝试latin-1编码(应该能读取任何文件,但可能解码不正确) with codecs.open(filename, "r", "latin-1") as f: return f.read() except Exception as e: print_color(f"无法读取文件 {filename}: {e}", Colors.RED) return ""
def is_chinese(char): """判断一个字符是否是中文字符""" # 检查是否是中文Unicode范围 if '\u4e00' <= char <= '\u9fff': return True # 检查是否是中文标点符号范围 if '\u3000' <= char <= '\u303f' or '\uff00' <= char <= '\uffef': return True return False
def extract_characters(lua_files): """从Lua文件中提取所有字符和中文字符""" print_color("开始提取字符...", Colors.YELLOW)
all_chars = set() chinese_chars = set() whitespacesCount = 0
for file in lua_files: content = read_file(file) if not content: continue
log(f"处理文件: {file} ({len(content)} 字节)")
# 将文件内容拆分为单个字符并分类 for char in content: # 跳过空白字符(空格、换行符、制表符等) if char.isspace(): whitespacesCount += 1 continue
all_chars.add(char) if is_chinese(char): chinese_chars.add(char)
# 转换为排序后的列表 all_chars_list = sorted(list(all_chars)) chinese_chars_list = sorted(list(chinese_chars))
print_color(f"共找到 {len(chinese_chars_list)} 个中文字符", Colors.GREEN) print_color(f"共找到 {len(all_chars_list)} 个非空白字符", Colors.GREEN) print_color(f"过滤掉 {whitespacesCount} 个空白字符", Colors.YELLOW)
return chinese_chars_list, all_chars_list
def main(): """主函数""" print_color("===== 字符提取工具 - Python版 =====", Colors.GREEN)
# 查找Lua文件 lua_files = find_lua_files()
if not lua_files: print_color("未找到Lua文件,程序结束", Colors.RED) return
# 提取字符 chinese_chars, all_chars = extract_characters(lua_files)
# 只将所有字符直接写入标准输出(无任何额外文本) all_chars_str = "".join(all_chars) sys.stdout.write(all_chars_str) # 不添加换行,保持输出内容的纯净性
print_color("\n===== 字符提取工具运行完成 =====", Colors.GREEN)
if __name__ == "__main__": main()
3. 压缩字体,并在代码中引用。
压缩字体需要先安装fontmin工具:
npm install -g fontmin
然后使用以下命令压缩字体(将提取的字符直接传递给fontmin):
text=`python3 tools/extract_chars.py` && fontmin -t "$text" fonts/ fonts-compressed/
这个命令会:
运行字符提取工具,获取所有使用的字符
从fonts/目录中读取字体文件
创建只包含游戏中使用字符的子集字体
将子集字体保存到fonts-compressed/目录
子集化后的字体大小通常只有原始字体的几分之一,极大地减小了游戏的体积。
领取专属 10元无门槛券
私享最新 技术干货