2018-05-29

【進度】Cyber Sprite外語版 (上)


大約一個月來都在弄一代外語版,二代美術只好先暫停一下,讓負責美術的人先等等。
雖然翻譯有找到人幫忙,但外語版不是只把譯者寫好的文字key進去就完成了,還有很多技術問題要解,要修改一代的程式。

從這篇開始試著使用APNG來顯示動畫,目前常用瀏覽器裡Firefox、Chrome、Opera都可以支援APNG動畫,只有Edge不支援,不支援的瀏覽器只能看到靜態圖。
不過試了幾個放圖空間,blogspot、plurk不支援APNG,只有巴哈姆特有支援,而巴哈姆特的圖不能從外部連結,想看動畫就只能看巴哈姆特的版本。

如果有人上星期的PF想找我,得說聲抱歉,這場我沒報攤位,只有一般入場進去逛一下。

同時發在巴哈姆特



首先過去一年來找人的感覺是,會翻譯遊戲劇情的人不是很好找,並不是隨便一個從事翻譯的都可以勝任。

日譯原本就有認識一位就請他來做,不過他動作很慢,弄了一年多才全部做好,翻譯品質是可以,但下次要不要找他想考慮一下。
英譯第一個人翻了一小段就放棄,理由是「太宅了」。學到的教訓是雖然本作的程度沒有girl game那麼多,但還是要懂一點ACG文化才行,所以找人要特別挑有玩過電動的。之後找到的人才有做出來。

就算請專門的人來做,送來的稿件多少要親自看一遍再放進去。
這一步電子妖精能幫忙的只有搜尋文字,檢查內容只能由人類處理。

結果要改的地方還不少
  • 有些專門用語對方不一定懂,因為題材的關係用到一些計算機科學用語;本作有出Linux版所以系統需求有提到發行版和套件;還有貫通、碰撞判定等射擊遊戲用語。
  • 中文有些句子沒寫主詞,但英文必須寫主詞,就誤會原來意思了,例如「要小心一點 → I must be careful.」,但我原本的意思是You。
  • 日文說明書感覺有些句子有點冗長,印象中看過的遊戲說明書也沒有這樣寫,就請譯者再看一下。(日文的特性是有沒有使用漢字、敬語長度會差很多,表達同一個意思也有多種不同寫法)
  • 前面的還情有可原,但有些錯誤不知是粗心還是怎麼樣,像是data寫成date,button寫成bottom。

還好我懂一些英文和日文,可以把關,如果是我看不懂的文字就無法發現問題了,錯的東西會不知不覺上到正式版。



我的計劃是一個遊戲裡同時放中、英、日語,在Option可切換語言,除了下載版是一開始就有多語言以外,光碟版也做成patch之後就內含多種語言。不太想把各語言都分成一版,或讓光碟版和下載版內容不一樣,因為版本多我自己也不好維護。

先修改說明書,原本只有繁體中文,改成可以邊看邊切換語言。
格式是網頁檔,寫網頁當然是用這個程式語言了。
艾瑪:包在我身上。(呼叫部下)HTML、CSS,出動吧!

做法是用<select>做個語言選單,然後用Javascript寫事件,值改變的時候就載入另一個頁面。
然後初次開網頁的時候,會偵測瀏覽器的語言設定,決定最初顯示的語言。

(此圖本來是APNG,由於blogspot不支援APNG,在巴哈姆特才看得到動畫)


發現有些地方要重新排版才比較好讀,於是加上表格排版,字型也換過,配合遊戲內容換成無襯線字型。




再來是修改遊戲本體了。

鈷寶:呃……要用舊系統?
艾莉兒:不想回去用一代的樣子做事……,感覺好不自在,一堆主人幫我新做的功能都不能用。

Cyber Sprite 2把艾莉兒和鈷寶做了大改造,可以說幫她們打造了新機體,很多寫法已經和一代不一樣,這次回頭看也得花時間習慣。
看一看覺得有必要移植一些二代新做的東西到一代。

遊戲本體第一步:Linux的技術淘汰很快,現在的發行版已經不是製作一代時的版本了,在新版上run一下看看,用個以前沒用過的發行版Korora 26……

艾莉兒:報告,找不到libpng12.so.0和libjpeg.so.8,Korora 26的是libpng15.so.15和libjpeg.so.62。
艾莉兒:但Mint 18.2用的是libpng12.so.0和libjpeg.so.8……,為什麼檔名要變來變去,煩死了。
艾莉兒:看來這個要優先:Linux版讀圖檔改成用gdk-pixbuf。


以前直接用libpng和libjpeg,但這兩個函式庫常常不同發行版或新舊版之間檔名不一樣,造成一個版build的程式在另一版不能執行。所以二代改用gdk-pixbuf,這個應該比較不會改名。把讀圖檔的系統移植到一代。

鈷寶:那個……系統需求呢?
一代製作時設定成Windows XP和Ubuntu 11.04,但如果維持這個需求很多新方法都不能用。
發售已過了很多年,電腦科技也淘汰一輪,比照開發中的二代應該也沒關係了,改成Windows 7和Ubuntu 16.04。

第一階段,選難度、角色和關卡的畫面。
這裡的文字主要是事先做成圖檔,不是引擎的畫字功能。

附帶一提這種把很多小圖放在一張大圖裡的圖(sprite sheet),如何把貼圖坐標輸入程式裡的。
艾莉兒:我和鈷寶一起弄的,有一套特殊方法,不用主人一個一個key數字。
不是寫在程式而是放在圖檔裡,這樣做有它的好處,方法以後有空再介紹。



遊戲本體第二階段,關卡中對話
這裡要鈷寶出場了,開LibreOffice Calc,執行這個巨集:exportcsv
鈷寶:……。(低頭工作,把LibreOffice Calc的內容輸出到純文字檔)

在公司做過遊戲出外語版,所以做一代的時候就幫艾莉兒和鈷寶留了延伸空間(遊戲本體和編輯對話的工具都有考慮未來製作多語言版),這一步不難做。
附帶一提Cyber Sprite第二關其實就是以此為題材,用試算表軟體填資料再用外掛輸出成引擎能用的格式,這技巧是在公司學到的。

英文版有個問題要弄:換行不能從單字中間斷開,要在空白字元換行,一般顯示文字的軟體都有做這個處理。
艾莉兒的畫字功能還做不到這樣,想一下如何做,是自己檢查字串內容算位置,還是用作業系統內建的功能。

先試試看二代使用的DirectWrite效果如何,修改二代的程式來測試。
const char CHANGE_LINE_TEST[]="aabbcc ddee ffgghhii jjkkll mmnn";
引擎裡用ID2D1RenderTarget::DrawText()來畫字。
MSDN裡此函式的說明
結果

黑色是文字包圍矩形,紅色是背景,包圍矩形故意設窄一點強迫它換行。

再試試中英文夾雜
const char CHANGE_LINE_TEST[]="一二三aabbcc四五六ddee七八";

中文變亂碼了。
程式碼本身是UTF-8+BOM編碼,裡面的中文字串應該也是UTF-8,艾莉兒的畫字功能是設計成使用UTF-8字串,為什麼會無法顯示呢?
艾莉兒,你收到的字串長度是幾byte?
艾莉兒:26。
8個中文字+10個英文字,如果是UTF-8長度是8×3+10×1=34 bytes,ANSI的話是8×2+10×1=26 bytes,大概猜到問題在哪了。

打個比較短的字串
const char CHANGE_LINE_TEST[]="一二三";
你們看一下字碼是什麼。
艾莉兒:(看可執行檔內部)88 ea 93 f1 8e 4f。(用16進位表示)
鈷寶:(看程式碼)e4 b8 80 e4 ba 8c e4 b8 89。

看來Visual C++會先把程式碼從UTF-8轉換成ANSI再編譯,裡面的中文字串也順便被轉換,VC竟然有這個問題。
以前就有經驗,Windows用的編碼是ANSI和UTF-16,Linux用的是UTF-8和UTF-32,剛好錯開,所以想用程式顯示中文要很注意編碼,VC和GCC看來分別跟這兩個規則。
正式發佈的軟體不會把中文寫在程式裡而是讀外部檔案,影響不大,但測試的時候有點麻煩。
艾莉兒:看這個,好像有個/utf-8參數可以用,不過要VC2015才有,主人用的是VC2013。
VC編譯參數/utf-8的說明

想顯示中文字,方法一是字串裡用\x打內碼。
如果字碼後面緊接著英數字,compiler會以為是十六進位數字而無法編譯,英數字也要打內碼。
//必須這樣寫
const char CHANGE_LINE_TEST[]="\xe4\xb8\x80\xe4\xba\x8c\xe4\xb8\x89\x61\x61\x62\x62\x63\x63\xe5\x9b\x9b\xe4\xba\x94\xe5\x85\xad\xe4\xb8\x83\x64\x64\x65\x65\xe5\x85\xab";

//這樣寫無法編譯
const char CHANGE_LINE_TEST[]="\xe4\xb8\x80\xe4\xba\x8c\xe4\xb8\x89aabbcc\xe5\x9b\x9b\xe4\xba\x94\xe5\x85\xad\xe4\xb8\x83ddee\xe5\x85\xab";

方法二是字串宣告成WCHAR型態,執行時再轉換成UTF-8
const WCHAR CHANGE_LINE_TEST[]=L"一二三aabbcc四五六七ddee八";

……

const int utf16Len = wcslen(CHANGE_LINE_TEST);
int utf8Len = WideCharToMultiByte(CP_UTF8,0,CHANGE_LINE_TEST,utf16Len,NULL,0,0,0);
char* utf8Array = (char*)_alloca(utf8Len);
WideCharToMultiByte(CP_UTF8,0,CHANGE_LINE_TEST,utf16Len,utf8Array,utf8Len,0,0);

可以顯示中文,也不會從英文單字中間切開了。


原本想測試DirectWrite的換行功能,意外發現VC的一個問題。



回來繼續做英文換行。

艾莉兒:主人,別忘了打字機效果。
對哦,如果要用打字機效果寫But,送給引擎的指令不是「畫But」,而是「畫B→等待→畫u→等待→畫t→等待→畫空白」,換行要事先找出空白的位置,但畫B,u,t的時候引擎並不知道空白在哪裡。
(顯示對話不是瞬間顯示全部,而是一個字一個字出現的效果,在很多遊戲都常見,有個朋友把它叫做打字機效果)
也就是核心和邏輯層都要做一份,先研究核心怎麼做吧。

一代的畫字方法很難做到這個效果,想了一下決定把二代的畫字方法移植到一代。
更新艾莉兒的裝備
Windows版:Windows API的GetGlyphOutline() → DirectWrite
Linux版:freetype → cairo
再來要檢查何時該換行,上面試的ID2D1RenderTarget::DrawText()可自動換行但靈活度不足,所以我用另一個函式ID2D1RenderTarget::DrawGlyphRun()畫字,要自己處理換行。

研究規則並做進核心裡
(途中)
寫好了,畫這個字串看看:"aabb cc ddee ffgghhii jjkkll mmnn"
艾莉兒:好。
看來沒問題,再試這個:"\xe4\xb8\x80\xe4\xba\x8c\xe4\xb8\x89\x61\x61\…… (如上面所說,想測中文必須打內碼)
艾莉兒:畫好了,這樣行嗎?
有中文就錯了,看來這規則不對,改一下。

(數小時後)
研究出來了。檢查相鄰的兩個字元,也就是像「___XY___」的一串字元,滿足下面其中一項則X,Y中間可以換行。
1.X是中日韓文字
2.Y是中日韓文字
3.X是空白或-
其實詳細要查Unicode各個區段並分別定義哪些字元可換行,本遊戲上面的方法就夠用。這樣不算難,規則整理出來後對話部分也不成問題了。
現在不會在單字中間換行了。


不過中文另外有個規定「標點符號不能在一行的開頭」,這個目前還無法。

打字機效果還有一個地方要弄,因為英文字母寬度約當半個中文字,如果兩者用相同速率顯示,英文看起來會比較慢,所以如果一個frame畫一個中文字,英文就要每frame畫兩個字母。

(未完待續,下一步是處理字型和字大小的問題)



關於插圖:開頭的圖和頭像使用不同上色法,想熟練新型塗抹工具為主的上色法。

用一種新方法畫透視:透視尺

可用來求出消失點的位置,刻度當然不是隨便畫的,是用數學算出來的。

沒有留言 :

張貼留言