Files
dungeons/Assets/HurricaneVR/Framework/Scripts/Components/HVRLever.cs

247 lines
8.3 KiB
C#

using HurricaneVR.Framework.Core;
using HurricaneVR.Framework.Core.Grabbers;
using HurricaneVR.Framework.Core.Utils;
using HurricaneVR.Framework.Shared;
using UnityEngine;
namespace HurricaneVR.Framework.Components
{
/// <summary>
/// Depecrecated and left in for older projects, superceded by HVRPhysicsLever and HVRRotationTracker
/// </summary>
[RequireComponent(typeof(HVRGrabbable))]
[RequireComponent(typeof(HingeJoint))]
public class HVRLever : MonoBehaviour
{
[Header("Transforms")]
[Tooltip("Target transform to rotate if the visual handle is not this object")]
public Transform RotationTarget;
[Tooltip("Used to track the delta angle change, use with TrackerAxis.")]
public Transform Tracker;
[Header("Settings")]
[Tooltip("When the lever is released with Snap mode, should the grabbable handle snap to the RotationTarget")]
public bool DiscardAngle;
[Tooltip("Amount of steps the lever will click")]
public int Steps;
[Tooltip("Rotation Target snaps to the step angle size")]
public bool SnapTarget = true;
[Tooltip("Angle offset the lever starts in relation to LocalAxisStart field")]
public float Offset;
[Tooltip("Vector of the starting 0 angle of the lever.")]
public Vector3 LocalAxisStart;
[Tooltip("Choose an axis that isn't the same as the hinge axis")]
public Vector3 TrackerAxis = new Vector3(0, 1, 0);
[Tooltip("SFX to play whenever the lever snaps")]
public AudioClip AudioClip;
protected HingeJoint HingeJoint;
[Header("Debug")]
public float MinAngle;
public float MaxAngle;
public Vector3 LocalRotationAxis;
public float StepSize;
public int CurrentStep;
public float CurrentAngle;
public LeverMovedEvent Moved = new LeverMovedEvent();
public LeverSteppedEvent Stepped = new LeverSteppedEvent();
private float _previousAngle;
private Vector3 _previousVector;
private Vector3 _originalVector;
private float _previousAngleFromStart;
public HVRGrabbable Grabbable { get; private set; }
public Rigidbody Rigidbody => Grabbable.Rigidbody;
public HVRGrabberBase PrimaryGrabber => Grabbable.PrimaryGrabber;
public float Range { get; set; }
protected void Start()
{
if (!RotationTarget)
RotationTarget = transform;
Grabbable = GetComponent<HVRGrabbable>();
HingeJoint = GetComponent<HingeJoint>();
MinAngle = HingeJoint.limits.min;
MaxAngle = HingeJoint.limits.max;
Range = MaxAngle - MinAngle;
LocalRotationAxis = HingeJoint.axis;
if (Steps <= 1)
{
StepSize = 0f;
}
else if (Steps > 1 && Mathf.Approximately(StepSize, 0f))
{
StepSize = Range / Steps;
}
_originalVector = transform.TransformDirection(LocalAxisStart);
_previousVector = LocalAxisStart;
SetupStartRotation();
transform.rotation = RotationTarget.rotation;
Grabbable.Grabbed.AddListener(OnGrabbed);
Grabbable.Released.AddListener(OnReleased);
}
protected virtual void FixedUpdate()
{
CheckForRotation();
}
private void CheckForRotation(bool force = false)
{
if (!force && Rigidbody.isKinematic && !PrimaryGrabber)
return;
var currentVector = Tracker.TransformDirection(TrackerAxis);
var worldRotationAxis = transform.TransformDirection(LocalRotationAxis);
var angleFromStart = Vector3.SignedAngle(_originalVector, currentVector, worldRotationAxis);
angleFromStart += Offset;
if ((int)angleFromStart < 0)
angleFromStart = 360 + angleFromStart;
var deltaAngle = Vector3.SignedAngle(_previousVector, currentVector, worldRotationAxis);
CurrentAngle = angleFromStart;
var stepAngle = angleFromStart;
if (Steps > 1)
{
CurrentStep = Mathf.RoundToInt(angleFromStart / StepSize);
stepAngle = CurrentStep * StepSize;
if (!Mathf.Approximately(stepAngle, _previousAngle))
{
OnStepChanged(CurrentStep);
_previousAngle = stepAngle;
if (SnapTarget)
{
var percent = Mathf.Approximately(0f, Range) ? 0f : stepAngle / Range;
OnAngleChanged(stepAngle, deltaAngle, percent);
}
}
}
_previousVector = currentVector;
var steppedVector = Quaternion.AngleAxis(stepAngle - Offset, worldRotationAxis) * _originalVector;
var steppedAngle = Vector3.SignedAngle(currentVector, steppedVector, worldRotationAxis);
var steppedRotation = Quaternion.AngleAxis(steppedAngle, worldRotationAxis) * transform.rotation;
if (!SnapTarget && !Mathf.Approximately(_previousAngleFromStart, angleFromStart))
{
var percent = Mathf.Approximately(0f, Range) ? 0f : angleFromStart / Range;
OnAngleChanged(angleFromStart, deltaAngle, percent);
}
if (SnapTarget)
{
RotationTarget.rotation = steppedRotation;
}
else
{
RotationTarget.rotation = transform.rotation;
}
_previousAngleFromStart = angleFromStart;
}
private void SetupStartRotation()
{
var currentVector = transform.TransformDirection(TrackerAxis);
var worldRotationAxis = transform.TransformDirection(LocalRotationAxis);
var angleFromStart = Vector3.SignedAngle(_originalVector, currentVector, worldRotationAxis);
angleFromStart += Offset;
if ((int)angleFromStart < 0)
angleFromStart = 360 + angleFromStart;
CurrentAngle = angleFromStart;
var stepAngle = angleFromStart;
if (Steps > 1)
{
CurrentStep = Mathf.RoundToInt(angleFromStart / StepSize);
stepAngle = CurrentStep * StepSize;
if (!Mathf.Approximately(stepAngle, _previousAngle))
{
_previousAngle = stepAngle;
}
}
_previousVector = currentVector;
var steppedVector = Quaternion.AngleAxis(stepAngle - Offset, worldRotationAxis) * _originalVector;
var steppedAngle = Vector3.SignedAngle(currentVector, steppedVector, worldRotationAxis);
var steppedRotation = Quaternion.AngleAxis(steppedAngle, worldRotationAxis) * transform.rotation;
if (SnapTarget)
{
RotationTarget.rotation = steppedRotation;
}
else
{
RotationTarget.rotation = transform.rotation;
}
_previousAngleFromStart = angleFromStart;
}
protected void OnGrabbed(HVRGrabberBase grabber, HVRGrabbable hvrGrabbable)
{
if (DiscardAngle)
{
transform.rotation = RotationTarget.rotation;
transform.position = RotationTarget.position;
}
}
protected void OnReleased(HVRGrabberBase grabber, HVRGrabbable hvrGrabbable)
{
if (DiscardAngle)
{
transform.rotation = RotationTarget.rotation;
transform.position = RotationTarget.position;
}
}
protected virtual void OnStepChanged(int step, bool raiseEvents = true)
{
if (AudioClip)
if(SFXPlayer.Instance) SFXPlayer.Instance.PlaySFXRandomPitch(AudioClip, transform.position, .9f, 1.1f);
if (raiseEvents)
Stepped.Invoke(step);
}
protected virtual void OnAngleChanged(float angle, float delta, float percent, bool raiseEvents = true)
{
if (raiseEvents)
Moved.Invoke(angle, delta, percent);
}
}
}