2024-04-20 11:58:46 +02:00
|
|
|
using System;
|
2024-04-20 01:40:53 +02:00
|
|
|
using UnityEngine;
|
2024-04-20 11:58:46 +02:00
|
|
|
using UnityEngine.Assertions;
|
2024-04-20 01:40:53 +02:00
|
|
|
|
2024-04-20 01:47:17 +02:00
|
|
|
public enum ArcOrientation
|
|
|
|
{
|
|
|
|
HORIZONTAL,
|
|
|
|
VERTICAL,
|
|
|
|
}
|
|
|
|
|
2024-04-20 01:40:53 +02:00
|
|
|
[RequireComponent(typeof(LineRenderer))]
|
|
|
|
public class EditableArc : MonoBehaviour
|
|
|
|
{
|
2024-04-20 01:47:17 +02:00
|
|
|
[SerializeField] private ArcOrientation orientation = ArcOrientation.HORIZONTAL;
|
2024-04-20 01:40:53 +02:00
|
|
|
[SerializeField, Range(5, 50)] private int samples = 15;
|
|
|
|
[SerializeField] private float visualRadius = 1f;
|
2024-04-20 11:58:46 +02:00
|
|
|
[SerializeField] private SliderKnob knob;
|
|
|
|
[SerializeField] private float knobSensitiviy = 1f;
|
|
|
|
[SerializeField] private string moveKnobAxisName = "Mouse X";
|
2024-04-20 01:40:53 +02:00
|
|
|
|
|
|
|
[SerializeField] private Vector2 rotationMinMax = new Vector2(-30f, 30f);
|
|
|
|
private LineRenderer lineRenderer;
|
|
|
|
|
|
|
|
public Observer<float> Value { get; private set; } = new(0);
|
|
|
|
public Vector2 RotationMinMax => rotationMinMax;
|
|
|
|
|
2024-04-20 15:56:26 +02:00
|
|
|
public Vector3 normal => orientation == ArcOrientation.HORIZONTAL ? transform.up : transform.forward;
|
|
|
|
public Vector3 tangent => orientation == ArcOrientation.HORIZONTAL ? transform.forward : transform.up;
|
2024-04-20 11:58:46 +02:00
|
|
|
|
2024-04-20 13:15:33 +02:00
|
|
|
public Vector3 ToKnobVector => Quaternion.AngleAxis(Value.Value, normal) * tangent;
|
|
|
|
|
2024-04-20 01:40:53 +02:00
|
|
|
private void Awake()
|
|
|
|
{
|
|
|
|
lineRenderer = GetComponent<LineRenderer>();
|
|
|
|
|
|
|
|
Value.AddListener(UpdateArc);
|
2024-04-20 11:58:46 +02:00
|
|
|
Value.AddListener(UpdateKnobPosition);
|
2024-04-20 01:40:53 +02:00
|
|
|
|
2024-04-20 13:15:33 +02:00
|
|
|
// Set default rotation to average between min max
|
2024-04-20 16:49:40 +02:00
|
|
|
// Value.Value = (rotationMinMax.x + rotationMinMax.y) / 2f;
|
2024-04-20 11:58:46 +02:00
|
|
|
|
|
|
|
Assert.IsNotNull(knob, $"No knob on {this}");
|
|
|
|
knob.OnDrag += PointerDraggedOnKnob;
|
|
|
|
|
|
|
|
// Initial
|
|
|
|
UpdateKnobPosition(Value);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Moves the knob to the right position based on the angle on the arc
|
|
|
|
private void UpdateKnobPosition(float angle)
|
|
|
|
{
|
|
|
|
Vector3 dir = Quaternion.AngleAxis(angle, normal) * tangent;
|
|
|
|
Vector3 knobPos = transform.position + dir;
|
|
|
|
|
|
|
|
knob.transform.position = knobPos;
|
|
|
|
}
|
|
|
|
|
|
|
|
private void OnDestroy()
|
|
|
|
{
|
|
|
|
Value.RemoveListener(UpdateArc);
|
|
|
|
Value.RemoveListener(UpdateKnobPosition);
|
|
|
|
knob.OnDrag -= PointerDraggedOnKnob;
|
|
|
|
}
|
|
|
|
|
|
|
|
private void PointerDraggedOnKnob(SliderKnob knob)
|
|
|
|
{
|
|
|
|
// Amount mouse have moved since last frame - ie. mouse velocity
|
|
|
|
float mouseMovement = Input.GetAxis(moveKnobAxisName);
|
|
|
|
|
|
|
|
// TODO: figure out this based on camera orientation
|
|
|
|
float sign = -1f;
|
|
|
|
|
|
|
|
float delta = mouseMovement * knobSensitiviy * sign;
|
2024-04-20 16:49:40 +02:00
|
|
|
float newAngle = ClampAngle(Value.Value + delta, rotationMinMax.x, rotationMinMax.y);
|
2024-04-20 11:58:46 +02:00
|
|
|
Value.Value = newAngle;
|
2024-04-20 01:40:53 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
private void Update()
|
|
|
|
{
|
|
|
|
UpdateArc(Value.Value);
|
|
|
|
}
|
|
|
|
|
|
|
|
private void UpdateArc(float rotation)
|
|
|
|
{
|
|
|
|
float angle = rotationMinMax.y - rotationMinMax.x;
|
|
|
|
float v = (Mathf.PI - Value * Mathf.Deg2Rad) / 2f;
|
2024-04-20 01:47:17 +02:00
|
|
|
Vector3 start = Quaternion.AngleAxis(rotationMinMax.x, normal) * tangent;
|
|
|
|
Vector3 end = Quaternion.AngleAxis(rotationMinMax.y, normal) * tangent;
|
2024-04-20 01:40:53 +02:00
|
|
|
|
|
|
|
// Sample LineRenderer points
|
|
|
|
lineRenderer.positionCount = samples + 1;
|
|
|
|
Vector3[] positions = new Vector3[samples + 1];
|
|
|
|
float stepSize = 1f / samples;
|
|
|
|
float t = 0;
|
|
|
|
for (int i = 0; i <= samples; i++)
|
|
|
|
{
|
|
|
|
positions[i] = SamplePointOnArc(start, end, visualRadius, t);
|
|
|
|
// Debug.Log($"t: = {t}, pos: {positions[i]}");
|
|
|
|
t += stepSize;
|
|
|
|
}
|
|
|
|
|
|
|
|
lineRenderer.SetPositions(positions);
|
2024-04-20 16:49:40 +02:00
|
|
|
|
|
|
|
// Set looop
|
|
|
|
if (angle >= 360)
|
|
|
|
lineRenderer.loop = true;
|
|
|
|
else
|
|
|
|
lineRenderer.loop = false;
|
2024-04-20 01:40:53 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
public Vector3 SamplePointOnArc(Vector3 startPoint, Vector3 endPoint, float radius, float t)
|
|
|
|
{
|
2024-04-20 16:49:40 +02:00
|
|
|
float angle = Mathf.Lerp(rotationMinMax.x, rotationMinMax.y, t);
|
2024-04-20 11:58:46 +02:00
|
|
|
Vector3 dir = Quaternion.AngleAxis(angle, normal) * tangent;
|
|
|
|
return transform.position + dir.normalized * radius;
|
2024-04-20 01:40:53 +02:00
|
|
|
}
|
2024-04-20 16:49:40 +02:00
|
|
|
|
|
|
|
public static float ClampAngle(float current, float min, float max)
|
|
|
|
{
|
|
|
|
float dtAngle = Mathf.Abs(((min - max) + 180) % 360 - 180);
|
|
|
|
float hdtAngle = dtAngle * 0.5f;
|
|
|
|
float midAngle = min + hdtAngle;
|
|
|
|
|
|
|
|
float offset = Mathf.Abs(Mathf.DeltaAngle(current, midAngle)) - hdtAngle;
|
|
|
|
if (offset > 0)
|
|
|
|
current = Mathf.MoveTowardsAngle(current, midAngle, offset);
|
|
|
|
return current;
|
|
|
|
}
|
2024-04-20 01:40:53 +02:00
|
|
|
}
|