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.

131 lines
4.7 KiB
C#

6 months ago
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace Autohand{
[RequireComponent(typeof(Rigidbody)), DefaultExecutionOrder(-1)]
public class PhysicsFollower : MonoBehaviour{
[Header("Follow Settings"), Space]
[Tooltip("Follow target, the hand will always try to match this transforms rotation and position with rigidbody movements")]
public Transform follow;
[Tooltip("Stops hand physics follow - to freeze from all forces change rigidbody to kinematic or change rigidbody constraints")]
public bool freezePos = false;
[Tooltip("Stops hand physics follow - to freeze from all forces change rigidbody to kinematic or change rigidbody constraints")]
public bool freezeRot = false;
[Tooltip("This will offset the position without offsetting the rotation pivot")]
public Vector3 followPositionOffset;
public Vector3 rotationOffset;
[Tooltip("Follow target speed (This will cause jittering if turned too high)"), Min(0)]
public float followPositionStrength = 30;
[Tooltip("Follow target rotation speed (This will cause jittering if turned too high)"), Min(0)]
public float followRotationStrength = 30;
[Tooltip("The maximum allowed velocity of the hand"), Min(0)]
public float maxVelocity = 5;
internal Rigidbody body;
Transform moveTo;
public void Start() {
Set();
}
public virtual void Set() {
if(moveTo == null){
moveTo = new GameObject().transform;
moveTo.name = gameObject.name + " FOLLOW POINT";
moveTo.parent = follow.parent;
moveTo.position = follow.transform.position;
moveTo.rotation = follow.transform.rotation;
body = GetComponent<Rigidbody>();
}
}
public void Update() {
OnUpdate();
}
protected virtual void OnUpdate() {
if(follow == null)
return;
//Sets [Move To] Object
moveTo.position = follow.position + transform.rotation*followPositionOffset;
moveTo.rotation = follow.rotation * Quaternion.Euler(rotationOffset);
}
public void FixedUpdate() {
OnFixedUpdate();
}
protected virtual void OnFixedUpdate() {
if(follow == null)
return;
//Sets [Move To] Object
moveTo.position = follow.position + transform.rotation*followPositionOffset;
moveTo.rotation = follow.rotation * Quaternion.Euler(rotationOffset);
//Calls physics movements
if(!freezePos) MoveTo();
if(!freezeRot) TorqueTo();
}
/// <summary>Moves the hand to the controller position using physics movement</summary>
internal virtual void MoveTo() {
if(followPositionStrength <= 0)
return;
var movePos = moveTo.position;
var distance = Vector3.Distance(movePos, transform.position);
var velocityClamp = maxVelocity;
//Sets velocity linearly based on distance from hand
var vel = (movePos - transform.position).normalized * followPositionStrength * distance;
vel.x = Mathf.Clamp(vel.x, -velocityClamp, velocityClamp);
vel.y = Mathf.Clamp(vel.y, -velocityClamp, velocityClamp);
vel.z = Mathf.Clamp(vel.z, -velocityClamp, velocityClamp);
body.velocity = vel;
}
/// <summary>Rotates the hand to the controller rotation using physics movement</summary>
internal virtual void TorqueTo() {
var toRot = moveTo.rotation;
float angleDist = Quaternion.Angle(body.rotation, toRot);
Quaternion desiredRotation = Quaternion.Lerp(body.rotation, toRot, Mathf.Clamp(angleDist, 0, 2) / 4f);
var kp = 90f * followRotationStrength;
var kd = 60f;
Vector3 x;
float xMag;
Quaternion q = desiredRotation * Quaternion.Inverse(transform.rotation);
q.ToAngleAxis(out xMag, out x);
x.Normalize();
x *= Mathf.Deg2Rad;
Vector3 pidv = kp * x * xMag - kd * body.angularVelocity;
Quaternion rotInertia2World = body.inertiaTensorRotation * transform.rotation;
pidv = Quaternion.Inverse(rotInertia2World) * pidv;
pidv.Scale(body.inertiaTensor);
pidv = rotInertia2World * pidv;
body.AddTorque(pidv);
}
private void OnDestroy() {
Destroy(moveTo.gameObject);
}
}
}