

๊ธฐ๋ณธ LineRenderer ๊ตฌ์กฐ
- Line Renderer๋ ์๋ ์ขํ์์ ์ฌ๋ฌ ์ (Position ๋ฐฐ์ด)์ ๋ฐ์ ์ ์ ๊ตฌ์ฑํฉ๋๋ค.
- ๋ ์ ๋ง ์ค์ ํ๋ฉด โ์ง์ ์ธ๊ทธ๋จผํธโ๊ฐ ๋ฉ๋๋ค.
- ๋๊ป๋
startWidth, endWidth
- ์์์
Material ๋๋ colorGradient๋ก ์ ์ดํฉ๋๋ค.
using UnityEngine;
[RequireComponent(typeof(LineRenderer))]
public class SimpleLineBetweenPoints : MonoBehaviour
{
[Header("๋ผ์ธ ์ค์ ")]
[SerializeField] private float lineWidth = 0.05f;
[Header("์์ ๋ฒ์")]
[SerializeField] private Vector3 rangeMin = new Vector3(-5f, -5f, -5f);
[SerializeField] private Vector3 rangeMax = new Vector3(5f, 5f, 5f);
private LineRenderer lineRenderer;
void Awake()
{
lineRenderer = GetComponent<LineRenderer>();
lineRenderer.positionCount = 2;
lineRenderer.useWorldSpace = true;
lineRenderer.startWidth = lineWidth;
lineRenderer.endWidth = lineWidth;
lineRenderer.material = new Material(Shader.Find("Sprites/Default"));
lineRenderer.startColor = Color.white;
lineRenderer.endColor = Color.white;
lineRenderer.shadowCastingMode = UnityEngine.Rendering.ShadowCastingMode.Off;
lineRenderer.receiveShadows = false;
}
void Start()
{
Vector3 pointA = GetRandomPoint();
Vector3 pointB = GetRandomPoint();
lineRenderer.SetPosition(0, pointA);
lineRenderer.SetPosition(1, pointB);
}
private Vector3 GetRandomPoint()
{
return new Vector3(
Random.Range(rangeMin.x, rangeMax.x),
Random.Range(rangeMin.y, rangeMax.y),
Random.Range(rangeMin.z, rangeMax.z)
);
}
}
SurfaceLineManager (GameObject)
โโโ Line_000
โ โโโ LineRenderer โ ๋ ์ ์ฌ์ด ์
โ โโโ TrailA โ ์ A์ ๊ถค์
โ โ โโโ TrailRenderer
โ โโโ TrailB โ ์ B์ ๊ถค์
โ โโโ TrailRenderer
โโโ Line_001
โโโ ...
using System.Collections.Generic;
using UnityEngine;
public class SurfaceLineManager : MonoBehaviour
{
[Header("๋ฐ์ค ํฌ๊ธฐ (์ ๋ฐ extents)")]
[SerializeField] private Vector3 boxHalfSize = new Vector3(5f, 5f, 5f);
[Header("๋ผ์ธ ๊ฐ์")]
[SerializeField, Range(1, 256)] private int lineCount = 16;
[Header("๋ผ์ธ ๋๊ป")]
[SerializeField] private float lineWidth = 0.05f;
[Header("์ด๋ ์๋ ๋ฒ์")]
[SerializeField] private float minSpeed = 0.5f;
[SerializeField] private float maxSpeed = 3f;
[Header("์์")]
[SerializeField] private Color startColor = Color.white;
[SerializeField] private Color endColor = Color.white;
[SerializeField, Range(0f, 1f)] private float startAlpha = 1f;
[SerializeField, Range(0f, 1f)] private float endAlpha = 1f;
[Header("ํธ๋ ์ผ")]
[SerializeField] private bool enableTrail = true;
[SerializeField] private float trailTime = 0.5f;
[SerializeField] private float trailStartWidth = 0.04f;
[SerializeField] private float trailEndWidth = 0f;
[SerializeField] private Color trailStartColor = Color.white;
[SerializeField] private Color trailEndColor = Color.clear;
private struct SurfacePoint
{
public int faceIndex;
public Vector2 uv;
public Vector2 velocity;
}
private struct LineEntry
{
public LineRenderer lineRenderer;
public Transform trailTransformA;
public Transform trailTransformB;
}
private List<LineEntry> lines = new List<LineEntry>();
private SurfacePoint[] pointsA;
private SurfacePoint[] pointsB;
void Awake()
{
InitLines();
}
void Update()
{
for (int i = 0; i < lineCount; i++)
{
pointsA[i] = MovePoint(pointsA[i]);
pointsB[i] = MovePoint(pointsB[i]);
Vector3 worldA = ToWorld(pointsA[i]);
Vector3 worldB = ToWorld(pointsB[i]);
lines[i].lineRenderer.SetPosition(0, worldA);
lines[i].lineRenderer.SetPosition(1, worldB);
lines[i].trailTransformA.position = worldA;
lines[i].trailTransformB.position = worldB;
}
}
private void InitLines()
{
for (int i = transform.childCount - 1; i >= 0; i--)
Destroy(transform.GetChild(i).gameObject);
lines.Clear();
pointsA = new SurfacePoint[lineCount];
pointsB = new SurfacePoint[lineCount];
Shader lineShader = Shader.Find("Universal Render Pipeline/Particles/Unlit");
if (lineShader == null)
lineShader = Shader.Find("Sprites/Default");
Gradient lineGradient = BuildGradient(startColor, endColor, startAlpha, endAlpha);
Gradient trailGradient = BuildGradient(trailStartColor, trailEndColor, trailStartColor.a, 0f);
for (int i = 0; i < lineCount; i++)
{
GameObject lineGO = new GameObject($"Line_{i:000}");
lineGO.transform.SetParent(transform, false);
LineRenderer lr = lineGO.AddComponent<LineRenderer>();
SetupLineRenderer(lr, lineShader, lineGradient);
Transform trailA = CreateTrailObject("TrailA", lineGO.transform, lineShader, trailGradient);
Transform trailB = CreateTrailObject("TrailB", lineGO.transform, lineShader, trailGradient);
lines.Add(new LineEntry
{
lineRenderer = lr,
trailTransformA = trailA,
trailTransformB = trailB
});
pointsA[i] = SpawnPoint();
pointsB[i] = SpawnPoint();
Vector3 wa = ToWorld(pointsA[i]);
Vector3 wb = ToWorld(pointsB[i]);
lr.SetPosition(0, wa);
lr.SetPosition(1, wb);
trailA.position = wa;
trailB.position = wb;
}
}
private void SetupLineRenderer(LineRenderer lr, Shader shader, Gradient gradient)
{
lr.positionCount = 2;
lr.useWorldSpace = true;
lr.startWidth = lineWidth;
lr.endWidth = lineWidth;
lr.shadowCastingMode = UnityEngine.Rendering.ShadowCastingMode.Off;
lr.receiveShadows = false;
lr.material = new Material(shader);
lr.startColor = Color.white;
lr.endColor = Color.white;
lr.colorGradient = gradient;
}
private Transform CreateTrailObject(string name, Transform parent, Shader shader, Gradient gradient)
{
GameObject go = new GameObject(name);
go.transform.SetParent(parent, false);
TrailRenderer tr = go.AddComponent<TrailRenderer>();
tr.time = trailTime;
tr.startWidth = trailStartWidth;
tr.endWidth = trailEndWidth;
tr.shadowCastingMode = UnityEngine.Rendering.ShadowCastingMode.Off;
tr.receiveShadows = false;
tr.material = new Material(shader);
tr.colorGradient = gradient;
tr.enabled = enableTrail;
return go.transform;
}
private SurfacePoint SpawnPoint()
{
int face = Random.Range(0, 6);
Vector2 halfUV = GetFaceHalfExtents(face);
Vector2 uv = new Vector2(
Random.Range(-halfUV.x, halfUV.x),
Random.Range(-halfUV.y, halfUV.y)
);
float angle = Random.Range(0f, Mathf.PI * 2f);
float speed = Random.Range(minSpeed, maxSpeed);
Vector2 vel = new Vector2(Mathf.Cos(angle), Mathf.Sin(angle)) * speed;
return new SurfacePoint { faceIndex = face, uv = uv, velocity = vel };
}
private SurfacePoint MovePoint(SurfacePoint p)
{
Vector2 half = GetFaceHalfExtents(p.faceIndex);
Vector2 newUV = p.uv + p.velocity * Time.deltaTime;
Vector2 newVel = p.velocity;
if (newUV.x > half.x) { newUV.x = half.x; newVel.x = -newVel.x; }
if (newUV.x < -half.x) { newUV.x = -half.x; newVel.x = -newVel.x; }
if (newUV.y > half.y) { newUV.y = half.y; newVel.y = -newVel.y; }
if (newUV.y < -half.y) { newUV.y = -half.y; newVel.y = -newVel.y; }
return new SurfacePoint { faceIndex = p.faceIndex, uv = newUV, velocity = newVel };
}
private Vector3 ToWorld(SurfacePoint p)
{
float u = p.uv.x;
float v = p.uv.y;
switch (p.faceIndex)
{
case 0: return new Vector3(boxHalfSize.x, u, v);
case 1: return new Vector3(-boxHalfSize.x, u, v);
case 2: return new Vector3(u, boxHalfSize.y, v);
case 3: return new Vector3(u, -boxHalfSize.y, v);
case 4: return new Vector3(u, v, boxHalfSize.z);
case 5: return new Vector3(u, v, -boxHalfSize.z);
default: return Vector3.zero;
}
}
private Vector2 GetFaceHalfExtents(int faceIndex)
{
switch (faceIndex)
{
case 0: case 1: return new Vector2(boxHalfSize.y, boxHalfSize.z);
case 2: case 3: return new Vector2(boxHalfSize.x, boxHalfSize.z);
case 4: case 5: return new Vector2(boxHalfSize.x, boxHalfSize.y);
default: return Vector2.one;
}
}
private Gradient BuildGradient(Color colorA, Color colorB, float alphaA, float alphaB)
{
Gradient g = new Gradient();
g.SetKeys(
new GradientColorKey[] {
new GradientColorKey(colorA, 0f),
new GradientColorKey(colorB, 1f)
},
new GradientAlphaKey[] {
new GradientAlphaKey(alphaA, 0f),
new GradientAlphaKey(alphaB, 1f)
}
);
return g;
}
#if UNITY_EDITOR
void OnValidate()
{
if (!Application.isPlaying) return;
Gradient lg = BuildGradient(startColor, endColor, startAlpha, endAlpha);
Gradient tg = BuildGradient(trailStartColor, trailEndColor, trailStartColor.a, 0f);
foreach (var entry in lines)
{
if (entry.lineRenderer == null) continue;
entry.lineRenderer.startWidth = lineWidth;
entry.lineRenderer.endWidth = lineWidth;
entry.lineRenderer.colorGradient = lg;
ApplyTrailSettings(entry.trailTransformA, tg);
ApplyTrailSettings(entry.trailTransformB, tg);
}
}
private void ApplyTrailSettings(Transform t, Gradient gradient)
{
if (t == null) return;
TrailRenderer tr = t.GetComponent<TrailRenderer>();
if (tr == null) return;
tr.time = trailTime;
tr.startWidth = trailStartWidth;
tr.endWidth = trailEndWidth;
tr.colorGradient = gradient;
tr.enabled = enableTrail;
}
#endif
}