


using UnityEngine;
using System.Collections.Generic;
#if UNITY_EDITOR
using UnityEditor;
#endif
[ExecuteAlways]
public class PerlinNoiseSurface : MonoBehaviour
{
[Header("๊ทธ๋ฆฌ๋ ๊ตฌ์กฐ (๋ณ๊ฒฝ ์ ์ค๋ธ์ ํธ ์ฌ์์ฑ)")]
public int gridSize = 20;
public float spacing = 1.2f;
[Header("๋
ธ์ด์ฆ ์ค์ ")]
public float noiseScale = 0.15f;
public float heightScale = 3f;
public float noiseSpeed = 0.5f;
[Header("ํฌ๊ธฐ ์ค์ ")]
public float sphereSize = 0.5f;
[Header("์์ ์ค์ ")]
public Gradient gradient;
[Header("์ฌ์ง ์ค์ ")]
public Material baseMaterial;
// ---------------------------------------------------
// ๊ตฌ์กฐ ํ๋ผ๋ฏธํฐ ์บ์
// ---------------------------------------------------
private int _cachedGridSize;
private float _cachedSpacing;
// ---------------------------------------------------
// ๊ตฌ์ฒด ๋ฐ์ดํฐ
// ---------------------------------------------------
struct SphereData
{
public Transform tr;
public Material mat;
}
List<SphereData> _spheres = new List<SphereData>();
#if UNITY_EDITOR
bool _generateQueued = false;
#endif
// ---------------------------------------------------
// Unity ์ด๋ฒคํธ
// ---------------------------------------------------
void OnEnable()
{
if (gradient == null || gradient.colorKeys.Length == 0)
gradient = CreateDefaultGradient();
Generate();
CacheStructureParams();
}
void OnValidate()
{
#if UNITY_EDITOR
if (gradient == null || gradient.colorKeys.Length == 0)
gradient = CreateDefaultGradient();
if (StructureParamsChanged())
{
CacheStructureParams();
QueueGenerate();
}
else
{
ApplyAnimation(0f);
}
#endif
}
#if UNITY_EDITOR
void QueueGenerate()
{
if (_generateQueued) return;
_generateQueued = true;
EditorApplication.delayCall += () =>
{
_generateQueued = false;
if (this == null) return;
Generate();
};
}
#endif
void Update()
{
#if UNITY_EDITOR
if (!Application.isPlaying) return;
#endif
ApplyAnimation(Time.time * noiseSpeed);
}
// ---------------------------------------------------
// ๊ตฌ์กฐ ํ๋ผ๋ฏธํฐ ๋ณ๊ฒฝ ๊ฐ์ง
// ---------------------------------------------------
bool StructureParamsChanged() =>
gridSize != _cachedGridSize || spacing != _cachedSpacing;
void CacheStructureParams()
{
_cachedGridSize = gridSize;
_cachedSpacing = spacing;
}
// ---------------------------------------------------
// ์์ฑ
// ---------------------------------------------------
void Generate()
{
Clear();
float offset = (gridSize - 1) * spacing * 0.5f;
for (int x = 0; x < gridSize; x++)
for (int z = 0; z < gridSize; z++)
{
Vector3 pos = new Vector3(x * spacing - offset, 0f, z * spacing - offset);
SpawnSphere(pos);
}
ApplyAnimation(0f);
}
void SpawnSphere(Vector3 pos)
{
if (baseMaterial == null)
{
Debug.LogWarning("[PerlinNoiseSurface] baseMaterial์ด ๋น์ด ์์ต๋๋ค. Inspector์์ URP/Lit ๋จธํฐ๋ฆฌ์ผ์ ํ ๋นํด์ฃผ์ธ์.");
return;
}
GameObject go = GameObject.CreatePrimitive(PrimitiveType.Sphere);
go.transform.SetParent(transform);
go.transform.localPosition = pos;
go.transform.localScale = Vector3.one * sphereSize;
Material mat = Instantiate(baseMaterial);
mat.EnableKeyword("_EMISSION");
mat.globalIlluminationFlags = MaterialGlobalIlluminationFlags.RealtimeEmissive;
go.GetComponent<Renderer>().material = mat;
_spheres.Add(new SphereData { tr = go.transform, mat = mat });
}
void Clear()
{
_spheres.Clear();
while (transform.childCount > 0)
DestroyImmediate(transform.GetChild(0).gameObject);
}
// ---------------------------------------------------
// ์ ๋๋ฉ์ด์
// ---------------------------------------------------
void ApplyAnimation(float t)
{
for (int i = 0; i < _spheres.Count; i++)
{
SphereData s = _spheres[i];
if (s.tr == null || s.mat == null) continue;
Vector3 p = s.tr.localPosition;
float n = Mathf.PerlinNoise(
p.x * noiseScale + t,
p.z * noiseScale + t * 0.8f
);
p.y = n * heightScale;
s.tr.localPosition = p;
s.tr.localScale = Vector3.one * (sphereSize * (0.6f + n * 0.8f));
Color albedo = gradient.Evaluate(n);
Color emission = albedo * (n * 0.6f);
s.mat.SetColor("_BaseColor", albedo);
s.mat.SetColor("_EmissionColor", emission);
}
}
// ---------------------------------------------------
// ์ ํธ๋ฆฌํฐ
// ---------------------------------------------------
Gradient CreateDefaultGradient()
{
Gradient g = new Gradient();
g.SetKeys(
new GradientColorKey[] {
new GradientColorKey(Color.blue, 0.0f),
new GradientColorKey(Color.cyan, 0.4f),
new GradientColorKey(Color.green, 0.7f),
new GradientColorKey(Color.yellow, 1.0f)
},
new GradientAlphaKey[] {
new GradientAlphaKey(1f, 0f),
new GradientAlphaKey(1f, 1f)
}
);
return g;
}
}
using UnityEngine;
using System.Collections.Generic;
// UNITY_EDITOR: ์๋ํฐ ์ ์ฉ ๊ธฐ๋ฅ(EditorApplication ๋ฑ)์ ์ฌ์ฉํ๊ธฐ ์ํ ์กฐ๊ฑด๋ถ ๋ค์์คํ์ด์ค
#if UNITY_EDITOR
using UnityEditor;
#endif
// [ExecuteAlways]: ํ๋ ์ด ๋ชจ๋๋ฟ ์๋๋ผ ์๋ํฐ ์ฌ ๋ทฐ์์๋ Update/OnEnable ๋ฑ์ด ์คํ๋๋๋ก ํ์ฉ
[ExecuteAlways]
public class PerlinNoiseSurface : MonoBehaviour
{
// -------------------------------------------------------
// Inspector ๋
ธ์ถ ํ๋ผ๋ฏธํฐ
// -------------------------------------------------------
[Header("๊ทธ๋ฆฌ๋ ๊ตฌ์กฐ (๋ณ๊ฒฝ ์ ์ค๋ธ์ ํธ ์ฌ์์ฑ)")]
// gridSize: ๊ฐ๋กยท์ธ๋ก ๋ฐฉํฅ์ ๋ฐฐ์นํ ๊ตฌ์ฒด์ ์. ์ ์ฒด ๊ตฌ์ฒด ์ = gridSize * gridSize
public int gridSize = 20;
// spacing: ์ด์ํ ๊ตฌ์ฒด ์ฌ์ด์ ๊ฐ๊ฒฉ(์๋ ์ ๋). ๊ฐ์ด ํด์๋ก ๊ตฌ์ฒด๊ฐ ๋๊ฒ ํผ์ง
public float spacing = 1.2f;
[Header("๋
ธ์ด์ฆ ์ค์ ")]
// noiseScale: PerlinNoise์ ๋๊ธฐ๋ ์ขํ ๋ฐฐ์จ. ์์์๋ก ๋๊ณ ์๋งํ ํํ, ํด์๋ก ์ข๊ณ ๋น ๋ฅธ ํํ
public float noiseScale = 0.15f;
// heightScale: ๋
ธ์ด์ฆ ๊ฐ(0~1)์ ๊ณฑํด Y์ถ ๋ณ์๋ก ๋ณํํ๋ ์ค์ผ์ผ. ๋์์๋ก ํ๊ณ ๊ฐ ํผ
public float heightScale = 3f;
// noiseSpeed: ์๊ฐ์ถ ์คํ์
์๋. Update์์ Time.time์ ๊ณฑํด ๋
ธ์ด์ฆ๊ฐ ํ๋ฅด๋ ์๋๋ฅผ ๊ฒฐ์
public float noiseSpeed = 0.5f;
[Header("ํฌ๊ธฐ ์ค์ ")]
// sphereSize: ๊ฐ ๊ตฌ์ฒด์ ๊ธฐ๋ณธ localScale ๊ฐ. ์ ๋๋ฉ์ด์
์ค ๋งฅ๋์ ๊ธฐ์ค ํฌ๊ธฐ๋ก๋ ์ฌ์ฉ๋จ
public float sphereSize = 0.5f;
[Header("์์ ์ค์ ")]
// gradient: ๋
ธ์ด์ฆ ๊ฐ(0~1)์ ์์์ผ๋ก ๋งคํํ๋ ๊ทธ๋ผ๋์ธํธ.
// Evaluate(n)์ผ๋ก ๋ฎ์ ์ง์ ~๋์ ์ง์ ์ ์์ ์ฐ์์ ์ผ๋ก ๋ณด๊ฐํจ
public Gradient gradient;
[Header("์ฌ์ง ์ค์ ")]
// baseMaterial: ๊ตฌ์ฒด ์์ฑ ์ Instantiate์ ์๋ณธ์ด ๋๋ URP Lit ๋จธํฐ๋ฆฌ์ผ.
// Inspector์์ ๋ฐ๋์ URP/Lit ๊ธฐ๋ฐ ๋จธํฐ๋ฆฌ์ผ์ ์ง์ ํ ๋นํด์ผ ํจ
public Material baseMaterial;
// -------------------------------------------------------
// ๊ตฌ์กฐ ํ๋ผ๋ฏธํฐ ์บ์ โ OnValidate ๋น๊ต์ฉ
// -------------------------------------------------------
// _cachedGridSize / _cachedSpacing: ๋ง์ง๋ง์ผ๋ก Generate()๊ฐ ์คํ๋์ ๋์
// gridSizeยทspacing ๊ฐ์ ์ ์ฅ. OnValidate์์ ํ์ฌ ๊ฐ๊ณผ ๋น๊ตํด
// ์ค์ ๋ก ๊ตฌ์กฐ๊ฐ ๋ฐ๋์๋์ง ํ๋จํ๋ ๊ธฐ์ค์ผ๋ก ์ฌ์ฉ๋จ
private int _cachedGridSize;
private float _cachedSpacing;
// -------------------------------------------------------
// ๊ตฌ์ฒด ๋ฐ์ดํฐ ๊ตฌ์กฐ์ฒด
// -------------------------------------------------------
// SphereData: ๊ฐ๋ณ ๊ตฌ์ฒด์ Transform๊ณผ ์ธ์คํด์ค Material์ ๋ฌถ๋ ๊ฒฝ๋ ๊ตฌ์กฐ์ฒด.
// ํด๋์ค ๋์ struct๋ฅผ ์ฌ์ฉํด ํ ํ ๋น ์์ด List์ ๊ฐ ๋ณต์ฌ๋ก ์ ์ฅ๋จ
struct SphereData
{
// tr: ๊ตฌ์ฒด GameObject์ Transform. ์์น(Y)์ ํฌ๊ธฐ(localScale)๋ฅผ ๋งค ํ๋ ์ ๊ฐฑ์ ํ ๋ ์ฌ์ฉ
public Transform tr;
// mat: baseMaterial์ Instantiateํ ๊ฐ๋ณ ์ธ์คํด์ค. ๊ตฌ์ฒด๋ง๋ค ๋
๋ฆฝ์ ์ธ ์์ ์ ์ฉ์ ์ํด ๋ณต์ ํจ
public Material mat;
}
// _spheres: ์์ฑ๋ ๋ชจ๋ ๊ตฌ์ฒด์ SphereData๋ฅผ ๋ด๋ ๋ฆฌ์คํธ.
// ApplyAnimation()์ด ์ด ๋ฆฌ์คํธ๋ฅผ ์ํํ๋ฉฐ ๋งค ํ๋ ์ ์ ์ฒด ๊ตฌ์ฒด๋ฅผ ๊ฐฑ์ ํจ
List<SphereData> _spheres = new List<SphereData>();
// -------------------------------------------------------
// ์๋ํฐ ์ ์ฉ ์ํ ํ๋๊ทธ
// -------------------------------------------------------
#if UNITY_EDITOR
// _generateQueued: Generate()์ ์ค๋ณต ์์ฝ์ ๋ง๋ ํ๋๊ทธ.
// OnValidate๋ ํ๋ผ๋ฏธํฐ ๋ณ๊ฒฝ๋ง๋ค ์ฌ๋ฌ ๋ฒ ํธ์ถ๋ ์ ์์ผ๋ฏ๋ก
// delayCall์ด ์ด๋ฏธ ๋ฑ๋ก๋ผ ์์ผ๋ฉด ์ฌ๋ฑ๋กํ์ง ์๋๋ก ์ฐจ๋จํจ
bool _generateQueued = false;
#endif
// -------------------------------------------------------
// Unity ์ด๋ฒคํธ ๋ฉ์๋
// -------------------------------------------------------
// OnEnable: ์ปดํฌ๋ํธ๊ฐ ํ์ฑํ๋ ๋(์ฌ ๋ก๋, ์ปดํฌ๋ํธ ํ์ฑํ, ์๋ํฐ ์ฌ์ปดํ์ผ ํ ๋ฑ) ํธ์ถ.
// ๊ทธ๋ผ๋์ธํธ ์ด๊ธฐํ โ ๊ตฌ์ฒด ์ ์ฒด ์์ฑ โ ๊ตฌ์กฐ ํ๋ผ๋ฏธํฐ ์บ์ ์์ผ๋ก ์ด๊ธฐ ์ํ๋ฅผ ๊ตฌ์ฑํจ
void OnEnable()
{
// ๊ทธ๋ผ๋์ธํธ๊ฐ ๋น์ด ์์ผ๋ฉด ๊ธฐ๋ณธ๊ฐ์ผ๋ก ์ด๊ธฐํ
if (gradient == null || gradient.colorKeys.Length == 0)
gradient = CreateDefaultGradient();
Generate();
CacheStructureParams();
}
// OnValidate: Inspector์์ ์ด๋ค public ํ๋๋ ๊ฐ์ด ๋ฐ๋ ๋๋ง๋ค ์๋ํฐ์์ ์๋ ํธ์ถ.
// ๊ตฌ์กฐ ํ๋ผ๋ฏธํฐ(gridSize, spacing)๊ฐ ๋ฐ๋๋ฉด ๊ตฌ์ฒด๋ฅผ ์ฌ์์ฑํ๊ณ ,
// ๊ทธ ์ธ ํ๋ผ๋ฏธํฐ(๋
ธ์ด์ฆยท์์ยทํฌ๊ธฐ)๋ง ๋ฐ๋๋ฉด ์ฌ์์ฑ ์์ด ์ฆ์ ๋ฏธ๋ฆฌ๋ณด๊ธฐ๋ง ๊ฐฑ์ ํจ
void OnValidate()
{
#if UNITY_EDITOR
if (gradient == null || gradient.colorKeys.Length == 0)
gradient = CreateDefaultGradient();
if (StructureParamsChanged())
{
// ๊ตฌ์กฐ๊ฐ ๋ฐ๋์์ผ๋ฉด ์บ์๋ฅผ ๋จผ์ ๊ฐฑ์ ํ๊ณ ์ฌ์์ฑ ์์ฝ
CacheStructureParams();
QueueGenerate();
}
else
{
// ๋
ธ์ด์ฆยท์์ยทํฌ๊ธฐ ํ๋ผ๋ฏธํฐ๋ง ๋ณ๊ฒฝ๋ ๊ฒฝ์ฐ โ t=0 ๊ธฐ์ค์ผ๋ก ์ฆ์ ์ฌ ๋ทฐ ๋ฏธ๋ฆฌ๋ณด๊ธฐ ๊ฐฑ์
ApplyAnimation(0f);
}
#endif
}
#if UNITY_EDITOR
// QueueGenerate: Generate()๋ฅผ EditorApplication.delayCall์ ๋ฑ๋กํด ๋ค์ ์๋ํฐ ํ๋ ์์ ์คํ๋๊ฒ ์์ฝ.
// OnValidate ๋์ค์๋ ์ฌ ๊ทธ๋ํ ์์ ์ด ์ ํ๋๊ธฐ ๋๋ฌธ์,
// ์ง์ ํธ์ถ ๋์ delayCall๋ก ํ ํ๋ ์ ๋ค์ ์์ ํ๊ฒ ์คํํจ
void QueueGenerate()
{
if (_generateQueued) return; // ์ด๋ฏธ ์์ฝ๋ผ ์์ผ๋ฉด ์ค๋ณต ๋ฑ๋ก ๋ฐฉ์ง
_generateQueued = true;
EditorApplication.delayCall += () =>
{
_generateQueued = false;
if (this == null) return; // ์ปดํฌ๋ํธ๊ฐ ์ญ์ ๋ ๊ฒฝ์ฐ ์กฐ๊ธฐ ํ์ถ
Generate();
};
}
#endif
// Update: ๋งค ํ๋ ์ ํธ์ถ. ํ๋ ์ด ๋ชจ๋์์๋ง ์๊ฐ ๊ธฐ๋ฐ ์ ๋๋ฉ์ด์
์ ๊ตฌ๋ํจ.
// ์๋ํฐ ์ฌ ๋ทฐ์์๋ ์ ์ง ์ํ๋ฅผ ์ ์งํ๊ธฐ ์ํด isPlaying ์กฐ๊ฑด์ผ๋ก ์ฐจ๋จ
void Update()
{
#if UNITY_EDITOR
if (!Application.isPlaying) return;
#endif
// Time.time * noiseSpeed: ๊ฒฝ๊ณผ ์๊ฐ์ ์๋๋ฅผ ๊ณฑํด ๋
ธ์ด์ฆ ์คํ์
์ ์ ํ์ผ๋ก ์ฆ๊ฐ์ํด
ApplyAnimation(Time.time * noiseSpeed);
}
// -------------------------------------------------------
// ๊ตฌ์กฐ ํ๋ผ๋ฏธํฐ ๋ณ๊ฒฝ ๊ฐ์ง
// -------------------------------------------------------
// StructureParamsChanged: ํ์ฌ Inspector ๊ฐ๊ณผ ์บ์๋ ๊ฐ์ ๋น๊ตํด
// ๊ตฌ์ฒด๋ฅผ ์ฌ์์ฑํด์ผ ํ๋ ๋ณ๊ฒฝ์ธ์ง ํ๋จํจ.
// true๋ฅผ ๋ฐํํ๋ฉด OnValidate๊ฐ QueueGenerate()๋ฅผ ํธ์ถํจ
bool StructureParamsChanged() =>
gridSize != _cachedGridSize || spacing != _cachedSpacing;
// CacheStructureParams: ํ์ฌ gridSizeยทspacing์ ์บ์์ ์ ์ฅ.
// Generate() ์ง์ ๋๋ OnEnable์์ ํธ์ถํด ๊ธฐ์ค๊ฐ์ ์ต์ ์ํ๋ก ์ ์งํจ
void CacheStructureParams()
{
_cachedGridSize = gridSize;
_cachedSpacing = spacing;
}
// -------------------------------------------------------
// ๊ตฌ์ฒด ์์ฑ
// -------------------------------------------------------
// Generate: ๊ธฐ์กด ๊ตฌ์ฒด๋ฅผ ๋ชจ๋ ์ญ์ ํ๊ณ gridSizeรgridSize ๊ฐ์ ๊ตฌ์ฒด๋ฅผ ์๋ก ๋ฐฐ์นํจ.
// ๊ทธ๋ฆฌ๋๋ฅผ ๋ก์ปฌ ์์ ๊ธฐ์ค์ผ๋ก ์ค์ ์ ๋ ฌํ ๋ค ๊ฐ ์
์์น์ SpawnSphere๋ฅผ ํธ์ถํจ
void Generate()
{
Clear();
// offset: ๊ทธ๋ฆฌ๋ ์ ์ฒด ํญ์ ์ ๋ฐ. ๊ฐ ๊ตฌ์ฒด ์์น์์ ๋นผ๋ฉด XยทZ ์ถ์ด ์์ ๋์นญ์ผ๋ก ์ ๋ ฌ๋จ
// ์) gridSize=20, spacing=1.2 โ offset=11.4 โ X ๋ฒ์: -11.4 ~ +11.4
float offset = (gridSize - 1) * spacing * 0.5f;
for (int x = 0; x < gridSize; x++)
for (int z = 0; z < gridSize; z++)
{
Vector3 pos = new Vector3(x * spacing - offset, 0f, z * spacing - offset);
SpawnSphere(pos);
}
// ์์ฑ ์งํ t=0 ๊ธฐ์ค์ผ๋ก ๋
ธ์ด์ฆ ์ ์ฉ โ ์๋ํฐ์์๋ ์ด๊ธฐ ํํ๋ฅผ ์ฆ์ ํ์ธ ๊ฐ๋ฅ
ApplyAnimation(0f);
}
// SpawnSphere: ์ฃผ์ด์ง ๋ก์ปฌ ์์น์ ๊ตฌ์ฒด ํ๋๋ฅผ ์์ฑํ๊ณ _spheres ๋ฆฌ์คํธ์ ๋ฑ๋กํจ.
// baseMaterial์ Instantiateํด ๊ตฌ์ฒด๋ณ ๋
๋ฆฝ ๋จธํฐ๋ฆฌ์ผ์ ๋ง๋ค๊ณ ,
// _EMISSION ํค์๋๋ฅผ ํ์ฑํํด ์ค์๊ฐ ์๋ฏธ์
์์ ์ ์ฉ์ ์ค๋นํจ
void SpawnSphere(Vector3 pos)
{
if (baseMaterial == null)
{
Debug.LogWarning("[PerlinNoiseSurface] baseMaterial์ด ๋น์ด ์์ต๋๋ค. Inspector์์ URP/Lit ๋จธํฐ๋ฆฌ์ผ์ ํ ๋นํด์ฃผ์ธ์.");
return;
}
// CreatePrimitive: ๋ฉ์ยท์ฝ๋ผ์ด๋๊ฐ ๋ด์ฅ๋ ๊ธฐ๋ณธ ๊ตฌ์ฒด GameObject๋ฅผ ์์ฑํจ
GameObject go = GameObject.CreatePrimitive(PrimitiveType.Sphere);
go.transform.SetParent(transform); // ์ด ์ปดํฌ๋ํธ์ GameObject๋ฅผ ๋ถ๋ชจ๋ก ์ค์ ํด ๊ณ์ธต ์ ๋ฆฌ
go.transform.localPosition = pos;
go.transform.localScale = Vector3.one * sphereSize;
// Instantiate(baseMaterial): ์๋ณธ ๋จธํฐ๋ฆฌ์ผ์ ๋ณต์ ํด ๊ตฌ์ฒด๋ง๋ค ๊ณ ์ ํ ์ธ์คํด์ค๋ฅผ ๋ง๋ฆ.
// ๋ณต์ ํ์ง ์์ผ๋ฉด ๋ชจ๋ ๊ตฌ์ฒด๊ฐ ๊ฐ์ ๋จธํฐ๋ฆฌ์ผ์ ๊ณต์ ํด ์์์ ๊ฐ๋ณ ์ ์ดํ ์ ์์
Material mat = Instantiate(baseMaterial);
// _EMISSION ํค์๋: URP Lit ์
ฐ์ด๋์์ ์๋ฏธ์
๊ณ์ฐ์ ํ์ฑํํ๋ ํค์๋.
// ์ด ํค์๋ ์์ด _EmissionColor๋ฅผ ์ค์ ํด๋ ๋ฐ๊ด ํจ๊ณผ๊ฐ ์ ์ฉ๋์ง ์์
mat.EnableKeyword("_EMISSION");
// RealtimeEmissive: ์๋ฏธ์
์์์ด ์ค์๊ฐ์ผ๋ก GI์ ๊ธฐ์ฌํ๋๋ก ํ๋๊ทธ๋ฅผ ์ค์ ํจ
mat.globalIlluminationFlags = MaterialGlobalIlluminationFlags.RealtimeEmissive;
go.GetComponent<Renderer>().material = mat;
// SphereData ๊ตฌ์กฐ์ฒด๋ก ๋ฌถ์ด ๋ฆฌ์คํธ์ ์ถ๊ฐ โ ApplyAnimation์ด ์ด ๋ฐ์ดํฐ๋ฅผ ์ฐธ์กฐํจ
_spheres.Add(new SphereData { tr = go.transform, mat = mat });
}
// Clear: _spheres ๋ฆฌ์คํธ๋ฅผ ๋น์ฐ๊ณ ์์ GameObject๋ฅผ ์ ๋ถ ์ฆ์ ํ๊ดดํจ.
// DestroyImmediate: ์๋ํฐ ๋ชจ๋์์๋ Destroy๊ฐ ๋์ํ์ง ์์ผ๋ฏ๋ก ์ฆ์ ์ญ์ ๊ฐ ํ์ํจ
void Clear()
{
_spheres.Clear();
while (transform.childCount > 0)
DestroyImmediate(transform.GetChild(0).gameObject);
}
// -------------------------------------------------------
// ์ ๋๋ฉ์ด์
(๋
ธ์ด์ฆ ๊ธฐ๋ฐ ์์นยทํฌ๊ธฐยท์์ ๊ฐฑ์ )
// -------------------------------------------------------
// ApplyAnimation: ํ๋ผ๋ฏธํฐ t(์๊ฐ ์คํ์
)๋ฅผ ๋ฐ์ ์ ์ฒด ๊ตฌ์ฒด์ Y ์์นยทํฌ๊ธฐยท์์์ ๊ฐฑ์ ํจ.
// ํ๋ ์ด ๋ชจ๋์์๋ Update๊ฐ Time.time ๊ธฐ๋ฐ t๋ฅผ ์ ๋ฌํ๊ณ ,
// ์๋ํฐ ๋ฏธ๋ฆฌ๋ณด๊ธฐ ์์๋ t=0์ผ๋ก ํธ์ถํด ์ ์ ์ค๋
์ท์ ํ์ํจ
void ApplyAnimation(float t)
{
for (int i = 0; i < _spheres.Count; i++)
{
SphereData s = _spheres[i];
if (s.tr == null || s.mat == null) continue; // ์ธ๋ถ์์ ์ญ์ ๋ ๊ฒฝ์ฐ ๊ฑด๋๋
Vector3 p = s.tr.localPosition;
// PerlinNoise(x, y): ๋ ์ถ ์
๋ ฅ์ผ๋ก 0~1 ์ฌ์ด์ ์ฐ์์ ์ธ ๋
ธ์ด์ฆ ๊ฐ์ ๋ฐํํจ.
// p.xยทp.z์ noiseScale์ ๊ณฑํด ๊ณต๊ฐ ์ฃผํ์๋ฅผ ์กฐ์ ํ๊ณ ,
// t์ t*0.8f๋ก XยทZ ์ถ ์คํ์
์ ์๋ก ๋ค๋ฅธ ์๋๋ฅผ ์ฃผ์ด ๋๊ฐ์ ์ด ์๋ ์์ฐ์ค๋ฌ์ด 2D ํ๋ฆ์ ๋ง๋ฆ
float n = Mathf.PerlinNoise(
p.x * noiseScale + t,
p.z * noiseScale + t * 0.8f
);
// Y ์์น: ๋
ธ์ด์ฆ ๊ฐ์ heightScale์ ๊ณฑํด ํ๋์ฒ๋ผ ์์๋๋ก ๋ณ์์ํด
p.y = n * heightScale;
s.tr.localPosition = p;
// ํฌ๊ธฐ ๋งฅ๋: ๋
ธ์ด์ฆ๊ฐ ๋ฎ์ ๋ 0.6ร, ๋์ ๋ 1.4ร ํฌ๊ธฐ๊ฐ ๋๋๋ก ์ ํ ๋ณด๊ฐ.
// ๊ตฌ์ฒด๊ฐ ๋์ด ์์์๋ก ์ปค์ง๋ ์๊ฐ์ ๊ฐ์กฐ ํจ๊ณผ๋ฅผ ๋ง๋ฆ
s.tr.localScale = Vector3.one * (sphereSize * (0.6f + n * 0.8f));
// gradient.Evaluate(n): ๋
ธ์ด์ฆ ๊ฐ์ ๊ทธ๋ผ๋์ธํธ์ t ์์น๋ก ์ฌ์ฉํด ์์์ ์ํ๋งํจ
Color albedo = gradient.Evaluate(n);
// emission: albedo์ ๋
ธ์ด์ฆ ๊ฐ๋๋ฅผ ๊ณฑํด ๋์ ์ง์ ์ผ์๋ก ๋ฐ๊ฒ ๋ฐ๊ดํ๋๋ก ์ค์ผ์ผ๋งํจ
Color emission = albedo * (n * 0.6f);
// URP Lit ์
ฐ์ด๋์ ํ๋กํผํฐ ์ด๋ฆ์ผ๋ก ์์์ ์ง์ ์ง์
// _BaseColor: ํ๋ฉด์ ์๋ฒ ๋(๊ธฐ๋ณธ ๋ฐ์ฌ์)
// _EmissionColor: ๋ฐ๊ด ์์. EnableKeyword("_EMISSION")์ด ์ ํ๋ผ์ผ ์ค์ ๋ก ์ ์ฉ๋จ
s.mat.SetColor("_BaseColor", albedo);
s.mat.SetColor("_EmissionColor", emission);
}
}
// -------------------------------------------------------
// ์ ํธ๋ฆฌํฐ
// -------------------------------------------------------
// CreateDefaultGradient: gradient ํ๋๊ฐ ๋น์ด ์์ ๋ ์ฌ์ฉํ ๊ธฐ๋ณธ ์์ ๋ฐฐ์ด์ ์์ฑํจ.
// ๋ฎ์ ๋
ธ์ด์ฆ๊ฐ(0) โ ํ๋, ๋์ ๋
ธ์ด์ฆ๊ฐ(1) โ ๋
ธ๋ ์์ผ๋ก
// ์์ฌยท๊ณ ๋๊ฐ์ ์๊ฐ์ ์ผ๋ก ์์ํ๋ ํ๋ ํธ๋ฅผ ์ ๊ณตํจ
Gradient CreateDefaultGradient()
{
Gradient g = new Gradient();
g.SetKeys(
new GradientColorKey[] {
new GradientColorKey(Color.blue, 0.0f),
new GradientColorKey(Color.cyan, 0.4f),
new GradientColorKey(Color.green, 0.7f),
new GradientColorKey(Color.yellow, 1.0f)
},
// AlphaKey: ์ ๊ตฌ๊ฐ alpha=1 (์์ ๋ถํฌ๋ช
) ๊ณ ์
new GradientAlphaKey[] {
new GradientAlphaKey(1f, 0f),
new GradientAlphaKey(1f, 1f)
}
);
return g;
}
}
---