
GameObject์ ๋ถ์ฌ์ ์ฌ์ฉ
"๋ฉ๋ชจ๋ฆฌ๋ฅผ ์๋ผ๋ฉด์ ์์์ ๋ฐ๊พธ๋ ๋ง๋ฒ ์์"
๊ธฐ์กด ๋ฐฉ์์ ๋ฌธ์ ์ (renderer.material ์ง์ ์์ ):
renderer.material์ ์ ๊ทผํ์ฌ ์์์ ๋ฐ๊พธ๋ฉด, ์ ๋ํฐ๋ ์๋์ผ๋ก ํด๋น ๋จธํฐ๋ฆฌ์ผ์ ๋ณต์ (Clone)ํ์ฌ ์๋ก์ด ๋ฉ๋ชจ๋ฆฌ ๊ณต๊ฐ์ ํ ๋นํฉ๋๋ค.MaterialPropertyBlock์ ํด๊ฒฐ์ฑ :
์ฝ๋์์์ ์ ์ฉ:
// 1. ๋ธ๋ก ๋ฐ์ดํฐ ์์ฑ (ํ ๋ฒ๋ง ์์ฑํด๋๊ณ ์ฌ์ฌ์ฉ)
_propBlock.SetColor("_BaseColor", albedo);
// 2. ๋ ๋๋ฌ์ ์ ๋ฌ (๋จธํฐ๋ฆฌ์ผ ์๋ณธ์ ๊ฑด๋๋ฆฌ์ง ์์)
s.renderer.SetPropertyBlock(_propBlock);
"์ค์ ๊ท์ฐฎ์ ๋ ์์ด๋๋ฅผ ์์์ ์ฐพ์์ฃผ๋ ์๋ํ ํจ์"
์ด ๋ฉ์๋๋ ์คํฌ๋ฆฝํธ๊ฐ ์คํ๋ ๋ ์ธ์คํํฐ์ ๋จธํฐ๋ฆฌ์ผ์ด ์๋ค๋ฉด, URP ํ๊ฒฝ์ ๋ง๋ ํ์ค ๋จธํฐ๋ฆฌ์ผ์ ์ฝ๋๋ก ์ง์ ์์ฑํ๋ ์ญํ ์ ํฉ๋๋ค.
Shader.Find("Universal Render Pipeline/Lit"): ์ ๋ํฐ URP ํ๋ก์ ํธ์ ํ์ค ์์ด๋๋ฅผ ์ฐพ์ต๋๋ค. ์ด๋ Unity 6.0 URP ํ๊ฒฝ์์ ๊ฐ์ฅ ํธํ์ฑ์ด ์ข์ ์์ด๋์
๋๋ค.new Material(shader): ์ฐพ์ ์์ด๋๋ก ์๋ก์ด ๋จธํฐ๋ฆฌ์ผ ์ธ์คํด์ค๋ฅผ ๋ง๋ญ๋๋ค.mat.EnableKeyword("_EMISSION"): ๋จธํฐ๋ฆฌ์ผ์ ๋ฐ๊ด(Emission) ๊ธฐ๋ฅ์ ์ผญ๋๋ค. ์ด ํค์๋๊ฐ ์ผ์ ธ์ผ _EmissionColor ํ๋กํผํฐ๊ฐ ์๋ํฉ๋๋ค.baseMaterial ๋ณ์์ ํ ๋นํด ์ค๋๋ค."๋น์ ๋ด๋ฟ๋ ๋ ์์ด ์ฃผ๋ณ๋ ๋ฐํ๊ฒ ๋ง๋๋ ์ค์ "
์ด ์์ฑ์ ๋จธํฐ๋ฆฌ์ผ์ด ๊ธ๋ก๋ฒ ์ผ๋ฃจ๋ฏธ๋ค์ด์ (Global Illumination, GI) ์์คํ ๊ณผ ์ด๋ป๊ฒ ์ํธ์์ฉํ ์ง๋ฅผ ๊ฒฐ์ ํฉ๋๋ค.
์ค์ ๊ฐ RealtimeEmissive์ ์๋ฏธ:
์ฝ๋์์์ ์ญํ :
mat.globalIlluminationFlags = MaterialGlobalIlluminationFlags.RealtimeEmissive;
์ด ์ฝ๋๊ฐ ์๋ค๋ฉด ๊ตฌ์ฒด๋ ์ค์ค๋ก ๋น๋ ๋ณด์ผ ๋ฟ, ์ฃผ๋ณ ํ๊ฒฝ์๋ ์๋ฌด๋ฐ ๋น์ ์ํฅ์ ์ฃผ์ง ์๋ '์์ฒด ๋ฐ๊ด(Self-Illuminated)' ์ํ๋ก๋ง ๋จ๊ฒ ๋ฉ๋๋ค. ์ด ์ฝ๋๋ฅผ ํตํด ์๋์ง ํ๋๊ฐ ์ค์ ๊ด์์ฒ๋ผ ํ๋ํ๋๋ก ์ค์ ํฉ๋๋ค.
None์ผ๋ก ์ค์ ํ์ฌ ์ฃผ๋ณ ์กฐ๋ช
์ํฅ์ ๋๊ฑฐ๋, Light Probe๋ฅผ ํ์ฉํ๋ ๋ฐฉ์์ผ๋ก ์ต์ ํํ๊ธฐ๋ ํฉ๋๋ค. ํ์ง๋ง PC/์ฝ์ ๊ธ์ ๋น์ฃผ์ผ ํ๋ฆฌํฐ๋ฅผ ์ํด์๋ RealtimeEmissive๊ฐ ์ ์ ํฉ๋๋ค.using UnityEngine;
using System.Collections.Generic;
#if UNITY_EDITOR
using UnityEditor;
#endif
[ExecuteAlways]
public class RadialEnergyField : MonoBehaviour
{
[Header("๊ทธ๋ฆฌ๋ ๊ตฌ์กฐ (๋ณ๊ฒฝ ์ ์ค๋ธ์ ํธ ์ฌ์์ฑ)")]
public int ringCount = 15;
public int baseSegments = 6;
public float ringSpacing = 1.2f;
[Header("์๋์ง ํ๋ ์ค์ ")]
public float waveHeight = 2.0f;
public float waveSpeed = 1.4f;
public float waveFrequency = 1.5f;
[Header("ํ์ ํ๋ ์ค์ ")]
public float swirlStrength = 0.4f;
public float radialPulse = 0.6f;
[Header("ํฌ๊ธฐ ์ค์ ")]
public float scaleMin = 0.15f;
public float scaleMax = 0.9f;
[Header("์์ ์ค์ (์ง์ ์์ ๊ฐ๋ฅ)")]
[Tooltip("ํ๋์ ์ต์ ์์ (์ํ ํฌํจ)")]
public Color colorMin = new Color(0.0f, 0.5f, 1.0f, 1.0f);
[Tooltip("ํ๋์ ์ต๋ ์์ (์ํ ํฌํจ)")]
public Color colorMax = new Color(1.0f, 1.0f, 1.0f, 1.0f);
[Tooltip("๋ฐ๊ด ๊ฐ๋")]
public float emissionIntensity = 1f;
[Header("์ฌ์ง ์ค์ (URP)")]
[Tooltip("๋น์๋๋ฉด ์๋์ผ๋ก URP Lit ๋จธํฐ๋ฆฌ์ผ์ ์์ฑํฉ๋๋ค.")]
public Material baseMaterial;
// ๋ด๋ถ ์บ์ฑ ๋ณ์
private int _cachedRingCount;
private int _cachedBaseSegments;
private float _cachedRingSpacing;
struct SphereData
{
public Transform tr;
public Renderer renderer;
public float radius;
public float angle;
public Vector3 basePos;
}
List<SphereData> spheres = new List<SphereData>();
MaterialPropertyBlock _propBlock;
#if UNITY_EDITOR
bool _generateQueued = false;
#endif
void OnEnable()
{
transform.position = Vector3.zero;
transform.rotation = Quaternion.identity;
if (baseMaterial == null)
baseMaterial = CreateURPMaterial();
_propBlock = new MaterialPropertyBlock();
Generate();
CacheStructureParams();
}
void OnValidate()
{
#if UNITY_EDITOR
if (baseMaterial == null)
baseMaterial = CreateURPMaterial();
if (StructureParamsChanged())
{
QueueGenerate();
CacheStructureParams();
}
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 * waveSpeed);
}
bool StructureParamsChanged()
{
return ringCount != _cachedRingCount
|| baseSegments != _cachedBaseSegments
|| ringSpacing != _cachedRingSpacing;
}
void CacheStructureParams()
{
_cachedRingCount = ringCount;
_cachedBaseSegments = baseSegments;
_cachedRingSpacing = ringSpacing;
}
// ---------------------------------------------------
// ์์ฑ ๋ก์ง
// ---------------------------------------------------
void Generate()
{
Clear();
SpawnSphere(Vector3.zero, 0, 0);
for (int ring = 1; ring <= ringCount; ring++)
{
float radius = ring * ringSpacing;
int segments = baseSegments * ring;
for (int s = 0; s < segments; s++)
{
float angle = (float)s / segments * Mathf.PI * 2f;
Vector3 pos = new Vector3(Mathf.Cos(angle) * radius, 0, Mathf.Sin(angle) * radius);
SpawnSphere(pos, radius, angle);
}
}
ApplyAnimation(0f);
}
void SpawnSphere(Vector3 pos, float radius, float angle)
{
GameObject go = GameObject.CreatePrimitive(PrimitiveType.Sphere);
// ---------------------------------------------------
// ์ฝ๋ผ์ด๋ ๋ฐ ๋ฆฌ์ง๋๋ฐ๋ ์ถ๊ฐ ๋ก์ง
// ---------------------------------------------------
// 1. Collider: CreatePrimitive๊ฐ ์๋์ผ๋ก ์์ฑํ๋ฏ๋ก ์ญ์ ํ์ง ์์.
// ํ์ํ๋ค๋ฉด ํธ๋ฆฌ๊ฑฐ ์ค์ (์: ํ๋ ์ด์ด๊ฐ ํต๊ณผํ ๋ ์ด๋ฒคํธ์ฉ)
// SphereCollider col = go.GetComponent<SphereCollider>();
// col.isTrigger = true;
// 2. Rigidbody ์ถ๊ฐ
Rigidbody rb = go.AddComponent<Rigidbody>();
rb.isKinematic = true; // ์คํฌ๋ฆฝํธ๊ฐ ์์น๋ฅผ ์ ์ดํ๋ฏ๋ก ๋ฌผ๋ฆฌ์ ํ์ ๋ฐ์ง ์๋๋ก ์ค์ (์ค์)
rb.useGravity = false; // ์ค๋ ฅ ๊บผ๋๊ธฐ
go.transform.SetParent(transform);
go.transform.localPosition = pos;
go.transform.localScale = Vector3.one * scaleMin;
Renderer renderer = go.GetComponent<Renderer>();
renderer.sharedMaterial = baseMaterial;
spheres.Add(new SphereData
{
tr = go.transform,
renderer = renderer,
radius = radius,
angle = angle,
basePos = pos
});
}
// ---------------------------------------------------
// ์ ๋๋ฉ์ด์
๋ฐ ์์ ์ ์ฉ
// ---------------------------------------------------
void ApplyAnimation(float t)
{
if (_propBlock == null) _propBlock = new MaterialPropertyBlock();
for (int i = 0; i < spheres.Count; i++)
{
SphereData s = spheres[i];
if (s.tr == null || s.renderer == null) continue;
float wave = Mathf.Sin(s.radius * waveFrequency - t) * waveHeight;
float normalized = (wave / waveHeight + 1f) * 0.5f;
float swirl = Mathf.Sin(t + s.radius) * swirlStrength;
float angle = s.angle + swirl;
float r = s.radius + Mathf.Sin(t + s.radius) * radialPulse;
Vector3 p = new Vector3(Mathf.Cos(angle) * r, wave, Mathf.Sin(angle) * r);
s.tr.localPosition = p;
float scale = Mathf.Lerp(scaleMin, scaleMax, normalized);
s.tr.localScale = Vector3.one * scale;
Color albedo = Color.Lerp(colorMin, colorMax, normalized);
Color emission = albedo * emissionIntensity;
_propBlock.SetColor("_BaseColor", albedo);
_propBlock.SetColor("_EmissionColor", emission);
s.renderer.SetPropertyBlock(_propBlock);
}
}
Material CreateURPMaterial()
{
Shader shader = Shader.Find("Universal Render Pipeline/Lit");
if (shader == null)
{
Debug.LogError("URP Lit Shader๋ฅผ ์ฐพ์ ์ ์์ต๋๋ค.");
return new Material(Shader.Find("Standard"));
}
Material mat = new Material(shader);
mat.EnableKeyword("_EMISSION");
mat.globalIlluminationFlags = MaterialGlobalIlluminationFlags.RealtimeEmissive;
return mat;
}
void Clear()
{
foreach (Transform child in transform)
{
if (Application.isPlaying)
Destroy(child.gameObject);
else
DestroyImmediate(child.gameObject);
}
spheres.Clear();
}
}