You cannot select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
	
	
		
			254 lines
		
	
	
		
			8.1 KiB
		
	
	
	
		
			C#
		
	
			
		
		
	
	
			254 lines
		
	
	
		
			8.1 KiB
		
	
	
	
		
			C#
		
	
using UnityEngine;
 | 
						|
using System.Collections;
 | 
						|
 | 
						|
namespace RootMotion.FinalIK {
 | 
						|
 | 
						|
	/// <summary>
 | 
						|
	/// Class for creating procedural FBBIK hit reactions.
 | 
						|
	/// </summary>
 | 
						|
	public class HitReaction : OffsetModifier {
 | 
						|
 | 
						|
		/// <summary>
 | 
						|
		/// Hit point definition
 | 
						|
		/// </summary>
 | 
						|
		[System.Serializable]
 | 
						|
		public abstract class HitPoint {
 | 
						|
 | 
						|
			[Tooltip("Just for visual clarity, not used at all")]
 | 
						|
			public string name;
 | 
						|
			[Tooltip("Linking this hit point to a collider")]
 | 
						|
			public Collider collider;
 | 
						|
			[Tooltip("Only used if this hit point gets hit when already processing another hit")]
 | 
						|
			[SerializeField] float crossFadeTime = 0.1f;
 | 
						|
 | 
						|
			public bool inProgress { get { return timer < length; } }
 | 
						|
 | 
						|
			protected float crossFader { get; private set; }
 | 
						|
			protected float timer { get; private set; }
 | 
						|
			protected Vector3 force { get; private set; }
 | 
						|
			
 | 
						|
			private float length;
 | 
						|
			private float crossFadeSpeed;
 | 
						|
			private float lastTime;
 | 
						|
 | 
						|
			// Start processing the hit
 | 
						|
			public virtual void Hit(Vector3 force, Vector3 point) {
 | 
						|
				if (length == 0f) length = GetLength();
 | 
						|
				if (length <= 0f) {
 | 
						|
					Debug.LogError("Hit Point WeightCurve length is zero.");
 | 
						|
					return;
 | 
						|
				}
 | 
						|
 | 
						|
				// Start crossfading if the last hit has not completed yet
 | 
						|
				if (timer < 1f) crossFader = 0f;
 | 
						|
				crossFadeSpeed = crossFadeTime > 0f? 1f / crossFadeTime: 0f;
 | 
						|
				CrossFadeStart();
 | 
						|
 | 
						|
				// Reset timer
 | 
						|
				timer = 0f;
 | 
						|
 | 
						|
				// Remember hit direction and point
 | 
						|
				this.force = force;
 | 
						|
			}
 | 
						|
 | 
						|
			// Apply to IKSolverFullBodyBiped
 | 
						|
			public void Apply(IKSolverFullBodyBiped solver, float weight) {
 | 
						|
				float deltaTime = Time.time - lastTime;
 | 
						|
				lastTime = Time.time;
 | 
						|
 | 
						|
				if (timer >= length) {
 | 
						|
					return;
 | 
						|
				}
 | 
						|
 | 
						|
				// Advance the timer
 | 
						|
				timer = Mathf.Clamp(timer + deltaTime, 0f, length);
 | 
						|
 | 
						|
				// Advance the crossFader
 | 
						|
				if (crossFadeSpeed > 0f) crossFader = Mathf.Clamp(crossFader + (deltaTime * crossFadeSpeed), 0f, 1f);
 | 
						|
				else crossFader = 1f;
 | 
						|
 | 
						|
				// Pass this on to the hit points
 | 
						|
				OnApply(solver, weight);
 | 
						|
			}
 | 
						|
 | 
						|
			protected abstract float GetLength();
 | 
						|
			protected abstract void CrossFadeStart();
 | 
						|
			protected abstract void OnApply(IKSolverFullBodyBiped solver, float weight);
 | 
						|
		}
 | 
						|
 | 
						|
		/// <summary>
 | 
						|
		/// Hit Point for FBBIK effectors
 | 
						|
		/// </summary>
 | 
						|
		[System.Serializable]
 | 
						|
		public class HitPointEffector: HitPoint {
 | 
						|
 | 
						|
			/// <summary>
 | 
						|
			/// Linking a FBBIK effector to this effector hit point
 | 
						|
			/// </summary>
 | 
						|
			[System.Serializable]
 | 
						|
			public class EffectorLink {
 | 
						|
				[Tooltip("The FBBIK effector type")]
 | 
						|
				public FullBodyBipedEffector effector;
 | 
						|
				[Tooltip("The weight of this effector (could also be negative)")]
 | 
						|
				public float weight;
 | 
						|
 | 
						|
				private Vector3 lastValue;
 | 
						|
				private Vector3 current;
 | 
						|
 | 
						|
				// Apply an offset to this effector
 | 
						|
				public void Apply(IKSolverFullBodyBiped solver, Vector3 offset, float crossFader) {
 | 
						|
					current = Vector3.Lerp(lastValue, offset * weight, crossFader);
 | 
						|
 | 
						|
					solver.GetEffector(effector).positionOffset += current;
 | 
						|
				}
 | 
						|
 | 
						|
				// Remember the current offset value, so we can smoothly crossfade from it
 | 
						|
				public void CrossFadeStart() {
 | 
						|
					lastValue = current;
 | 
						|
				}
 | 
						|
			}
 | 
						|
 | 
						|
			[Tooltip("Offset magnitude in the direction of the hit force")]
 | 
						|
			public AnimationCurve offsetInForceDirection; // 
 | 
						|
			[Tooltip("Offset magnitude in the direction of character.up")]
 | 
						|
			public AnimationCurve offsetInUpDirection; // 
 | 
						|
			[Tooltip("Linking this offset to the FBBIK effectors")]
 | 
						|
			public EffectorLink[] effectorLinks;
 | 
						|
 | 
						|
			// Returns the length of this hit (last key in the AnimationCurves)
 | 
						|
			protected override float GetLength() {
 | 
						|
				float time1 = offsetInForceDirection.keys.Length > 0? offsetInForceDirection.keys[offsetInForceDirection.length - 1].time: 0f;
 | 
						|
				float time2 = offsetInUpDirection.keys.Length > 0? offsetInUpDirection.keys[offsetInUpDirection.length - 1].time: 0f;
 | 
						|
				return Mathf.Clamp(time1, time2, time1);
 | 
						|
			}
 | 
						|
 | 
						|
			// Remember the current offset values for each effector, so we can smoothly crossfade from it
 | 
						|
			protected override void CrossFadeStart() {
 | 
						|
				foreach (EffectorLink e in effectorLinks) e.CrossFadeStart();
 | 
						|
			}
 | 
						|
 | 
						|
			// Calculate offset, apply to FBBIK effectors
 | 
						|
			protected override void OnApply(IKSolverFullBodyBiped solver, float weight) {
 | 
						|
				Vector3 up = solver.GetRoot().up * force.magnitude;
 | 
						|
 | 
						|
				Vector3 offset = (offsetInForceDirection.Evaluate(timer) * force) + (offsetInUpDirection.Evaluate(timer) * up);
 | 
						|
				offset *= weight;
 | 
						|
 | 
						|
				foreach (EffectorLink e in effectorLinks) e.Apply(solver, offset, crossFader);
 | 
						|
			}
 | 
						|
 | 
						|
		}
 | 
						|
 | 
						|
		/// <summary>
 | 
						|
		/// Hit Point for simple bone Transforms that don't have a FBBIK effector
 | 
						|
		/// </summary>
 | 
						|
		[System.Serializable]
 | 
						|
		public class HitPointBone: HitPoint {
 | 
						|
 | 
						|
			/// <summary>
 | 
						|
			/// Linking a bone Transform to this bone hit point
 | 
						|
			/// </summary>
 | 
						|
			[System.Serializable]
 | 
						|
			public class BoneLink {
 | 
						|
				[Tooltip("Reference to the bone that this hit point rotates")]
 | 
						|
				public Transform bone;
 | 
						|
				[Tooltip("Weight of rotating the bone")]
 | 
						|
				[Range(0f, 1f)] public float weight;
 | 
						|
 | 
						|
				private Quaternion lastValue = Quaternion.identity;
 | 
						|
				private Quaternion current = Quaternion.identity;
 | 
						|
                
 | 
						|
				// Apply a rotational offset to this effector
 | 
						|
				public void Apply(IKSolverFullBodyBiped solver, Quaternion offset, float crossFader) {
 | 
						|
					current = Quaternion.Lerp(lastValue, Quaternion.Lerp(Quaternion.identity, offset, weight), crossFader);
 | 
						|
 | 
						|
					bone.rotation = current * bone.rotation;
 | 
						|
				}
 | 
						|
 | 
						|
				// Remember the current offset value, so we can smoothly crossfade from it
 | 
						|
				public void CrossFadeStart() {
 | 
						|
					lastValue = current;
 | 
						|
				}
 | 
						|
			}
 | 
						|
 | 
						|
			[Tooltip("The angle to rotate the bone around its rigidbody's world center of mass")]
 | 
						|
			public AnimationCurve aroundCenterOfMass;
 | 
						|
			[Tooltip("Linking this hit point to bone(s)")]
 | 
						|
			public BoneLink[] boneLinks;
 | 
						|
 | 
						|
			private Rigidbody rigidbody;
 | 
						|
            private Vector3 comAxis;
 | 
						|
 | 
						|
            public override void Hit(Vector3 force, Vector3 point)
 | 
						|
            {
 | 
						|
                base.Hit(force, point);
 | 
						|
 | 
						|
                if (rigidbody == null) rigidbody = collider.GetComponent<Rigidbody>();
 | 
						|
 | 
						|
                Vector3 com = rigidbody != null ? rigidbody.worldCenterOfMass : collider.transform.position;
 | 
						|
                comAxis = Vector3.Cross(force, point - com);
 | 
						|
            }
 | 
						|
 | 
						|
            // Returns the length of this hit (last key in the AnimationCurves)
 | 
						|
            protected override float GetLength() {
 | 
						|
				return aroundCenterOfMass.keys.Length > 0? aroundCenterOfMass.keys[aroundCenterOfMass.length - 1].time: 0f;
 | 
						|
			}
 | 
						|
 | 
						|
			// Remember the current offset values for each bone, so we can smoothly crossfade from it
 | 
						|
			protected override void CrossFadeStart() {
 | 
						|
				foreach (BoneLink b in boneLinks) b.CrossFadeStart();
 | 
						|
			}
 | 
						|
 | 
						|
			// Calculate offset, apply to the bones
 | 
						|
			protected override void OnApply(IKSolverFullBodyBiped solver, float weight) {
 | 
						|
				float comValue = aroundCenterOfMass.Evaluate(timer) * weight;
 | 
						|
				Quaternion offset = Quaternion.AngleAxis(comValue, comAxis);
 | 
						|
 | 
						|
				foreach (BoneLink b in boneLinks) b.Apply(solver, offset, crossFader);
 | 
						|
			}
 | 
						|
		}
 | 
						|
 | 
						|
		[Tooltip("Hit points for the FBBIK effectors")]
 | 
						|
		public HitPointEffector[] effectorHitPoints;
 | 
						|
		[Tooltip(" Hit points for bones without an effector, such as the head")]
 | 
						|
		public HitPointBone[] boneHitPoints;
 | 
						|
 | 
						|
		/// <summary>
 | 
						|
		/// Returns true if any of the hits are being processed.
 | 
						|
		/// </summary>
 | 
						|
		public bool inProgress {
 | 
						|
			get {
 | 
						|
				foreach (HitPointEffector h in effectorHitPoints) {
 | 
						|
					if (h.inProgress) return true;
 | 
						|
				}
 | 
						|
				foreach (HitPointBone h in boneHitPoints) {
 | 
						|
					if (h.inProgress) return true;
 | 
						|
				}
 | 
						|
				return false;
 | 
						|
			}
 | 
						|
		}
 | 
						|
 | 
						|
		// Called by IKSolverFullBody before updating
 | 
						|
		protected override void OnModifyOffset() {
 | 
						|
			foreach (HitPointEffector e in effectorHitPoints) e.Apply(ik.solver, weight);
 | 
						|
			foreach (HitPointBone b in boneHitPoints) b.Apply(ik.solver, weight);
 | 
						|
		}
 | 
						|
 | 
						|
		// Hit one of the hit points (defined by hit.collider)
 | 
						|
		public void Hit(Collider collider, Vector3 force, Vector3 point) {
 | 
						|
			if (ik == null) {
 | 
						|
				Debug.LogError("No IK assigned in HitReaction");
 | 
						|
				return;
 | 
						|
			}
 | 
						|
 | 
						|
			foreach (HitPointEffector e in effectorHitPoints) {
 | 
						|
				if (e.collider == collider) e.Hit(force, point);
 | 
						|
			}
 | 
						|
 | 
						|
			foreach (HitPointBone b in boneHitPoints) {
 | 
						|
				if (b.collider == collider) b.Hit(force, point);
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
}
 |