Search Unity

All game objects sharing the same instance of the class used in array

Discussion in 'Scripting' started by Graham-B, Apr 8, 2022.

  1. Graham-B

    Graham-B

    Joined:
    Feb 27, 2013
    Posts:
    331
    How can I get each game object to use it's own instanced array based on the "BlendShapeNamedValue" class?

    I've created a simple script for overriding blend shapes, and it works great on one mesh; but the moment I use this component on multiple meshes and have their arrays populated by the primary component, it breaks.

    The issues appears to be that they are all sharing the same instance of the second class "BlendShapeNamedValue" found at the end of this script.

    Code (CSharp):
    1.  
    2. using System;
    3. using System.Collections;
    4. using System.Collections.Generic;
    5. using UnityEngine;
    6.  
    7. namespace Test
    8. {
    9.     public class BlendShapesOverride : MonoBehaviour
    10.     {
    11.         public SkinnedMeshRenderer rend;
    12.         public BlendShapeNamedValue[] blendShapes = new BlendShapeNamedValue[0];
    13.         public BlendShapesOverride[] clothingOverrides;
    14.         private bool primaryOverride;
    15.        
    16.         public void PrepareBlendShapes()
    17.         {
    18.             for (int i=0; i< blendShapes.Length; i++)
    19.             {
    20.                 blendShapes[i].index = rend.sharedMesh.GetBlendShapeIndex(blendShapes[i].name);
    21.             }
    22.            
    23.             if (primaryOverride)
    24.             {
    25.                 for (int iClothing=0; iClothing< clothingOverrides.Length; iClothing++)
    26.                 {
    27.                     clothingOverrides[iClothing].blendShapes = new BlendShapeNamedValue[blendShapes.Length];
    28.                     blendShapes.CopyTo(clothingOverrides[iClothing].blendShapes, 0);
    29.                     clothingOverrides[iClothing].PrepareBlendShapes();
    30.                 }
    31.             }
    32.         }
    33.        
    34.         void LateUpdate()
    35.         {
    36.             for (int i=0; i< blendShapes.Length; i++)
    37.             {
    38.                 if (blendShapes[i].index >= 0) rend.SetBlendShapeWeight(blendShapes[i].index, blendShapes[i].value);
    39.             }
    40.         }
    41.     }
    42.    
    43.     [Serializable]
    44.     public class BlendShapeNamedValue
    45.     {
    46.         public string name;
    47.         public float value;
    48.         [HideInInspector]
    49.         public int index;
    50.     }
    51. }
    52.  
    Thanks for any insight!
     
  2. Brathnann

    Brathnann

    Joined:
    Aug 12, 2014
    Posts:
    7,187
    At a glance, I don't see anything that makes them all use the same instance of the BlendShapeNamedValue class.

    How are you creating your instances of BlendShapesOverride? Do you have several gameobjects in the scene already or do you instanciate several gameobjects with this script on them?
     
  3. Graham-B

    Graham-B

    Joined:
    Feb 27, 2013
    Posts:
    331
    Currently the game objects already exist in the scene with this script on them.
     
  4. Brathnann

    Brathnann

    Joined:
    Aug 12, 2014
    Posts:
    7,187
    Since they are already in scene, they each should have an array of BlendShapeNamedvalue on them. If you add to that array on the objects, it should let you create a new name and float value for each entry.

    Is this not what you're doing?
     
  5. Graham-B

    Graham-B

    Joined:
    Feb 27, 2013
    Posts:
    331
    That is exactly my intention, but they all end up sharing the same found index. For example the main body has a knee correction blend shape, and it happens to be found at index 40 in the body mesh blend shapes. The pants also have a corrective blend shape by the same name, but a different number of total blend shapes, and consequently the blend shape is found at index 3 on that mesh.

    Once the script does its thing, it ends up correctly using the blend shape at index 3 on the pants, but it also ends up using index 3 on the body mesh instead of the valid index 40, resulting in the wrong shapes being used on many of the meshes.
     
  6. StarManta

    StarManta

    Joined:
    Oct 23, 2006
    Posts:
    8,775
    Try saving, closing, and reopening the scene. It's possible that some copy and paste issue somehow caused them to be pointing to the same BlendShapeNamedValue reference (any number of possibilities depending on how they were created); however, there's virtually no way that such an inadvertently copied reference would survive serialization/deserialization.
     
  7. Graham-B

    Graham-B

    Joined:
    Feb 27, 2013
    Posts:
    331
    Thanks for the input guys. In the end I got fed up with whatever voodoo was going on under the hood and rewrote it from scratch, this time using a single component.

    Here it is in case anyone finds it useful.

    Code (CSharp):
    1. using System;
    2. using System.Collections;
    3. using System.Collections.Generic;
    4. using UnityEngine;
    5.  
    6. namespace MeshTools
    7. {
    8.     public class BlendShapesOverride : MonoBehaviour
    9.     {
    10.         public BlendShapeNamedValue[] blendShapes = new BlendShapeNamedValue[0];
    11.      
    12.         private List<BlendShapeDataPerMesh> meshes = new List<BlendShapeDataPerMesh>();
    13.      
    14.         void Awake()
    15.         {
    16.             RenewMeshList();
    17.         }
    18.      
    19.         public void RenewMeshList()
    20.         {
    21.             meshes.Clear();
    22.             SkinnedMeshRenderer[] tempMeshes = this.transform.parent.parent.GetComponentsInChildren<SkinnedMeshRenderer>(true);
    23.          
    24.             for (int i=0; i< tempMeshes.Length; i++)
    25.             {
    26.                 BlendShapeDataPerMesh nextMesh = new BlendShapeDataPerMesh();
    27.                 nextMesh.skinnedMesh = tempMeshes[i];
    28.                 nextMesh.blendshapes = new List<BlendShapeNamedValue>();
    29.                 meshes.Add(nextMesh);
    30.             }
    31.         }
    32.      
    33.         public void PrepareBlendShapes()
    34.         {
    35.             for (int iMeshes=0; iMeshes< meshes.Count; iMeshes++)
    36.             {
    37.                 meshes[iMeshes].blendshapes.Clear();
    38.              
    39.                 for (int iBlendshapes=0; iBlendshapes< blendShapes.Length; iBlendshapes++)
    40.                 {
    41.                     BlendShapeNamedValue nextValues = new BlendShapeNamedValue();
    42.                     nextValues.name = blendShapes[iBlendshapes].name;
    43.                     nextValues.value = blendShapes[iBlendshapes].value;
    44.                     nextValues.index = meshes[iMeshes].skinnedMesh.sharedMesh.GetBlendShapeIndex(nextValues.name);
    45.                     meshes[iMeshes].blendshapes.Add(nextValues);
    46.                 }
    47.             }
    48.         }
    49.      
    50.         void LateUpdate()
    51.         {
    52.             for (int iMeshes=0; iMeshes< meshes.Count; iMeshes++)
    53.             {
    54.                 for (int iBlendshapes=0; iBlendshapes< blendShapes.Length; iBlendshapes++)
    55.                 {
    56.                     if (meshes[iMeshes].blendshapes[iBlendshapes].index >= 0)
    57.                     {
    58.                         meshes[iMeshes].skinnedMesh.SetBlendShapeWeight(meshes[iMeshes].blendshapes[iBlendshapes].index, meshes[iMeshes].blendshapes[iBlendshapes].value);
    59.                     }
    60.                 }
    61.             }
    62.         }
    63.     }
    64.  
    65.     [Serializable]
    66.     public class BlendShapeNamedValue
    67.     {
    68.         public string name;
    69.         public float value;
    70.         [HideInInspector]
    71.         public int index;
    72.     }
    73.  
    74.     class BlendShapeDataPerMesh
    75.     {
    76.         public List<BlendShapeNamedValue> blendshapes;
    77.         public SkinnedMeshRenderer skinnedMesh;
    78.     }
    79. }
    80.