Broken Ragdoll

 avatar
unknown
csharp
3 years ago
8.4 kB
9
Indexable
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Animations.Rigging;

	public class Ragdoll : MonoBehaviour
	{
		Animator animator;
    [SerializeField]
    float ragdollMecanimBlendTime = 0.5f;  //How long do we blend when transitioning from ragdolled to animated
	public bool blendToAnim;
	float _ragdollingEndTime;   //A helper variable to store the time when we transitioned from ragdolled to blendToAnim state

	readonly List<Limb> limbs = new List<Limb>();
	readonly List<TransformComponent> _transforms = new List<TransformComponent>();

	Transform _hipsTransform;
	//Rigidbody _hipsTransformRigid;

	Vector3 _storedHipsPosition;
	Vector3 _storedHipsPositionPrivAnim;
	Vector3 _storedHipsPositionPrivBlend;

	private void Start()
    {
        animator = GetComponent<Animator>();
		_hipsTransform = animator.GetBoneTransform(HumanBodyBones.Hips);
		//_hipsTransformRigid = _hipsTransform.GetComponent<Rigidbody>();
		
		//Get all the rigid bodies that belong to the ragdoll
		Rigidbody[] rigidBodies = GetComponentsInChildren<Rigidbody>();

		foreach (Rigidbody rigidbody in rigidBodies)
		{
			if (rigidbody.transform == transform)
				continue;

			Limb thisLimb = new Limb(rigidbody);
			limbs.Add(thisLimb);
		}

		// disable ragdoll by default
		setRagdoll(false);
	}

	struct Limb
	{
		public readonly Rigidbody RigidBody;
		public readonly CharacterJoint Joint;
		public readonly Vector3 ConnectedAnchorDefault;

		public Limb(Rigidbody rigid)
		{
			RigidBody = rigid;
			Joint = rigid.GetComponent<CharacterJoint>();
			if (Joint != null)
				ConnectedAnchorDefault = Joint.connectedAnchor;
			else
				ConnectedAnchorDefault = Vector3.zero;
		}
	}

	private void setRagdoll(bool state)
    {
		// SET HERE CHARACTER RELATED STUFF \\ -- CharacterEnable(!state);

		foreach (var limb in limbs)
		{
			Collider partColider = limb.RigidBody.GetComponent<Collider>();

			// fix for RagdollHelper (bone collider - BoneHelper.cs)
			if (partColider == null)
			{
				const string colliderNodeSufix = "_ColliderRotator"; //Make empty child for root(hips) and rename it to same was root self + _ColliderRotator
				string childName = limb.RigidBody.name + colliderNodeSufix;
				Transform transform = limb.RigidBody.transform.Find(childName);
				partColider = transform.GetComponent<Collider>();
			}

			partColider.isTrigger = !state;

			if (state)
			{
				limb.RigidBody.isKinematic = false;
				StartCoroutine(FixTransformAndEnableJoint(limb));
			}
			else
				limb.RigidBody.isKinematic = true;
		}
	}

	private void GetUp()
	{
		//Transition from ragdolled to animated through the blendToAnim state
		_ragdollingEndTime = Time.time;     //store the state change time
											//_anim.SetFloat(_animatorForward, 0f);
											//_anim.SetFloat(_animatorTurn, 0f);
		animator.enabled = true;               //enable animation
		blendToAnim = true;
		_storedHipsPositionPrivAnim = Vector3.zero;
		_storedHipsPositionPrivBlend = Vector3.zero;

		_storedHipsPosition = _hipsTransform.position;

		// get distanse to floor
		Vector3 shiftPos = _hipsTransform.position - transform.position;
		//shiftPos.y = GetDistanceToFloor(shiftPos.y);

		// shift and rotate character node without children
		//MoveNodeWithoutChildren(shiftPos);

		//Store the ragdolled position for blending
		foreach (TransformComponent trComp in _transforms)
		{
			trComp.StoredRotation = trComp.Transform.localRotation;
			trComp.PrivRotation = trComp.Transform.localRotation;

			trComp.StoredPosition = trComp.Transform.localPosition;
			trComp.PrivPosition = trComp.Transform.localPosition;
		}

		//Initiate the get up animation
		string getUpAnim = "GetUp.StandUp_Back"; //"GetUp.StandUp_Belly"
		animator.Play(getUpAnim, 0, 0);    // you have to set time to 0, or if your animation will interrupt, next time animation starts from previous position
		setRagdoll(false);    // disable gravity on ragdollParts.
	}

	static IEnumerator FixTransformAndEnableJoint(Limb joint)
	{
		if (joint.Joint == null || !joint.Joint.autoConfigureConnectedAnchor)
			yield break;

		SoftJointLimit highTwistLimit = new SoftJointLimit();
		SoftJointLimit lowTwistLimit = new SoftJointLimit();
		SoftJointLimit swing1Limit = new SoftJointLimit();
		SoftJointLimit swing2Limit = new SoftJointLimit();

		SoftJointLimit curHighTwistLimit = highTwistLimit = joint.Joint.highTwistLimit;
		SoftJointLimit curLowTwistLimit = lowTwistLimit = joint.Joint.lowTwistLimit;
		SoftJointLimit curSwing1Limit = swing1Limit = joint.Joint.swing1Limit;
		SoftJointLimit curSwing2Limit = swing2Limit = joint.Joint.swing2Limit;

		float aTime = 0.3f;
		Vector3 startConPosition = joint.Joint.connectedBody.transform.InverseTransformVector(joint.Joint.transform.position - joint.Joint.connectedBody.transform.position);

		joint.Joint.autoConfigureConnectedAnchor = false;
		for (float t = 0.0f; t < 1.0f; t += Time.deltaTime / aTime)
		{
			Vector3 newConPosition = Vector3.Lerp(startConPosition, joint.ConnectedAnchorDefault, t);
			joint.Joint.connectedAnchor = newConPosition;

			curHighTwistLimit.limit = Mathf.Lerp(177, highTwistLimit.limit, t);
			curLowTwistLimit.limit = Mathf.Lerp(-177, lowTwistLimit.limit, t);
			curSwing1Limit.limit = Mathf.Lerp(177, swing1Limit.limit, t);
			curSwing2Limit.limit = Mathf.Lerp(177, swing2Limit.limit, t);

			joint.Joint.highTwistLimit = curHighTwistLimit;
			joint.Joint.lowTwistLimit = curLowTwistLimit;
			joint.Joint.swing1Limit = curSwing1Limit;
			joint.Joint.swing2Limit = curSwing2Limit;


			yield return null;
		}
		joint.Joint.connectedAnchor = joint.ConnectedAnchorDefault;
		yield return new WaitForFixedUpdate();
		joint.Joint.autoConfigureConnectedAnchor = true;


		joint.Joint.highTwistLimit = highTwistLimit;
		joint.Joint.lowTwistLimit = lowTwistLimit;
		joint.Joint.swing1Limit = swing1Limit;
		joint.Joint.swing2Limit = swing2Limit;
	}


	private void Update()
    {
        if(Input.GetKeyDown(KeyCode.DownArrow))
        {
			setRagdoll(true);
			animator.enabled = false;
        }

		if (Input.GetKeyDown(KeyCode.UpArrow))
		{
			setRagdoll(false);
			GetUp();
		}
	}

    private void LateUpdate()
    {
		if (!blendToAnim)
			return;

		float ragdollBlendAmount = 1f - Mathf.InverseLerp(_ragdollingEndTime, _ragdollingEndTime + ragdollMecanimBlendTime, Time.time);

		// In LateUpdate(), Mecanim has already updated the body pose according to the animations.
		// To enable smooth transitioning from a ragdoll to animation, we lerp the position of the hips
		// and slerp all the rotations towards the ones stored when ending the ragdolling

		if (_storedHipsPositionPrivBlend != _hipsTransform.position)
		{
			_storedHipsPositionPrivAnim = _hipsTransform.position;
		}
		_storedHipsPositionPrivBlend = Vector3.Lerp(_storedHipsPositionPrivAnim, _storedHipsPosition, ragdollBlendAmount);
		_hipsTransform.position = _storedHipsPositionPrivBlend;

		foreach (TransformComponent trComp in _transforms)
		{
			//rotation is interpolated for all body parts
			if (trComp.PrivRotation != trComp.Transform.localRotation)
			{
				trComp.PrivRotation = Quaternion.Slerp(trComp.Transform.localRotation, trComp.StoredRotation, ragdollBlendAmount);
				trComp.Transform.localRotation = trComp.PrivRotation;
			}

			//position is interpolated for all body parts
			if (trComp.PrivPosition != trComp.Transform.localPosition)
			{
				trComp.PrivPosition = Vector3.Slerp(trComp.Transform.localPosition, trComp.StoredPosition, ragdollBlendAmount);
				trComp.Transform.localPosition = trComp.PrivPosition;
			}
		}

		//if the ragdoll blend amount has decreased to zero, move to animated state
		if (Mathf.Abs(ragdollBlendAmount) < Mathf.Epsilon)
		{
			blendToAnim = false;
		}
	}

	//Declare a class that will hold useful information for each body part
	sealed class TransformComponent
	{
		public readonly Transform Transform;
		public Quaternion PrivRotation;
		public Quaternion StoredRotation;

		public Vector3 PrivPosition;
		public Vector3 StoredPosition;

		public TransformComponent(Transform t)
		{
			Transform = t;
		}
	}
}
Editor is loading...