
- Materail์ ๋ง๋ค๊ณ Shader์ ์ข
๋ฅ๋ฅผ
Sprite/Default ๋๋ Particle/Standard Unlit์ผ๋ก ์ง์ ํ๊ธฐ


- GameObject์
FlowFieldLine.cs๋ฅผ ์ปดํฌ๋ํธ๋ก ๋ถ์ด๊ณ
- Inspector์ Line Renderer ํญ๋ชฉ์ ์๋ Material์
FlowFieldLine์ ํ ๋นํ๊ธฐ

- Inspector์ Flow Field Line (Script) ์์ญ์์ ์ค์ ํ๊ธฐ

Code
๐ FlowFieldLine.cs
using System.Collections.Generic;
using UnityEngine;
[RequireComponent(typeof(LineRenderer))]
public class FlowFieldLine : MonoBehaviour
{
[Header("Particle Settings")]
[SerializeField] private int count = 200;
[SerializeField] private int randomSeed = 42;
[SerializeField] private float radius = 5f;
[SerializeField] private float speed = 1f;
[Header("Flow Field Settings")]
[SerializeField] private float noiseScale = 0.3f;
[SerializeField] private float noiseSpeed = 0.5f;
[Header("Line Settings")]
[SerializeField] private float startWidth = 0.05f;
[SerializeField] private float endWidth = 0.01f;
[Header("Color Settings")]
[Tooltip("๋ผ์ธ ์์ ์์")]
[SerializeField] private Color startColor = new Color(0.2f, 0.8f, 1f, 1f);
[Tooltip("๋ผ์ธ ๋ ์์")]
[SerializeField] private Color endColor = new Color(0.8f, 0.2f, 1f, 0f);
[Tooltip("์ผ๋ฉด ์๋ Gradient๋ฅผ ์ง์ ์ฌ์ฉํฉ๋๋ค (๋๋ฉด Start/End Color ์๋ ์ ์ฉ)")]
[SerializeField] private bool useCustomGradient = false;
[SerializeField] private Gradient customGradient;
private struct ParticleData
{
public Vector3 position;
public Vector3 velocity;
public float life;
public float maxLife;
}
private readonly List<ParticleData> particles = new List<ParticleData>();
private Vector3[] positions;
private LineRenderer lineRenderer;
void Awake()
{
lineRenderer = GetComponent<LineRenderer>();
positions = new Vector3[count];
particles.Capacity = count;
}
void Start()
{
Random.InitState(randomSeed);
InitializeParticles();
SetupLineRenderer();
}
void Update()
{
Step(Time.deltaTime);
ApplyLine();
}
void InitializeParticles()
{
particles.Clear();
for (int i = 0; i < count; i++)
{
Vector3 pos = Random.insideUnitSphere * radius;
particles.Add(new ParticleData
{
position = pos,
velocity = Random.onUnitSphere * speed,
life = Random.Range(2f, 5f),
maxLife = 5f
});
positions[i] = pos;
}
}
void SetupLineRenderer()
{
lineRenderer.positionCount = count;
lineRenderer.useWorldSpace = true;
lineRenderer.startWidth = startWidth;
lineRenderer.endWidth = endWidth;
lineRenderer.textureMode = LineTextureMode.Stretch;
lineRenderer.alignment = LineAlignment.View;
ApplyColor();
}
void ApplyColor()
{
Gradient gradient;
if (useCustomGradient && customGradient != null)
{
gradient = customGradient;
}
else
{
gradient = BuildGradientFromColors(startColor, endColor);
}
lineRenderer.colorGradient = gradient;
}
Gradient BuildGradientFromColors(Color a, Color b)
{
var gradient = new Gradient();
gradient.SetKeys(
new GradientColorKey[]
{
new GradientColorKey(a, 0f),
new GradientColorKey(b, 1f)
},
new GradientAlphaKey[]
{
new GradientAlphaKey(a.a, 0f),
new GradientAlphaKey(b.a, 1f)
}
);
return gradient;
}
void Step(float dt)
{
float t = Time.time * noiseSpeed;
for (int i = 0; i < particles.Count; i++)
{
var p = particles[i];
Vector3 flow = new Vector3(
Mathf.PerlinNoise(p.position.y * noiseScale + t, p.position.z * noiseScale) - 0.5f,
Mathf.PerlinNoise(p.position.z * noiseScale + t, p.position.x * noiseScale) - 0.5f,
Mathf.PerlinNoise(p.position.x * noiseScale + t, p.position.y * noiseScale) - 0.5f
);
p.velocity += flow * dt;
p.position += p.velocity * dt;
p.life -= dt;
if (p.life <= 0f)
{
p.position = Random.insideUnitSphere * radius;
p.velocity = Random.onUnitSphere * speed;
p.life = p.maxLife;
}
particles[i] = p;
positions[i] = p.position;
}
}
void ApplyLine()
{
lineRenderer.SetPositions(positions);
}
#if UNITY_EDITOR
void OnValidate()
{
if (lineRenderer == null) return;
ApplyColor();
}
#endif
}