화살표 만들기 (Direction Arrow)
간단하게 화살표로 방향이나 위치를 표시하고 싶을때 간단하게 사용하기 위해서
Graphic을 상속받아서 화살표를 만들었다.
반응형
< 화살표 곡선 >
Sgement Per Curve값에 따라서 곡선의 정도를 처리할 수 있게 했다.
< Insepctor >
< 활용도 >
포인터를 추가해서 각종 곡선과 해당 움직임을 표시할수 있도록 했고 실질적으로 해당 위치의 좌표를 리턴이 가능하다.
< 코드 >
using UnityEngine;
using UnityEngine.UI;
using System.Collections.Generic;
public class UIArrow : Graphic
{
public List<Vector2> pathPoints = new List<Vector2>() {
new Vector2(0, 0), new Vector2(20, 10), new Vector2(40.8f, 10), new Vector2(48.6f, 20.94f),
new Vector2(15.6f, 23), new Vector2(-4.08f, 16.2f)
};
public float arrowHeadLength = 20f;
public float arrowHeadAngle = 25f;
public float lineThickness = 10f;
public int segmentPerCurve = 30;
[SerializeField] private float debugSphereSize = 0.05f;
#if UNITY_EDITOR
private void OnDrawGizmosSelected()
{
if (pathPoints == null || pathPoints.Count == 0) return;
Gizmos.color = Color.yellow;
foreach (Vector2 point in pathPoints)
{
Vector3 worldPos = transform.TransformPoint(point);
Gizmos.DrawSphere(worldPos, debugSphereSize);
}
}
#endif
protected override void OnPopulateMesh(VertexHelper vh)
{
vh.Clear();
Debug.Log($"[Arrow] pathPoints: {pathPoints?.Count}, curvePts: {GenerateCatmullRomSpline(pathPoints, segmentPerCurve).Count}");
if (pathPoints == null || pathPoints.Count < 2) return;
List<Vector2> curve = GenerateCatmullRomSpline(pathPoints, segmentPerCurve);
if (curve.Count < 2) return;
UIVertex vertex = UIVertex.simpleVert;
vertex.color = color;
void Add(Vector2 pos) => vh.AddVert(new UIVertex { position = pos, color = color });
// 누적 길이 측정
float totalLength = 0f;
for (int i = 1; i < curve.Count; i++)
totalLength += Vector2.Distance(curve[i - 1], curve[i]);
float cutoffLength = Mathf.Max(0f, totalLength - arrowHeadLength);
int vertIndex = 0;
float currentLength = 0f;
bool done = false;
for (int i = 1; i < curve.Count; i++)
{
Vector2 p0 = curve[i - 1];
Vector2 p1 = curve[i];
float segLen = Vector2.Distance(p0, p1);
if (!done && currentLength + segLen >= cutoffLength)
{
float remain = cutoffLength - currentLength;
float t = Mathf.Clamp01(remain / segLen);
p1 = Vector2.Lerp(p0, p1, t);
done = true;
}
Vector2 dir = (p1 - p0).normalized;
if (dir == Vector2.zero) continue;
Vector2 normal = new Vector2(-dir.y, dir.x) * (lineThickness / 2f);
Vector2 v0 = p0 - normal;
Vector2 v1 = p0 + normal;
Vector2 v2 = p1 - normal;
Vector2 v3 = p1 + normal;
Add(v0); Add(v1); Add(v2); Add(v3);
vh.AddTriangle(vertIndex, vertIndex + 1, vertIndex + 2);
vh.AddTriangle(vertIndex + 1, vertIndex + 3, vertIndex + 2);
vertIndex += 4;
currentLength += segLen;
if (done)
break; // 선을 끊지 않고 마지막 잘린 세그먼트까지 그리고 나면 멈춤
}
// 화살촉
Vector2 tip = pathPoints[pathPoints.Count - 1];
Vector2 back = curve[Mathf.Max(0, curve.Count - 2)];
Vector2 direction = tip - back;
if (direction.sqrMagnitude > 1e-5f)
{
direction.Normalize();
Vector2 arrowBase = tip - direction * arrowHeadLength;
float angleRad = Mathf.Deg2Rad * arrowHeadAngle;
float halfBase = Mathf.Tan(angleRad / 2f) * arrowHeadLength;
Vector2 perp = new Vector2(-direction.y, direction.x);
Vector2 left = arrowBase + perp * halfBase;
Vector2 right = arrowBase - perp * halfBase;
Add(tip);
Add(left);
Add(right);
vh.AddTriangle(vertIndex, vertIndex + 1, vertIndex + 2);
}
}
private List<Vector2> GenerateCatmullRomSpline(List<Vector2> points, int resolution)
{
List<Vector2> result = new List<Vector2>();
int count = points.Count;
for (int i = 0; i < count - 1; i++)
{
Vector2 p0 = (i == 0) ? points[i] : points[i - 1];
Vector2 p1 = points[i];
Vector2 p2 = points[i + 1];
Vector2 p3 = (i + 2 < count) ? points[i + 2] : p2;
for (int j = 0; j < resolution; j++)
{
float t = j / (float)resolution;
Vector2 pos = 0.5f * (
2f * p1 +
(-p0 + p2) * t +
(2f * p0 - 5f * p1 + 4f * p2 - p3) * t * t +
(-p0 + 3f * p1 - 3f * p2 + p3) * t * t * t
);
result.Add(pos);
}
}
result.Add(points[count - 1]);
return result;
}
public Vector2 GetPointOnPath(float time)
{
if (pathPoints == null || pathPoints.Count < 2) return Vector2.zero;
List<Vector2> curve = GenerateCatmullRomSpline(pathPoints, segmentPerCurve);
if (curve.Count < 2) return Vector2.zero;
// 전체 길이 계산
float totalLength = 0f;
List<float> segmentLengths = new List<float>();
for (int i = 1; i < curve.Count; i++)
{
float d = Vector2.Distance(curve[i - 1], curve[i]);
segmentLengths.Add(d);
totalLength += d;
}
float targetLength = Mathf.Clamp01(time) * totalLength;
// 누적 거리 따라 보간 위치 찾기
float currentLength = 0f;
for (int i = 1; i < curve.Count; i++)
{
float segLen = segmentLengths[i - 1];
if (currentLength + segLen >= targetLength)
{
float remain = targetLength - currentLength;
float t = segLen > 0f ? remain / segLen : 0f;
return Vector2.Lerp(curve[i - 1], curve[i], t);
}
currentLength += segLen;
}
return curve[curve.Count - 1]; // fallback
}
}
★★★★☆
반응형
'개발 > Unity' 카테고리의 다른 글
Unity) DebugCustom(Time, Tag, Parser)<message truncated> (0) | 2025.06.21 |
---|---|
Unity) UniTask Tracker (UniTask 순서 확인용) (0) | 2025.06.04 |
Unity) 투사체 궤적 표현하기 (LineRendere Custom) (0) | 2025.04.23 |
Unity) Built-in Shader 확인방법 ( 내장 셰이더 코드 확인 ) (0) | 2025.04.11 |
Unity) 앱 위변조 방지 방법들 (0) | 2025.02.25 |
댓글