日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

关于低分辨率像素游戏下显示非防锯齿中文 / 汉字的研究

發布時間:2024/8/26 编程问答 56 豆豆
生活随笔 收集整理的這篇文章主要介紹了 关于低分辨率像素游戏下显示非防锯齿中文 / 汉字的研究 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

面臨的問題

像素游戲是獨立游戲的一種常用表現方式,在制作中文游戲時我們要面臨顯示點陣漢字的問題。當前各大游戲引擎中都會有顯示中文的功能,但顯示出來的中文字體效果一般都差強人意任意,在低分辨率的像素游戲畫面下會產生一些問題:
?

  • 默認的防鋸齒使得字體跟游戲畫面的整體風格不搭;
  • 關掉防鋸齒后,矢量的字庫渲染到低分辨率畫面上字型比較難看;
  • 為了實現在各設備上的統一效果,可以將字體嵌入到游戲中,但是一個中文字庫動輒十幾 M 的容量會消耗大量的資源空間,甚至超過游戲本體的容量大小,對于 HTML5 這樣的平臺更會增加下載時間。


令我困擾的是像 Construct 這樣的 HTML5 引擎在使用原生字體渲染的時候,無法把字號調整到最小(也許跟設備有關),這是我把字號設置為0.1pt 的情況:
?


很多 GBA 漢化游戲都做得很好,這就是我要追求的效果:
?


解決思路
?

  • 使用貼圖的形式來顯示漢字,把用到的漢字當作圖片存儲和顯示;
  • 漢字庫只存放常用漢字,或者說只存使用到的漢字庫;
  • 使用點陣漢字庫,而不是矢量漢字庫;


具體的實現

首先使用貼圖的形式會比直接渲染矢量的會更節省性能的消耗,大多數游戲引擎都提供了 Bitmap Font 或者 Sprite Font 這樣的使用貼圖來顯示字體的功能。實際上就是把所有可能用到的字符事先畫到一張貼圖上,需要時再逐個渲染出來。當然英文及類似的拼音文字系統所使用的字符數目比較少,所以在這方面比較省事,一張貼圖就能搞定??墒侵形目梢栽谝粡堎N圖內搞定么?讓我們來算一下,比如我們理想中的點陣漢字大小為 16x16 像素,那么在一張 1024x1024 的貼圖中,一共可以存放 4096 個漢字,太好了,因為我們所常用的漢字也就是 3500 個,你可以上網搜索到這3500個漢字的表。有了這個常用漢字表,我們就可以用這張表來生成貼圖。有些游戲引擎,比如 game maker studio 是內置了這樣的點陣字庫生成功能的。另外一些就需要借助一些工具。有不少可以生成字庫點陣貼圖,比較有名的是?BMFont。這些軟件可以讓你輸入要生成的字符表,選擇你想要的字體,設定生成的字體大小,還可以設定顏色和描邊的效果以及是不是防鋸齒等。這些軟件生成貼圖的同時會生成一個數據文件,這個數據文件會保存有常用漢字對應貼圖中的位置等信息,游戲在需要渲染點陣字體時可以使用這些數據來得到每個漢字對應的貼圖區域。這樣做還有一個額外的好處:可以預先疊加效果到字體上,比如描邊和漸變等,這樣也會省去處理這些效果時產生的性能消耗。

Construct 中默認的像素字體,西文的好處就是字符量很少,你甚至可以自己手寫設計,工作量不大:
?


比較常見的位圖字體生成工具 BMFont:
?


BMFont 使用微軟雅黑輸出16像素非防鋸齒的漢字,很不好看:
?


字體的選擇

現在有了工具,我們接下來要選擇使用什么樣的字體了。這個問題需要注意,因為大多數字體都不是免費的,特別是你要用在商業用途上,所以在選擇字體時一定要注意看準字體的版權聲明。中文可免費商用的字體其實并不多,其中最有名的是 Google 和 Adobe 開發的思源系列字體。不過我測試了一些的這樣免費商用的矢量字體,都普遍存在一個問題:這些字體并不是為了點陣顯示而制作的,在選擇比較小的字號同時關掉防鋸齒時,出來的效果是機器不美觀的。因為我當前追求的是低像素分辨率的畫面,所以這些字體并不能符合我的要求。

我要尋找在低字號大小無防鋸齒情況下都能表現良好的字體。回想一下,在 DOS 時代,我們的漢字字體都是點陣的,如果你現在搜索 HZK16 時可以搜索到不少信息的,但是關于以前 DOS 時代的這些漢字字體的版權,能夠查到的信息并不多。我們暫且把這個作為一個備選方案。另外,其實我們很多主機游戲的漢化都會涉及點陣漢字字體的問題,我的印象中不少 GBA/3DS 漢化游戲的字體都是處理得不錯的,當然因為是非商用,字體選擇可以很多。同時,雖然現在我們的大多數設備都可以渲染矢量字體,但還是有很多設備是需要顯示點陣的,比如各種 LCD 顯示屏。所以我覺得還是有針對點陣顯示設備設計的字體。我搜索到了“最像素”這個字體,這個字體似乎是一個人開發的,而且是專門為極小分辨率點陣顯示準備的。不過唯一的問題是,商業使用還是需要付費授權的。

DOS 時代,320x240 256 色是比較常見的分辨率,當時的中文處理是這樣的:
?


UCDOS:
?


WPS:
?


最像素字體:
?


當我在嘗試各種可以免費商用的字體時,我發現了“文泉驛”(http://wenq.org)這個開源的字體系列。里面竟然有一款專為點陣設計的宋體,字號從9像素到12像素,顯示效果非常的不錯,那么決定就是它了!


補充一下:文泉驛為 GPL 協議,商用需要作者授權,提醒大家注意~

輸出的問題

在一般情況下,二手手游出售平臺使用前面提到的 bmfont 這樣的軟件工具,以及文泉驛點陣宋體,已經可以解決大多數需求,只要你使用的游戲引擎支持使用的 bmfont 生成的數據文件就可以了。不過因為我用的是 Consturct ,一個 HTML5 游戲制作軟件,它支持使用的點陣貼圖要求每個字符是同等大小的,但是那些字體貼圖軟件大多數都不支持生成等寬的字體貼圖,或者是支持生成等寬字體貼圖的軟件有各種缺陷,比如貼圖大小不可控,無法關掉防鋸齒,不支持太多字符集等。

BMFont 輸出的字體都是不等寬的,也就是說輸出時要經過計算矯正:


有人專門做了給 Construct 用的工具,原先的問題是不支持太大的字符集,現在已經修正,現在唯一的問題就是沒有去掉防鋸齒的功能:
?


編寫工具

最后還是得自己動手做工具,既然在前面我們已經研究了這么多,生成一張這樣的貼圖對于做游戲的我們來說就不是什么難事了。我現在面臨的選擇就是用什么來做。本來我是很熟悉 Javascript 這一塊的,但是我所知道的 HTML5 相關的引擎都很難渲染出小字號的不帶防鋸齒的字體。那么用 Lua 呢?我以前用過一段時間 Love2D 感覺處理這樣的 2D 像素是比較好的,以前我還用它來制作過處理像素畫的軟件。但是問題是我沒發現它能夠渲染沒有防鋸齒的字體,可惜,而且 Love2D 還有一個缺點就是處于安全性的考慮,它只能寫入文件到一個固定的 sandbox 文件夾中,這樣做出來的工具使用上比較麻煩些。

最后,我開始考慮到我可以使用的另外一個腳本式語言:Python。如果這個還不行,我就只能考慮 Haxe 和 C 之類的了。說到 Python,我會熟悉 Python 主要是因為我使用 Blender ,使用 Python 可以讓我做一些插件擴展。所以以前我是考核過它的游戲制作能力的。它的最出名的游戲庫就是 Pygame(https://pygame.org),不過這個 Pygame 的確很 Old School,它是個 2D 引擎,有很多跟像素相關的功能,而且很多概念還停留在 Blit 位圖的層面上。不過我仔細看了一下它的最新版的文檔,發現它的字體處理應該可以滿足我的要求,因為我明確的看到了它可以關掉字體的防鋸齒渲染。所以決定就是它了!拿出 Python 書,臨時溫習一下,同時看下 Pygame 的文檔,很快我就做出了自己想要的工具,輸出了合適的位圖。

推薦使用 PyCharm ,用來寫 Python 體驗還是很好的:
?


收尾

在收尾工作中,我需要處理一些問題:

- 因為對話中也不免出現英文。這個時候會遇到一點小麻煩,因為中文基本都是等寬的,而英文每個字符有可能是不等寬的,如果我們按照漢字的方式來顯示每一個英文字母的話,會出現英文字符之間間隔過寬的問題,看起來就是不好看。不過 construct 是考慮到這個情況的,你只要輸出對應需要調整寬度的字符列表及其寬度就可以了。

沒有寬度矯正和有寬度矯正的西文字符的區別:
?


還有一個比較麻煩的問題就是英字其實是有基線的,在我們單獨輸出某一個小寫的英文字母時會失去基線的對齊,幸好 Pygame 里面是可以取得基線的信息的,輸出字母時調整這個高度即可。

所謂的基線就是紅線標的位置:
?


沒有考慮基線時輸出的西文小寫字符:
?


按照基線調整輸出的西文小寫字符:
?


如果不考慮基線輸出的話,結果會是這樣:
?


最后,在使用過程中還是出現缺字的情況。這主要是因為我們使用的某個漢字不在常用漢字列表里面,這個時候我們只需要在常用漢字表中加入這個字就可以了。其實我碰到的這個字是“哦”字,顯然人們的漢語表達用語也在不斷的變化中,現在的一些常用口語有可能并不在這個常用漢字表中。也許以后根據游戲的結構,我會考慮做一個只按照使用過的漢字生成貼圖的功能。

Python 源碼

  • import pygame? ?? ?? ?? ?? ?? ? # Pygame 游戲模塊
  • from pygame import freetype? ???# 處理矢量字庫的 Pygame 模塊
  • import codecs? ?? ?? ?? ?? ?? ? # 處理 unicode 所需模塊
  • import json? ?? ?? ?? ?? ?? ?? ?# 輸出 json 格式 所需模塊
  • # 等寬部分的字符表
  • fixWCharset = codecs.open( "hz3500.txt","r","utf-8" ).read()? ???# 讀取3500個常用漢字的表
  • fixWCharset = fixWCharset + u"哦"? ?? ?? ?? ?? ?? ?? ?? ?? ?? ? # 加入常用字中沒有的字
  • # 需要記錄寬度信息的字符表
  • varWCharset = codecs.open( "ascii.txt","r","utf-8" ).read()? ?? ?# 加入常用的 ascii 字符 表
  • varWCharset = varWCharset + u",。;“”、:?《》"? ?? ?? ?? ?? ???# 加入拳腳的漢字標點
  • gridW? ???= 14 # 每個字符輸出區域的寬度
  • gridH? ???= 14 # 每個字符輸出區域的高度
  • outColNum = 90 # 每行輸出的字符數
  • outRowNum = 42 # 一共輸出的行數
  • textureW??= gridW * outColNum # 最終輸出的貼圖寬度
  • textureH??= gridH * outRowNum # 最終輸出的貼圖高度
  • pygame.init()? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?# 初始化游戲引擎
  • pygame.display.set_caption("像素點陣漢字生成")? ?? ?? ?? ?? ???# 窗口的標題
  • screen = pygame.display.set_mode( (textureW, textureH) )? ?? ? # 打開的窗口大小
  • buffer = pygame.Surface( (textureW,textureH),pygame.SRCALPHA ) # 建立一個透明貼圖大小的緩沖區,貼圖先
  • # 因為非等寬字體還要需要處理基線的問題,所以同一個字體載入到兩個變量之中,可以進行不同的設置
  • fixWFont = pygame.freetype.Font( 'wenquanyi_9pt.pcf' ) # 等寬字符所用字體
  • varWFont = pygame.freetype.Font( 'wenquanyi_9pt.pcf' ) # 非等寬字體所用字體
  • # 關掉防鋸齒
  • fixWFont.antialiased = False
  • varWFont.antialiased = False
  • varWFont.origin = True # 使用基線方式渲染字體
  • varWFontSize? ? = 12? ?# 非等寬字體的固定輸出為 12 像素
  • baseLine? ?? ???= 10? ?# 設定從頂部往下 10 個像素為基線
  • x = 0 # 字符輸出的行坐標
  • y = 0 # 字符輸出的列坐標
  • fontColor? ? = ( 255,255,255 )? ? # 字體顏色
  • outlineColor = ( 0,0,0 )? ?? ?? ? # 描邊顏色
  • for i in range( 0, len(fixWCharset) ): # 遍歷常用漢字表
  • ? ? fx? ?= x * gridW # 字符輸出的像素坐標 x
  • ? ? fy? ?= y * gridH # 字符輸出的像素坐標 y
  • ? ? char = fixWCharset[i]
  • ? ? # 渲染字符描邊
  • ? ? fixWFont.render_to( buffer, (fx+1, fy+0), char, outlineColor )
  • ? ? fixWFont.render_to( buffer, (fx+1, fy+2), char, outlineColor )
  • ? ? fixWFont.render_to( buffer, (fx+0, fy+1), char, outlineColor )
  • ? ? fixWFont.render_to( buffer, (fx+2, fy+1), char, outlineColor )
  • ? ? # 渲染字符
  • ? ? fixWFont.render_to( buffer, (fx+1, fy+1), char, fontColor )
  • ? ? # 行列遞增
  • ? ? x = x + 1
  • ? ? if (x>=outColNum):
  • ? ?? ???x = 0
  • ? ?? ???y = y + 1
  • widthDict = {} # 記錄寬度的字典
  • for enIndex in range(0, len(varWCharset)):
  • ? ? fx = x * gridW # 字符輸出的像素坐標 x
  • ? ? fy = y * gridH # 字符輸出的像素坐標 y
  • ? ? char = varWCharset[enIndex]
  • ? ? # 渲染字符描邊
  • ? ? varWFont.render_to( buffer, (fx+1, baseLine+fy+0), char, outlineColor, size=varWFontSize )
  • ? ? varWFont.render_to( buffer, (fx+0, baseLine+fy+1), char, outlineColor, size=varWFontSize )
  • ? ? varWFont.render_to( buffer, (fx+2, baseLine+fy+1), char, outlineColor, size=varWFontSize )
  • ? ? varWFont.render_to( buffer, (fx+1, baseLine+fy+2), char, outlineColor, size=varWFontSize )
  • ? ? # 渲染字符
  • ? ? varWFont.render_to( buffer, (fx+1, baseLine + fy+1), char, fontColor, size=varWFontSize )
  • ? ? # 記錄字符寬度
  • ? ? m? ???=??varWFont.get_metrics( char, size=varWFontSize )
  • ? ? lineX = fx + m[0][1]
  • ? ? charW = m[0][1] + 3
  • ? ? if not charW in widthDict : widthDict[charW] = []
  • ? ? widthDict[charW].append( char )
  • ? ? # 行列遞增
  • ? ? x = x + 1
  • ? ? if ( x >= outColNum ):
  • ? ?? ???x = 0
  • ? ?? ???y = y + 1
  • # 輸出 construct 3 所需的寬度 json 文件
  • outputList = []
  • for wKey in widthDict:
  • ? ? charStr = ""
  • ? ? for char in widthDict[wKey] : charStr = charStr + char
  • ? ? outputList.append( [wKey,charStr] )
  • outJson = json.dumps( outputList )
  • print( "Json String For Construct : " )
  • print( outJson )
  • filename = "construct-spriteFont-spaceData.json"
  • file? ???= open( filename, "w" )
  • file.write( outJson )
  • file.close()
  • print( "saved to : " + filename )
  • # 輸出整體字符集文件
  • charset??= fixWCharset + varWCharset
  • filename = "charset.txt"
  • file? ???= codecs.open( filename, "w", "utf-8" )
  • file.write( charset )
  • file.close()
  • print( "charset saved to : " + filename )
  • # 保存貼圖文件
  • filename = "pixel-hz.png"
  • pygame.image.save( buffer, filename )
  • print( "texture saved to : " + filename )
  • # 主循環
  • running = True
  • while running:
  • ? ? # 在窗口中顯示貼圖
  • ? ? screen.blit( buffer, (0, 0) )
  • ? ? pygame.display.update()
  • ? ? for event in pygame.event.get():
  • ? ?? ???if event.type == pygame.QUIT:
  • ? ?? ?? ?? ?running = False
  • 復制代碼


    完成結果

    最終生成的貼圖,我還加入了常用的全角中文標點符號:
    ?


    在 Aseprite 中檢查,每個字都加上了黑色描邊:
    ?


    到此,對于在低分辨率像素游戲中使用點陣漢字的心得分享就這么多,希望對你有所幫助。

    補充一下:文泉驛為 GPL 協議,商用需要作者授權,提醒大家注意~

    總結

    以上是生活随笔為你收集整理的关于低分辨率像素游戏下显示非防锯齿中文 / 汉字的研究的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。