using UnityEngine; using System.Collections; using RootMotion.FinalIK; namespace RootMotion.Demos { /// /// Leg of the Mech spider. Controls stepping and positioning the IK target /// public class MechSpiderLeg : MonoBehaviour { public MechSpider mechSpider; // Reference to the target public MechSpiderLeg unSync; // One of the other legs that we dont want to be completely in sync with, that is stepping at the same time public Vector3 offset; // Offset from the default position public float minDelay = 0.2f, maxOffset = 1.0f, stepSpeed = 5.0f, footHeight = 0.15f, velocityPrediction = 0.2f, raycastFocus = 0.1f; // Parameters for stepping public AnimationCurve yOffset; public Transform foot; public Vector3 footUpAxis; public float footRotationSpeed = 10f; public ParticleSystem sand; // FX for sand private IK ik; private float stepProgress = 1f, lastStepTime; private Vector3 defaultPosition; private RaycastHit hit = new RaycastHit(); private Quaternion lastFootLocalRotation; private Vector3 smoothHitNormal = Vector3.up; private Vector3 lastStepPosition; // Is the leg stepping? public bool isStepping { get { return stepProgress < 1f; } } // Gets and sets the IK position for this leg public Vector3 position { get { return ik.GetIKSolver().GetIKPosition(); } set { ik.GetIKSolver().SetIKPosition(value); } } void Awake() { // Find the ik component ik = GetComponent(); if (foot != null) { if (footUpAxis == Vector3.zero) footUpAxis = Quaternion.Inverse(foot.rotation) * Vector3.up; lastFootLocalRotation = foot.localRotation; ik.GetIKSolver().OnPostUpdate += AfterIK; } } private void AfterIK() { if (foot == null) return; foot.localRotation = lastFootLocalRotation; smoothHitNormal = Vector3.Slerp(smoothHitNormal, hit.normal, Time.deltaTime * footRotationSpeed); Quaternion f = Quaternion.FromToRotation(foot.rotation * footUpAxis, smoothHitNormal); foot.rotation = f * foot.rotation; } void Start() { // Workaround for Unity Win Store/Phone serialization bug stepProgress = 1f; hit = new RaycastHit(); var points = ik.GetIKSolver().GetPoints(); position = points[points.Length - 1].transform.position; lastStepPosition = position; hit.point = position; // Store the default rest position of the leg defaultPosition = mechSpider.transform.InverseTransformPoint(position + offset * mechSpider.scale); StartCoroutine(Step(position, position)); } // Find the relaxed grounded positon of the leg relative to the body in world space. private Vector3 GetStepTarget(out bool stepFound, float focus, float distance) { stepFound = false; // place hit.point to the default position relative to the body Vector3 stepTarget = mechSpider.transform.TransformPoint(defaultPosition); stepTarget += (hit.point - position) * velocityPrediction; Vector3 up = mechSpider.transform.up; // Focus the ray directions towards the spider body Vector3 toBody = mechSpider.body.position - position; Vector3 axis = Vector3.Cross(up, toBody); up = Quaternion.AngleAxis(focus, axis) * up; // Raycast to ground the relaxed position if (Physics.Raycast(stepTarget + up * mechSpider.raycastHeight * mechSpider.scale, -up, out hit, mechSpider.raycastHeight * mechSpider.scale + distance, mechSpider.raycastLayers)) stepFound = true; //return hit.point + mechSpider.transform.up * footHeight * mechSpider.scale; return hit.point + hit.normal * footHeight * mechSpider.scale; } private void UpdatePosition(float distance) { Vector3 up = mechSpider.transform.up; if (Physics.Raycast(lastStepPosition + up * mechSpider.raycastHeight * mechSpider.scale, -up, out hit, mechSpider.raycastHeight * mechSpider.scale + distance, mechSpider.raycastLayers)) { position = hit.point + hit.normal * footHeight * mechSpider.scale; } } void Update () { UpdatePosition(mechSpider.raycastDistance * mechSpider.scale); // if already stepping, do nothing if (isStepping) return; // Minimum delay before stepping again if (Time.time < lastStepTime + minDelay) return; // If the unSync leg is stepping, do nothing if (unSync != null) { if (unSync.isStepping) return; } // Find the ideal relaxed position for the leg relative to the body bool stepFound = false; Vector3 idealPosition = GetStepTarget(out stepFound, raycastFocus, mechSpider.raycastDistance * mechSpider.scale); if (!stepFound) idealPosition = GetStepTarget(out stepFound, -raycastFocus, mechSpider.raycastDistance * 3f * mechSpider.scale); // Try again with inverted focus if (!stepFound) return; // If distance to that ideal position is less than the threshold, do nothing if (Vector3.Distance(position, idealPosition) < maxOffset * mechSpider.scale * UnityEngine.Random.Range(0.9f, 1.2f)) return; // Need to step closer to the ideal position StopAllCoroutines(); StartCoroutine(Step(position, idealPosition)); } // Stepping co-routine private IEnumerator Step(Vector3 stepStartPosition, Vector3 targetPosition) { stepProgress = 0f; // Moving the IK position while (stepProgress < 1) { stepProgress += Time.deltaTime * stepSpeed; position = Vector3.Lerp(stepStartPosition, targetPosition, stepProgress); position += mechSpider.transform.up * yOffset.Evaluate(stepProgress) * mechSpider.scale; lastStepPosition = position; yield return null; } position = targetPosition; lastStepPosition = position; // Emit sand if (sand != null) { sand.transform.position = position - mechSpider.transform.up * footHeight * mechSpider.scale; sand.Emit(20); } lastStepTime = Time.time; } } }