【问题标题】:Iterating through all the possible combinations of several lists items遍历几个列表项的所有可能组合
【发布时间】:2019-03-04 12:23:13
【问题描述】:

我有一个包含 12 个混合形状的面部模型,其中每个混合形状只是 0(中性面部表情)和 1(最大激活表情)之间的浮点值列表,但是我只从前 2 个混合形状开始;即现在只有两个列表,比如微笑和怀疑的表情。

我的目的是浏览这两个列表中所有项目的所有可能组合,并制作面部动作的镜头(电影剪辑),以显示 blendshape 值/权重的所有可能组合的样子。

所以,我写了以下内容来简化这个场景,目前只有两个 blendshapes,并在应用程序关闭后立即将它们保存到文件中:

public class BlendShapesVAL : MonoBehaviour
{
    private List<float> _weightValues_Preset1_smile   = new List<float>();
    private List<float> _weightValues_Preset2_skeptic = new List<float>();

    public bool _TransitionAnimation = true;
    public float _TransitionAnimationSpeed = 2f;

    public BlendShapesPresetController _BSPC;

    private List<float> _weightsList = new List<float>();

    public List<bool> _ActivationsList = new List<bool>();
    public List<string> _PresetsNamesList = new List<string>();


    private void Awake()
    {        
        _weightsList.Clear();
        _ActivationsList.Clear();
        for (int i = 0; i < _PresetsNamesList.Count; ++i)
        {
            _ActivationsList.Add(false);
            _weightsList.Add(0);
        }
     }


    private void Start()
    {
        if (_BSPC != null)
        {
            // . . .
        }
        else
        {
            _BSPC = GetComponent<BlendShapesPresetController>();
        }

        StartCoroutine("Interpolate");
    }


    /// <summary>
    /// Writes (i.e. saves) blendshape values to file when the application quits.
    /// </summary>
    /// 
    private void OnApplicationQuit()
    {
        SaveBlendShapesValues(_weightValues_Preset1_smile);
        SaveBlendShapesValues(_weightValues_Preset2_skeptic);

        PlayerPrefs.DeleteAll();
    }


    /// <summary>
    /// Goes thorugh all the possible combinations of blendshape weights.
    /// For now, only the first two though!
    /// </summary>
    /// 
    private IEnumerator Interpolate()
    {
        for (int i = 0; i <= 100; i++)
        {
            float weightValuesmile = (float)i / 100.0f;
            _BSPC.SetWeight("Preset1_smile", weightValuesmile);
            _weightValues_Preset1_smile.Add(weightValuesmile);

            for (int j = 0; j <= 100; j++)
            {
                float weightValueSkeptic = (float)j / 100.0f;
                _BSPC.SetWeight("Preset2_skeptic", weightValueSkeptic);
                _weightValues_Preset2_skeptic.Add(weightValueSkeptic);
            }

            yield return null;
        }
    }


    /// <summary>
    /// Writes (i.e. saves) blendshape values to file.
    /// <param name="blendShapesValuesFilePath">
    /// The path to the file that will store the list of float values;
    /// i.e. "Application.dataPath" plus the name of the CSV file.
    /// </param>
    /// <param name="values">
    /// The float values that are the blendshape weights.
    /// </param>
    /// </summary>
    /// 
    private static void SaveBlendShapesValues(List<float> values)
    {
        List<string> lines = new List<string>
        {
            /// Add a header row for the labels.
            "TimeStamp,Preset1_smile,Preset2_skeptic"
        };

        foreach (var value in values)
        {
            /// Iterate through all the elements.
            lines.Add(DateTime.Now + "," + value);
        }

        /// Load the old counter.
        int counter = PlayerPrefs.GetInt("_counter_", 0);

        /// Concatenate the file name constituents and combine it with the application data path.
        string fileName = string.Format("BlendShapesValues_{0}.csv", counter.ToString() );
        string tempPath = Path.Combine(Application.dataPath, fileName);

        try
        {            
            File.WriteAllLines(tempPath, lines.ToArray() );
            Debug.Log("Saved blendshape weight values to: " + tempPath);

            /// Increment the counter.
            counter++;

            /// Save the current counter.
            PlayerPrefs.SetInt("_counter_", counter);
            PlayerPrefs.Save();
        }
        catch (Exception e)
        {
            Debug.LogWarning("Failed to save to PlayerPrefs: " + tempPath);
            Debug.LogWarning("Error: " + e.Message);
        }        
    }
}

在 Unity 编辑器中,混合形状的值显示为 0 到 100,因此我在代码中进行了转换,如下图所示:

第一个文件有 101 个值(0...100 加上列标签的顶行)在此屏幕截图中可以看到一个 sn-p:

第二个文件有 10201 值。我的第一个问题是这种在应用程序停止后将迭代值保存到文件的方法是否是一个好的解决方案,因为随着我添加更多列表(即 blendshapes),值会大幅增长?

我的第二个问题是如何减慢迭代速度,因为(在第一个屏幕截图中)微笑值开始从 0 计数到 100 并且我可以看到它们(脸部缓慢移动一种可见的方式),但在发生这种情况时,我注意到第二个列表(怀疑论者)显然立即跳到 100,所以它完成得如此之快以至于它无法被 Win 屏幕录像机记录......

【问题讨论】:

    标签: c# file unity3d


    【解决方案1】:

    我不确定这是评论还是答案,所以如果我遗漏了什么,请告诉我。

    对于您的第一个问题,如果您对人类无法读取的那些保存的文件感到满意,我建议您使用BinaryReader。因为保存文件的大小会更小,当您想读回它们以制作剪辑时,它会更快地读取它。此外,考虑到您想要 12 个混合形状及其组合,这个文件可能会很大。

    对于您的第二个问题,迭代跳至 100,因为您仅在内部循环完成时才屈服。因此,在每一帧中,100 个对 1 个微笑表示怀疑的组合被添加到列表中。我建议对这样的任务使用多线程或 Unity 作业系统,因为使用 12 个混合形状和所有组合在计算上可能会很昂贵。

    如果我能提供进一步帮助,请告诉我。祝你好运!

    【讨论】:

    • 谢谢你,阿里。我将yield 语句放在内部循环中,所以现在它完全符合我的需要,因此将您的答案标记为正确!我更喜欢这些文件是人类可读的,至少现在是这样,所以也许我应该使用StreamWriter
    • 我不确定StreamWriter,因为现在你可以写所有行,我不知道StreamWriter 是否提供这个也通常写作不是问题,但阅读总是有问题。我建议在阅读时使用BufferedStream,如果我是你,我会以不需要任何string.split 操作的方式构建我的csv 文件,因为split 可能真的很慢。祝你好运!
    猜你喜欢
    • 1970-01-01
    • 2017-08-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-08-15
    • 1970-01-01
    • 2014-07-14
    • 1970-01-01
    相关资源
    最近更新 更多