Files
2026-03-04 10:03:45 +08:00

170 lines
4.6 KiB
C#

using System;
using System.Collections;
using RootMotion.FinalIK;
using UnityEngine;
namespace RootMotion.Demos
{
public class MechSpiderLeg : MonoBehaviour
{
public MechSpider mechSpider;
public MechSpiderLeg unSync;
public Vector3 offset;
public float minDelay = 0.2f;
public float maxOffset = 1f;
public float stepSpeed = 5f;
public float footHeight = 0.15f;
public float velocityPrediction = 0.2f;
public float raycastFocus = 0.1f;
public AnimationCurve yOffset;
public Transform foot;
public Vector3 footUpAxis;
public float footRotationSpeed = 10f;
public ParticleSystem sand;
private IK ik;
private float stepProgress = 1f;
private float lastStepTime;
private Vector3 defaultPosition;
private RaycastHit hit;
private Quaternion lastFootLocalRotation;
private Vector3 smoothHitNormal = Vector3.up;
private Vector3 lastStepPosition;
public bool isStepping => stepProgress < 1f;
public Vector3 position
{
get
{
return ik.GetIKSolver().GetIKPosition();
}
set
{
ik.GetIKSolver().SetIKPosition(value);
}
}
private void Awake()
{
ik = GetComponent<IK>();
if (foot != null)
{
if (footUpAxis == Vector3.zero)
{
footUpAxis = Quaternion.Inverse(foot.rotation) * Vector3.up;
}
lastFootLocalRotation = foot.localRotation;
IKSolver iKSolver = ik.GetIKSolver();
iKSolver.OnPostUpdate = (IKSolver.UpdateDelegate)Delegate.Combine(iKSolver.OnPostUpdate, new IKSolver.UpdateDelegate(AfterIK));
}
}
private void AfterIK()
{
if (!(foot == null))
{
foot.localRotation = lastFootLocalRotation;
smoothHitNormal = Vector3.Slerp(smoothHitNormal, hit.normal, Time.deltaTime * footRotationSpeed);
Quaternion quaternion = Quaternion.FromToRotation(foot.rotation * footUpAxis, smoothHitNormal);
foot.rotation = quaternion * foot.rotation;
}
}
private void Start()
{
stepProgress = 1f;
hit = default(RaycastHit);
position = ik.GetIKSolver().GetPoints()[^1].transform.position;
lastStepPosition = position;
hit.point = position;
defaultPosition = mechSpider.transform.InverseTransformPoint(position + offset * mechSpider.scale);
StartCoroutine(Step(position, position));
}
private Vector3 GetStepTarget(out bool stepFound, float focus, float distance)
{
stepFound = false;
Vector3 vector = mechSpider.transform.TransformPoint(defaultPosition) + (hit.point - position) * velocityPrediction;
Vector3 up = mechSpider.transform.up;
Vector3 rhs = mechSpider.body.position - position;
Vector3 axis = Vector3.Cross(up, rhs);
up = Quaternion.AngleAxis(focus, axis) * up;
if (Physics.Raycast(vector + up * mechSpider.raycastHeight * mechSpider.scale, -up, out hit, mechSpider.raycastHeight * mechSpider.scale + distance, mechSpider.raycastLayers))
{
stepFound = true;
}
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;
}
}
private void Update()
{
UpdatePosition(mechSpider.raycastDistance * mechSpider.scale);
if (!isStepping && !(Time.time < lastStepTime + minDelay) && (!(unSync != null) || !unSync.isStepping))
{
bool stepFound = false;
Vector3 stepTarget = GetStepTarget(out stepFound, raycastFocus, mechSpider.raycastDistance * mechSpider.scale);
if (!stepFound)
{
stepTarget = GetStepTarget(out stepFound, 0f - raycastFocus, mechSpider.raycastDistance * 3f * mechSpider.scale);
}
if (stepFound && !(Vector3.Distance(position, stepTarget) < maxOffset * mechSpider.scale * UnityEngine.Random.Range(0.9f, 1.2f)))
{
StopAllCoroutines();
StartCoroutine(Step(position, stepTarget));
}
}
}
private IEnumerator Step(Vector3 stepStartPosition, Vector3 targetPosition)
{
stepProgress = 0f;
while (stepProgress < 1f)
{
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;
if (sand != null)
{
sand.transform.position = position - mechSpider.transform.up * footHeight * mechSpider.scale;
sand.Emit(20);
}
lastStepTime = Time.time;
}
}
}