in XNA

XNA – 中文字(各國文字)

終究還是到了這一天…
RPG最不可或缺的文字部分,在怎樣都無法忽略掉…
這種時候,咱們偉大的文字,卻讓不少人止步…
究竟為何如此恐怖呢?
很簡單,用數字就能看得出來了!
英文Game,只需要讀取A~z + 一些基本標點符號,頂多兩百多個字元。
中文Game,隨便數數也有一萬字,日文、韓文等等語系也差不多。
10000 和 200 這個就算不用懂程式也知道差了多少事情要做。

何況,即使在中文Game裡面還是會用到英文字,所以英文的部分我們也不能忽略,嘖嘖…
從DX到現在的XNA,都一直存在著這種困境。
真希望有官方解決的方法哪~

XNA 各國語言文字顯示!!
裡面提到了許多方式,其中和XNA有關聯的就是使用SpriteFont
事實上它就只是一個Xml檔,記錄著一些訊息。
詳細請看上面的文章吧~
它裡面也提到,在編譯的過程十分浪費,更甭說Runtime時佔用的空間了。
所以就甭談啦~

當然,SpriteFont是可以自己選擇要匯入的編碼範圍,也就是把需要的才匯進去。
哎呀~還要檢視整個遊戲需要的文字,多麼麻煩哪~?
teexit1224大大還特別做了一個分析程式,真是太有愛了XDD
XNA Font Convert」可以試試看~

再來就是在各種PC可用的範疇都可見到的方式,使用GDI+畫成一張Texture,在貼上去。
這個方法雖然常見,但耗效能也是無與倫比的…
至於有多耗,嗯~用想的就很耗XD
而且,PC限定…這是我最不想用的原因

最後就是上面那篇回文中,pupu大大提供的方式。
事先把文字畫到紋理圖上,再依照需求分別畫到畫面上。
這個就沒有PC限定的問題,而且看起來有趣多了
其實這個方法也廣為流傳,只是一直不敢嘗試罷了…
這次使用的就是這種方法!


計算Font的Size

此方法有個比較重要的問題,那就是文字大小的計算。
「新細明體,大小為14pt」,但它的長寬並非14個pixel,更甭說英文那類的。
「標楷體,大小為14pt」,但它的實際大小卻又和前者不同…
關於字型的設計單位換算,可參考「HOW TO:取得字型度量資訊」。
確實可以準確算出「中文」部分…其他有些稍微不協調的字型,有些英文或符號就會卡到…

一些比較「正」的文字就蠻準確的…

但是只有高度的部分啊!寬度卻沒提到 囧…
後來找了一些方法,比較簡便的方式就是利用Graphics.MeasureString來測量。
使用方式很簡單…

// 取得 Graphics
Graphics g = Graphics.FromHwnd(this.Handle);
// 先取得一個字的大小
SizeF strSize1 = g.MeasureString("一", font);
// 接著取得兩個字的大小
SizeF strSize2 = g.MeasureString("一二", font);
// 相減得字元寬度
int charWidth = (int)(strSize1.Width – strSize2.Width);

之所以要用 2 – 1 的方式,是因為它測量(和繪製)的時候會預留一點點空間。
基本上,高度也是可以用這方法,只是會比上面的公式來得大一些。
以「標楷體、14pt」來說,測量後的大小約為19*19。


繪製文字紋理圖

將pupu大大的code整理一下:

 // exportPicNum – 輸出的張數
 //
colNum – 每張圖裡橫向的字數
 //
rowNum – 每張圖裡的縱向字數
 // realSize – 測量後的字體大小
 //
drawOffset – 繪製字元時的偏移值
 // fontColor – 字體的顏色
 //
beginChar – 一開始為起始字元,而後用來做為走訪用
 //
targetStr – 字元轉成 string 的暫存

 for (int exportNum = 0; exportNum < exportPicNum; exportNum++)
 {
     Bitmap tmBmp = new Bitmap((int)(colNum * realSize.Width), (int)(colNum * realSize.Height));
     Graphics picture = System.Drawing.Graphics.FromImage(tmBmp);
     for (int i = 0; i < rowNum; i++)
     {

        
for (int j = 0; j < colNum; j++)
         {
             PointF point = new PointF(j * realSize.Width – drawOffset.X, i * realSize.Height – drawOffset.Y);
             picture.DrawString(targetStr, font, new SolidBrush(fontColor), point);
             beginChar++;
             targetStr = beginChar.ToString();
         }
     }

     picture.Dispose();
     tmBmp.Save(fileName + exportNum.ToString() + ".PNG");
 }

變數名稱我自己改過,不是很漂亮的命名方式,勉強看看吧

利用它的code寫了一個小程式,有興趣可以玩玩,看到一堆字會莫名的興奮呢XDD
FontBitmap by NaCl

工具介面



以上是設定的部分。


記得改檔名~
都設定好之後就按「輸出」即可。


開始繪製之前,會先跳出一個視窗跟你說字體大小。
結束後會跳出個視窗通知你。
以上!


在XNA繪製文字

終於來到的重頭戲,要到XNA實行啦~
請確認你已經備妥文字紋理圖,不管自製或者用上面的小工具都可。
這部份繪製2D紋理圖的基本觀念就不再贅述
如果你也是2D3D同時存在的話,繪製文字的時間點就和一般2D圖形相同。
希望你也知道如何繪製紋理圖上的某個區塊。

 public void DrawChar(SpriteBatch spriteBatch, char c, Vector2 position, Color color)
        {
            // 轉換成int
            // 事實上還要扣除掉起始字元,目前為了方便一律從'\u0000'開始
            int offset = (int)c;
            // 計算出字元位在第幾張圖
            int picIndex = offset / this.mGridPreTexture;
            // 計算出在圖中的位移量
            offset -= picIndex * this.mGridPreTexture;

            // 取得字元的 rectangle
            Rectangle rect = new Rectangle((offset % this.mTextureGrid.Width) * this.mFontSize.Width,
                (offset / this.mTextureGrid.Height) * this.mFontSize.Height,
                this.mFontSize.Width,
                this.mFontSize.Height
                );

            // 繪製
            spriteBatch.Draw(this.mFontTexture[picIndex], position, rect, color);
        }

基本上就是這樣,剩下的就看你如何去應用了O_O+
嗯~
強烈建議除非必要,不然盡可能避免即時繪製(Draw時期)。
目前想到的方式就是在邏輯運算(Update時期)的時候,預先將需要的文字畫好。
當然,在Update不斷的重畫也只是換湯不換藥。
簡單來說,該畫的時候再畫囉!
基本上,大部分的文字都是靜態的,所以,甚至可以在初始化的時候就繪製。
這就讓我想到Render to Texture的方式,預先畫成一張紋理圖,執行時就不斷繪製那張圖即可。
Render to Texture又是一們難搞的部分,有機會再說了~

倘若採取即時繪製,一串長度大約20~30的字串,每次繪圖
都畫上個200次左右,FPS就開始脫離60了…
剛才完成初步的事先繪製方式,兩串加起來長度約40~50的字串,要到700次左右才開始脫離60!
※當執行夠快時,XNA會把FPS鎖在60左右。


(下面的「Press Any Key」是圖片,別想太多XDD)

說真的,比想像中簡單多了…
剩下的美化問題的確也不簡單就是。
考慮是否要和SpriteFont混搭使用,效果應該會不錯~
這篇就暫且告一段落了

※程式碼部分的HeightLight版本

–End

發表迴響

在下方填入你的資料或按右方圖示以社群網站登入:

WordPress.com 標誌

您的留言將使用 WordPress.com 帳號。 登出 /  變更 )

Google photo

您的留言將使用 Google 帳號。 登出 /  變更 )

Twitter picture

您的留言將使用 Twitter 帳號。 登出 /  變更 )

Facebook照片

您的留言將使用 Facebook 帳號。 登出 /  變更 )

連結到 %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.