2014-01-31 47 views
10

我正在使用Unity3D遊戲引擎的項目。對於一些管道需求,最好能夠使用Python更新來自外部工具的某些文件。 Unity的meta和anim文件都在YAML中,所以我認爲這將使用PyYAML足夠向前。PyYAML和不尋常的標籤

問題是Unity的格式使用自定義屬性,我不確定如何使用它們,因爲所有示例都顯示了Python和Ruby使用的更常見標記。

下面是一個文件的頂部線條看起來是這樣的:

%YAML 1.1 
%TAG !u! tag:unity3d.com,2011: 
--- !u!74 &7400000 
AnimationClip: 
    m_ObjectHideFlags: 0 
    m_PrefabParentObject: {fileID: 0} 
    ... 

當我嘗試讀取這個文件我得到這個錯誤:

could not determine a constructor for the tag 'tag:unity3d.com,2011:74' 

現在看所有其他問題後,問,這個標籤方案似乎不像這些問題和答案。例如,這個文件使用「!u!」我無法弄清楚它意味着什麼或類似的行爲(我的狂野無知的猜測說它看起來像一個別名或命名空間)。

我可以做一個黑客方式並將標籤去掉,但這不是嘗試這樣做的理想方法。我正在尋求解決方案的幫助,該解決方案將正確處理標籤,並允許我以保留正確格式的方式對數據進行編碼。

感謝, -R

回答

13

我也有這個問題,互聯網是不是非常有幫助。在對付這個問題三天之後,我就能夠把它整理出來......或者至少得到一個可行的解決方案。如果有人想添加更多信息,請做。但這是我得到的。

1)統一的YAML文件格式的文檔(他們稱之爲「文本場景文件」,因爲它包含的文字是人類可讀) - http://docs.unity3d.com/Manual/TextualSceneFormat.html

這是一個YAML 1.1兼容的格式。所以你應該可以使用PyYAML或任何其他的Python YAML庫來加載一個YAML對象。

好,太好了。但它不起作用。每個YAML庫都有這個文件的問題。

2)文件格式不正確。事實證明,Unity文件存在一些使YAML解析器錯誤的語法問題。具體來說:

2a)在頂部,它使用%TAG指令爲字符串「unity3d.com,2011」創建一個別名。它看起來像:

%TAG !u! tag:unity3d.com,2011: 

這意味着任何你看到的,將其替換爲 「U!」 「標籤:unity3d.com,2011」。

2b)然後繼續使用「!u!」在每個對象流之前的所有地方。但問題是 - 要符合YAML 1.1標準 - 它應該爲每個流實際聲明一個標籤別名(任何時候新對象都以「---」開頭)。在頂部聲明一次,永不再次只對第一個流有效,下一個流對「!!」一無所知,所以它錯誤了。

此外,這個標籤是無用的。它基本上將「tag:unity3d.com,2011」附加到流中的每個條目。我們不關心。我們已經知道這是一個Unity YAML文件。爲什麼混淆數據?

3)對象類型由Unity的Class ID給出。以下是相關文檔: http://docs.unity3d.com/Manual/ClassIDReference.html

基本上,每個流都被定義爲一個新的對象類...對應於該鏈接中的ID。因此,一個「遊戲對象」爲「1」,等行看起來是這樣的:

--- !u!1 &100000 

所以「---」定義一個新的數據流。 「!你!」是「tag:unity3d.com,2011」的別名,「& 100000」是該對象的文件ID(在該文件內部,如果某事引用了該對象,則使用此ID ....記住YAML是節點基於表示,因此ID用於表示節點連接)。

下一行是YAML對象的根,它恰好是Unity類的名稱...示例「GameObject」。所以事實證明,我們實際上並不需要從Class ID轉換爲Human Readable節點類型。它就在那裏。如果您需要使用它,只需取根節點即可。如果你需要爲Unity創建一個YAML對象,那麼只要保留一個基於該文檔鏈接的字典來將「GameObject」翻譯爲「1」等。

另一個問題是大多數YAML解析器(PyYAML是一個我測試)只支持3種類型的YAML對象開箱:

  1. 標量
  2. 序列
  3. 映射

可以定義/定製的延伸節點。但這等於手寫你自己的YAML解析器,因爲你必須定義EXPLICITLY每個YAML構造函數是如何創建的以及輸出的。爲什麼我會使用像PyYAML這樣的庫,然後繼續編寫我自己的解析器來讀取這些自定義節點?使用圖書館的重點是利用以前的工作,並從第一天起就獲得所有功能。我花了2天的時間嘗試爲每個類ID創建一個新的構造函數。它從來沒有工作過,而且我試圖正確地構建構造函數。

好消息/解決方案:

事實證明,所有我曾經碰到迄今爲止團結節點是在YAML基本的「映射」節點。所以你可以放棄定製節點映射,讓PyYAML自動檢測節點類型。從那裏,一切都很好!

在PyYAML中,您可以傳遞文件對象或字符串。所以,我的解決方案是編寫一個簡單的5行預解析器去除混淆了PyYAML(Unity不正確地解釋的位)的位並將這個新的字符串提供給PyYAML。

1)刪除第2行完全,或只是忽略它:

%TAG !u! tag:unity3d.com,2011: 

我們不在乎。我們知道這是一個統一文件。標籤對我們什麼都不做。

2)對於每個流聲明,刪除標籤別名(「!u!」)並刪除類ID。保留文件ID。讓PyYAML自動檢測節點作爲Mapping節點。

--- !u!1 &100000 

變成...

--- &100000 

3)其餘的,按原樣輸出。

的預解析器的代碼看起來是這樣的:

def removeUnityTagAlias(filepath): 
    """ 
    Name:    removeUnityTagAlias() 

    Description:  Loads a file object from a Unity textual scene file, which is in a pseudo YAML style, and strips the 
         parts that are not YAML 1.1 compliant. Then returns a string as a stream, which can be passed to PyYAML. 
         Essentially removes the "!u!" tag directive, class type and the "&" file ID directive. PyYAML seems to handle 
         rest just fine after that. 

    Returns:    String (YAML stream as string) 


    """ 
    result = str() 
    sourceFile = open(filepath, 'r') 

    for lineNumber,line in enumerate(sourceFile.readlines()): 
     if line.startswith('--- !u!'):   
      result += '--- ' + line.split(' ')[2] + '\n' # remove the tag, but keep file ID 
     else: 
      # Just copy the contents... 
      result += line 

    sourceFile.close() 

    return result 

要創建一個統一文本的場景文件PyYAML對象,調用該文件解析器預功能:

import yaml 

# This fixes Unity's YAML %TAG alias issue. 
fileToLoad = '/Users/vlad.dumitrascu/<SOME_PROJECT>/Client/Assets/Gear/MeleeWeapons/SomeAsset_test.prefab' 

UnityStreamNoTags = removeUnityTagAlias(fileToLoad) 

ListOfNodes = list() 

for data in yaml.load_all(UnityStreamNoTags): 
    ListOfNodes.append(data) 

# Example, print each object's name and type 
for node in ListOfNodes: 
    if 'm_Name' in node[ node.keys()[0] ]: 
     print('Name: ' + node[ node.keys()[0] ]['m_Name'] + ' NodeType: ' + node.keys()[0]) 
    else: 
     print('Name: ' + 'No Name Attribute' + ' NodeType: ' + node.keys()[0]) 

希望有幫助!

-Vlad

PS。回答下一個問題使其可用:

您還需要遍歷整個項目目錄並解析所有「.ID」文件作爲Unity的文件間引用「GUID」。因此,當您在Unity YAML文件中看到類似以下內容的參考時:

m_Materials: 
    - {fileID: 2100000, guid: 4b191c3a6f88640689fc5ea3ec5bf3a3, type: 2} 

該文件位於其他位置。你可以重新打開這個文件來找出任何依賴關係。

我只是通過遊戲項目翻錄並保存了一個GUID字典:Filepath Key:值對,我可以匹配。