Cocos 中的腳本綁定

語言綁定技術

綁定技術是指在目標語言中創造一套簡單的 API 介面並將它指向另一種語言中的實際實現。往往其功能的實現非常龐大複雜,可能是目標語言所無法承載的,但是通過這種方式可以使目標語言具備使用這些功能的能力。

Cocos中的腳本綁定

早在2012年,時任 Zygna 工程師的 Rolando Abarca 主導開發了一套基於 Cocos2d-x 和 SpiderMonkey 的 JavaScript 自動綁定技術。這套綁定技術將 Cocos2d-x 的 C++ API 暴露到了JavaScript 語言中,使得開發者可以使用 JavaScript 來編寫遊戲代碼,並享受 C++ 底層引擎框架高性能。後來 Cocos2d-x 團隊的成員用同樣的框架導出了 Lua API,完成了 C++ 到 Lua 的綁定。

這也是目前 Cocos 中腳本綁定技術的根基,由 Cocos 團隊一直維護至今。目前 Cocos 的三種語言支持 C++/Lua/JS,其中的 Lua 和 JS 就是腳本綁定技術的成果。

綁定基本框架

讓我們來先看一下 Cocos 引擎腳本綁定技術的框架圖:

可以看到 JS 綁定和 Lua 綁定技術的結構是一致的,讓我們自底向上一層一層解析:

  1. 首先在最底層的是 Cocos2d-x 的 C++ 實現,綁定技術歸根結底只是將其實現的功能暴露到對應的語言層。

  2. 往上一層是 JavaScript 和 Lua 的腳本引擎,其中 JS 綁定使用的是 SpiderMonkey 作為JavaScript引擎。SpiderMonkey 是由 Mozilla 維護的世界上第一個 JavaScript 引擎,也是目前 JavaScript 執行性能最優秀的引擎之一。

  3. 腳本引擎這一層提供了兩個重要的能力:首先當然是執行腳本的能力,但是最重要的是它還提供了對腳本層進行訪問的 C API。第二個能力成為了 C++ 引擎到腳本層的橋樑,讓 C++ 可以截獲腳本層的 API 調用,訪問腳本環境中的變數,構造新的對象,主動調用腳本層的函數等。

  4. 在腳本引擎提供的 C API 基礎之上,Cocos 引擎中通過兩種方式將 C++ API 暴露到腳本層:自動綁定和手動綁定。同時,使用純腳本實現來改造一些 API 讓其更適合腳本程序員的習慣,並且提供一些不需要綁定的 API。這三者共同構成了腳本層 API。

  5. 有了腳本層的 API,開發者們自然就順理成章得可以使用腳本來開發 Cocos 遊戲了。

關於自動綁定

自動綁定技術是 Cocos 引擎最引以為傲的利器之一,為什麼說它是 Cocos 腳本綁定技術的根基呢?綁定本身並不困難,有了腳本引擎和它的 API 就可以完成,真正困難在於以下幾點:

  1. 如何將 Cocos 中如此巨量的 C++ API 全部綁定出來?
  2. 因為 Cocos 的版本迭代所帶來的變化勢必會影響到其綁定代碼,那麼如何保證綁定代碼的可維護性?
  3. 如何讓一個不懂得綁定技術的人也可以為他所做的功能提供腳本綁定?

正因為這些困難,自動化的綁定技術才真正讓 Cocos 的腳本綁定成為現實。目前 Cocos 內部主要的綁定代碼都是由自動綁定工具生成的,並且版本迭代當中,都會根據代碼修改自動更新自動綁定代碼。除此之外,用戶也可以自由使用自動綁定工具綁定自己編寫的 C++ 類。

Cocos 中的自動綁定技術基於 Bindings Generator 工具,配合上 C++ 類配置信息,通過掃描 C++ 類的頭文件,Bindings Generator 就可以生成所有公有 API 的腳本綁定代碼。

關於手動綁定

雖然 Bindings Generator 已經非常強大,但是它依然有其局限性。目前的 Bindings Generator 的局限性主要是以下的幾點:

  1. 只能夠針對類生成綁定,不可以綁定結構體,獨立函數等
  2. 不能夠生成 Delegate 類型的 API,因為腳本中的對象是無法繼承 C++ 中的 Delegate 類並重寫其中的 Delegate 函數的。
  3. 子類中重寫了父類的 API 的同時,又重載了這個 API。
  4. 部分 API 實現內容並沒有完全體現在其 API 定義中。
  5. 在運行時由 C++ 主動調用的 API。

對於這些類型的 API,我們就需要通過手動編寫綁定代碼來將這些 API 綁定到腳本層。手動綁定出來的綁定代碼的基本原理和自動綁定一致,只是自動綁定因其局限性綁定出來的內容可能會導致編譯失敗或者行為錯誤。

前三種類型的 API 通過手動綁定都可以解決,但是第四種類型,無法簡單通過綁定來實現。因為綁定技術實現的是將 C++ 暴露到腳本層,使得腳本層可以調用 C++ 層實現的 API。而從 C++ 調用的 API,並不能主動得調用到腳本層,這是相反的過程。所以就需要在 C++ 源碼中做一些特殊的處理,比如 Cocos 中 Node 類的 onEnter,update 函數,都有做類似的處理。同時,Cocos 中的 Action 類,都無法在腳本層被繼承,根本原因也在於此,腳本層重寫的 update 函數無法被 C++ 的 ActionManager調用到。

後篇:自動綁定工具 zhuanlan.zhihu.com/pand


推薦閱讀:

使用Hbuild快速構建生成現代化前端項目
某熊周刊系列:一周推薦外文技術資料(12.1)
在手持設備上使用 touchstart 事件代替 click 事件是不是個好主意?
PHP 和 node.js 共存的問題?
英文不好可以看關於JS的英文原版書嗎?

TAG:JavaScript | SpiderMonkey | cocos2d |