Chatgpt가 제공해준 코드를 분석한다.
Exemple Code
더보기
※ 원하는 이름의 프리펩과 머터리얼을 한번에 만드는 방법도 있을까?
using UnityEngine;
using UnityEditor;
using System.IO;
public class AssetAutoCreator : EditorWindow
{
string assetName = "NewAsset";
[MenuItem("Tools/Prefab & Material Creator")]
public static void ShowWindow()
{
GetWindow<AssetAutoCreator>("Prefab & Material Creator");
}
void OnGUI()
{
GUILayout.Label("Create Prefab and Material", EditorStyles.boldLabel);
assetName = EditorGUILayout.TextField("Asset Name", assetName);
if (GUILayout.Button("Create Assets"))
{
CreateAssets(assetName);
}
}
void CreateAssets(string name)
{
// 기본 경로 (원하면 바꿀 수 있음)
string basePath = "Assets/GeneratedAssets/";
string prefabPath = Path.Combine(basePath, name + ".prefab");
string materialPath = Path.Combine(basePath, name + "_Mat.mat");
// 폴더가 없으면 생성
if (!AssetDatabase.IsValidFolder(basePath))
{
Directory.CreateDirectory(basePath);
}
// 머터리얼 생성
Material newMat = new Material(Shader.Find("Standard"));
AssetDatabase.CreateAsset(newMat, materialPath);
Debug.Log("Material created at: " + materialPath);
// 오브젝트 생성 후 머터리얼 적용
GameObject obj = GameObject.CreatePrimitive(PrimitiveType.Cube);
obj.name = name;
obj.GetComponent<Renderer>().sharedMaterial = newMat;
// 프리팹 생성
PrefabUtility.SaveAsPrefabAsset(obj, prefabPath);
Debug.Log("Prefab created at: " + prefabPath);
// 임시 오브젝트 제거
DestroyImmediate(obj);
AssetDatabase.Refresh();
}
}
※ EditorWindow 에 대해서 알려줘
using UnityEditor;
using UnityEngine;
public class MyWindow : EditorWindow
{
// 메뉴에서 호출할 이름 설정
[MenuItem("Tools/My Custom Window")]
public static void ShowWindow()
{
// 윈도우 띄우기
GetWindow<MyWindow>("My Window");
}
// UI 그리기 (에디터 전용 OnGUI)
void OnGUI()
{
GUILayout.Label("Hello, EditorWindow!", EditorStyles.boldLabel);
if (GUILayout.Button("Click Me"))
{
Debug.Log("Button clicked!");
}
}
}
※게임 오브젝트를 생성했다 파괴하지 말고 프리펩에 바로 적용하는 방법은 없어?
using UnityEditor;
using UnityEngine;
using System.IO;
public class PurePrefabCreator
{
[MenuItem("Tools/Create Prefab Without Spawning")]
public static void CreatePurePrefab()
{
string path = "Assets/GeneratedAssets/";
string prefabName = "PurePrefab";
if (!Directory.Exists(path))
Directory.CreateDirectory(path);
// 씬에 등장하지 않는 GameObject 생성 (메모리상)
GameObject tempGO = new GameObject(prefabName);
// 여기에 컴포넌트나 자식 추가 가능
tempGO.AddComponent<BoxCollider>();
tempGO.transform.localScale = new Vector3(2, 1, 2);
// 프리팹으로 저장
string fullPath = Path.Combine(path, prefabName + ".prefab");
PrefabUtility.SaveAsPrefabAsset(tempGO, fullPath);
// 생성 후 메모리에서 제거 (씬에 등록되지 않았지만 확실하게)
Object.DestroyImmediate(tempGO);
AssetDatabase.Refresh();
Debug.Log($"Created prefab at: {fullPath}");
}
}
유니티에서는 게임 오브젝트 기반으로 프리펩을 생성하므로 한번은 만들어야 한다.
Destroy 와 DestroyImmediate의 주요 차이는 다음 프레임이 진행될 때 삭제될 것인지 즉시 삭제할 것인지다.
※TextField 말고 배열을 사용하는 방법
방법1 배열 편집
using UnityEditor;
using UnityEngine;
public class ArrayData : ScriptableObject
{
public string[] items;
}
public class ArrayEditorWindow : EditorWindow
{
SerializedObject serializedObj;
SerializedProperty itemsProp;
[MenuItem("Tools/Array Editor")]
public static void Open()
{
GetWindow<ArrayEditorWindow>("Array Editor");
}
void OnEnable()
{
var asset = ScriptableObject.CreateInstance<ArrayData>();
serializedObj = new SerializedObject(asset);
itemsProp = serializedObj.FindProperty("items");
}
void OnGUI()
{
serializedObj.Update();
EditorGUILayout.PropertyField(itemsProp, new GUIContent("Items"), true);
serializedObj.ApplyModifiedProperties();
}
}
방법 2 수동 for문
using UnityEditor;
using UnityEngine;
using System.Collections.Generic;
public class DynamicInputWindow : EditorWindow
{
private List<string> names = new List<string>() { "" };
[MenuItem("Tools/Dynamic TextFields")]
public static void ShowWindow()
{
GetWindow<DynamicInputWindow>("Dynamic Inputs");
}
void OnGUI()
{
GUILayout.Label("Enter names (new field appears as you type)", EditorStyles.boldLabel);
// 리스트 그리기
for (int i = 0; i < names.Count; i++)
{
names[i] = EditorGUILayout.TextField($"Name {i + 1}", names[i]);
}
// 마지막 항목이 비어있지 않으면 새 입력 필드 추가
if (!string.IsNullOrWhiteSpace(names[names.Count - 1]))
{
names.Add("");
}
GUILayout.Space(10);
if (GUILayout.Button("Log Names"))
{
foreach (var name in names)
{
if (!string.IsNullOrWhiteSpace(name))
Debug.Log("Name: " + name);
}
}
}
}
방법 3 ReorderableList
using UnityEditor;
using UnityEditorInternal;
using UnityEngine;
using System.Collections.Generic;
public class StringListEditorWindow : EditorWindow
{
private ReorderableList reorderableList;
private List<string> stringList = new List<string> { "Apple", "Banana", "Cherry" };
[MenuItem("Window/ String List Editor")]
public static void ShowWindow()
{
GetWindow<StringListEditorWindow>("String List Editor");
}
private void OnEnable()
{
reorderableList = new ReorderableList(stringList, typeof(string), true, true, true, true);
reorderableList.drawHeaderCallback = (Rect rect) =>
{
EditorGUI.LabelField(rect, "Fruits List");
};
reorderableList.drawElementCallback = (Rect rect, int index, bool isActive, bool isFocused) =>
{
stringList[index] = EditorGUI.TextField(
new Rect(rect.x, rect.y + 2, rect.width, EditorGUIUtility.singleLineHeight),
$"Item {index + 1}",
stringList[index]
);
};
}
private void OnGUI()
{
if (reorderableList != null)
reorderableList.DoLayoutList();
}
}
기본적으로 에디터가 동작하면서, ShowWindow() 와 OnGUI()를 호출한다.
이때 OnGUI()은 update loop와 비슷하게 호출되고 있다.
[MenuItem("Tools/My Custom Window")]
- 에디터 상단 메뉴에 항목 추가
void ShowWindow()
- 내부 동작으로 윈도우를 생성하고 Show()까지 실행한다.
- GetWindow<WindowClass>("Window name");
void OnEnable()
- 활성되 될 시 호출, 초기화 담당
void OnGUI()
- update loop와 비슷하게 호출되고 있다.
- loop 적으로 호출되고 있음을 유의한다.
- GUILayout, EditorGUILayout 등의 클래스와 다양한 요소가 많다.
- 비주얼 관련
- GUILayout.Label("Create Prefab and Material", EditorStyles.boldLabel);
- GUILayout.Space(10);
- 입력 관련
- assetNames = EditorGUILayout.TextField("Asset name", assetNames);
- if (GUILayout.Button("Create Assetes"))
- 창 내부에 요소를 추가할수있다.
ScriptableObject, SerializedObject
- 아직 완벽하게 이해한건 아니지만, ScriptableObject 파일과 직접적으로 관련된 데이터이고, 이를 한번 랩핑해서 유용한 기능을 제공하는게 SerializedObject이다.
- SerializedObject를 만들었다면, .FindProperty("변수 이름"); 으로 데이터를 담고있는 속성 오브젝트를 가져올 수 있다.
- EditorGUILayout.PropertyField(itemsProp, new GUIContent("Assat names"), true);
ReorderableList
- using UnityEditorInternal에 존재하는 클래스로, List를 넣어주면 에디터에서 순서를 바꿀수 있는 입력칸을 만들어준다.
- 비주얼 관련해 여러 Callback함수가 제공하는 Rect : 해당하는 공간에 같이 오는 변수를 사용해 구성한다.
reorderableList.drawElementCallback = (Rect rect, int index, bool isActive, bool isFocused) =>
{
assetNames[index] = EditorGUI.TextField(rect,$"Item {index + 1}", assetNames[index]);
//assetNames[index] = EditorGUILayout.TextField($"Item {index + 1}", assetNames[index]);
};
주석처리된 코드를 사용하면, 칸에 벗어나서 입력란이 생긴다.
GUILayout.Label("Create Prefab and Material", EditorStyles.boldLabel);
GUILayout.Space(10);
GUILayout.Label("BasePath : " + basePath + "Prefabs/ || Materials/", EditorStyles.label);
//custom method (assetNames[i] = EditorGUILayout.TextField("Asset name", assetNames[i]);)
DynamicInputWindow();
GUILayout.Space(10);
if (GUILayout.Button("Create Assetes"))
{
CreateAssets(assetNames);
}
assetNames[i] = EditorGUILayout.TextField("Asset name", assetNames[i]);
- 텍스트 필드 입력시 사용하며, 동일한 타입의 변수가 필요하다.
- EditorGUILayout.TextField("Asset name") 입력은 가능하나 적용은 불가
Custom Code
더보기
using UnityEngine;
using UnityEditor;
using System.IO;
using System.Collections.Generic;
using UnityEditor.Search;
public class AssetAutoCreator : EditorWindow
{
string basePath = "Assets/GeneratedAssets/";
List<string> assetNames = new List<string>() { "" };
[MenuItem("Tools/Prefab & Material Creator")]
public static void ShowWindow()
{
GetWindow<AssetAutoCreator>("Prefab & Material Creator");
}
private void OnGUI()
{
GUILayout.Label("Create Prefab and Material", EditorStyles.boldLabel);
GUILayout.Space(10);
GUILayout.Label("BasePath : " + basePath + "Prefabs/ || Materials/", EditorStyles.label);
DynamicInputWindow();
GUILayout.Space(10);
if (GUILayout.Button("Create Assetes"))
{
CreateAssets(assetNames.ToArray());
}
}
private void DynamicInputWindow()
{
for (int i = 0; i < assetNames.Count; i++)
{
assetNames[i] = EditorGUILayout.TextField("Asset name", assetNames[i]);
}
if (!string.IsNullOrWhiteSpace(assetNames[assetNames.Count - 1]))
{
assetNames.Add("");
}
}
void CreateAssets(string[] names)
{
foreach(string name in names)
{
if (string.IsNullOrWhiteSpace(name)) continue;
string prefabPath = Path.Combine(basePath + "Prefabs/", name + ".prefab");
string materialPath = Path.Combine(basePath + "Materials/", name + "_Mat.mat");
if (!AssetDatabase.IsValidFolder(basePath + "Prefabs/"))
{
Directory.CreateDirectory(basePath + "Prefabs/");
}
if (!AssetDatabase.IsValidFolder(basePath + "Materials/"))
{
Directory.CreateDirectory(basePath + "Materials/");
}
if(File.Exists(prefabPath))
{
Debug.Log(name + " File exists already");
continue;
}
if (File.Exists(materialPath))
{
Debug.Log(name + " File exists already");
continue;
}
Material newMat = new Material(Shader.Find("Standard"));
AssetDatabase.CreateAsset(newMat, materialPath);
GameObject obj = GameObject.CreatePrimitive(PrimitiveType.Cube);
obj.name = name;
obj.GetComponent<Renderer>().sharedMaterial = newMat;
PrefabUtility.SaveAsPrefabAsset(obj, prefabPath);
DestroyImmediate(obj);
AssetDatabase.Refresh();
}
}
}
using UnityEngine;
using UnityEditor;
using System.IO;
using System.Collections.Generic;
using Unity.VisualScripting;
using UnityEditor.PackageManager.UI;
public class AssetAutoCreatorTwo : EditorWindow
{
public class ArrayData : ScriptableObject
{
public string[] items;
}
SerializedObject serializedObj;
SerializedProperty itemsProp;
string basePath = "Assets/GeneratedAssets/";
[MenuItem("Tools/Prefab Material Creator2")]
public static void ShowWindow()
{
GetWindow<AssetAutoCreatorTwo>("Prefab & Material Creator2").minSize = new Vector2(500, 300); ;
}
void OnEnable()
{
var ScriptableObj = ScriptableObject.CreateInstance<ArrayData>();
serializedObj = new SerializedObject(ScriptableObj);
itemsProp = serializedObj.FindProperty("items");
}
private void OnGUI()
{
GUILayout.Label("Create Prefab and Material", EditorStyles.boldLabel);
GUILayout.Label("Path : " + basePath + "Materials || Prefabs", EditorStyles.label);
GUILayout.Space(10);
serializedObj.Update();
EditorGUILayout.PropertyField(itemsProp, new GUIContent("Assat names"), true);
serializedObj.ApplyModifiedProperties();
if (GUILayout.Button("Create Assets"))
{
string[] names = new string[itemsProp.arraySize];
for(int i=0; i < itemsProp.arraySize; i++)
{
names[i] = itemsProp.GetArrayElementAtIndex(i).stringValue;
}
CreateAssets(names);
}
}
void CreateAssets(string[] names)
{//동일
}
}
using NUnit.Framework;
using UnityEditorInternal;
using UnityEngine;
using System.Collections.Generic;
using UnityEditor;
using Unity.VisualScripting;
using System.IO;
public class AssetAutoCreatorThree : EditorWindow
{
private ReorderableList reorderableList;
private List<string> assetNames = new List<string>{ };
string basePath = "Assets/GeneratedAssets/";
[MenuItem("Tools/Prefab Material Creator3")]
public static void ShowWindow()
{
GetWindow<AssetAutoCreatorThree>("Prefab Material Creator3");
}
private void OnEnable()
{
reorderableList = new ReorderableList(assetNames, typeof(string) , true, true, true, true);
reorderableList.drawElementCallback = (Rect rect, int index, bool isActive, bool isFocused) =>
{
assetNames[index] = EditorGUI.TextField(rect,$"Item {index + 1}", assetNames[index]);
//assetNames[index] = EditorGUILayout.TextField($"Item {index + 1}", assetNames[index]);
};
}
private void OnGUI()
{
GUILayout.Label("Create Prefab and Material", EditorStyles.boldLabel);
GUILayout.Label("Path : " + basePath + "Materials || Prefabs", EditorStyles.label);
GUILayout.Space(10);
if (reorderableList != null)
reorderableList.DoLayoutList();
if (GUILayout.Button("Create Assets"))
{
CreateAssets(assetNames.ToArray());
}
}
void CreateAssets(string[] names)
{//동일
}
}
'Endeavor > Unity Stuff' 카테고리의 다른 글
Blender remind (0) | 2025.04.29 |
---|---|
유니티 각도 관련 (0) | 2025.04.26 |
Unity - FPS microgame code 분석 (0) | 2024.05.24 |
Unity Learn - Creative Core Pathway - Final Submission (0) | 2024.05.16 |