Search Unity

  1. Unity 6 Preview is now available. To find out what's new, have a look at our Unity 6 Preview blog post.
    Dismiss Notice
  2. Unity is excited to announce that we will be collaborating with TheXPlace for a summer game jam from June 13 - June 19. Learn more.
    Dismiss Notice

Question Using GatherProperties for zero-length BlendShape array

Discussion in 'Timeline' started by simtrip, Mar 3, 2022.

  1. simtrip

    simtrip

    Joined:
    Oct 18, 2016
    Posts:
    4
    Hi,

    I'm working on a Timeline clip which is able to manipulate the BlendShape weights of a SkinnedMeshRenderer. I'd like these modifications to preview correctly such that they reset to whatever value they were last serialized to when exiting preview mode in the editor.

    During the GatherProperties method of IPropertyPreview, implemented in a PlayableAsset, I have currently found two ways of accomplishing this:

    One way is to explicitly gather the property of each blend shape that will be modified:

    Code (CSharp):
    1.             // Add all blend shape properties that we know will be modified (array of bools to dictate this)
    2.             for (var i = 0; i < WillModifyBlendShapes.Length; i++)
    3.             {
    4.                 if (WillModifyBlendShapes[i])
    5.                 {
    6.                     driver.AddFromName(SkinnedMeshRenderer, $"m_BlendShapeWeights.Array.data[{i}]");
    7.                 }
    8.             }

    Or a much more catch-all solution is to gather properties on the entire component:
    Code (CSharp):
    1.           driver.AddFromComponent(SkinnedMeshRenderer.gameObject, SkinnedMeshRenderer)

    This works most of the time, however it is possible for the m_BlendShapeWeights property to actually be serialized as an empty array. This does not mean the mesh itself has no blend shapes, just that none of them were overridden as scene or prefab modifications so the serializer won't bother copying out their values. In this case, gathering the properties fails because they don't exist. It will fail explicitly in the first method (showing errors for each invalid property name) but silently with the second method, as the properties simply aren't collected.

    After that, the first modification that my PlayableBehaviour makes to the blend shapes using SkinnedMeshRenderer.SetBlendShapeWeight() causes all the array properties to now exist, with whichever values the behaviour had set (depending on where the playback header was when previewing.)
    The next time GatherProperties runs, it succeeds, but it's gathered the modified values which will eventually be saved with the scene next time something causes it to be dirty.

    Given I may be working with prefabs which already contain blend shapes in some default configuration, I don't want to accidentally leak prefab overrides into my scene, just because the scene itself didn't contain any overrides before.

    I'm wondering if I've approached this the wrong way. Is there some a way I can essentially treat this zero-length array property as if it was an N-length array of zeroes (where N is the number of blend shapes on the mesh) but still avoid saving the entire array back to the scene after the preview has ended?

    Apologies for such a long-winded explanation. I feel I may be battling with something inherent to the way SkinnedMeshRenderer is serialized.

    I should note I am using Unity 2020.3.14f1 with the Timeline package 1.4.8

    Thanks for reading