程式碼生成器外掛與Creator預製體檔案解析

deeply發表於2021-09-09

前言

  1. 之前寫過一篇自動生成指令碼的工具,但是我給它起名叫半自動程式碼生成器。之所以稱之為半自動,因為我覺得全自動程式碼生成器應該做到兩點:程式碼生成+自動繫結。之前的工具只做了程式碼生成,並沒有做自動繫結,所以鄙人又花時間研究了CocosCreator的預製體檔案,實現了自動繫結的能力,並且支援了外掛使用方式。

  2. 本篇內容,不僅僅是宣傳自己的外掛工具,還會幫助大家分析一下Creator的預製體檔案格式,使購買外掛的同學可以將外掛價值最大化,也能讓讀者對Creator的預製體檔案有所瞭解。

Creator 預製體檔案格式分析

  1. 首先我們展示一小段預製體檔案。
[
  {
    "__type__": "cc.Prefab",
    "_name": "",
    "_objFlags": 0,
    "_native": "",
    "data": {
      "__id__": 1
    },
    "optimizationPolicy": 0,
    "asyncLoadAssets": false,
    "readonly": false
  },
  {
    "__type__": "cc.Node",
    "_name": "LoginView",
    "_objFlags": 0,
    "_parent": null,
    "_children": [
      {
        "__id__": 2
      },
      {
        "__id__": 6
      },
      {
        "__id__": 18
      },
      {
        "__id__": 21
      },
      {
        "__id__": 33
      },
      {
        "__id__": 42
      },
      {
        "__id__": 63
      },
      {
        "__id__": 84
      }
    ],
    "_active": true,
    "_components": [
      {
        "__id__": 105
      },
      {
        "__id__": 106
      }
    ],
    "_prefab": {
      "__id__": 107
    },
    "_opacity": 255,
    "_color": {
      "__type__": "cc.Color",
      "r": 255,
      "g": 255,
      "b": 255,
      "a": 255
    },
    "_contentSize": {
      "__type__": "cc.Size",
      "width": 960,
      "height": 640
    },
    "_anchorPoint": {
      "__type__": "cc.Vec2",
      "x": 0.5,
      "y": 0.5
    },
    "_trs": {
      "__type__": "TypedArray",
      "ctor": "Float64Array",
      "array": [
        480,
        320,
        0,
        0,
        0,
        0,
        1,
        1,
        1,
        1
      ]
    },
    "_eulerAngles": {
      "__type__": "cc.Vec3",
      "x": 0,
      "y": 0,
      "z": 0
    },
    "_skewX": 0,
    "_skewY": 0,
    "_is3DNode": false,
    "_groupIndex": 0,
    "groupIndex": 0,
    "_id": ""
  }
]
  1. 當你第一次看到這個檔案的時候可能會有所疑惑,此檔案是一個一維陣列的json物件。
    陣列第一個位置是預製體檔案的描述可以忽略。第二個位置的物件就是這個檔案根節點對應的屬性了。也是最重要的部分。從資料的_children 和_components欄位可以看出,引擎是根據__id__這個欄位來尋找其他資料的。只不過這個__id__並非你所理解的id,而是陣列的索引位置。並且配置中所有涉及到尋找其他資料都是用的這種__id__方式。包括給自己的指令碼元件繫結節點。

  2. 由於我們是要匯出和繫結其中的屬性,所以我們只關注__type__,_name,_childern,_components這幾個屬性就可以了。

  3. __type__: 是一個比較重要的屬性,對於普通的元件,型別會是cc.Node,cc.Sprite,cc.Button之類的。對於指令碼的型別比較特別,它並不是你定義的類名,而已一串經過處理的字元。但是它的特點就是長。所以我會用長度是否大於20來判斷是否是指令碼元件。
    下方是我已經做好繫結指令碼的配置內容。也說明如果你要給預製體新增指令碼也就是給預製體的陣列新增一個下面格式的配置而已。通過下方的配置也可以看出我給這個指令碼繫結了start$VButton,login$VLabel,login$\VLabel,show$VRichText四個變數和testItem$ATestItemView一個陣列。

  {
    "__type__": "3e347nRcdVAiYpq6icd+8+w",
    "_name": "",
    "_objFlags": 0,
    "node": {
      "__id__": 1
    },
    "_enabled": true,
    "start$VButton": {
      "__id__": 14
    },
    "login$VLabel": {
      "__id__": 19
    },
    "back$VButton": {
      "__id__": 29
    },
    "show$VRichText": {
      "__id__": 40
    },
//繫結的是一個陣列,長度為3
    "testItem$ATestItemView": [
      {
        "__id__": 61
      },
      {
        "__id__": 82
      },
      {
        "__id__": 103
      }
    ],
    "_id": ""
  },
  1. _name: 它是所有節點的名稱,注意,元件是沒有名稱的,所以我們處理檔案的時候也是從根節點深度遍歷所有的子節點,然後在遍歷節點的時候出理它的元件。
    因為Creator的節點都是有名稱的,而且還有空格,支援重名,這些都是程式碼生成器所不允許的。所以如果你想匯出一個屬性就要用特定的字元標識。
    比如工具現在使用的是$V標識輸出的是一個變數。$A標識輸出的是一個陣列,當然陣列還需要名稱都相同。
var TAG = '$V'
var LIST = '$A'

當然這裡邊還有一個問題就是一個節點可能掛了很多元件,那我們要怎麼出理呢?
首先是做一個篩選,只有給定的元件才可以輸出
var NORMAL_COMP_LIST = ['cc.Label', 'cc.Sprite', 'cc.RichText', 'cc.Button', 'cc.ScrollView']
其次工具會將名稱後邊加上型別,以區別不同的元件的情況。
因此這也是一個可自定義和擴充套件的部分。

  1. _children: 就是遍歷的節點,我們不用關心這個配置檔案的陣列有多長,就從這些子節點深度遍歷就可以了。

  2. _components: 遍歷每個節點的時候我們再遍歷一次它的所有元件,然後做上邊我們說的操作。這樣一個檔案走一遍之後我們的類需要的資訊也就生成了。

問題討論

  1. 既然指令碼的型別是 "type": "3e347nRcdVAiYpq6icd+8+w",那麼我們怎麼找到對應的指令碼檔案的名稱呢?
    當你右鍵一個指令碼檔案,點選開啟Library中的資源選項,會跳轉到一個js檔案中
    image.png
    這個檔案有四個特點,一是它的檔名是你指令碼的uuid.js。二是檔案帶有cc._RF.push(module,,三是帶有型別"3e347nRcdVAiYpq6icd+8+w"。四是帶有指令碼名稱。所以你只要查詢檔案中帶有cc._RF.push(module,欄位並且帶有你傳入的型別的檔案並返回後處理一下就可以獲得指令碼名稱了。當然也可以得到路徑並做好import from 的設定。
    這裡可能有人會覺得,搜尋整個imports路徑是不是很慢,其實不是,即使再慢還能比手動繫結慢嗎?所以這個速度可以無視。

  2. 怎麼處理的creator自帶的meta機制,我是將程式碼生成和自動繫結分成兩個指令碼。程式碼生成之後會重新整理生成指令碼的目錄,然後再呼叫自動繫結的指令碼。重新整理預製體目錄。
    這裡邊一直強調的就是重新整理指定的目錄,而不是重新整理整個assets或者整個Script。原因是我在使用的時候發現,重新整理整個目錄會造成編輯器卡死,體驗很不好,所以當你不設定的指令碼目錄不存在就做匯出時,creator顯示的目錄位置不是實際中硬碟中顯示的位置,重啟編輯器後才會正確,就是因為我重新整理的目錄是你指定的目錄。如果指定的目錄不存在,目錄會建立,但是不會重新整理。
    重新整理後的預製體檔案可能會報錯,可以無視,雙擊預製體檔案重新檢視就可以了。

  3. 如何做到繫結巢狀的預製體的節點?
    首先在匯出程式碼的時候一旦遇到有指令碼的節點就返回。繼續遍歷其他子節點。這樣你的指令碼里就不會宣告其他指令碼要宣告的內容。
    然後在繫結的時候首先會找到應該做資料繫結的指令碼,然後再深入遍歷,當你發現你處理的這個節點上有指令碼的時候就會用它身上指令碼處理它的子節點。所以如果一個節點有多個指令碼就只能繫結第一個指令碼的資料。

功能介紹

  1. 根據預製體檔案生成指令碼
    在這裡插入圖片描述

  2. 自定義輸入目錄和輸出目錄
    在這裡插入圖片描述

  3. 自動繫結屬性+陣列
    使用$V標識變數
    使用$A標識陣列
    屬性名稱預設帶有型別。
    在這裡插入圖片描述

  4. 自動繫結button事件
    在這裡插入圖片描述

  5. 可擴充套件匯出屬性型別
    在這裡插入圖片描述

  6. 自動繫結其他預製體的屬性,和button事件
    在這裡插入圖片描述

  7. 自動匯入使用到的其他指令碼
    在這裡插入圖片描述

手懶不想自己實現的可以關注公眾號,進入微店購買。

歡迎掃碼關注公眾號《微笑遊戲》,瀏覽更多內容。

image

歡迎掃碼關注公眾號《微笑遊戲》,瀏覽更多內容。

相關文章