Unity中Animator循环依赖的问题

问题

假设有2个Animator Controller,分别命名为TestControllerLhs.controller以及TestControllerRhs.controller。在TestControllerLhs.controller中设置状态如下:
Unity中Animator循环依赖的问题_第1张图片
TestControllerLhs.controller

当拷贝了包含Transitions并且该Transitions的Dst State不存在的Animator State到另一个Animator Controller时,就会出现游离依赖数据。以TestControllerLhs.controller为例,查看该文件能够发现,Attack01包含了Transitions数据:
Unity中Animator循环依赖的问题_第2张图片
Attack01包含的Transition
该Transition的Dst State为Attack02。如果我们拷贝Attack01但没有拷贝Attack02到TestControllerRhs.controller,那么就出现了游离依赖数据:
Unity中Animator循环依赖的问题_第3张图片
TestControllerRhs.controller
Unity中Animator循环依赖的问题_第4张图片
游离依赖数据
出现依赖的原因是该Transition的Dst State,即Attack02,仍然位于TestControllerLhs.controller中,没有被拷贝过来。说该数据是游离的原因是在TestControllerRhs.controller中,我们完全看不见他,也无法删除它。

在这里注意到这个问题的原因是,美术在制作Animator Controller时经常会使用拷贝、黏贴的操作,很容易在无意间产生游离依赖数据,而最关键也最严重的问题是循环依赖,即A.controller依赖B.controller,并且B.controller依赖A.controller。如果这两个Controller都是AssetBundle的话,就会产生无限依赖加载。

解决方案

通过之前的游离依赖数据分析可知他们的共性为m_DstState项包含了所依赖的.controller文件的guid,因此我们通过读取.controller文件将这些游离依赖数据删除。
以下的代码包含了检测循环依赖,打印依赖的Animator Controller以及去除游离依赖数据的功能,开发版本为Unity 5.5.2f1:

/******************************************************************************
 * DESCRIPTION: AnimatorController处理器
 * 
 *     Copyright (c) 2017, 谭伟俊 (TanWeijun)
 *     All rights reserved
 * 
 * COMPANY: Metek
 * CREATED: 2017.09.20, 15:48, CST
*******************************************************************************/

using System.IO;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
using UnityEditor.Animations;
using GameFramework;

public class AnimatorControllerProcessor
{
    [MenuItem("Assets/Artist Tools/Animator Controller/Correct Data")]
    private static void CorrectData()
    {
        string block = null;
        bool isDependOtherAnimatorController = false;
        AnimatorController animatorController = Selection.activeObject as AnimatorController;
        string filePathName = Path.GetFullPath(AssetDatabase.GetAssetPath(animatorController));
        string tempFilePathName = Application.dataPath + "/" + System.DateTime.Now.Ticks.ToString() + ".controller";
        using (StreamWriter writer = File.CreateText(tempFilePathName))
        {
            using (StreamReader reader = File.OpenText(filePathName))
            {
                string content;
                while (null != (content = reader.ReadLine()))
                {
                    if (content.StartsWith("--- !u"))
                    {
                        if (!string.IsNullOrEmpty(block))
                            writer.Write(block);

                        block = content + System.Environment.NewLine;
                        isDependOtherAnimatorController = false;
                    }
                    else
                    {
                        if (isDependOtherAnimatorController)
                            continue;

                        if (string.IsNullOrEmpty(block))
                            writer.WriteLine(content);
                        else
                        {
                            block += (content + System.Environment.NewLine);

                            // 检测是否依赖其他的Animator Controller
                            if (content.Contains("m_DstState:") && content.Contains("guid"))
                            {
                                block = null;
                                isDependOtherAnimatorController = true;
                            }
                        }
                    }
                }

                // 写入最后的数据
                if (!string.IsNullOrEmpty(block))
                    writer.Write(block);
            }
        }

        FileUtil.ReplaceFile(tempFilePathName, filePathName);
        AssetDatabase.Refresh();
    }

    [MenuItem("Assets/Artist Tools/Animator Controller/Collect Animator Controller Dependencies")]
    private static void CollectAnimatorControllerDependencies()
    {
        AnimatorController animatorController = Selection.activeObject as AnimatorController;
        string[] dependencyArray = AssetDatabase.GetDependencies(AssetDatabase.GetAssetPath(animatorController));

        Log.Debug("************************* Animator Controller Dependencies (" + animatorController.name + ") *************************");
        foreach (string dependency in dependencyArray)
        {
            if (dependency.EndsWith(".controller"))
                Log.Debug(dependency);
        }
        Log.Debug("************************************************* End *************************************************");
    }

    [MenuItem("ArtistTools/Check Animator Controller Dependencies")]
    private static void CheckAnimatorControllerDependencies()
    {
        List dependencyCheckNameList = new List();
        string[] filePathNameArray = Directory.GetFiles(Application.dataPath + "/BundleResources/Animator", "*.controller", SearchOption.TopDirectoryOnly);
        foreach (string filePathName in filePathNameArray)
        {
            string[] dependencyArray = AssetDatabase.GetDependencies(filePathName.Substring(filePathName.IndexOf("/Assets/") + 1));
            foreach (string dependency in dependencyArray)
            {
                if (dependency.EndsWith(".controller"))
                {
                    string assetName = Path.GetFileNameWithoutExtension(filePathName);
                    string dependencyName = Path.GetFileNameWithoutExtension(dependency);

                    // A依赖于B,如果"B_A"存在,表示B也依赖于A,则是循环依赖
                    string checkName = dependencyName + "_" + assetName;
                    if (dependencyCheckNameList.Contains(checkName))
                        Log.Debug(Path.GetFileName(filePathName) + " and " + Path.GetFileName(dependency) + " depend each other");

                    dependencyCheckNameList.Add(assetName + "_" + dependencyName);
                }
            }
        }
    }

    [MenuItem("Assets/Artist Tools/Animator Controller/Correct Data", true)]
    [MenuItem("Assets/Artist Tools/Animator Controller/Collect Animator Controller Dependencies", true)]
    private static bool ValidateCorrectData()
    {
        return Selection.activeObject is AnimatorController;
    }
}

打印依赖的Animator Controller:
Unity中Animator循环依赖的问题_第5张图片
TestControllerLhs依赖TestControllerRhs
Unity中Animator循环依赖的问题_第6张图片
TestControllerRhs依赖TestControllerLhs

检测循环依赖:
循环依赖
使用工具清理游离依赖数据后:
除了自身不再依赖其他的Animator Controller

本文固定链接: http://www.cnblogs.com/twjcnblog/p/7663048.html
转载请注明: EnigmaJJ 2017年10月13日 于 博客园 发表

你可能感兴趣的:(Unity中Animator循环依赖的问题)