๐Ÿ“”Boilerplate (Cube Sculpture)

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

Unity GenArt

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

Boilerplate Code for Generative Art

[์‚ฌ์šฉ ๋ฐฉ๋ฒ•]

  1. ์ด ํŒŒ์ผ์„ ๋ณต์‚ฌํ•ด์„œ ์ƒˆ ์ด๋ฆ„์œผ๋กœ ์ €์žฅํ•ฉ๋‹ˆ๋‹ค.
  2. ํด๋ž˜์Šค ์ด๋ฆ„์„ ํŒŒ์ผ๋ช…๊ณผ ๋™์ผํ•˜๊ฒŒ ๋ณ€๊ฒฝํ•ฉ๋‹ˆ๋‹ค.
  3. [์•„ํŠธ์›Œํฌ ๋กœ์ง] ์„น์…˜๋งŒ ์ˆ˜์ •ํ•ด์„œ ์ƒˆ๋กœ์šด ์•„ํŠธ์›Œํฌ๋ฅผ ๋งŒ๋“ญ๋‹ˆ๋‹ค.
  4. ๋‚˜๋จธ์ง€ ์„น์…˜([์ธํ”„๋ผ ์ฝ”๋“œ])์€ ์ˆ˜์ •ํ•˜์ง€ ์•Š์•„๋„ ๋ฉ๋‹ˆ๋‹ค.

[์ฝ”๋“œ์˜ ๊ธฐ๋Šฅ]

[GPU ํŒŒํ‹ฐํด ๋ฒ„์ „(GenArtBoilerplate)๊ณผ์˜ ์ฐจ์ด]

  • ๊ฐ ํ๋ธŒ๊ฐ€ ์‹ค์ œ GameObject๋กœ ์ƒ์„ฑ๋ฉ๋‹ˆ๋‹ค.
  • ์ˆ˜์‹ญ~์ˆ˜๋ฐฑ ๊ฐœ ๊ทœ๋ชจ์˜ ์กฐํ˜•๋ฌผ์— ์ ํ•ฉํ•ฉ๋‹ˆ๋‹ค.
  • ๊ฐ ํ๋ธŒ์— Collider, Rigidbody ๋“ฑ ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋ถ™์ผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • ์ˆ˜์ฒœ ๊ฐœ ์ด์ƒ์ด ํ•„์š”ํ•˜๋ฉด GPU ๋ฒ„์ „์„ ์‚ฌ์šฉํ•˜์„ธ์š”.

๐Ÿ“„BoilerplateSculpture.cs

using UnityEngine;
using System.Collections.Generic;

#if UNITY_EDITOR
using UnityEditor; // EditorApplication.delayCall ์‚ฌ์šฉ์„ ์œ„ํ•ด ์—๋””ํ„ฐ ์ „์šฉ์œผ๋กœ ์ž„ํฌํŠธ
#endif

[ExecuteAlways] // ์—๋””ํ„ฐ ๋ชจ๋“œ(Play ์ „)์—์„œ๋„ Update ๋“ฑ Unity ์ด๋ฒคํŠธ๊ฐ€ ํ˜ธ์ถœ๋˜๊ฒŒ ํ•ฉ๋‹ˆ๋‹ค
public class BoilerplateSculpture : MonoBehaviour
{
    // ============================================================
    // ์ธ์ŠคํŽ™ํ„ฐ ํŒŒ๋ผ๋ฏธํ„ฐ
    // ============================================================

    // ----- ๊ตฌ์กฐ ํŒŒ๋ผ๋ฏธํ„ฐ ----------------------------------------
    // ์ด ๊ฐ’๋“ค์ด ๋ฐ”๋€Œ๋ฉด ๋ชจ๋“  GameObject๋ฅผ ์‚ญ์ œํ•˜๊ณ  ๋‹ค์‹œ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค.
    [Header("๊ตฌ์กฐ ์„ค์ • (๋ณ€๊ฒฝ ์‹œ ์˜ค๋ธŒ์ ํŠธ ์žฌ์ƒ์„ฑ)")]
    public int   countX   = 5;    // X์ถ• ํ๋ธŒ ๊ฐœ์ˆ˜
    public int   countY   = 5;    // Y์ถ• ํ๋ธŒ ๊ฐœ์ˆ˜
    public int   countZ   = 5;    // Z์ถ• ํ๋ธŒ ๊ฐœ์ˆ˜
    public float spacing  = 1.2f; // ํ๋ธŒ ๊ฐ„๊ฒฉ
    public float cubeSize = 0.8f; // ํ๋ธŒ ํฌ๊ธฐ (spacing๋ณด๋‹ค ์ž‘์œผ๋ฉด ํ‹ˆ์ด ์ƒ๊น๋‹ˆ๋‹ค)
    public int   colorSeed = 0;   // ์ƒ‰์ƒ ๋ฌด์ž‘์œ„ ์‹œ๋“œ (๊ฐ™์€ ์‹œ๋“œ = ๊ฐ™์€ ์ƒ‰์ƒ ๋ฐฐ์น˜)

    // ----- ์• ๋‹ˆ๋ฉ”์ด์…˜ ํŒŒ๋ผ๋ฏธํ„ฐ ----------------------------------
    // ์ด ๊ฐ’๋“ค์ด ๋ฐ”๋€Œ๋ฉด ์žฌ์ƒ์„ฑ ์—†์ด ApplyAnimation(0f)๋งŒ ํ˜ธ์ถœํ•ฉ๋‹ˆ๋‹ค.
    [Header("์• ๋‹ˆ๋ฉ”์ด์…˜ ์„ค์ • (์‹ค์‹œ๊ฐ„ ๋ฐ˜์˜)")]
    public float pulseSpeed     = 1.0f; // ๋งฅ๋™ ์†๋„
    public float pulseIntensity = 0.15f; // ๋งฅ๋™ ํฌ๊ธฐ (ํ๋ธŒ ํฌ๊ธฐ ๋ณ€ํ™”๋Ÿ‰)
    public float pulseFrequency = 1.5f;  // ํ๋ธŒ ๊ฐ„ ์œ„์ƒ ์ฐจ์ด (ํด์ˆ˜๋ก ํŒŒ๋™ ๋А๋‚Œ)

    // ----- ์ƒ‰์ƒ ํŒŒ๋ผ๋ฏธํ„ฐ ----------------------------------------
    [Header("์ƒ‰์ƒ ์„ค์ • (์‹ค์‹œ๊ฐ„ ๋ฐ˜์˜)")]
    [Range(0f, 1f)] public float hueMin      = 0.55f; // ์ƒ‰์ƒ ๋ฒ”์œ„ ์ตœ์†Ÿ๊ฐ’ (ํŒŒ๋ž€ ๊ณ„์—ด)
    [Range(0f, 1f)] public float hueMax      = 0.75f; // ์ƒ‰์ƒ ๋ฒ”์œ„ ์ตœ๋Œ“๊ฐ’ (๋ณด๋ผ ๊ณ„์—ด)
    [Range(0f, 1f)] public float saturation  = 0.80f; // ์ฑ„๋„
    [Range(0f, 1f)] public float brightness  = 0.90f; // ๋ฐ๊ธฐ
    public float emissionIntensity           = 0.5f;  // ๋ฐœ๊ด‘ ๊ฐ•๋„

    // ----- ์žฌ์งˆ ์„ค์ • --------------------------------------------
    [Header("์žฌ์งˆ ์„ค์ •")]
    public Material baseMaterial; // null์ด๋ฉด ์ž๋™ ์ƒ์„ฑ

    // ============================================================
    // ๋‚ด๋ถ€ ๋ฐ์ดํ„ฐ ๊ตฌ์กฐ
    // ============================================================

    // ํ๋ธŒ ํ•˜๋‚˜์˜ ๋ฐ์ดํ„ฐ (Generate ์‹œ ๊ฒฐ์ •, ์ดํ›„ ApplyAnimation์—์„œ ์ฐธ์กฐ)
    struct CubeData
    {
        public Transform tr;       // ํ๋ธŒ์˜ Transform (์œ„์น˜/ํฌ๊ธฐ ์กฐ์ž‘์— ์‚ฌ์šฉ)
        public Material  mat;      // ํ๋ธŒ ์ „์šฉ ๋จธํ‹ฐ๋ฆฌ์–ผ ์ธ์Šคํ„ด์Šค (์ƒ‰์ƒ ๊ฐœ๋ณ„ ์ ์šฉ)
        public Vector3   basePos;  // ์ƒ์„ฑ ์‹œ ๊ฒฐ์ •๋œ ๊ธฐ์ค€ ์œ„์น˜
        public float     hue;      // ์ƒ์„ฑ ์‹œ ๊ฒฐ์ •๋œ ๊ณ ์œ  ์ƒ‰์ƒ hue
        public float     phase;    // ์ƒ์„ฑ ์‹œ ๊ฒฐ์ •๋œ ๋งฅ๋™ ์œ„์ƒ ์˜คํ”„์…‹ (ํ๋ธŒ๋งˆ๋‹ค ๋‹ค๋ฅธ ํƒ€์ด๋ฐ)
    }

    List<CubeData> _cubes = new List<CubeData>(); // ์ „์ฒด ํ๋ธŒ ๋ฐ์ดํ„ฐ ๋ชฉ๋ก

    // ๋ Œ๋” ํŒŒ์ดํ”„๋ผ์ธ ์ข…๋ฅ˜ (OnEnable์—์„œ ์ž๋™ ๊ฐ์ง€)
    enum RenderPipeline { BuiltIn, URP, HDRP }
    RenderPipeline _pipeline;

    // ============================================================
    // ๊ตฌ์กฐ ํŒŒ๋ผ๋ฏธํ„ฐ ์บ์‹œ
    // OnValidate์—์„œ ์‹ค์ œ๋กœ ๊ฐ’์ด ๋ฐ”๋€Œ์—ˆ๋Š”์ง€ ๋น„๊ตํ•˜๊ธฐ ์œ„ํ•œ ์ด์ „ ๊ฐ’ ์ €์žฅ์†Œ
    // ============================================================
    int   _cachedCountX;
    int   _cachedCountY;
    int   _cachedCountZ;
    float _cachedSpacing;
    float _cachedCubeSize;
    int   _cachedColorSeed;

#if UNITY_EDITOR
    // QueueGenerate ์ค‘๋ณต ํ˜ธ์ถœ ๋ฐฉ์ง€ ํ”Œ๋ž˜๊ทธ (์—๋””ํ„ฐ ์ „์šฉ)
    bool _generateQueued = false;
#endif

    // ============================================================
    // Unity ์ด๋ฒคํŠธ ๋ฉ”์„œ๋“œ
    // ============================================================

    // OnEnable: ์ปดํฌ๋„ŒํŠธ ํ™œ์„ฑํ™” ์‹œ ํ˜ธ์ถœ
    void OnEnable()
    {
        _pipeline = DetectPipeline(); // ๋ Œ๋” ํŒŒ์ดํ”„๋ผ์ธ ์ž๋™ ๊ฐ์ง€

        if (baseMaterial == null)
            baseMaterial = CreateFallbackMaterial(); // ๋จธํ‹ฐ๋ฆฌ์–ผ ์ž๋™ ์ƒ์„ฑ

        Generate();             // ํ๋ธŒ GameObject ์ƒ์„ฑ
        CacheStructureParams(); // ๊ตฌ์กฐ ํŒŒ๋ผ๋ฏธํ„ฐ ์บ์‹œ ์ €์žฅ
    }

    // OnValidate: ์ธ์ŠคํŽ™ํ„ฐ์—์„œ ๊ฐ’์ด ๋ฐ”๋€” ๋•Œ๋งˆ๋‹ค ํ˜ธ์ถœ
    // ์ฃผ์˜: ์ด ์ฝœ๋ฐฑ ์•ˆ์—์„œ SetParent(), CreatePrimitive()๋ฅผ ์ง์ ‘ ํ˜ธ์ถœํ•˜๋ฉด
    //       Unity ๊ฒฝ๊ณ ๊ฐ€ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค โ†’ QueueGenerate()๋กœ ์šฐํšŒํ•ฉ๋‹ˆ๋‹ค
    void OnValidate()
    {
#if UNITY_EDITOR
        _pipeline = DetectPipeline();

        if (baseMaterial == null)
            baseMaterial = CreateFallbackMaterial();

        if (StructureParamsChanged())
        {
            // ๊ตฌ์กฐ ํŒŒ๋ผ๋ฏธํ„ฐ๊ฐ€ ๋ฐ”๋€Œ์—ˆ์„ ๋•Œ โ†’ ๋‹ค์Œ ํ”„๋ ˆ์ž„์œผ๋กœ ์žฌ์ƒ์„ฑ ์˜ˆ์•ฝ
            CacheStructureParams();
            QueueGenerate();
        }
        else
        {
            // ์• ๋‹ˆ๋ฉ”์ด์…˜/์ƒ‰์ƒ ํŒŒ๋ผ๋ฏธํ„ฐ๋งŒ ๋ฐ”๋€Œ์—ˆ์„ ๋•Œ โ†’ ์ฆ‰์‹œ ์‹œ๊ฐ ๊ฐฑ์‹ 
            ApplyAnimation(0f);
        }
#endif
    }

#if UNITY_EDITOR
    // QueueGenerate: Generate()๋ฅผ ํ•œ ํ”„๋ ˆ์ž„ ๋’ค๋กœ ๋ฏธ๋ฃจ์–ด ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค.
    // OnValidate ์ปจํ…์ŠคํŠธ์—์„œ ์”ฌ ๊ทธ๋ž˜ํ”„๋ฅผ ์ง์ ‘ ์ˆ˜์ •ํ•˜๋ฉด Unity ๋‚ด๋ถ€ ์ง๋ ฌํ™”์™€
    // ์ถฉ๋Œํ•˜๊ธฐ ๋•Œ๋ฌธ์— EditorApplication.delayCall๋กœ ์•ˆ์ „ํ•œ ํƒ€์ด๋ฐ์— ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค.
    void QueueGenerate()
    {
        if (_generateQueued) return; // ์ด๋ฏธ ์˜ˆ์•ฝ๋˜์–ด ์žˆ์œผ๋ฉด ์ค‘๋ณต ์˜ˆ์•ฝํ•˜์ง€ ์•Š์Œ

        _generateQueued = true;

        EditorApplication.delayCall += () => // ๋‹ค์Œ ์—๋””ํ„ฐ ๋ฃจํ”„ ํƒ€์ด๋ฐ์— ์‹คํ–‰
        {
            _generateQueued = false;

            if (this == null) return; // ์˜ค๋ธŒ์ ํŠธ๊ฐ€ ์‚ญ์ œ๋œ ๊ฒฝ์šฐ ์•ˆ์ „ํ•˜๊ฒŒ ๊ฑด๋„ˆ๋œ€

            Generate();
        };
    }
#endif

    // Update: ๋งค ํ”„๋ ˆ์ž„ ํ˜ธ์ถœ
    void Update()
    {
#if UNITY_EDITOR
        // ์—๋””ํ„ฐ Scene ๋ทฐ: ์• ๋‹ˆ๋ฉ”์ด์…˜ ์—†์ด t=0 ์ •์ง€ ์ƒํƒœ ์œ ์ง€
        if (!Application.isPlaying) return;
#endif
        // Play ๋ชจ๋“œ: ์‹œ๊ฐ„ ๊ธฐ๋ฐ˜ ์• ๋‹ˆ๋ฉ”์ด์…˜ ์ ์šฉ
        ApplyAnimation(Time.time * pulseSpeed);
    }

    // ============================================================
    // ๊ตฌ์กฐ ํŒŒ๋ผ๋ฏธํ„ฐ ์บ์‹œ ์œ ํ‹ธ
    // ============================================================

    // ์ด์ „ ์บ์‹œ ๊ฐ’๊ณผ ํ˜„์žฌ ๊ฐ’์„ ๋น„๊ตํ•ฉ๋‹ˆ๋‹ค. ํ•˜๋‚˜๋ผ๋„ ๋‹ค๋ฅด๋ฉด true ๋ฐ˜ํ™˜
    bool StructureParamsChanged()
    {
        return countX    != _cachedCountX
            || countY    != _cachedCountY
            || countZ    != _cachedCountZ
            || spacing   != _cachedSpacing
            || cubeSize  != _cachedCubeSize
            || colorSeed != _cachedColorSeed;
    }

    // ํ˜„์žฌ ๊ฐ’์„ ์บ์‹œ์— ์ €์žฅํ•ฉ๋‹ˆ๋‹ค
    void CacheStructureParams()
    {
        _cachedCountX    = countX;
        _cachedCountY    = countY;
        _cachedCountZ    = countZ;
        _cachedSpacing   = spacing;
        _cachedCubeSize  = cubeSize;
        _cachedColorSeed = colorSeed;
    }

    // ============================================================
    // ์ƒ์„ฑ
    // ์‹ค์ œ GameObject ํ๋ธŒ๋ฅผ ์ƒ์„ฑํ•˜๊ณ  this.transform ํ•˜์œ„์— ๋ฐฐ์น˜ํ•ฉ๋‹ˆ๋‹ค.
    // ============================================================
    void Generate()
    {
        Clear(); // ๊ธฐ์กด ํ๋ธŒ๋ฅผ ์ „๋ถ€ ์‚ญ์ œํ•ฉ๋‹ˆ๋‹ค

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

        // ---- [์•„ํŠธ์›Œํฌ ๋กœ์ง] ์—ฌ๊ธฐ๋ฅผ ์ˆ˜์ •ํ•˜์„ธ์š” ----
        // ์˜ˆ์‹œ: 3์ฐจ์› ๊ทธ๋ฆฌ๋“œ ๋ฐฐ์น˜

        // ๊ฐ ์ถ•์˜ ๊ทธ๋ฆฌ๋“œ๋ฅผ ๋กœ์ปฌ ์›์  ๊ธฐ์ค€์œผ๋กœ ์ค‘์•™ ์ •๋ ฌํ•ฉ๋‹ˆ๋‹ค.
        // offset ์—†์ด ๋ฐฐ์น˜ํ•˜๋ฉด (0,0,0)~(max,max,max) ๋ฒ”์œ„๊ฐ€ ๋˜์–ด ํ•œ์ชฝ์œผ๋กœ ์น˜์šฐ์นฉ๋‹ˆ๋‹ค.
        float offsetX = (countX - 1) * spacing * 0.5f;
        float offsetY = (countY - 1) * spacing * 0.5f;
        float offsetZ = (countZ - 1) * spacing * 0.5f;

        for (int x = 0; x < countX; x++)
        {
            for (int y = 0; y < countY; y++)
            {
                for (int z = 0; z < countZ; z++)
                {
                    // offset์„ ๋นผ์„œ ๊ทธ๋ฆฌ๋“œ ์ค‘์‹ฌ์ด (0,0,0)์ด ๋˜๋„๋ก ํ•ฉ๋‹ˆ๋‹ค
                    Vector3 pos = new Vector3(
                        x * spacing - offsetX,
                        y * spacing - offsetY,
                        z * spacing - offsetZ
                    );

                    // ํ๋ธŒ๋งˆ๋‹ค hueMin~hueMax ๋ฒ”์œ„ ์•ˆ์—์„œ ๋ฌด์ž‘์œ„ hue๋ฅผ ๋ถ€์—ฌํ•ฉ๋‹ˆ๋‹ค
                    float hue = Random.Range(hueMin, hueMax);

                    // ํ๋ธŒ๋งˆ๋‹ค ๋งฅ๋™ ์œ„์ƒ์„ ๋‹ค๋ฅด๊ฒŒ ์ค˜์„œ ๋™์‹œ์— ์›€์ง์ด์ง€ ์•Š๊ฒŒ ํ•ฉ๋‹ˆ๋‹ค
                    // x/y/z ์ขŒํ‘œ๋ฅผ ๋ชจ๋‘ ํ™œ์šฉํ•ด 3์ฐจ์›์ ์œผ๋กœ ์œ„์ƒ ์ฐจ์ด๋ฅผ ๋งŒ๋“ญ๋‹ˆ๋‹ค
                    float phase = (x + y + z) * pulseFrequency;

                    SpawnCube(pos, hue, phase);
                }
            }
        }
        // ---- [์•„ํŠธ์›Œํฌ ๋กœ์ง ๋] ----

        ApplyAnimation(0f); // ์ƒ์„ฑ ์งํ›„ t=0 ๊ธฐ์ค€ ์ดˆ๊ธฐ ์ƒํƒœ ์ ์šฉ
    }

    // ํ๋ธŒ GameObject ํ•˜๋‚˜๋ฅผ ์ƒ์„ฑํ•˜๊ณ  _cubes ๋ฆฌ์ŠคํŠธ์— ๋“ฑ๋กํ•ฉ๋‹ˆ๋‹ค
    void SpawnCube(Vector3 pos, float hue, float phase)
    {
        // ํ๋ธŒ ํ”„๋ฆฌ๋ฏธํ‹ฐ๋ธŒ ์ƒ์„ฑ
        GameObject go = GameObject.CreatePrimitive(PrimitiveType.Cube);

        // ์ด ์Šคํฌ๋ฆฝํŠธ๊ฐ€ ๋ถ™์€ ์˜ค๋ธŒ์ ํŠธ์˜ ์ž์‹์œผ๋กœ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค
        // โ†’ ๋ถ€๋ชจ๋ฅผ ์ด๋™/ํšŒ์ „ํ•˜๋ฉด ์ „์ฒด ์กฐํ˜•๋ฌผ์ด ํ•จ๊ป˜ ์›€์ง์ž…๋‹ˆ๋‹ค
        go.transform.SetParent(transform);
        go.transform.localPosition = pos;
        go.transform.localScale    = Vector3.one * cubeSize;

        // ํ๋ธŒ๋งˆ๋‹ค ๋…๋ฆฝ์ ์ธ ๋จธํ‹ฐ๋ฆฌ์–ผ ์ธ์Šคํ„ด์Šค๋ฅผ ๋งŒ๋“ค์–ด์•ผ
        // ์ƒ‰์ƒ์„ ๊ฐœ๋ณ„์ ์œผ๋กœ ์ œ์–ดํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
        // baseMaterial์„ ์ง์ ‘ ์ˆ˜์ •ํ•˜๋ฉด ๋ชจ๋“  ํ๋ธŒ ์ƒ‰์ƒ์ด ๋™์‹œ์— ๋ฐ”๋€๋‹ˆ๋‹ค.
        Material mat = Instantiate(baseMaterial);
        mat.EnableKeyword("_EMISSION");
        mat.globalIlluminationFlags = MaterialGlobalIlluminationFlags.RealtimeEmissive;

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

        // ๋ฐ์ดํ„ฐ๋ฅผ ๋ฆฌ์ŠคํŠธ์— ์ €์žฅํ•ด์„œ ApplyAnimation์—์„œ ์ฐธ์กฐํ•ฉ๋‹ˆ๋‹ค
        _cubes.Add(new CubeData
        {
            tr      = go.transform,
            mat     = mat,
            basePos = pos,
            hue     = hue,
            phase   = phase
        });
    }

    // ๋ชจ๋“  ์ž์‹ ํ๋ธŒ๋ฅผ ์‚ญ์ œํ•˜๊ณ  ๋ฆฌ์ŠคํŠธ๋ฅผ ๋น„์›๋‹ˆ๋‹ค
    void Clear()
    {
        _cubes.Clear(); // ๋ฐ์ดํ„ฐ ๋ฆฌ์ŠคํŠธ ์ดˆ๊ธฐํ™”

        // DestroyImmediate: ์—๋””ํ„ฐ ๋ชจ๋“œ์—์„œ๋Š” Destroy ๋Œ€์‹  ์ด๊ฒƒ์„ ์‚ฌ์šฉํ•ด์•ผ
        // ๊ฐ™์€ ํ”„๋ ˆ์ž„ ์•ˆ์—์„œ ์‚ญ์ œ๊ฐ€ ์™„๋ฃŒ๋ฉ๋‹ˆ๋‹ค.
        // Destroy๋Š” Play ๋ชจ๋“œ ์ „์šฉ์ด๋ฉฐ ์—๋””ํ„ฐ์—์„œ๋Š” ๋‹ค์Œ ํ”„๋ ˆ์ž„๊นŒ์ง€ ์ง€์—ฐ๋ฉ๋‹ˆ๋‹ค.
        while (transform.childCount > 0)
            DestroyImmediate(transform.GetChild(0).gameObject);
    }

    // ============================================================
    // ์• ๋‹ˆ๋ฉ”์ด์…˜
    // t = 0              โ†’ Scene ์—๋””ํ„ฐ ์ •์ง€ ์ƒํƒœ
    // t = Time.time * pulseSpeed โ†’ Play ๋ชจ๋“œ ์• ๋‹ˆ๋ฉ”์ด์…˜
    // ============================================================
    void ApplyAnimation(float t)
    {
        for (int i = 0; i < _cubes.Count; i++)
        {
            CubeData c = _cubes[i];

            // DestroyImmediate ์งํ›„ ๋ฆฌ์ŠคํŠธ ์ •๋ฆฌ ์ „์— ๋ฌดํšจํ™”๋œ ์ฐธ์กฐ์—
            // ์ ‘๊ทผํ•˜๋ฉด MissingReferenceException์ด ๋ฐœ์ƒํ•˜๋ฏ€๋กœ null ์ฒดํฌํ•ฉ๋‹ˆ๋‹ค
            if (c.tr == null || c.mat == null) continue;

            // ---- [์•„ํŠธ์›Œํฌ ๋กœ์ง] ์—ฌ๊ธฐ๋ฅผ ์ˆ˜์ •ํ•˜์„ธ์š” ----
            // ์˜ˆ์‹œ: ๊ฐ ํ๋ธŒ๊ฐ€ ์ž์‹ ์˜ ์œ„์ƒ์œผ๋กœ ๋งฅ๋™ (ํฌ๊ธฐ ๋ณ€ํ™”)

            // ํ๋ธŒ๋งˆ๋‹ค phase๊ฐ€ ๋‹ฌ๋ผ์„œ ์„œ๋กœ ๋‹ค๋ฅธ ํƒ€์ด๋ฐ์œผ๋กœ ์ปค์ง€๊ณ  ์ž‘์•„์ง‘๋‹ˆ๋‹ค
            float pulse      = Mathf.Sin(t + c.phase) * pulseIntensity;
            float scale      = cubeSize + pulse;                    // ๊ธฐ๋ณธ ํฌ๊ธฐ ยฑ ๋งฅ๋™๋Ÿ‰
            float normalized = (pulse / pulseIntensity + 1f) * 0.5f; // 0~1 (์ƒ‰์ƒ ๋ณ€์กฐ์šฉ)

            c.tr.localScale = Vector3.one * Mathf.Max(0.01f, scale); // ํฌ๊ธฐ๊ฐ€ ์Œ์ˆ˜๊ฐ€ ๋˜์ง€ ์•Š๊ฒŒ ๋ณดํ˜ธ

            // ๋งฅ๋™ ์ƒํƒœ์— ๋”ฐ๋ผ ๋ฐ๊ธฐ๋ฅผ ๋ณ€์กฐํ•ฉ๋‹ˆ๋‹ค
            // ์ปค์งˆ ๋•Œ ๋ฐ์•„์ง€๊ณ  ์ž‘์•„์งˆ ๋•Œ ์–ด๋‘์›Œ์ง€๋Š” ํšจ๊ณผ
            float animBrightness = brightness * Mathf.Lerp(0.4f, 1.0f, normalized);
            Color albedo         = Color.HSVToRGB(c.hue, saturation, animBrightness);
            Color emission       = Color.HSVToRGB(c.hue, 1f, normalized) * emissionIntensity;
            // ---- [์•„ํŠธ์›Œํฌ ๋กœ์ง ๋] ----

            ApplyColor(c.mat, albedo, emission);
        }
    }

    // ============================================================
    // ํŒŒ์ดํ”„๋ผ์ธ๋ณ„ ์ƒ‰์ƒ ์ ์šฉ
    // ์…ฐ์ด๋” ํ”„๋กœํผํ‹ฐ ์ด๋ฆ„์ด ํŒŒ์ดํ”„๋ผ์ธ๋งˆ๋‹ค ๋‹ค๋ฅด๊ธฐ ๋•Œ๋ฌธ์— ๋ถ„๊ธฐ ์ฒ˜๋ฆฌํ•ฉ๋‹ˆ๋‹ค.
    //   URP:      _BaseColor,  _EmissionColor
    //   HDRP:     _BaseColor,  _EmissiveColor
    //   Built-in: _Color,      _EmissionColor
    // ============================================================
    void ApplyColor(Material mat, Color albedo, Color emission)
    {
        switch (_pipeline)
        {
            case RenderPipeline.URP:
                mat.SetColor("_BaseColor",     albedo);
                mat.SetColor("_EmissionColor", emission);
                break;

            case RenderPipeline.HDRP:
                mat.SetColor("_BaseColor",     albedo);
                mat.SetColor("_EmissiveColor", emission);
                break;

            default: // Built-in
                mat.SetColor("_Color",         albedo);
                mat.SetColor("_EmissionColor", emission);
                break;
        }
    }

    // ============================================================
    // ์œ ํ‹ธ๋ฆฌํ‹ฐ
    // ============================================================

    // ํ”„๋กœ์ ํŠธ์˜ ๋ Œ๋” ํŒŒ์ดํ”„๋ผ์ธ์„ ์ž๋™ ๊ฐ์ง€ํ•ฉ๋‹ˆ๋‹ค
    RenderPipeline DetectPipeline()
    {
        var pipeline = UnityEngine.Rendering.GraphicsSettings.currentRenderPipeline;

        if (pipeline == null) return RenderPipeline.BuiltIn; // Built-in์€ ์—์…‹์ด null

        string name = pipeline.GetType().Name;

        if (name.Contains("Universal"))                                return RenderPipeline.URP;
        if (name.Contains("HighDefinition") || name.Contains("HDRP")) return RenderPipeline.HDRP;

        return RenderPipeline.BuiltIn;
    }

    // ์ธ์ŠคํŽ™ํ„ฐ์—์„œ baseMaterial์ด ํ• ๋‹น๋˜์ง€ ์•Š์€ ๊ฒฝ์šฐ ์ž๋™์œผ๋กœ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค.
    // ํ”„๋กœ์ ํŠธ์˜ ๋ Œ๋” ํŒŒ์ดํ”„๋ผ์ธ์— ๋งž๋Š” ์…ฐ์ด๋”๋ฅผ ์ˆœ์„œ๋Œ€๋กœ ํƒ์ƒ‰ํ•ฉ๋‹ˆ๋‹ค.
    Material CreateFallbackMaterial()
    {
        string[] candidates = {
            "Universal Render Pipeline/Lit", // URP
            "HDRP/Lit",                      // HDRP
            "Standard"                       // Built-in
        };

        foreach (string shaderName in candidates)
        {
            Shader s = Shader.Find(shaderName);
            if (s != null)
            {
                Material mat = new Material(s);
                mat.EnableKeyword("_EMISSION");
                mat.globalIlluminationFlags = MaterialGlobalIlluminationFlags.RealtimeEmissive;
                Debug.Log($"[GenArtSculpture] ์…ฐ์ด๋” ์ž๋™ ๊ฐ์ง€: {shaderName}");
                return mat;
            }
        }

        // ์–ด๋–ค ์…ฐ์ด๋”๋„ ์ฐพ์ง€ ๋ชปํ•œ ๊ฒฝ์šฐ (๋งค์šฐ ๋“œ๋ฌธ ์ƒํ™ฉ)
        Debug.LogWarning("[GenArtSculpture] Inspector์—์„œ baseMaterial์„ ์ง์ ‘ ํ• ๋‹นํ•ด์ฃผ์„ธ์š”.");
        return new Material(Shader.Find("Diffuse"));
    }
}

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

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