기능.
프로젝트 창에서 텍스처 선택 했다면 Ctrl+c 로 머티리얼에 적용 하려고 하는 텍스처를 리스트에 저장.
머티리얼 선택하고 Ctrl+p 하면 리스트에 저장했던 텍스처가 머티리얼 텍스처 프로퍼티에 등록됨.
드레그 엔 드랍 귀찮을 때 쓸만 함.
생성 : Claude AI
개선 가는성 : Yes
향후 개선 방향 : 프로퍼티 검증 룰베이스 추가 해서 고도화 가능해 보임.
TextureCopyPasteExtension.cs
using UnityEngine;
using UnityEditor;
using System.Collections.Generic;
// Unity 6.1 호환 텍스처 복사/붙여넣기 확장 도구
public static class TextureCopyPasteExtension
{
// 복사된 텍스처 저장 리스트
private static List<Texture> textureClipboard = new List<Texture>();
// 에디터 시작 시 한 번 호출되는 초기화 함수
[InitializeOnLoadMethod]
private static void Initialize()
{
// 이벤트 핸들러 등록
EditorApplication.update += OnEditorUpdate;
EditorApplication.projectWindowItemOnGUI += OnProjectItemGUI;
SceneView.onSceneGUIDelegate += OnSceneGUI;
Debug.Log("<color=#00FF88>텍스처 복사/붙여넣기 도구</color>: 초기화 완료. Ctrl+C/V 단축키를 사용할 수 있습니다.");
}
// 에디터 업데이트 이벤트
private static void OnEditorUpdate()
{
// 인스펙터 창에서도 키 이벤트를 감지하기 위한 처리
if (EditorWindow.focusedWindow != null && EditorWindow.focusedWindow.GetType().Name == "InspectorWindow")
{
Event e = Event.current;
if (e != null)
{
// Ctrl+C 처리
if (e.control && e.type == EventType.KeyDown && e.keyCode == KeyCode.C)
{
TryHandleCopy();
e.Use();
}
// Ctrl+V 처리
if (e.control && e.type == EventType.KeyDown && e.keyCode == KeyCode.V)
{
TryHandlePaste();
e.Use();
}
}
}
}
// 씬 뷰 GUI 이벤트 처리
private static void OnSceneGUI(SceneView sceneView)
{
Event e = Event.current;
// Ctrl+C 처리
if (e.control && e.type == EventType.KeyDown && e.keyCode == KeyCode.C)
{
TryHandleCopy();
e.Use();
}
// Ctrl+V 처리
if (e.control && e.type == EventType.KeyDown && e.keyCode == KeyCode.V)
{
TryHandlePaste();
e.Use();
}
}
// 프로젝트 창 GUI 이벤트 처리
private static void OnProjectItemGUI(string guid, Rect selectionRect)
{
Event e = Event.current;
// Ctrl+C 처리
if (e.control && e.type == EventType.KeyDown && e.keyCode == KeyCode.C)
{
TryHandleCopy();
e.Use();
}
// Ctrl+V 처리
if (e.control && e.type == EventType.KeyDown && e.keyCode == KeyCode.V)
{
TryHandlePaste();
e.Use();
}
}
// 복사 기능 처리
private static bool TryHandleCopy()
{
// 프로젝트 창에서 현재 선택된 객체들 가져오기
Object[] selectedObjects = Selection.GetFiltered<Object>(SelectionMode.Assets);
// 리스트 초기화
textureClipboard.Clear();
bool hasTextureCopied = false;
// 선택된 객체들 순회
foreach (Object obj in selectedObjects)
{
// 텍스처 유형 확인 (Texture, Texture2D, Sprite 등)
if (obj is Texture || AssetDatabase.GetMainAssetTypeAtPath(AssetDatabase.GetAssetPath(obj)) == typeof(Texture2D))
{
Texture texture = obj as Texture;
if (texture != null)
{
textureClipboard.Add(texture);
hasTextureCopied = true;
Debug.Log($"<color=#80FFFF>텍스처 복사</color>: {obj.name}");
}
}
// 스프라이트도 처리
else if (obj is Sprite)
{
Sprite sprite = obj as Sprite;
if (sprite != null && sprite.texture != null)
{
textureClipboard.Add(sprite.texture);
hasTextureCopied = true;
Debug.Log($"<color=#80FFFF>스프라이트 텍스처 복사</color>: {obj.name}");
}
}
}
if (hasTextureCopied)
{
// 팝업 대신 로그로 복사 완료 알림
Debug.Log($"<color=#00FF00>복사 완료</color>: {textureClipboard.Count}개의 텍스처가 클립보드에 저장되었습니다.");
return true;
}
return false;
}
// 붙여넣기 기능 처리
private static bool TryHandlePaste()
{
// 클립보드에 텍스처가 없으면 아무 작업도 하지 않음
if (textureClipboard.Count == 0)
{
return false;
}
// 현재 선택된 머티리얼 가져오기
Object[] selectedObjects = Selection.objects;
bool pasteSucceeded = false;
foreach (Object obj in selectedObjects)
{
// 선택된 객체가 머티리얼인지 확인
if (obj is Material)
{
Material material = obj as Material;
// 현재 인스펙터에서 포커스된 컨트롤 이름 가져오기
string focusedControlName = GUI.GetNameOfFocusedControl();
// 포커스된 컨트롤이 없거나 머티리얼 인스펙터에서 처리해야할 경우
// 가장 적합한 텍스처 슬롯 찾기
ApplyTexturesToMaterial(material, focusedControlName);
pasteSucceeded = true;
}
}
if (pasteSucceeded)
{
// 에디터 리프레시 및 변경 내용 저장
AssetDatabase.Refresh();
return true;
}
return false;
}
// 머티리얼에 텍스처 적용
private static void ApplyTexturesToMaterial(Material material, string focusedPropertyName)
{
if (material == null || textureClipboard.Count == 0)
return;
Texture textureToPaste = textureClipboard[0];
bool applied = false;
// 머티리얼 변경 기록 시작 (Undo 지원)
Undo.RecordObject(material, "Paste Texture");
// 포커스된 특정 프로퍼티가 있는 경우
if (!string.IsNullOrEmpty(focusedPropertyName))
{
// 해당 프로퍼티에 텍스처 적용
material.SetTexture(focusedPropertyName, textureToPaste);
Debug.Log($"<color=#FFCC00>텍스처 붙여넣기 완료</color>: '{textureToPaste.name}'를 머티리얼 '{material.name}'의 '{focusedPropertyName}' 프로퍼티에 적용했습니다.");
applied = true;
}
else
{
// 포커스된 프로퍼티가 없는 경우, 일반적인 텍스처 슬롯을 찾아 적용
string[] commonTextureProperties = {
"_MainTex",
"_BaseMap",
"_BumpMap",
"_NormalMap",
"_EmissionMap",
"_OcclusionMap",
"_MetallicGlossMap",
"_SpecGlossMap",
"_DetailAlbedoMap",
"_DetailNormalMap"
};
// 이름을 기반으로 적절한 텍스처 슬롯 자동 선택
string textureName = textureToPaste.name.ToLower();
if (textureName.Contains("albedo") || textureName.Contains("diffuse") || textureName.Contains("color"))
{
TrySetTexture(material, new[] { "_MainTex", "_BaseMap" });
applied = true;
}
else if (textureName.Contains("normal") || textureName.Contains("bump"))
{
TrySetTexture(material, new[] { "_BumpMap", "_NormalMap" });
applied = true;
}
else if (textureName.Contains("emission") || textureName.Contains("emissive"))
{
TrySetTexture(material, new[] { "_EmissionMap" });
applied = true;
}
else if (textureName.Contains("metallic") || textureName.Contains("smoothness"))
{
TrySetTexture(material, new[] { "_MetallicGlossMap" });
applied = true;
}
else if (textureName.Contains("occlusion") || textureName.Contains("ao"))
{
TrySetTexture(material, new[] { "_OcclusionMap" });
applied = true;
}
else if (textureName.Contains("specular") || textureName.Contains("glossiness"))
{
TrySetTexture(material, new[] { "_SpecGlossMap" });
applied = true;
}
else
{
// 텍스처 이름으로 적절한 슬롯을 찾지 못한 경우 메인 텍스처에 적용
TrySetTexture(material, new[] { "_MainTex", "_BaseMap" });
applied = true;
}
// 텍스처가 적용된 경우 함수 종료
if (applied)
return;
// 일반적인 텍스처 슬롯에 적용 시도
foreach (string propName in commonTextureProperties)
{
if (material.HasProperty(propName))
{
material.SetTexture(propName, textureToPaste);
Debug.Log($"<color=#FFCC00>텍스처 붙여넣기 완료</color>: '{textureToPaste.name}'를 머티리얼 '{material.name}'의 '{propName}' 프로퍼티에 적용했습니다.");
applied = true;
break;
}
}
}
// 아무 프로퍼티에도 적용하지 못한 경우
if (!applied)
{
Debug.LogWarning($"<color=#FF6666>적용 실패</color>: 머티리얼 '{material.name}'에 적용할 수 있는 텍스처 프로퍼티를 찾지 못했습니다.");
}
}
// 텍스처 적용 도우미 함수
private static bool TrySetTexture(Material material, string[] propertyNames)
{
if (textureClipboard.Count == 0) return false;
foreach (string propName in propertyNames)
{
if (material.HasProperty(propName))
{
material.SetTexture(propName, textureClipboard[0]);
Debug.Log($"<color=#FFCC00>텍스처 붙여넣기 완료</color>: '{textureClipboard[0].name}'를 머티리얼 '{material.name}'의 '{propName}' 프로퍼티에 적용했습니다.");
return true;
}
}
return false;
}
// 메뉴에 항목 추가 (수동으로 텍스처 복사/붙여넣기 관리)
[MenuItem("Assets/텍스처 복사", true)]
private static bool ValidateTextureCopy()
{
return Selection.activeObject is Texture || Selection.activeObject is Sprite;
}
[MenuItem("Assets/텍스처 복사", false, 20)]
private static void TexureCopyMenuItem()
{
TryHandleCopy();
}
[MenuItem("Assets/텍스처 붙여넣기", true)]
private static bool ValidateTexturePaste()
{
return textureClipboard.Count > 0 && Selection.activeObject is Material;
}
[MenuItem("Assets/텍스처 붙여넣기", false, 21)]
private static void TexurePasteMenuItem()
{
TryHandlePaste();
}
}
// 텍스처 복사/붙여넣기 관리 윈도우
public class TextureCopyPasteWindow : EditorWindow
{
// 복사된 텍스처 참조를 위한 변수
private List<Texture> _clippedTextures = new List<Texture>();
private Vector2 _scrollPosition;
// 메뉴에 윈도우 항목 추가
[MenuItem("Window/텍스처 복사 붙여넣기 관리")]
public static void ShowWindow()
{
TextureCopyPasteWindow window = GetWindow<TextureCopyPasteWindow>("텍스처 복사/붙여넣기");
window.minSize = new Vector2(300, 200);
}
private void OnGUI()
{
// 윈도우 제목 및 설명
GUILayout.Label("텍스처 복사/붙여넣기 도구", EditorStyles.boldLabel);
EditorGUILayout.Space();
// 사용 방법 설명
EditorGUILayout.HelpBox(
"사용 방법:\n\n" +
"1. 프로젝트 윈도우에서 텍스처를 선택하고 Ctrl+C를 누르세요.\n" +
"2. 머티리얼을 선택한 후, 텍스처 프로퍼티 인터페이스의 빈칸을 클릭하고 Ctrl+V를 누르세요.\n\n" +
"또는 컨텍스트 메뉴(우클릭)에서 '텍스처 복사'와 '텍스처 붙여넣기' 메뉴를 사용할 수 있습니다.",
MessageType.Info);
EditorGUILayout.Space(10);
// 주의사항 설명
EditorGUILayout.HelpBox(
"주의사항:\n\n" +
"1. 텍스처 프로퍼티에 포커스를 맞춘 상태에서 붙여넣기를 해야 정확한 슬롯에 적용됩니다.\n" +
"2. 포커스가 없는 경우, 텍스처 이름을 기반으로 적절한 슬롯을 찾아 자동으로 적용합니다.",
MessageType.Warning);
// 스크립트의 private 필드에 직접 접근할 수 없으므로
// UI만 표시하고 실제 데이터는 참조하지 못함
EditorGUILayout.Space(10);
EditorGUILayout.LabelField("이 도구는 Unity 6.1에 맞춰 최적화되었습니다.", EditorStyles.boldLabel);
}
// 업데이트 함수 (윈도우가 열려 있는 동안 주기적으로 호출)
private void Update()
{
// 변화가 있으면 윈도우 갱신
Repaint();
}
}
'UNITY3D' 카테고리의 다른 글
Data Validation Toolset 개발. 버전 0.2 (0) | 2025.05.23 |
---|---|
적응형 CSM by FOV (0) | 2025.05.17 |
Unity Shading Rate (0) | 2025.04.03 |
내장 셰이더 벡터 파라메터 (1) | 2025.03.03 |
Cursor AI IDE 와 UNITY3D 연동하여 개발하기. (0) | 2025.03.02 |