Files
2024-03-12 17:53:12 +01:00

108 lines
3.4 KiB
C#

using System.Collections.Generic;
using UnityEngine;
public class ArrowRenderer : MonoBehaviour
{
public float height = 0.5f;
public float segmentLength = 0.5f;
public float fadeDistance = 0.35f;
public float speed = 1f;
[SerializeField] GameObject arrowPrefab;
[SerializeField] GameObject segmentPrefab;
[Space] [SerializeField] Vector3 start;
[SerializeField] Vector3 end;
[SerializeField] Vector3 upwards = Vector3.up;
Transform arrow;
readonly List<Transform> segments = new List<Transform>();
readonly List<MeshRenderer> renderers = new List<MeshRenderer>();
public void SetPositions(Vector3 start0, Vector3 end0)
{
start = start0;
end = end0;
UpdateSegments();
}
void Update()
{
UpdateSegments();
}
void UpdateSegments()
{
Debug.DrawLine(start, end, Color.yellow);
float distance = Vector3.Distance(start, end);
float radius = height / 2f + distance * distance / (8f * height);
float diff = radius - height;
float angle = 2f * Mathf.Acos(diff / radius);
float length = angle * radius;
float segmentAngle = segmentLength / radius * Mathf.Rad2Deg;
Vector3 center = new Vector3(0, -diff, distance / 2f);
Vector3 left = Vector3.zero;
Vector3 right = new Vector3(0, 0, distance);
int segmentsCount = (int)(length / segmentLength) + 1;
CheckSegments(segmentsCount);
float offset = Time.time * speed * segmentAngle;
Vector3 firstSegmentPos =
Quaternion.Euler(Mathf.Repeat(offset, segmentAngle), 0f, 0f) * (left - center) + center;
float fadeStartDistance = (Quaternion.Euler(segmentAngle / 2f, 0f, 0f) * (left - center) + center).z;
for (int i = 0; i < segmentsCount; i++)
{
Vector3 pos = Quaternion.Euler(segmentAngle * i, 0f, 0f) * (firstSegmentPos - center) + center;
segments[i].localPosition = pos;
segments[i].localRotation = Quaternion.FromToRotation(Vector3.up, pos - center);
MeshRenderer rend = renderers[i];
if (!rend)
continue;
Color currentColor = rend.material.color;
currentColor.a = GetAlpha(pos.z - left.z, right.z - fadeDistance - pos.z, fadeStartDistance);
rend.material.color = currentColor;
}
if (!arrow)
arrow = Instantiate(arrowPrefab, transform).transform;
arrow.localPosition = right;
arrow.localRotation = Quaternion.FromToRotation(Vector3.up, right - center);
transform.position = start;
transform.rotation = Quaternion.LookRotation(end - start, upwards);
}
void CheckSegments(int segmentsCount)
{
while (segments.Count < segmentsCount)
{
Transform segment = Instantiate(segmentPrefab, transform).transform;
segments.Add(segment);
renderers.Add(segment.GetComponent<MeshRenderer>());
}
for (int i = 0; i < segments.Count; i++)
{
GameObject segment = segments[i].gameObject;
if (segment.activeSelf != i < segmentsCount)
segment.SetActive(i < segmentsCount);
}
}
static float GetAlpha(float distance0, float distance1, float distanceMax)
{
return Mathf.Clamp01(Mathf.Clamp01(distance0 / distanceMax) + Mathf.Clamp01(distance1 / distanceMax) - 1f);
}
}