jjzjj

python - PyYAML 和不寻常的标签

coder 2023-08-16 原文

我正在开发一个使用 Unity3D 游戏引擎的项目。对于某些管道要求,最好能够使用 Python 从外部工具更新某些文件。 Unity 的 meta 和 anim 文件在 YAML 中,所以我认为使用 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

最佳答案

我也有这个问题,而且互联网不是很有帮助。在我对这个问题猛烈抨击 3 天之后,我能够解决它……或者至少得到一个可行的解决方案。如果有人想添加更多信息,请做。但这是我得到的。

1) Unity 的 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!”的任何地方,将其替换为“tag:unity3d.com,2011”。

2b) 然后继续使用“!u!”在每个对象流之前到处都是。但问题是——要符合 YAML 1.1——它实际上应该为每个流声明一个标签别名(任何时候新对象以“---”开头)。在顶部声明一次并且不再声明它只对第一个流有效,而下一个流对“!u!”一无所知,所以它出错了。

此外,这个标签是无用的。它基本上将“tag:unity3d.com,2011”附加到流中的每个条目。我们不关心的。我们已经知道它是一个 Unity YAML 文件。为什么要弄乱数据?

3) 对象类型由 Unity 的类 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”。所以事实证明我们实际上并不需要从类 ID 转换为人类可读的节点类型。它就在那里。如果您需要使用它,只需使用根节点即可。如果您需要为 Unity 构建一个 YAML 对象,只需根据该文档链接保留一个字典,将“GameObject”转换为“1”等。

另一个问题是大多数 YAML 解析器(PyYAML 是我测试过的)只支持 3 种类型的 YAML 对象:
  • 标量
  • 序列
  • 制图

  • 您可以定义/扩展自定义节点。但这相当于手动编写您自己的 YAML 解析器,因为您必须明确定义每个 YAML 构造函数的创建和输出方式。为什么我要使用像 PyYAML 这样的库,然后继续编写自己的解析器来读取这些自定义节点?使用库的全部意义在于利用以前的工作并从第一天起获得所有功能。我花了 2 天的时间尝试为每个类 ID 统一创建一个新的构造函数。它从未奏效,我陷入了试图正确构建构造函数的杂草中。

    好消息/解决方案:

    事实证明,到目前为止我遇到的所有 Unity 节点都是 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
    

    要从 Unity 文本场景文件创建 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] )
    

    希望有帮助!

    -弗拉德

    附注。回答下一个问题,使其可用:

    您还需要遍历整个项目目录并解析“GUID”的所有“.meta”文件,这是 Unity 的文件间引用。因此,当您在 Unity YAML 文件中看到类似以下内容的引用时:
    m_Materials:
      - {fileID: 2100000, guid: 4b191c3a6f88640689fc5ea3ec5bf3a3, type: 2}
    

    该文件在其他地方。您可以递归地打开它以找出任何依赖项。

    我刚刚翻阅了游戏项目并保存了一个 GUID:Filepath Key:Value 对的字典,我可以与之匹配。

    关于python - PyYAML 和不寻常的标签,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21473076/

    有关python - PyYAML 和不寻常的标签的更多相关文章

    1. python - 如何使用 Ruby 或 Python 创建一系列高音调和低音调的蜂鸣声? - 2

      关闭。这个问题是opinion-based.它目前不接受答案。想要改进这个问题?更新问题,以便editingthispost可以用事实和引用来回答它.关闭4年前。Improvethisquestion我想在固定时间创建一系列低音和高音调的哔哔声。例如:在150毫秒时发出高音调的蜂鸣声在151毫秒时发出低音调的蜂鸣声200毫秒时发出低音调的蜂鸣声250毫秒的高音调蜂鸣声有没有办法在Ruby或Python中做到这一点?我真的不在乎输出编码是什么(.wav、.mp3、.ogg等等),但我确实想创建一个输出文件。

    2. ruby - 在院子里用@param 标签警告 - 2

      我试图使用yard记录一些Ruby代码,尽管我所做的正是所描述的here或here#@param[Integer]thenumberoftrials(>=0)#@param[Float]successprobabilityineachtrialdefinitialize(n,p)#initialize...end虽然我仍然得到这个奇怪的错误@paramtaghasunknownparametername:the@paramtaghasunknownparametername:success然后生成的html看起来很奇怪。我称yard为:$yarddoc-mmarkdown我做错了什么?

    3. Python 相当于 Perl/Ruby ||= - 2

      这个问题在这里已经有了答案:关闭10年前。PossibleDuplicate:Pythonconditionalassignmentoperator对于这样一个简单的问题表示歉意,但是谷歌搜索||=并不是很有帮助;)Python中是否有与Ruby和Perl中的||=语句等效的语句?例如:foo="hey"foo||="what"#assignfooifit'sundefined#fooisstill"hey"bar||="yeah"#baris"yeah"另外,类似这样的东西的通用术语是什么?条件分配是我的第一个猜测,但Wikipediapage跟我想的不太一样。

    4. css - 用 watir 检查标签类? - 2

      我有一个div,它根据表单是否正确提交而改变。我想知道是否可以检查类的特定元素?开始元素看起来像这样。如果输入不正确,添加错误类。 最佳答案 试试这个:browser.div(:id=>"myerrortest").class_name更多信息:http://watir.github.com/watir-webdriver/doc/Watir/HTMLElement.html#class_name-instance_method另一种选择是只查看具有您期望的类的div是否存在browser.div((:id=>"myerrortes

    5. java - 什么相当于 ruby​​ 的 rack 或 python 的 Java wsgi? - 2

      什么是ruby​​的rack或python的Java的wsgi?还有一个路由库。 最佳答案 来自Python标准PEP333:Bycontrast,althoughJavahasjustasmanywebapplicationframeworksavailable,Java's"servlet"APImakesitpossibleforapplicationswrittenwithanyJavawebapplicationframeworktoruninanywebserverthatsupportstheservletAPI.ht

    6. 华为OD机试用Python实现 -【明明的随机数】 2023Q1A - 2

      华为OD机试题本篇题目:明明的随机数题目输入描述输出描述:示例1输入输出说明代码编写思路最近更新的博客华为od2023|什么是华为od,od薪资待遇,od机试题清单华为OD机试真题大全,用Python解华为机试题|机试宝典【华为OD机试】全流程解析+经验分享,题型分享,防作弊指南华为o

    7. python - 如何读取 MIDI 文件、更改其乐器并将其写回? - 2

      我想解析一个已经存在的.mid文件,改变它的乐器,例如从“acousticgrandpiano”到“violin”,然后将它保存回去或作为另一个.mid文件。根据我在文档中看到的内容,该乐器通过program_change或patch_change指令进行了更改,但我找不到任何在已经存在的MIDI文件中执行此操作的库.他们似乎都只支持从头开始创建的MIDI文件。 最佳答案 MIDIpackage会为您完成此操作,但具体方法取决于midi文件的原始内容。一个MIDI文件由一个或多个音轨组成,每个音轨是十六个channel中任何一个上的

    8. 「Python|Selenium|场景案例」如何定位iframe中的元素? - 2

      本文主要介绍在使用Selenium进行自动化测试或者任务时,对于使用了iframe的页面,如何定位iframe中的元素文章目录场景描述解决方案具体代码场景描述当我们在使用Selenium进行自动化测试的时候,可能会遇到一些界面或者窗体是使用HTML的iframe标签进行承载的。对于iframe中的标签,如果直接查找是无法找到的,会抛出没有找到元素的异常。比如近在咫尺的例子就是,CSDN的登录窗体就是使用的iframe,大家可以尝试通过F12开发者模式查看到的tag_name,class_name,id或者xpath来定位中的页面元素,会抛出NoSuchElementException异常。解决

    9. python ffmpeg 使用 pyav 转换 一组图像 到 视频 - 2

      2022/8/4更新支持加入水印水印必须包含透明图像,并且水印图像大小要等于原图像的大小pythonconvert_image_to_video.py-f30-mwatermark.pngim_dirout.mkv2022/6/21更新让命令行参数更加易用新的命令行使用方法pythonconvert_image_to_video.py-f30im_dirout.mkvFFMPEG命令行转换一组JPG图像到视频时,是将这组图像视为MJPG流。我需要转换一组PNG图像到视频,FFMPEG就不认了。pyav内置了ffmpeg库,不需要系统带有ffmpeg工具因此我使用ffmpeg的python包装p

    10. Python 刷Leetcode题库,顺带学英语单词(31) - 2

      ValidPalindromeGivenastring,determineifitisapalindrome,consideringonlyalphanumericcharactersandignoringcases. [#125]Example:"Aman,aplan,acanal:Panama"isapalindrome."raceacar"isnotapalindrome.Haveyouconsiderthatthestringmightbeempty?Thisisagoodquestiontoaskduringaninterview.Forthepurposeofthisproblem

    随机推荐