- Home /
Can a mesh Collider work with an animated skinned Mesh.
Can I assign a Mesh Collider to a skinned Mesh and have it follow the mesh during animation? To help demonstrate my question I have created a simple animation of a cone which is skinned to a joint. The Joint animates to simply move the cone up. As you can see the Collider has been assigned to the cone.
But once the cone moves, the collider does not.
A simple solution to this one animation is to simply parent the Cone to the Joint. Or to assign the Copy the Cone's Mesh Collider and assign it to the Joint. But these two options would only be efficient for an example as small as the one I have demonstrated. In my current Project I am working with the human anatomy and animations where there are hundreds of skinned mesh objects. From what I have read, this might be impossible but I am curious if Unity has made this possible in recent years. Thank you very much in advance for any help.
Answer by birns92 · Jun 23, 2016 at 07:03 PM
From my own experience trying to update a mesh collider frame by frame is terribly slow, instead I found a work around by simply updating the mesh collider when I need it per theANMATOR2b suggestion. I tweaked the script I found here and added it to each game object with a mesh collider I would like to update.
using UnityEngine;
using System.Collections;
public class SkinnedCollisionHelper : MonoBehaviour
{
// Public variables
// Instance variables
private CWeightList[] nodeWeights; // array of node weights (one per node)
private Vector3[] newVert; // array for the regular update of the collision mesh
private Mesh mesh; // the dynamically-updated collision mesh
private MeshCollider collide; // quick pointer to the mesh collider that we're updating
// Function: Start
// This basically translates the information about the skinned mesh into
// data that we can internally use to quickly update the collision mesh.
void Start()
{
SkinnedMeshRenderer rend = GetComponent(typeof(SkinnedMeshRenderer)) as SkinnedMeshRenderer;
collide = GetComponent(typeof(MeshCollider)) as MeshCollider;
if (collide != null && rend != null)
{
Mesh baseMesh = rend.sharedMesh;
mesh = new Mesh();
mesh.vertices = baseMesh.vertices;
mesh.uv = baseMesh.uv;
mesh.triangles = baseMesh.triangles;
newVert = new Vector3[baseMesh.vertices.Length];
short i;
// Make a CWeightList for each bone in the skinned mesh
nodeWeights = new CWeightList[rend.bones.Length];
for (i = 0; i < rend.bones.Length; i++)
{
nodeWeights[i] = new CWeightList();
nodeWeights[i].transform = rend.bones[i];
}
// Create a bone weight list for each bone, ready for quick calculation during an update...
Vector3 localPt;
for (i = 0; i < baseMesh.vertices.Length; i++)
{
BoneWeight bw = baseMesh.boneWeights[i];
if (bw.weight0 != 0.0f)
{
localPt = baseMesh.bindposes[bw.boneIndex0].MultiplyPoint3x4(baseMesh.vertices[i]);
nodeWeights[bw.boneIndex0].weights.Add(new CVertexWeight(i, localPt, bw.weight0));
}
if (bw.weight1 != 0.0f)
{
localPt = baseMesh.bindposes[bw.boneIndex1].MultiplyPoint3x4(baseMesh.vertices[i]);
nodeWeights[bw.boneIndex1].weights.Add(new CVertexWeight(i, localPt, bw.weight1));
}
if (bw.weight2 != 0.0f)
{
localPt = baseMesh.bindposes[bw.boneIndex2].MultiplyPoint3x4(baseMesh.vertices[i]);
nodeWeights[bw.boneIndex2].weights.Add(new CVertexWeight(i, localPt, bw.weight2));
}
if (bw.weight3 != 0.0f)
{
localPt = baseMesh.bindposes[bw.boneIndex3].MultiplyPoint3x4(baseMesh.vertices[i]);
nodeWeights[bw.boneIndex3].weights.Add(new CVertexWeight(i, localPt, bw.weight3));
}
}
UpdateCollisionMesh();
}
else
{
Debug.LogError(gameObject.name + ": SkinnedCollisionHelper: this object either has no SkinnedMeshRenderer or has no MeshCollider!");
}
}
// Function: UpdateCollisionMesh
// Manually recalculates the collision mesh of the skinned mesh on this
// object.
public void UpdateCollisionMesh()
{
if (mesh != null)
{
// Start by initializing all vertices to 'empty'
for (int i = 0; i < newVert.Length; i++)
{
newVert[i] = new Vector3(0, 0, 0);
}
// Now get the local positions of all weighted indices...
foreach (CWeightList wList in nodeWeights)
{
foreach (CVertexWeight vw in wList.weights)
{
newVert[vw.index] += wList.transform.localToWorldMatrix.MultiplyPoint3x4(vw.localPosition) * vw.weight;
}
}
// Now convert each point into local coordinates of this object.
for (int i = 0; i < newVert.Length; i++)
{
newVert[i] = transform.InverseTransformPoint(newVert[i]);
}
// Update the mesh (& collider) with the updated vertices
mesh.vertices = newVert;
mesh.RecalculateBounds();
collide.sharedMesh = mesh;
}
}
}
class CVertexWeight
{
public int index;
public Vector3 localPosition;
public float weight;
public CVertexWeight(int i, Vector3 p, float w)
{
index = i;
localPosition = p;
weight = w;
}
}
class CWeightList
{
public Transform transform;
public ArrayList weights;
public CWeightList()
{
weights = new ArrayList();
}
}
I called the update collision mesh function from another script and implemented it into another function like this
SkinnedCollisionHelper[] items = FindObjectsOfType<SkinnedCollisionHelper>();
foreach (SkinnedCollisionHelper item in items)
{
item.UpdateCollisionMesh();
}
I use a play animation, and a pause animation function in my game. I simply add the previous short script into my pause function and whenever in the game I click pause, the mesh updates to fit. This is how I implement that into my Play and Pause Animation script.
using UnityEngine;
using System.Collections;
using System;
public class Play_Pause : MonoBehaviour
{
public Animation anim;
public void Play()
{
GetComponent<Animation>().Play();
anim = GetComponent<Animation>();
foreach (AnimationState state in anim)
{
state.speed = 1F;
}
}
public void Pause()
{
SkinnedCollisionHelper[] items = FindObjectsOfType<SkinnedCollisionHelper>();
foreach (SkinnedCollisionHelper item in items)
{
item.UpdateCollisionMesh();
}
anim = GetComponent<Animation>();
foreach (AnimationState state in anim)
{
state.speed = 0F;
}
}
}
This does require you to have a Pause feature in your game. At least for my situation this works great. Hopefully it can be useful for anyone else who may have something similar
I'm was tried that, work slower than Skinned$$anonymous$$eshRenderer.bake$$anonymous$$esh(); But, thanks. :)
Great! Thank you. You provided very clear and excellent solution. I solved my challenges with your code. Thanks again.
Answer by Zodiarc · Jun 03, 2016 at 01:17 PM
I would simply add a capsule collider to every bone that may collide with other game objects (assuming the mesh is rigged). With good clooision layer setup you could even use a single collider to look for collisions with the terrain and the bobe colliders for collision between other non static objects. Assuming you would have a giant creature and a small player you would have cheap collision detection between those two and the environment but the player would still be able to run between the creature's legs without clipping into them.
Answer by Hanz-Olo · Jan 30, 2017 at 10:53 AM
Mesh colliders on moving objects is a strain to the physics simulation. Changing the mesh collider frame by frame to match the animation of a mesh utterly kills it. It is great to see some code that does it, but I wouldn't let this near a commercial product. Best practice : If it moves and needs collision geometry; Use primitive colliders. Only use mesh colliders on complex static objects.
Answer by theANMATOR2b · Jun 03, 2016 at 01:14 PM
Looks like it's not impossible - but pretty 'slow' http://forum.unity3d.com/threads/how-to-update-a-mesh-collider.32467/#post-401202
Might consider an asset to help in your instance. Mega-Fliers or something similar. Probably the best approach.
Also in your case - consider optimization techniques - swapping out complete/combined meshes for individual meshes only when needed.
Seems like a complicated/complex project. Fun!
Answer by chingwaifunggames · Jul 11, 2019 at 03:48 AM
I just wanna say thanks to birns92. your code works perfectly on Unity2019.1.7! you save my day! thank you!
Your answer
Follow this Question
Related Questions
Two nearly identical animated meshes....why does one work and one does not. 1 Answer
Changing skin weights in maya destroys character prefab 0 Answers
Make mesh collider constantly change 1 Answer
Problem with collision detection in an animated object 1 Answer
Best practices for a giant moving environement/enemy 0 Answers