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.
		
		
		
		
		
			
		
			
	
	
		
			114 lines
		
	
	
		
			3.4 KiB
		
	
	
	
		
			C#
		
	
		
		
			
		
	
	
			114 lines
		
	
	
		
			3.4 KiB
		
	
	
	
		
			C#
		
	
| 
								 
											1 year ago
										 
									 | 
							
								using UnityEngine;
							 | 
						||
| 
								 | 
							
								using System.Collections;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								namespace RootMotion.FinalIK {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									/// <summary>
							 | 
						||
| 
								 | 
							
									/// Base class for all FBBIK effector positionOffset modifiers. Works with animatePhysics, safe delegates, offset limits.
							 | 
						||
| 
								 | 
							
									/// </summary>
							 | 
						||
| 
								 | 
							
									public abstract class OffsetModifier: MonoBehaviour {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										/// <summary>
							 | 
						||
| 
								 | 
							
										/// Limiting effector position offsets
							 | 
						||
| 
								 | 
							
										/// </summary>
							 | 
						||
| 
								 | 
							
										[System.Serializable]
							 | 
						||
| 
								 | 
							
										public class OffsetLimits {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											[Tooltip("The effector type (this is just an enum)")]
							 | 
						||
| 
								 | 
							
											public FullBodyBipedEffector effector;
							 | 
						||
| 
								 | 
							
											[Tooltip("Spring force, if zero then this is a hard limit, if not, offset can exceed the limit.")]
							 | 
						||
| 
								 | 
							
											public float spring = 0f;
							 | 
						||
| 
								 | 
							
											[Tooltip("Which axes to limit the offset on?")]
							 | 
						||
| 
								 | 
							
											public bool x, y, z;
							 | 
						||
| 
								 | 
							
											[Tooltip("The limits")]
							 | 
						||
| 
								 | 
							
											public float minX, maxX, minY, maxY, minZ, maxZ;
							 | 
						||
| 
								 | 
							
											
							 | 
						||
| 
								 | 
							
											// Apply the limit to the effector
							 | 
						||
| 
								 | 
							
											public void Apply(IKEffector e, Quaternion rootRotation) {
							 | 
						||
| 
								 | 
							
												Vector3 offset = Quaternion.Inverse(rootRotation) * e.positionOffset;
							 | 
						||
| 
								 | 
							
												
							 | 
						||
| 
								 | 
							
												if (spring <= 0f) {
							 | 
						||
| 
								 | 
							
													// Hard limits
							 | 
						||
| 
								 | 
							
													if (x) offset.x = Mathf.Clamp(offset.x, minX, maxX);
							 | 
						||
| 
								 | 
							
													if (y) offset.y = Mathf.Clamp(offset.y, minY, maxY);
							 | 
						||
| 
								 | 
							
													if (z) offset.z = Mathf.Clamp(offset.z, minZ, maxZ);
							 | 
						||
| 
								 | 
							
												} else {
							 | 
						||
| 
								 | 
							
													// Soft limits
							 | 
						||
| 
								 | 
							
													if (x) offset.x = SpringAxis(offset.x, minX, maxX);
							 | 
						||
| 
								 | 
							
													if (y) offset.y = SpringAxis(offset.y, minY, maxY);
							 | 
						||
| 
								 | 
							
													if (z) offset.z = SpringAxis(offset.z, minZ, maxZ);
							 | 
						||
| 
								 | 
							
												}
							 | 
						||
| 
								 | 
							
												
							 | 
						||
| 
								 | 
							
												// Apply to the effector
							 | 
						||
| 
								 | 
							
												e.positionOffset = rootRotation * offset;
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
											
							 | 
						||
| 
								 | 
							
											// Just math for limiting floats
							 | 
						||
| 
								 | 
							
											private float SpringAxis(float value, float min, float max) {
							 | 
						||
| 
								 | 
							
												if (value > min && value < max) return value;
							 | 
						||
| 
								 | 
							
												if (value < min) return Spring(value, min, true);
							 | 
						||
| 
								 | 
							
												return Spring(value, max, false);
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
											
							 | 
						||
| 
								 | 
							
											// Spring math
							 | 
						||
| 
								 | 
							
											private float Spring(float value, float limit, bool negative) {
							 | 
						||
| 
								 | 
							
												float illegal = value - limit;
							 | 
						||
| 
								 | 
							
												float s = illegal * spring;
							 | 
						||
| 
								 | 
							
												
							 | 
						||
| 
								 | 
							
												if (negative) return value + Mathf.Clamp(-s, 0, -illegal);
							 | 
						||
| 
								 | 
							
												return value - Mathf.Clamp(s, 0, illegal);
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										[Tooltip("The master weight")]
							 | 
						||
| 
								 | 
							
										public float weight = 1f;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										[Tooltip("Reference to the FBBIK component")]
							 | 
						||
| 
								 | 
							
										public FullBodyBipedIK ik;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										// not using Time.deltaTime or Time.fixedDeltaTime here, because we don't know if animatePhysics is true or not on the character, so we have to keep track of time ourselves.
							 | 
						||
| 
								 | 
							
										protected float deltaTime { get { return Time.time - lastTime; }}
							 | 
						||
| 
								 | 
							
										protected abstract void OnModifyOffset();
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										protected float lastTime;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										protected virtual void Start() {
							 | 
						||
| 
								 | 
							
											StartCoroutine(Initiate());
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
										
							 | 
						||
| 
								 | 
							
										private IEnumerator Initiate() {
							 | 
						||
| 
								 | 
							
											while (ik == null) yield return null;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											// You can use just LateUpdate, but note that it doesn't work when you have animatePhysics turned on for the character.
							 | 
						||
| 
								 | 
							
											ik.solver.OnPreUpdate += ModifyOffset;
							 | 
						||
| 
								 | 
							
											lastTime = Time.time;
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										// The main function that checks for all conditions and calls OnModifyOffset if they are met
							 | 
						||
| 
								 | 
							
										private void ModifyOffset() {
							 | 
						||
| 
								 | 
							
											if (!enabled) return;
							 | 
						||
| 
								 | 
							
											if (weight <= 0f) return;
							 | 
						||
| 
								 | 
							
											if (ik == null) return;
							 | 
						||
| 
								 | 
							
											weight = Mathf.Clamp(weight, 0f, 1f);
							 | 
						||
| 
								 | 
							
											if (deltaTime <= 0f) return;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											OnModifyOffset();
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											lastTime = Time.time;
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										protected void ApplyLimits(OffsetLimits[] limits) {
							 | 
						||
| 
								 | 
							
											// Apply the OffsetLimits
							 | 
						||
| 
								 | 
							
											foreach (OffsetLimits limit in limits) {
							 | 
						||
| 
								 | 
							
												limit.Apply(ik.solver.GetEffector(limit.effector), transform.rotation);
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										// Remove the delegate when destroyed
							 | 
						||
| 
								 | 
							
										protected virtual void OnDestroy() {
							 | 
						||
| 
								 | 
							
											if (ik != null) ik.solver.OnPreUpdate -= ModifyOffset;
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								}
							 |