遊戲中經常要輸入漢字,但當我們遊戲沒有自己實作輸入法視窗時,windows會使用使用者安裝的輸入法,但這個輸入法視窗隻會顯示在遊戲視窗外頭,而且當我們遊戲全屏時(真全屏,不是那種視窗式的假全屏),螢幕上無法顯示其他程式的視窗,也就看不到任何輸入法視窗了。此時玩家輸入文字,無法看到候選詞清單,使用者輸入将會變的舉步維艱。是以一般網絡遊戲都會有一個遊戲内置的輸入法視窗。由于這個輸入法視窗式遊戲程式自己渲染的,不管全屏與否都會正常顯示。如何實作遊戲内置輸入法視窗呢?
通過查閱相關資料,以及個人實踐,我完成了一個教學版的輸入法視窗實作。實作遊戲内置輸入法視窗并不難,和遊戲相關的邏輯其實比較簡單,難點主要在熟悉Windows對于輸入法的消息管理,以及相關子產品的API。首先讓我們看一下Windows對于輸入法的一下消息及相關API。
WM_INPUTLANGCHANGE
根據字面意思,就是輸入語言切換消息。實際上就是當我們更換輸入法就會發出這個消息。我們通過監聽這個消息,來顯示隐藏遊戲内置輸入法視窗。因為當我們切換成Windows的【EN】輸入法時,我們是純英文輸入,無需任何輸入法視窗,是以這時我們要隐藏我們的輸入法視窗。如何判斷是不是【EN】輸入法呢?
ImmEscape()
此API可以擷取目前輸入法的一些基本資訊,如輸入法的名稱等。具體API的用法MSDN寫的十厘清楚了,這裡就不講解了。這裡需要注意【EN】輸入法是沒有名稱的,是以可以當使用 ImmEscape() 擷取輸入法名稱失敗時,我們就隐藏遊戲内置輸入法,因為之時表明使用者切換到了【EN】輸入法。
WM_IME_COMPOSITION
有IME( Input Method Edit )字首的消息都是輸入法消息。此消息表明輸入法混合狀态改變。什麼是混合?什麼是混合狀态?混合是指通過多個鍵盤按鍵組合生成一些其他語言字元過程。是以混合狀态可以認為是一些和混合相關記錄資訊,如輸入的拼音改變,輸入法候選詞清單改變。此消息是個消息大類,包含了很多子消息。多個子消息可以組合在一起,存放在消息的 lParam 參數中。我們可以通過 ( lParam & 子消息 )來判斷此消息中是否包含該子消息。下面看一下和我們相關的兩個子消息。
GCS_COMPSTR
此消息是輸入法“混合字元”改變的消息。什麼是混合字元呢?當我們用搜狗等中文輸入法的時候,我們打漢字都是有拼音,我們通過打拼音混合出漢字,拼字字元串就是混合字元。是以說 ni 是混合字元, nih 也是混合字元,由 ni 改變成 nih,表明使用者添加或者删除了拼音。混合字元對我們輸入法視窗是很有用的。你用搜狗輸入法打個字會發現,第一行顯示的就是混合字元。是以我們通過監聽這個消息,繪畫輸入法視窗中使用者輸入的拼音。
ImmGetCompositionStringW()
此API比較強大,通過傳入一些Flag,可以擷取輸入法的混合字元,以及混合之後的生成字元等。API在MSDN中講的比較清楚,而且後面我會附帶源碼,可以參考一下我是如何使用的(^-^)。
GCS_RESULTSTR
此消息是使用者通過輸入法生成了某些字元,這些字元需要輸入到對應的編輯框中。另外再說一點,CEGUI支援中文顯示之後,并不能通過輸入法輸入中文字元,這是因為CEGUI沒有監聽這個消息。是以在這個消息中,我們将把IME生成中文字元注入到CEGUI中( CEGUI::System::getSingleton().injectChar() )。擷取生成字元也是通過ImmGetCompositionStringW()擷取。
WM_IME_NOTIFY
此消息包括IME的各種通知消息,如候選詞清單改變,打開關閉,圓角半角切換等。暫時,這裡面我們比較關心的是候選詞清單的打開,改變,關閉消息。候選詞清單我們應該都知道,當我們輸入 ni 的時候搜狗輸入法會給出5個候選詞。我們需要從中選擇一個。候選詞清單也是輸入法視窗渲染中最重要的子產品。其中用到的API是:
ImmGetCandidateListW()
相關資訊查閱MSDN,參考一下我的用法可能會了解的快一點。其中最主要是一個以char [1]數組結尾結構體,一般用于變長結構體。它将候選詞的偏移量以及候選詞字元串全都放在結構體最後。
IME相關的Windows隻是我們大體已經了解了。現在介紹一下如果使用CEGUI實作這個教學班的輸入法視窗。讓我們分析一下:
首先,我們需要一個能顯示多項候選詞清單,以及混合字元串的控件。我們自定義控件當然可以,但是作為教學,我們盡可能的利用存在的控件。其實我覺得CEGUI::Listbox已經很合适了,我們将混合字元串放在清單框的最下層,然後将候選詞清單從上到下排列。
但直接使用清單框,不能很好的提供便利函數,比如設定候選詞清單,更改字元串,以及一些可能存在的輸入法視窗狀态。是以我覺得派生自清單框,服用清單框的LookNFeel,WindowRenderer。這樣我們需要做的隻是添加一個新的控件類。
基本上就這些了。下面是IME控件類的實作代碼:
GetIME()靜态函數是為了友善遊戲中隻有一個IME視窗。本想将IME控件弄成【單件模式】,但是這樣之後和CEGUI的【工廠模式】不相容。是以提供一個【單件接口】,建立和使用IME都使用這個接口。而不要使用CEGUI::WindowManager::createWindow()。
重載SetLookFeel(),是為了擷取清單框外框的一些尺寸,這樣才能正确的設定整個清單框的大小。
CEGUI::Listbox的視窗不會根據候選詞的寬度變化而變化,是以我們需要在設定候選詞以及混合字元串的時候手動的更改視窗的大小。
IME控件在出現在編輯框,或者多行編輯框的左上角。具體方法是:IME控件會監聽全局編輯框,多行編輯框激活,以及取消激活的消息,然後根據目前激活的編輯框設定IME視窗的位置。
我們隻另外提供兩個公用接口。是以IME控件使用起來非常簡單。
當然控件還需要在CEGUI控件工廠中注冊。
在 CEGUISystem.cpp 檔案的 void System::addStandardWindowFactories() 函數内添加如下代碼,并添加頭檔案包含:
IME控件建立完成,我們接下來看一下,如何根據Windows的IME消息來使用這個控件。
其中ImmGetContext()是擷取輸入法上下文句柄(即輸入法相關各種資訊的句柄),通過這個句柄才能拿到輸入法各種資訊。
其中ImmReleaseContext()是釋放上下文句柄。很多句柄都是要擷取并釋放(估計是将使用此句柄所做的更改進行儲存之類的)。
其他的API最好也在MSDN上查閱一下(英文閱讀能讓你擁有更多的知識來源),明白這些API,結合之前的講解,應該能明白這些代碼。
這樣之後我們程式中已經可以使用的IME視窗。但是還存在一個問題,就是當我們使用輸入法時,一些按鍵會通過 WM_KEYDOWN 消息進入CEGUI,幹擾IME控件的使用。如:當我們輸入nihaoa,IME控件顯示出了候選詞清單,但是我們想删除最後一個 a 字母拼音,我們按【Backspace】,結果不光候選詞改變,我們之前在編輯框輸入的一個漢字也被删除了。是以我們在顯示候選詞清單的時候需要屏蔽這些按鍵消息。方法如下:在【CEGUISystem.cpp】的【bool System::injectKeyDown(uint
key_code)】函數中添加三行判斷代碼:
這樣教學版IME視窗基本完成。下面是我的使用截圖:

如何疑問,敬請咨詢。