【Unity】エディタ拡張でランダムにマテリアルを割り当てる

投稿者: | 2024-03-22

夜景のビルの窓にランダムでマテリアルを割り当てました。

スクリプト

Editorフォルダーに新規C#スクリプトを作成します。

using UnityEngine;
using UnityEditor;
using System.Collections.Generic;

public class RandomMaterialSetter : EditorWindow
{
    private List<Material> materials = new List<Material>();
    string objTag;
    float emissiveWindowRate;


    [MenuItem("Window/Random Material Setter")]
    public static void ShowWindow()
    {
        GetWindow("Random Material Setter");
    }

    private void OnGUI()
    {
        // テキストフィールドを表示
        objTag = EditorGUILayout.TextField("Tag",objTag);

        // マテリアルをアタッチする欄を表示
        for (int i = 0; i <  materials.Count; i++)
        {          
            materials[i] = (Material)EditorGUILayout.ObjectField(materials[i], typeof(Material), true);
        }

        // スライダーを表示
        using (var h = new EditorGUILayout.HorizontalScope("Slider", GUILayout.Height(10f)))
        {
            EditorGUILayout.LabelField("Emissive Window Rate", GUILayout.Width(150f));
            emissiveWindowRate = Mathf.Round(EditorGUILayout.Slider(emissiveWindowRate, 0f, 1f) * 100f) / 100f;
        }

        // 追加ボタン
        if (GUILayout.Button("Add Material Slot"))
        {
            materials.Add(null);
        }

        // 除去ボタン
        if (GUILayout.Button("Remove Material Slot"))
        {
            if (materials.Count > 0)
            {
                materials.RemoveAt(materials.Count - 1);
            }
        }

        // フィールド数を表示
        if (materials != null)
        {
            GUILayout.Label(materials.Count + "");
            
        }

        // ランダムで適用
        if (GUILayout.Button("Apply Random Material"))
        {
            ApplyRandomMaterial();
        }
    }

    private void ApplyRandomMaterial()
    {
        // マテリアルがなければ終了
        if (materials.Count == 0)
        {
            Debug.LogWarning("No materials set in Random Material Setter.");
            return;
        }

        // nullでないマテリアルのリストを作る
        List<Material> nonNullMaterials = new List<Material>();

        foreach(var m in materials)
        {
            if(m != null)
            {
                nonNullMaterials.Add(m);
            }
        }

        if (nonNullMaterials.Count == 0) return;

        // 確率に基づいてマテリアルリストを調整
        var adjustedList = AdjustMaterialList(nonNullMaterials, emissiveWindowRate, 100);


        foreach (GameObject go in Resources.FindObjectsOfTypeAll(typeof(GameObject)) as GameObject[])
        {
            // ヒエラルキーにあるゲームオブジェクト
            if (!EditorUtility.IsPersistent(go.transform.root.gameObject) && !(go.hideFlags == HideFlags.NotEditable || go.hideFlags == HideFlags.HideAndDontSave))
            {

                if (go.tag != objTag) continue;

                var r = go.GetComponent<MeshRenderer>();

                if (r == null) continue;

                Undo.RecordObject(r, "Set Random Material to " + go.name);

                // ランダムでマテリアルを選択
                var randomMaterial = adjustedList[Random.Range(0, adjustedList.Count)];

                if (randomMaterial == null)
                {
                    // nullのときレンダラーを無効化
                    r.enabled = false;
                }
                else
                {
                    // レンダラーを有効化してマテリアルを設定
                    r.enabled = true;
                    r.sharedMaterial = randomMaterial;
                }
            }
        }
    }


    public static List<Material> AdjustMaterialList(List<Material> availableMaterials, float probability, int listSize = 100)
    {
        List<Material> adjustedList = new List<Material>();

        // リストサイズに確率を掛ける
        int litMaterialCount = Mathf.RoundToInt(listSize * probability);

        // リストに光るマテリアルを追加
        for (int i = 0; i < litMaterialCount; i++)
        {
            // ランダムにマテリアルを選択するか、何らかの方法で決定
            //Material selectedMaterial = availableMaterials[Random.Range(0, availableMaterials.Count)];
            Material selectedMaterial = availableMaterials[i % availableMaterials.Count];
            adjustedList.Add(selectedMaterial);
        }

        // 残りのスペースにはnullを追加
        for (int i = litMaterialCount; i < listSize; i++)
        {
            adjustedList.Add(null); 
        }

        return adjustedList;
    }
}

シーンを設定

ビルの窓に合わせてPlaneが配置されています。

窓には専用のタグが設定されています。

エディタウィンドウに複数の窓マテリアルをアタッチします。追加ボタンでフィールドが増えます。スライダーで光る窓の出現率を設定できます。対象オブジェクトのタグを入力します。

適用ボタンで窓にマテリアルが割り当てられます。

適用前
適用後

窓にバリエーションがついて自然に見えるようになりました。

コメントを残す

メールアドレスが公開されることはありません。