So overview of my system:
Bear in mind, that I'm not programmer so some of my code might look really stupid and unefficient.
I've separated every limb in blender to make my work easier in Unity.
I've also parented every mesh same way rig is parented.
This way Unity will automatically make mesh hierarchy same as rig hierarchy. This is not mandatory but just makes things easier in Unity.
I'm using Puppetmaster to control character physics and FinalIK for Full Body IK movement which I use to control both hands.
Every breakable skinnedMesh has reference to of all of it's child skinnedMeshes which is called MyLimbs.
Every limb in Puppetmaster ragdoll has a reference to same skinnedMesh limb.
When ragdoll limbs gets too damaged it will break and call method BakeLimbs() from it's skinnedMesh counter part.
SkinnedMesh will then make new temporary GameObject, change position of this empty gameObject same as it is in Ragdoll limb and bake current vertex positions of skinnedMesh to a new gameobject and copy skinnedMesh materials.
Bakedmesh will copy rigidbody from puppetmaster and use reflection to copy whole ConfigurableJoint component from puppetmaster ragdoll to bakedMesh with one change,
ConfigurableJoint.connectedBody changed to correct parent.
Items are parented to Puppetmaster ragdoll so I need to check what is current item and change it's FixedJoint.connectedBody to bakedmesh hand gameobject.
Then I apply same velocities as it's on Puppetmaster Ragdoll so the dismembered limb will break naturally instead of just dropping off.
I had quite alot problems with adding collider with correct orientation so I add collider as a child of this new bakedMesh. This new collider copies properties from Puppetmaster ragdoll, and changes layer to correct one. (I did not know about reflection when I was doing collider part but ofc I could use it also here just like with ConfigurableJoint)
And finally disable old skinned mesh renderer.
It was much more work than I first thought but that is how game development always goes :-)
Here is my current codes, quite un-commented but I hope these are clear enough. Leave a comment if you have any questions!
First the code which is on every detachable skinnedmesh
Second code is on every breakable Puppetmaster ragdoll limb which can break.
Third is both Bone and Item components.
Every limb in Puppetmaster ragdoll has a reference to same skinnedMesh limb.
When ragdoll limbs gets too damaged it will break and call method BakeLimbs() from it's skinnedMesh counter part.
SkinnedMesh will then make new temporary GameObject, change position of this empty gameObject same as it is in Ragdoll limb and bake current vertex positions of skinnedMesh to a new gameobject and copy skinnedMesh materials.
Bakedmesh will copy rigidbody from puppetmaster and use reflection to copy whole ConfigurableJoint component from puppetmaster ragdoll to bakedMesh with one change,
ConfigurableJoint.connectedBody changed to correct parent.
Items are parented to Puppetmaster ragdoll so I need to check what is current item and change it's FixedJoint.connectedBody to bakedmesh hand gameobject.
Then I apply same velocities as it's on Puppetmaster Ragdoll so the dismembered limb will break naturally instead of just dropping off.
I had quite alot problems with adding collider with correct orientation so I add collider as a child of this new bakedMesh. This new collider copies properties from Puppetmaster ragdoll, and changes layer to correct one. (I did not know about reflection when I was doing collider part but ofc I could use it also here just like with ConfigurableJoint)
And finally disable old skinned mesh renderer.
It was much more work than I first thought but that is how game development always goes :-)
Here is my current codes, quite un-commented but I hope these are clear enough. Leave a comment if you have any questions!
First the code which is on every detachable skinnedmesh
Second code is on every breakable Puppetmaster ragdoll limb which can break.
Third is both Bone and Item components.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using UnityEngine; | |
using System.Collections; | |
using System.Collections.Generic; | |
public class SkinnedMeshBaker : MonoBehaviour { | |
public Bone MyBone; | |
public List<SkinnedMeshRenderer> MyLimbs = new List<SkinnedMeshRenderer>(); | |
public GameObject[] StaticMeshes; | |
private CapsuleCollider MyCollider; | |
public Material injured; | |
// Use this for initialization | |
void Start () | |
{ | |
//MyLimbs.Add(this.GetComponent<SkinnedMeshRenderer>()); | |
foreach (SkinnedMeshRenderer smk in transform.GetComponentsInChildren<SkinnedMeshRenderer>()) | |
{ | |
MyLimbs.Add(smk); | |
} | |
StaticMeshes = new GameObject[MyLimbs.Count]; | |
} | |
public void BakeLimbs(CapsuleCollider[] limbColliders, Item item) | |
{ | |
injured = Resources.Load("Materials/Injured",typeof(Material)) as Material; | |
for (int i = 0; i < MyLimbs.Count; i++) | |
{ | |
Mesh bakedMesh = new Mesh(); | |
MyLimbs[i].transform.position = limbColliders[i].transform.position; | |
MyLimbs[i].BakeMesh(bakedMesh); | |
StaticMeshes[i] = new GameObject("dis_"+MyLimbs[i].name); | |
// Setup its transforms | |
StaticMeshes[i].transform.position = MyLimbs[i].transform.position; | |
StaticMeshes[i].transform.rotation = MyLimbs[i].transform.rotation; | |
StaticMeshes[i].transform.localScale = Vector3.one; | |
// Parents | |
if (i == 0) | |
StaticMeshes[i].transform.parent = null; | |
else | |
StaticMeshes[i].transform.parent = StaticMeshes[i - 1].transform; | |
// Colliders | |
GameObject collider = new GameObject("col_"+MyLimbs[i].name); | |
collider.gameObject.layer = 9; | |
CapsuleCollider myCollider = collider.AddComponent<CapsuleCollider>(); | |
collider.transform.position = limbColliders[i].transform.position; | |
collider.transform.rotation = limbColliders[i].transform.rotation; | |
myCollider.center = limbColliders[i].center; | |
myCollider.radius = limbColliders[i].radius; | |
myCollider.height = limbColliders[i].height; | |
myCollider.direction = limbColliders[i].direction; | |
collider.transform.parent = StaticMeshes[i].transform; | |
// Setup the rendering components | |
StaticMeshes[i].AddComponent<MeshFilter>().sharedMesh = bakedMesh; | |
Material[] tempMaterials = MyLimbs[i].sharedMaterials; | |
tempMaterials[1] = injured; | |
StaticMeshes[i].AddComponent<MeshRenderer>().sharedMaterials = tempMaterials; | |
StaticMeshes[i].GetComponent<MeshRenderer>().shadowCastingMode = UnityEngine.Rendering.ShadowCastingMode.On; | |
// Setup rigidbodies | |
StaticMeshes[i].AddComponent<Rigidbody>().mass = limbColliders[i].GetComponent<Rigidbody>().mass; | |
// joints... | |
if (i == 0) | |
{ | |
//nothing | |
} | |
else | |
{ | |
ConfigurableJoint tempjoint = limbColliders[i].GetComponent<ConfigurableJoint>(); | |
StaticMeshes[i].AddComponent<ConfigurableJoint>().GetCopyOf(tempjoint); | |
StaticMeshes[i].GetComponent<ConfigurableJoint>().connectedBody = StaticMeshes[i - 1].GetComponent<Rigidbody>(); | |
StaticMeshes[i].GetComponent<ConfigurableJoint>().anchor = new Vector3(0, 0, 0); | |
} | |
// Setup item | |
if (i == (MyLimbs.Count - 1)) | |
{ | |
item.transform.SetParent(StaticMeshes[i].transform); | |
item.ChangeMyConnectedRigidbody(StaticMeshes[i].GetComponent<Rigidbody>()); | |
} | |
//apply velocity | |
StaticMeshes[i].GetComponent<Rigidbody>().velocity = limbColliders[i].GetComponent<Rigidbody>().velocity*2; | |
StaticMeshes[i].GetComponent<Rigidbody>().angularVelocity = limbColliders[i].GetComponent<Rigidbody>().angularVelocity*2; | |
MyLimbs[i].gameObject.SetActive(false); | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using UnityEngine; | |
using UnityEngine.UI; | |
using System.Collections; | |
using System.Collections.Generic; | |
using RootMotion; | |
using RootMotion.Dynamics; | |
public class JointBreak : MonoBehaviour | |
{ | |
public PuppetMaster puppet; | |
public SkinnedMeshRenderer mySkinnedMesh; | |
private Item myItem; | |
private CapsuleCollider[] _colliders; | |
[SerializeField] | |
private Text myText; | |
[SerializeField] | |
private Text myText2; | |
public bool breakMe = false; | |
public bool breakMe2 = false; | |
// Use this for initialization | |
void Start() | |
{ | |
if (puppet == null) | |
{ | |
Debug.Log(transform.name + ": Puppet empty"); | |
puppet = GetComponentInParent<PuppetMaster>(); | |
} | |
_colliders = GetComponentsInChildren<CapsuleCollider>(); | |
CheckForItem(); | |
} | |
public void CheckForItem() | |
{ | |
foreach (Item child in GetComponentsInChildren<Item>()) | |
myItem = child; | |
} | |
// Update is called once per frame | |
void Update() | |
{ | |
if (myText) | |
{ | |
myText.text = this.GetComponent<Rigidbody>().velocity.ToString(); | |
myText2.text = this.GetComponent<Rigidbody>().angularVelocity.ToString(); | |
} | |
if (breakMe) | |
{ | |
if (Input.GetKeyDown(KeyCode.U)) | |
{ | |
mySkinnedMesh.GetComponent<SkinnedMeshBaker>().BakeLimbs(_colliders, myItem); | |
puppet.RemoveMuscleRecursive (this.GetComponent<ConfigurableJoint> (), false); | |
} | |
} | |
if (breakMe2) | |
{ | |
if (Input.GetKeyDown(KeyCode.I)) | |
{ | |
mySkinnedMesh.GetComponent<SkinnedMeshBaker>().BakeLimbs(_colliders, myItem); | |
puppet.RemoveMuscleRecursive (this.GetComponent<ConfigurableJoint> (), false); | |
} | |
} | |
} | |
void OnJointBreak(float breakForce) | |
{ | |
Debug.Log("A joint '"+transform.name+ "' has just been broken!, force: " + breakForce); | |
DismemberMe(); | |
//transform.parent = null; | |
} | |
public void DismemberMe() | |
{ | |
mySkinnedMesh.GetComponent<SkinnedMeshBaker>().BakeLimbs(_colliders, myItem); | |
puppet.RemoveMuscleRecursive (this.GetComponent<ConfigurableJoint> (), false); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using UnityEngine; | |
using System.Collections; | |
[ExecuteInEditMode] | |
public class Bone : MonoBehaviour { | |
public Bone[] myBones; | |
// Use this for initialization | |
void Awake() | |
{ | |
myBones = GetComponentsInChildren<Bone>(); | |
} | |
-------- | |
item | |
-------- | |
using UnityEngine; | |
using System.Collections; | |
public class Item : MonoBehaviour { | |
// Use this for initialization | |
private FixedJoint fj; | |
void Start () | |
{ | |
fj = GetComponent<FixedJoint>(); | |
} | |
public void ChangeMyConnectedRigidbody(Rigidbody rb) | |
{ | |
fj.connectedBody = rb; | |
} |