Search Unity

Question SpriteSkin rootBone and boneTransforms can not be edited at runtime

Discussion in '2D' started by shatulsky, Jan 21, 2023.

  1. shatulsky

    shatulsky

    Joined:
    Jul 10, 2022
    Posts:
    1
    I want to assign UnityEngine.U2D.Animation.SpriteSkin to my gameObject programmatically.
    My object has SpiteRenderer. I assign Sprite with configured bones to it.
    After that, I create the Sprite bones structure using the code I copied from UnityEngine.U2D.Animation.SpriteSkinUtility.CreateBoneHierarchy

    Code (CSharp):
    1.  
    2. var spriteBones = spriteRenderer.sprite.GetBones();
    3.         var boneTransforms = new Transform[spriteBones.Length];
    4.         Transform rootBone = null;
    5.         for (var i = 0; i < spriteBones.Length; ++i)
    6.         {
    7.             CreateBoneGameObject(i, spriteBones, boneTransforms, transform);
    8.             if (spriteBones[i].parentId < 0 && rootBone == null)
    9.             {
    10.                 rootBone = boneTransforms[i];
    11.             }
    12.         }
    13.  
    14. static void CreateBoneGameObject(int index, SpriteBone[] spriteBones, Transform[] transforms, Transform root)
    15.     {
    16.         if (transforms[index] != null) return;
    17.         var spriteBone = spriteBones[index];
    18.         if (spriteBone.parentId >= 0)
    19.         {
    20.             CreateBoneGameObject(spriteBone.parentId, spriteBones, transforms, root);
    21.         }
    22.  
    23.         var go = new GameObject(spriteBone.name);
    24.         var transform = go.transform;
    25.         if (spriteBone.parentId >= 0)
    26.         {
    27.             transform.SetParent(transforms[spriteBone.parentId]);
    28.         }
    29.         else
    30.         {
    31.             transform.SetParent(root);
    32.         }
    33.  
    34.         transform.localPosition = spriteBone.position;
    35.         transform.localRotation = spriteBone.rotation;
    36.         transform.localScale = Vector3.one;
    37.         transforms[index] = transform;
    38.     }
    After that I add SpriteSkin to my object

    Code (CSharp):
    1. var spriteSkin = GetComponent<SpriteSkin>();
    2.         if (spriteSkin == null)
    3.         {
    4.             spriteSkin = gameObject.AddComponent<SpriteSkin>();
    5.             spriteSkin.enabled = true;
    6.         }
    But SpriteSkin properties rootBone and boneTransforms are internal and cannot be changed.
    upload_2023-1-21_2-7-26.png

    upload_2023-1-21_2-8-12.png

    I was still able to change them using reflection like this:
    Code (CSharp):
    1. var rootBoneProperty = typeof(SpriteSkin).GetProperty(nameof(SpriteSkin.rootBone));
    2.         rootBoneProperty!.SetValue(spriteSkin, rootBone, BindingFlags.NonPublic | BindingFlags.Instance, null, null, CultureInfo.InvariantCulture);
    3.         //spriteSkin.rootBone = rootBone;
    4.  
    5.         var boneTransformsProperty = typeof(SpriteSkin).GetProperty(nameof(SpriteSkin.boneTransforms));
    6.         boneTransformsProperty!.SetValue(spriteSkin, boneTransforms, BindingFlags.NonPublic | BindingFlags.Instance, null, null, CultureInfo.InvariantCulture);
    7.         //spriteSkin.boneTransforms = boneTransforms;
    8.  
    After that, I add the Animator component to the object, and the animation works as expected.
    But that's probably not the best solution.

    Am I missing some obvious way of setting SpriteSkin data at runtime,
    Or programmatically changing bones is not supported intentionally?
     
    tonytopper, Thorax- and RamonLion like this.
  2. RamonLion

    RamonLion

    Joined:
    Jul 19, 2012
    Posts:
    11
    Although a bit brute force, I was completely unable to find any other solution. This broke me through a wall I didn't think I could make it through. Thanks so much!
     
  3. tonytopper

    tonytopper

    Joined:
    Jun 25, 2018
    Posts:
    226
    You should be able to edit the bone information via UnityEngine.Sprite. Even though the GetBones and SetBones methods aren't documented they are public functions.

    I am not sure why Unity devs didn't opted to have wrapper functions for this on the SpriteSkin component though.
     
    EvOne likes this.
  4. tonytopper

    tonytopper

    Joined:
    Jun 25, 2018
    Posts:
    226
    This is for editing the SpriteBone data though. Not the boneTransforms. My mistake.

    I am asking the same question about Auto Rebind and ended up having to use the reflection hack for now as well
     
    EvOne likes this.
  5. MarekUnity

    MarekUnity

    Unity Technologies

    Joined:
    Jan 6, 2017
    Posts:
    204
    Thank you for your feedback @shatulsky @tonytopper

    In 2D Animation 10.1.0 we've added the SetRootBone, and SetBoneTransforms methods that will allow you to modify the root bone as well as bone transforms.

    We usually don't backport new public APIs to previous Unity versions, however, you could use these two helper methods that should work with previous Unity versions (SetRootBone, SetBoneTransforms). Please note that these helper methods are provided as is and didn't go through our QA process. I hope this will unblock you!
     
  6. tonytopper

    tonytopper

    Joined:
    Jun 25, 2018
    Posts:
    226
    MarekUnity likes this.
  7. MarekUnity

    MarekUnity

    Unity Technologies

    Joined:
    Jan 6, 2017
    Posts:
    204