- Home /
Dynamically instantiated skinned mesh not playing animations
I'm searching for a stop-gap solution to the problem described in these posts:
Our artist is in the process of adding/removing bones to fit budget, and we don't want to have to go through the prefab creation process every time changes are made to the mesh. My hope is that I can write a script that will instantiate the necessary game objects from an FBX imported character model, the move these objects directly into an instance of our "character template" prefab.
I'm able to instantiate the mesh and move all of its children into the prefab, but when I do so, the skinned mesh does not animate. References to the animation field of our animation-handling component don't fail or throw warnings. Weirder still, when the root game object (with skinned mesh and bone transform hierarchy children) is selected and anything that changes the object's state is triggered from the editor, animations start working. For example, if I play an animation on the mesh from the animation viewer, or even just check and uncheck an inspector property on the Animation component, the driver we're using to determine which animations should play activates and functions correctly.
To explain a little more concretely:
We have a prefab in our library called Character_Prefab. This prefab has a bunch of gameplay and networking scripts attached to it, and no game object children.
We also have a mesh in our Art directory that we'll call Character_Mesh.
I've attached a script to Character_Prefab that takes has one inspector field, a transform that is supposed to point to the character mesh you want to appear on the prefab when it is created. In this case, this transform is the Character_Mesh object generated from the FBX import.
The small bit of script that executes this is as follows:
void OnEnable() { if(!_instantiated && gameObject != null) { if(_modelPrefab == null) { log.Error( "ModelPrefab hasn't been assigned a prefab! Please assign a mesh prefab to this character prefab's SpawnMesh component!" ); } else {
Animation animationComponent = gameObject.GetComponent<Animation>();
if(animationComponent == null) {
log.MissingComponent(typeof(Animation));
}
Transform newMesh = (Transform)Instantiate(_modelPrefab, Vector3.zero, Quaternion.identity);
newMesh.parent = gameObject.transform;
newMesh.localPosition = Vector3.zero;
// Create a list of the children we need to move. Setting child.parent to a new
// value while iterating over the list seems to cause us to miss some elements.
ArrayList children = new ArrayList();
foreach(Transform child in newMesh) {
children.Add(child);
}
// Move them.
foreach(Transform child in children) {
child.parent = gameObject.transform;
}
print("Prefab animation component = " + newMesh.animation);
print("This animation component = " + animationComponent);
// Delete the prefab object.
DestroyImmediate(newMesh.gameObject);
_instantiated = true;
}
}
}
The class is also set to trigger OnEnable when the prefab is dragged into the editor scene window via the [ExecuteInEditMode] metadata tag, but I don't imagine this is part of the problem.
My hypothesis is that some internal state variable isn't being updated to notify the Skinned Mesh Renderer that the Animation component has changed. Normally, were you to drag the FBX-imported mesh directly into the scene, it would begin with an Animation component attached to the parent object. In my case, this component is being abandoned in favor of an Animation component that already exists on our Character_Prefab prefab. When I change something in the editor, that might trigger a "hard" reset on a bunch of the objects such that the change is propagated. This syncs everything up and permits the animations to influence the skeletal transforms. However, the animation field of the Component object is read-only, so I can't simply assign the mesh object's animation field to the "real" Animation component. Also, kludgey efforts to replicate the "reset" via state changes post-mesh-instantiation (setting a boolean to one value on update 0, then back on update 1) don't seem to trigger the "fix" I'm observing when I make a change in the editor.
That being said, here's the crux of my question:
Does anyone know what might be changing in either the Animation component, the Skinned Mesh Renderer, or the game object hierarchy when I make an adjustment to the instantiated game object via the inspector, such that animations start affecting the bone transforms? Or for that matter, tips/tricks about spawning a skinned mesh onto an existing prefab containing an initialized Animation component?
Alternatively, I was thinking it might work if I leave the instantiated prefab intact, parent it to the prefab with game logic scripts, and adjust all of our game logic scripts to compensate for the object hierarchy change. I'd rather not do that, but if push comes to shove, it would be an acceptable-ish solution.
Thanks in advance for any help and/or suggestions!
Does anyone know if an official answer has come from Unity on the subject? This seems like a fairly big production pipeline blocker.
Answer by frederik_krogh · Oct 11, 2020 at 07:17 PM
Better late than never.. the problem is still here 2020, but there is a undocumented function in Animator called Rebind you can just call it on the animator like this animatorcomponent.Rebind();
https://github.com/Unity-Technologies/UnityCsReference/blob/master/Modules/Animation/ScriptBindings/Animator.bindings.cs at line 1176 hope it helps in the future
Answer by sneftel · May 09, 2011 at 08:07 PM
I'm doing something very similar (creating skinned mesh, copying over skeleton and animation component from prefab), with no problems at all. One difference, though: I'm doing it in Update(), not in OnEnable(). Weird things happen if you mess with the hierarchy too much during the Awake/Enable/Start phase, with later initializations happening with stale data; I moved it into Update() because I was sick of looking at the Unity crash reporter. The behavior sets up the mesh, copies over the skeleton, then deletes itself (so that it doesn't get called next Update()). You might give that a try.
Here's my BakeOnCreate script, which is subclassed by my various procedural generation scripts. It seems pretty robust. The selection-check stuff is important if and only if you're gonna be deleting GameObjects.
using UnityEngine; using System.Collections; using UnityEditor;
[ExecuteInEditMode] public abstract class BakeOnCreate : MonoBehaviour {
private bool shouldBake = true;
private bool alreadyBaked = false;
void Update()
{
if(!shouldBake)
{
return;
}
if(alreadyBaked)
{
Debug.LogError("Already baked!");
}
GameObject stagingNode = GameObject.Find("/Staging");
if(stagingNode && transform.IsChildOf(stagingNode.transform))
{
shouldBake = false;
return;
}
if(Selection.activeGameObject == null || !gameObject.transform.IsChildOf(Selection.activeGameObject.transform))
{
return;
}
Bake();
shouldBake = false;
alreadyBaked = true;
if(this && gameObject)
{
Object.DestroyImmediate(this);
}
}
// Use this for initialization
protected abstract void Bake();
}
Interesting note about Update versus OnEnable / Awake / Start. Didn't seem to resolve the problem though. In your post you mention that you're doing a similar copy operation in some subclass' Bake implementation. Does that entail anything besides instantiating the mesh prefab with the skinned mesh / bone data and reparenting the created objects to the target prefab's root transform? Also, it sounds as though in your case, the clip-initialized Animation component (and its object) is copied onto a new object ins$$anonymous$$d of the mesh being copied to the Animation's object. Is that accurate?
Here's how it works: There's a prefab with an Animation component (preloaded with a clip) and a $$anonymous$$ixAnd$$anonymous$$atch$$anonymous$$esh component (which inherits from BakeOnCreate). $$anonymous$$ixAnd$$anonymous$$atch$$anonymous$$esh.Bake() does the following things:
It creates a new gameobject to hold a skeletal mesh, and creates a new mesh to put into it.
It copies the skeleton from a provided FBX-imported object. It does this by instantiating only that skeleton root, then setting its transform parent. It also manually sets the name to remove the "(clone)" suffix.
It semi-randomly selects a set of skinned meshes from a provided prefab. It combines them into the skinned mesh. The bone references are remapped to refer to the new skeleton.
It parents the skinned mesh object to the current gameObject.
Answer by sirival · Apr 04, 2012 at 06:03 PM
I know this is an old thread, however I had the same problem and I solved it by making sure that playAutomatically was false on the animation component of the root game object that contained the final skinned mesh renderer. When I combine the meshes I manually call animation.Play and it works. Note that it does not work if playAutomatically is set to true in the beginning even if you manually call Play...
Answer by Hardcore Games · Apr 02, 2013 at 02:45 AM
Just in case none of the above have help u. Have in mind that the animations configured to the prefab before making it a prefab were aiming to its normal name, Example:
horse_01.animation.CrossFade('idle', 1);
Being horse_01 a GameObject to which you make reference by
horse_01 = GameObject.Find('/PrefabName/some hierarchy.../');
Now, when you instantiate a prefab, it will be named "PrefabName(Clone)". Therefore the script wont find it anymore by the name you gave it in the level edition. Change script to incorporate the (Clone) sufix or find the way to get rid of it! I hope this will throw some light upon any lost pilgrim as myself ;)
Your answer
Follow this Question
Related Questions
problems importing bone based vertex animation 3 Answers
Animation issues With Game Character 1 Answer
Animating a character made of multiple objects 0 Answers
First Person Legs. 3 Answers