1
+ using System ;
2
+ using System . Collections ;
3
+ using System . Linq ;
4
+ using System . Net ;
5
+ using System . Reflection ;
6
+ using UnityEngine ;
7
+ using UnityEditor ;
8
+ using Object = UnityEngine . Object ;
9
+
10
+ namespace IntelligentCopyAndPaste
11
+ {
12
+ public static class ArrayCopyPasteComponent
13
+ {
14
+ public static System . Collections . Generic . List < Object > _data ; //TODO 本当はprivateにしたいな
15
+
16
+ //# SHIFT
17
+ //% CTRL
18
+ //& ALT
19
+
20
+ /// <summary>
21
+ /// 普通のコピー
22
+ /// </summary>
23
+ [ MenuItem ( "Assets/Copy Assets Paths %c" , false , - 101 ) ]
24
+ private static void CopyAssetsPaths ( )
25
+ {
26
+ _data = new System . Collections . Generic . List < Object > ( Selection . objects ) ;
27
+ IntelligentClipBoardWinow . Open ( ) ;
28
+ }
29
+
30
+ /// <summary>
31
+ /// 追加コピー
32
+ /// </summary>
33
+ [ MenuItem ( "Assets/AppendCopy Assets Paths #%c" , false , - 101 ) ]
34
+ private static void AppendCopyAssetsPaths ( )
35
+ {
36
+ if ( _data == null ) _data = new System . Collections . Generic . List < Object > ( ) ;
37
+ _data . AddRange ( Selection . objects ) ;
38
+ IntelligentClipBoardWinow . Open ( ) ;
39
+ }
40
+
41
+ /// <summary>
42
+ /// GameObjectを指定してのペースト ALT+V
43
+ /// 可能性が非常に多いのでChoiceWindowを出す必要がある
44
+ /// </summary>
45
+ [ MenuItem ( "Assets/Paste From Assets Paths %v" , false , - 101 ) ]
46
+ private static void _PasteFromCopiedObjects ( )
47
+ {
48
+ if ( Validate ( ) == false ) return ;
49
+ var selection = Selection . gameObjects . First ( ) ;
50
+ var monoBehaviours = selection . GetComponents < MonoBehaviour > ( ) ;
51
+ ArrayCopyAndPaste ( true , monoBehaviours ) ;
52
+ }
53
+
54
+ /// <summary>
55
+ /// GameObjectを指定しての追加ペースト ALT+SHIFT+V
56
+ /// 可能性が非常に多いのでChoiceWindowを出す必要がある
57
+ /// </summary>
58
+ [ MenuItem ( "Assets/AppendPaste From Assets Paths #%v" , false , - 101 ) ]
59
+ private static void _AppendPasteFromCopiedObjects ( )
60
+ {
61
+ if ( Validate ( ) == false ) return ;
62
+ var selection = Selection . gameObjects . First ( ) ;
63
+ var monoBehaviours = selection . GetComponents < MonoBehaviour > ( ) ;
64
+ ArrayCopyAndPaste ( false , monoBehaviours ) ;
65
+ }
66
+
67
+ /// <summary>
68
+ /// ContextMenuからのペースト ALT+V
69
+ /// </summary>
70
+ [ MenuItem ( "CONTEXT/MonoBehaviour/Intelligent Paste %v" , false ) ]
71
+ private static void _PasetFromCopiedObjects ( MenuCommand menuCommand )
72
+ {
73
+ if ( Validate ( ) == false ) return ;
74
+ ArrayCopyAndPaste ( true , menuCommand . context as MonoBehaviour ) ;
75
+ }
76
+
77
+ /// <summary>
78
+ /// ContextMenuからの追加ペースト ALT+SHIFT+V
79
+ /// </summary>
80
+ [ MenuItem ( "CONTEXT/MonoBehaviour/Intelligent AppendPaste #%v" , false ) ]
81
+ private static void _AppendPasteFromCopiedObjects ( MenuCommand menuCommand )
82
+ {
83
+ if ( Validate ( ) == false ) return ;
84
+ ArrayCopyAndPaste ( false , menuCommand . context as MonoBehaviour ) ;
85
+ }
86
+
87
+
88
+ private static void ArrayCopyAndPaste ( bool isOverride , params MonoBehaviour [ ] monoBehaviours )
89
+ {
90
+ var arrayCopyAndPasteInfos = new System . Collections . Generic . List < ArrayCopyAndPasteInfo > ( ) ;
91
+ foreach ( var mono in monoBehaviours )
92
+ {
93
+ var type = mono . GetType ( ) ;
94
+ var allFields = type . GetFields ( BindingFlags . NonPublic | BindingFlags . Public | BindingFlags . GetField | BindingFlags . SetField | BindingFlags . Instance ) ;
95
+
96
+ foreach ( var info in allFields )
97
+ {
98
+ //PrivateFieldはSerializeFieldAttributeが指定されてないものは対象にできない
99
+ if ( info . IsPrivate && info . GetCustomAttributes ( typeof ( SerializeField ) , false ) . Any ( ) == false ) continue ;
100
+
101
+ //複数の場合があるので、どの配列に対して、どのインデックスから(追加か、上書きか)張り付けるかを判断するためのwindowを表示する
102
+ var pasteInfo = new ArrayCopyAndPasteInfo
103
+ {
104
+ IsOverride = isOverride ,
105
+ TargetMonoBehaviour = mono ,
106
+ TargetFieldInfo = info ,
107
+ } ;
108
+
109
+ //配列かListか単品か判断
110
+ var arrayType = info . FieldType ;
111
+ if ( arrayType . IsGenericType && arrayType . GetGenericTypeDefinition ( ) == typeof ( System . Collections . Generic . List < > ) ) pasteInfo . IsTypeList = true ;
112
+ else if ( arrayType . IsArray ) pasteInfo . IsTypeList = false ;
113
+ else pasteInfo . IsSingle = true ;
114
+
115
+ //配列(またはList)の要素のType
116
+ if ( pasteInfo . IsSingle )
117
+ {
118
+ pasteInfo . ArrayElementType = info . FieldType ;
119
+ }
120
+ else
121
+ {
122
+ pasteInfo . ArrayElementType = pasteInfo . IsTypeList ? arrayType . GetGenericArguments ( ) . FirstOrDefault ( ) : arrayType . GetElementType ( ) ;
123
+ }
124
+ if ( pasteInfo . ArrayElementType == null ) continue ;
125
+
126
+ //対象がこのMonoBehaviourのこのFieldだった場合のコピー元から張り付ける事ができる要素をまとめる
127
+ pasteInfo . CopySrcDatas = _data . Select ( o =>
128
+ {
129
+ if ( o . GetType ( ) == pasteInfo . ArrayElementType ) return o ;
130
+
131
+ Debug . Log ( pasteInfo . ArrayElementType . Name ) ;
132
+ if ( pasteInfo . ArrayElementType . IsSubclassOf ( typeof ( Component ) ) )
133
+ {
134
+ var attachedComponent = ( o as GameObject ) ? . GetComponent ( pasteInfo . ArrayElementType ) ;
135
+ if ( attachedComponent ? . GetType ( ) == pasteInfo . ArrayElementType ) return attachedComponent ;
136
+ }
137
+ return AssetDatabase . LoadAssetAtPath ( AssetDatabase . GetAssetPath ( o ) , pasteInfo . ArrayElementType ) ; //TODO Spriteそのものの場合と、Spriteを持っているTextureの場合がある
138
+ } ) . Where ( o => o != null ) . ToArray ( ) ;
139
+
140
+ if ( pasteInfo . CopySrcDatas . Any ( ) == false ) continue ; //コピー元が張り付ける先が無かった
141
+
142
+ if ( pasteInfo . IsTypeList )
143
+ {
144
+ var originalData = pasteInfo . TargetFieldInfo . GetValue ( pasteInfo . TargetMonoBehaviour ) as IList ;
145
+ pasteInfo . OriginalDatas = new Object [ originalData . Count ] ;
146
+ originalData . CopyTo ( pasteInfo . OriginalDatas , 0 ) ;
147
+ }
148
+ else if ( pasteInfo . IsSingle )
149
+ {
150
+ var originalData = pasteInfo . TargetFieldInfo . GetValue ( pasteInfo . TargetMonoBehaviour ) as Object ;
151
+ pasteInfo . OriginalDatas = new [ ] { originalData } ;
152
+ }
153
+ else
154
+ {
155
+ var originalData = pasteInfo . TargetFieldInfo . GetValue ( pasteInfo . TargetMonoBehaviour ) as Array ;
156
+ pasteInfo . OriginalDatas = originalData as Object [ ] ;
157
+ }
158
+
159
+ pasteInfo . DisplayName = pasteInfo . TargetFieldInfo . DeclaringType . FullName + Environment . NewLine ;
160
+
161
+ if ( pasteInfo . IsSingle )
162
+ {
163
+ pasteInfo . DisplayName += pasteInfo . ArrayElementType . Name ;
164
+ }
165
+ else if ( pasteInfo . IsTypeList )
166
+ {
167
+ pasteInfo . DisplayName += "List<" + pasteInfo . ArrayElementType . Name + "> " ;
168
+ }
169
+ else
170
+ {
171
+ pasteInfo . DisplayName += pasteInfo . ArrayElementType . Name + "[] " ;
172
+ }
173
+
174
+ //継承関係においては、同じフィールドが複数出てきてしまう可能性があるのでチェック
175
+ if ( arrayCopyAndPasteInfos . Any ( inf => inf . TargetFieldInfo == pasteInfo . TargetFieldInfo ) ) continue ;
176
+
177
+ arrayCopyAndPasteInfos . Add ( pasteInfo ) ;
178
+ }
179
+ }
180
+
181
+ //TODO 対象が1つしか無かった場合にWindowを出すべきかどうか悩むところ
182
+ if ( arrayCopyAndPasteInfos . Any ( ) )
183
+ {
184
+ var message = arrayCopyAndPasteInfos . Count == 1 ? "以下のFieldが候補にあがりました。よろしいですか?" : "複数のFieldが候補にあがりました。どちらのFieldにペーストしますか?" ;
185
+ CopyAndPasteChoiceWindow . Open ( message , arrayCopyAndPasteInfos , pasteInfo =>
186
+ {
187
+ if ( pasteInfo != null )
188
+ {
189
+ CopyProcess ( pasteInfo ) ;
190
+ }
191
+ } ) ;
192
+ }
193
+ }
194
+
195
+ private static void CopyProcess ( ArrayCopyAndPasteInfo pasteInfo )
196
+ {
197
+ //Undoへ登録
198
+ Undo . RecordObject ( pasteInfo . TargetMonoBehaviour , "IntelligentCopyAndPaste - Paste" ) ;
199
+
200
+ if ( pasteInfo . IsSingle )
201
+ {
202
+ //一つしかないので、IsOverride関係ない
203
+ var pastData = pasteInfo . CopySrcDatas . FirstOrDefault ( ) ;
204
+ if ( pastData == null )
205
+ {
206
+ pasteInfo . TargetFieldInfo . SetValue ( pasteInfo . TargetMonoBehaviour , null ) ;
207
+ }
208
+ else
209
+ {
210
+ pasteInfo . TargetFieldInfo . SetValue ( pasteInfo . TargetMonoBehaviour , pastData ) ;
211
+ }
212
+ }
213
+ else if ( pasteInfo . IsTypeList )
214
+ {
215
+ var list = pasteInfo . TargetFieldInfo . GetValue ( pasteInfo . TargetMonoBehaviour ) as IList ;
216
+
217
+ if ( pasteInfo . IsOverride ) list . Clear ( ) ; //ペーストしたもので全て入れ替える場合
218
+ foreach ( var o in pasteInfo . CopySrcDatas )
219
+ {
220
+ list . Add ( o ) ;
221
+ }
222
+ }
223
+ else
224
+ {
225
+ if ( pasteInfo . IsOverride ) //ペーストしたもので全て入れ替える場合
226
+ {
227
+ var converted = Array . CreateInstance ( pasteInfo . ArrayElementType , pasteInfo . CopySrcDatas . Length ) ;
228
+ Array . Copy ( pasteInfo . CopySrcDatas , 0 , converted , 0 , pasteInfo . CopySrcDatas . Length ) ;
229
+ pasteInfo . TargetFieldInfo . SetValue ( pasteInfo . TargetMonoBehaviour , converted ) ;
230
+ }
231
+ else //元データを維持して、追加する場合
232
+ {
233
+ var array = pasteInfo . TargetFieldInfo . GetValue ( pasteInfo . TargetMonoBehaviour ) as Array ;
234
+ var converted = Array . CreateInstance ( pasteInfo . ArrayElementType , pasteInfo . CopySrcDatas . Length + array . Length ) ;
235
+ Array . Copy ( array , 0 , converted , 0 , array . Length ) ;
236
+ Array . Copy ( pasteInfo . CopySrcDatas , 0 , converted , array . Length , pasteInfo . CopySrcDatas . Length ) ;
237
+ pasteInfo . TargetFieldInfo . SetValue ( pasteInfo . TargetMonoBehaviour , converted ) ;
238
+ }
239
+ }
240
+ EditorUtility . SetDirty ( pasteInfo . TargetMonoBehaviour ) ;
241
+ pasteInfo . TargetMonoBehaviour . SendMessage ( "OnValidate" ) ; //TODO mmmmmmmmmmmmmm これをやらないとEditor上で画像などが反映されないのツライ
242
+ }
243
+
244
+
245
+ private static bool Validate ( bool isShowAlert = true )
246
+ {
247
+ if ( _data == null || ! _data . Any ( ) ) //C# 6.0 → if(data?.Any() != true)return;
248
+ {
249
+ if ( isShowAlert ) UnityEditor . EditorUtility . DisplayDialog ( "Notice" , "なにもコピーされていないようです" , "OK" ) ;
250
+ return false ;
251
+ }
252
+
253
+ if ( Selection . gameObjects . Length >= 2 )
254
+ {
255
+ if ( isShowAlert ) UnityEditor . EditorUtility . DisplayDialog ( "Notice" , "複数GameObjectへの同時ペーストは出来ません" , "OK" ) ;
256
+ return false ;
257
+ }
258
+
259
+ if ( Selection . gameObjects . Any ( ) == false )
260
+ {
261
+ if ( isShowAlert ) UnityEditor . EditorUtility . DisplayDialog ( "Notice" , "GameObjectが選択されていません" , "OK" ) ;
262
+ return false ;
263
+ }
264
+
265
+ return true ;
266
+ }
267
+ }
268
+ }
0 commit comments