using UnityEngine; using System.Collections; namespace RootMotion.Dynamics { [System.Serializable] public class SubBehaviourCOM: SubBehaviourBase { [System.Serializable] public enum Mode { FeetCentroid, CenterOfPressure } public Mode mode; public float velocityDamper = 1f; public float velocityLerpSpeed = 5f; public float velocityMax = 1f; public float centerOfPressureSpeed = 5f; public Vector3 offset; public Vector3 position { get; private set; } public Vector3 direction { get; private set; } public float angle { get; private set; } public Vector3 velocity { get; private set; } public Vector3 centerOfPressure { get; private set; } public Quaternion rotation { get; private set; } public Quaternion inverseRotation { get; private set; } public bool isGrounded { get; private set; } public float lastGroundedTime { get; private set; } [HideInInspector] public bool[] groundContacts; [HideInInspector] public Vector3[] groundContactPoints; private LayerMask groundLayers; public void Initiate(BehaviourBase behaviour, LayerMask groundLayers) { this.behaviour = behaviour; this.groundLayers = groundLayers; rotation = Quaternion.identity; groundContacts = new bool[behaviour.puppetMaster.muscles.Length]; groundContactPoints = new Vector3[groundContacts.Length]; behaviour.OnPreActivate += OnPreActivate; behaviour.OnPreLateUpdate += OnPreLateUpdate; behaviour.OnPreDeactivate += OnPreDeactivate; behaviour.OnPreMuscleCollision += OnPreMuscleCollision; behaviour.OnPreMuscleCollisionExit += OnPreMuscleCollisionExit; behaviour.OnHierarchyChanged += OnHierarchyChanged; } #region Behaviour Delegates private void OnHierarchyChanged() { System.Array.Resize (ref groundContacts, behaviour.puppetMaster.muscles.Length); System.Array.Resize (ref groundContactPoints, behaviour.puppetMaster.muscles.Length); } private void OnPreMuscleCollision(MuscleCollision c) { if (!LayerMaskExtensions.Contains(groundLayers, c.collision.gameObject.layer)) return; if (c.collision.contacts.Length == 0) return; lastGroundedTime = Time.time; groundContacts[c.muscleIndex] = true; if (mode == Mode.CenterOfPressure) groundContactPoints[c.muscleIndex] = GetCollisionCOP(c.collision); } private void OnPreMuscleCollisionExit(MuscleCollision c) { if (!LayerMaskExtensions.Contains(groundLayers, c.collision.gameObject.layer)) return; groundContacts[c.muscleIndex] = false; groundContactPoints[c.muscleIndex] = Vector3.zero; } private void OnPreActivate() { position = GetCenterOfMass(); centerOfPressure = GetFeetCentroid(); direction = position - centerOfPressure; angle = Vector3.Angle(direction, Vector3.up); velocity = Vector3.zero; } private void OnPreLateUpdate(float deltaTime) { // Ground contact isGrounded = IsGrounded(); // COP if (mode == Mode.FeetCentroid || !isGrounded) { centerOfPressure = GetFeetCentroid(); } else { Vector3 centerOfPressureTarget = isGrounded? GetCenterOfPressure(): GetFeetCentroid(); centerOfPressure = centerOfPressureSpeed <= 02? centerOfPressureTarget: Vector3.Lerp(centerOfPressure, centerOfPressureTarget, deltaTime * centerOfPressureSpeed); } // COM position = GetCenterOfMass(); // COM Velocity Vector3 velocityPosition = GetCenterOfMassVelocity(); Vector3 velocityTarget = velocityPosition - position; velocityTarget.y = 0f; velocityTarget = Vector3.ClampMagnitude(velocityTarget, velocityMax); // Add velocity to position velocity = velocityLerpSpeed <= 0f? velocityTarget: Vector3.Lerp(velocity, velocityTarget, deltaTime * velocityLerpSpeed); position += velocity * velocityDamper; position += behaviour.puppetMaster.targetRoot.rotation * offset; // Calculate COM direction, rotation and angle direction = position - centerOfPressure; rotation = Quaternion.FromToRotation(Vector3.up, direction); inverseRotation = Quaternion.Inverse(rotation); angle = Quaternion.Angle(Quaternion.identity, rotation); } private void OnPreDeactivate() { velocity = Vector3.zero; } #endregion Behaviour Delegates private Vector3 GetCollisionCOP(Collision collision) { Vector3 sum = Vector3.zero; for (int i = 0; i < collision.contacts.Length; i++) { sum += collision.contacts[i].point; } return sum / (float)collision.contacts.Length; } private bool IsGrounded() { for (int i = 0; i < groundContacts.Length; i++) { if (groundContacts[i]) return true; } return false; } private Vector3 GetCenterOfMass() { Vector3 CoM = Vector3.zero; float c = 0f; foreach (Muscle m in behaviour.puppetMaster.muscles) { CoM += m.rigidbody.worldCenterOfMass * m.rigidbody.mass; c += m.rigidbody.mass; } return CoM /= c; } private Vector3 GetCenterOfMassVelocity() { Vector3 CoM = Vector3.zero; float c = 0f; foreach (Muscle m in behaviour.puppetMaster.muscles) { CoM += m.rigidbody.worldCenterOfMass * m.rigidbody.mass; CoM += m.rigidbody.velocity * m.rigidbody.mass; c += m.rigidbody.mass; } return CoM /= c; } private Vector3 GetMomentum() { Vector3 sum = Vector3.zero; for (int i = 0; i < behaviour.puppetMaster.muscles.Length; i++) { sum += behaviour.puppetMaster.muscles[i].rigidbody.velocity * behaviour.puppetMaster.muscles[i].rigidbody.mass; } return sum; } private Vector3 GetCenterOfPressure() { Vector3 sum = Vector3.zero; int contacts = 0; for (int i = 0; i < groundContacts.Length; i++) { if (groundContacts[i]) { sum += groundContactPoints[i]; contacts ++; } } if (contacts != 0) sum /= (float)contacts; return sum; } private Vector3 GetFeetCentroid() { Vector3 sum = Vector3.zero; int contacts = 0; /* int feetGrounded = 0; for (int i = 0; i < behaviour.puppetMaster.muscles.Length; i++) { if (behaviour.puppetMaster.muscles[i].props.group == Muscle.Group.Foot && groundContacts[i]) feetGrounded ++; } for (int i = 0; i < behaviour.puppetMaster.muscles.Length; i++) { if (behaviour.puppetMaster.muscles[i].props.group == Muscle.Group.Foot) { if (feetGrounded == 0 || (feetGrounded > 0 && groundContacts[i])) { sum += behaviour.puppetMaster.muscles[i].rigidbody.worldCenterOfMass; contacts ++; } } } */ for (int i = 0; i < behaviour.puppetMaster.muscles.Length; i++) { if (behaviour.puppetMaster.muscles[i].props.group == Muscle.Group.Foot) { sum += behaviour.puppetMaster.muscles[i].rigidbody.worldCenterOfMass; contacts ++; } } if (contacts == 0) Debug.LogError("Puppet has no muscles assigned to the Foot group. Please make sure you have a muscle group assigned for every muscle in PuppetMaster."); else sum /= (float)contacts; return sum; } } }