2014-01-31 34 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:值对,我可以匹配。