๐ŸซงArt_002 Random Sphere

BamgasiJMยท2026๋…„ 3์›” 13์ผ

Unity GenArt

๋ชฉ๋ก ๋ณด๊ธฐ
11/41
post-thumbnail

๐Ÿ“’ RandomSphereGenerator.cs

using UnityEngine;
using System.Collections.Generic;

#if UNITY_EDITOR
using UnityEditor;
#endif

[ExecuteAlways]
public class RandomSphereGenerator : MonoBehaviour
{
    [Header("๊ตฌ์ฒด ๊ตฌ์กฐ (๋ณ€๊ฒฝ ์‹œ ์˜ค๋ธŒ์ ํŠธ ์žฌ์ƒ์„ฑ)")]
    public int sphereCount = 500;
    public float spawnRange = 5f;
    public int randomSeed = 42;         // ๋™์ผํ•œ ์‹œ๋“œ = ๋™์ผํ•œ ๋ฐฐ์น˜

    [Header("๋ถ€์œ  ์• ๋‹ˆ๋ฉ”์ด์…˜ ์„ค์ •")]
    public float floatSpeed = 0.8f;     // ๋ถ€์œ  ์ฃผ๊ธฐ
    public float floatAmplitude = 0.4f; // ๋ถ€์œ  ๋†’์ด
    public float phaseSpread = 3.0f;    // ๊ตฌ์ฒด ๊ฐ„ ์œ„์ƒ ์ฐจ์ด

    [Header("ํฌ๊ธฐ ์„ค์ •")]
    public float scaleMin = 0.15f;
    public float scaleMax = 0.6f;

    [Header("์ƒ‰์ƒ")]
    [Range(0f, 1f)] public float hueBase = 0.6f;
    [Range(0f, 1f)] public float hueRange = 0.4f;
    [Range(0f, 1f)] public float saturation = 0.75f;
    [Range(0f, 1f)] public float brightness = 1.0f;

    // ---------------------------------------------------
    // ๊ตฌ์กฐ ํŒŒ๋ผ๋ฏธํ„ฐ ์บ์‹œ (OnValidate ๋น„๊ต์šฉ)
    // ---------------------------------------------------
    private int   _cachedSphereCount;
    private float _cachedSpawnRange;
    private int   _cachedRandomSeed;

    // ---------------------------------------------------
    // ๊ตฌ์ฒด ๋ฐ์ดํ„ฐ
    // ---------------------------------------------------
    struct SphereData
    {
        public Transform tr;
        public Material  mat;
        public Vector3   basePos;    // ์ƒ์„ฑ ์‹œ ๊ธฐ์ค€ ์œ„์น˜
        public float     phase;      // ๋ถ€์œ  ์œ„์ƒ ์˜คํ”„์…‹
        public float     normalized; // 0~1 (์ƒ‰์ƒยทํฌ๊ธฐ์šฉ)
    }

    List<SphereData> _spheres = new List<SphereData>();

#if UNITY_EDITOR
    bool _generateQueued = false;
#endif

    // ---------------------------------------------------
    // Unity ์ด๋ฒคํŠธ
    // ---------------------------------------------------

    void OnEnable()
    {
        Generate();
        CacheStructureParams();
    }

    void OnValidate()
    {
#if UNITY_EDITOR
        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()
    {
        // ์—๋””ํ„ฐ Scene ๋ทฐ์—์„œ๋Š” ์• ๋‹ˆ๋ฉ”์ด์…˜ ์ •์ง€
#if UNITY_EDITOR
        if (!Application.isPlaying) return;
#endif
        ApplyAnimation(Time.time);
    }

    // ---------------------------------------------------
    // ๊ตฌ์กฐ ํŒŒ๋ผ๋ฏธํ„ฐ ๋ณ€๊ฒฝ ๊ฐ์ง€
    // ---------------------------------------------------

    bool StructureParamsChanged()
    {
        return sphereCount  != _cachedSphereCount
            || spawnRange   != _cachedSpawnRange
            || randomSeed   != _cachedRandomSeed;
    }

    void CacheStructureParams()
    {
        _cachedSphereCount = sphereCount;
        _cachedSpawnRange  = spawnRange;
        _cachedRandomSeed  = randomSeed;
    }

    // ---------------------------------------------------
    // ์ƒ์„ฑ
    // ---------------------------------------------------

    void Generate()
    {
        Clear();

        Random.InitState(randomSeed); // ์‹œ๋“œ ๊ณ ์ • โ†’ ์žฌ์ƒ์„ฑํ•ด๋„ ๋™์ผํ•œ ๋ฐฐ์น˜

        for (int i = 0; i < sphereCount; i++)
        {
            float t = (sphereCount > 1) ? (float)i / (sphereCount - 1) : 0f;

            Vector3 pos = new Vector3(
                Random.Range(-spawnRange, spawnRange),
                Random.Range(-spawnRange, spawnRange),
                Random.Range(-spawnRange, spawnRange)
            );

            float phase = Random.value * phaseSpread; // ๊ตฌ์ฒด๋งˆ๋‹ค ๋‹ค๋ฅธ ์œ„์ƒ

            SpawnSphere(pos, phase, t);
        }

        ApplyAnimation(0f);
    }

    void SpawnSphere(Vector3 pos, float phase, float normalized)
    {
        GameObject go = GameObject.CreatePrimitive(PrimitiveType.Sphere);
        go.transform.SetParent(transform);
        go.transform.localPosition = pos;

        Material mat = new Material(Shader.Find("Universal Render Pipeline/Lit"));
        mat.EnableKeyword("_EMISSION");
        mat.globalIlluminationFlags = MaterialGlobalIlluminationFlags.RealtimeEmissive;

        go.GetComponent<Renderer>().material = mat;

        _spheres.Add(new SphereData
        {
            tr         = go.transform,
            mat        = mat,
            basePos    = pos,
            phase      = phase,
            normalized = normalized
        });
    }

    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;

            // ๊ฐ ๊ตฌ์ฒด๊ฐ€ ์ž์‹ ์˜ ์œ„์ƒ์œผ๋กœ Y์ถ• ๋ถ€์œ 
            float bob = Mathf.Sin(t * floatSpeed + s.phase) * floatAmplitude;

            s.tr.localPosition = new Vector3(
                s.basePos.x,
                s.basePos.y + bob,
                s.basePos.z
            );

            // ๋ถ€์œ  ์ƒํƒœ์— ๋”ฐ๋ผ ํฌ๊ธฐ ๋งฅ๋™ (์œ„๋กœ ์˜ฌ๋ผ๊ฐˆ์ˆ˜๋ก ์ปค์ง)
            float bobNormalized = (bob / floatAmplitude + 1f) * 0.5f; // 0~1
            float scale = Mathf.Lerp(scaleMin, scaleMax, bobNormalized);
            s.tr.localScale = Vector3.one * scale;

            // ์ƒ‰์ƒ
            float hue     = (hueBase + s.normalized * hueRange) % 1f;
            Color albedo   = Color.HSVToRGB(hue, saturation, brightness);
            Color emission = Color.HSVToRGB(hue, 1f, bobNormalized) * 0.5f;

            s.mat.SetColor("_BaseColor",     albedo);
            s.mat.SetColor("_EmissionColor", emission);
        }
    }
}

profile
Coding Art with Blender / oF / Processing / p5.js / nannou

0๊ฐœ์˜ ๋Œ“๊ธ€