修改 同时新增ux编辑器
This commit is contained in:
parent
88159964d4
commit
366d0a8f21
8
Client/Assets/Bundles/UI/Dadsa.meta
Normal file
8
Client/Assets/Bundles/UI/Dadsa.meta
Normal file
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a42453f5b0b0adc428878645f13264f0
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
8
Client/Assets/Bundles/UI/Window.meta
Normal file
8
Client/Assets/Bundles/UI/Window.meta
Normal file
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 45accde758bbe4f4986be1cb8aa8eac9
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -1,6 +1,6 @@
|
||||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!1 &3529610083755649764
|
||||
--- !u!1 &3291706644560179776
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
@ -8,101 +8,10 @@ GameObject:
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 6054056368442846410}
|
||||
- component: {fileID: 1594745885832791941}
|
||||
- component: {fileID: 7369916918447278109}
|
||||
- component: {fileID: 1744287959665422355}
|
||||
m_Layer: 5
|
||||
m_Name: UICardWidget
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!224 &6054056368442846410
|
||||
RectTransform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 3529610083755649764}
|
||||
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||
m_LocalPosition: {x: 0, y: 0, z: 0}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children:
|
||||
- {fileID: 4201042986197880034}
|
||||
- {fileID: 6299928650586651613}
|
||||
m_Father: {fileID: 0}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
m_AnchorMin: {x: 0.5, y: 0.5}
|
||||
m_AnchorMax: {x: 0.5, y: 0.5}
|
||||
m_AnchoredPosition: {x: 0, y: 0}
|
||||
m_SizeDelta: {x: 300, y: 500}
|
||||
m_Pivot: {x: 0.5, y: 0.5}
|
||||
--- !u!223 &1594745885832791941
|
||||
Canvas:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 3529610083755649764}
|
||||
m_Enabled: 1
|
||||
serializedVersion: 3
|
||||
m_RenderMode: 0
|
||||
m_Camera: {fileID: 0}
|
||||
m_PlaneDistance: 100
|
||||
m_PixelPerfect: 0
|
||||
m_ReceivesEvents: 1
|
||||
m_OverrideSorting: 0
|
||||
m_OverridePixelPerfect: 0
|
||||
m_SortingBucketNormalizedSize: 0
|
||||
m_VertexColorAlwaysGammaSpace: 1
|
||||
m_AdditionalShaderChannelsFlag: 31
|
||||
m_UpdateRectTransformForStandalone: 0
|
||||
m_SortingLayerID: 0
|
||||
m_SortingOrder: 0
|
||||
m_TargetDisplay: 0
|
||||
--- !u!114 &7369916918447278109
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 3529610083755649764}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: dc42784cf147c0c48a680349fa168899, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
m_IgnoreReversedGraphics: 1
|
||||
m_BlockingObjects: 0
|
||||
m_BlockingMask:
|
||||
serializedVersion: 2
|
||||
m_Bits: 4294967295
|
||||
--- !u!114 &1744287959665422355
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 3529610083755649764}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: e9394045d7c436b418b7e8b879ebe7ca, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
--- !u!1 &6335583502871219895
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 4201042986197880034}
|
||||
- component: {fileID: 5601054129539157368}
|
||||
- component: {fileID: 5487560666036449699}
|
||||
- component: {fileID: 3845232356774058299}
|
||||
- component: {fileID: 9098732005674451575}
|
||||
- component: {fileID: 984006520201918201}
|
||||
- component: {fileID: 7000239766839884593}
|
||||
m_Layer: 5
|
||||
m_Name: UXImage
|
||||
m_TagString: Untagged
|
||||
@ -110,13 +19,13 @@ GameObject:
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!224 &4201042986197880034
|
||||
--- !u!224 &3845232356774058299
|
||||
RectTransform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 6335583502871219895}
|
||||
m_GameObject: {fileID: 3291706644560179776}
|
||||
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||
m_LocalPosition: {x: 0, y: 0, z: 0}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
@ -124,33 +33,33 @@ RectTransform:
|
||||
m_Children: []
|
||||
m_Father: {fileID: 6054056368442846410}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
m_AnchorMin: {x: 0, y: 0}
|
||||
m_AnchorMax: {x: 1, y: 1}
|
||||
m_AnchorMin: {x: 0.5, y: 0.5}
|
||||
m_AnchorMax: {x: 0.5, y: 0.5}
|
||||
m_AnchoredPosition: {x: 0, y: 0}
|
||||
m_SizeDelta: {x: 0, y: 0}
|
||||
m_SizeDelta: {x: 100, y: 100}
|
||||
m_Pivot: {x: 0.5, y: 0.5}
|
||||
--- !u!222 &5601054129539157368
|
||||
--- !u!222 &9098732005674451575
|
||||
CanvasRenderer:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 6335583502871219895}
|
||||
m_GameObject: {fileID: 3291706644560179776}
|
||||
m_CullTransparentMesh: 1
|
||||
--- !u!114 &5487560666036449699
|
||||
--- !u!114 &984006520201918201
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 6335583502871219895}
|
||||
m_GameObject: {fileID: 3291706644560179776}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: af0993b503fa4dd1adf519458df05486, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
m_Material: {fileID: 2100000, guid: 624af9784554f4047997278dfbb22e47, type: 2}
|
||||
m_Color: {r: 1, g: 0.3537736, b: 0.3537736, a: 1}
|
||||
m_Color: {r: 1, g: 1, b: 1, a: 1}
|
||||
m_RaycastTarget: 1
|
||||
m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
|
||||
m_Maskable: 1
|
||||
@ -206,7 +115,32 @@ MonoBehaviour:
|
||||
m_FlipEdgeVertical: 5
|
||||
m_FlipFillCenter: 3
|
||||
m_FlipDirection: 3
|
||||
--- !u!1 &7794214711916059980
|
||||
--- !u!114 &7000239766839884593
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 3291706644560179776}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 35829408994740a08c32ac2f519442f0, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
_id: 22448871
|
||||
_controller: {fileID: 2727887776950860579}
|
||||
_stateEntries:
|
||||
- State:
|
||||
rid: 6739296021839609878
|
||||
ControllerName: Controller
|
||||
ControllerIndex: 1
|
||||
references:
|
||||
version: 2
|
||||
RefIds:
|
||||
- rid: 6739296021839609878
|
||||
type: {class: GameObjectPropertyStateBase, ns: AlicizaX.UI.Runtime, asm: AlicizaX.UI.Extension}
|
||||
data:
|
||||
--- !u!1 &3529610083755649764
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
@ -214,131 +148,91 @@ GameObject:
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 6299928650586651613}
|
||||
- component: {fileID: 608087438234450738}
|
||||
- component: {fileID: 2143352804330890108}
|
||||
- component: {fileID: 6054056368442846410}
|
||||
- component: {fileID: 1594745885832791941}
|
||||
- component: {fileID: 7369916918447278109}
|
||||
- component: {fileID: 2727887776950860579}
|
||||
m_Layer: 5
|
||||
m_Name: UXTextMeshPro
|
||||
m_Name: UICardWidget
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!224 &6299928650586651613
|
||||
--- !u!224 &6054056368442846410
|
||||
RectTransform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 7794214711916059980}
|
||||
m_GameObject: {fileID: 3529610083755649764}
|
||||
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||
m_LocalPosition: {x: 0, y: 0, z: 0}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children: []
|
||||
m_Father: {fileID: 6054056368442846410}
|
||||
m_Children:
|
||||
- {fileID: 3845232356774058299}
|
||||
m_Father: {fileID: 0}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
m_AnchorMin: {x: 0.5, y: 0.5}
|
||||
m_AnchorMax: {x: 0.5, y: 0.5}
|
||||
m_AnchoredPosition: {x: 0, y: 0}
|
||||
m_SizeDelta: {x: 160, y: 30}
|
||||
m_SizeDelta: {x: 300, y: 500}
|
||||
m_Pivot: {x: 0.5, y: 0.5}
|
||||
--- !u!222 &608087438234450738
|
||||
CanvasRenderer:
|
||||
--- !u!223 &1594745885832791941
|
||||
Canvas:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 7794214711916059980}
|
||||
m_CullTransparentMesh: 1
|
||||
--- !u!114 &2143352804330890108
|
||||
m_GameObject: {fileID: 3529610083755649764}
|
||||
m_Enabled: 1
|
||||
serializedVersion: 3
|
||||
m_RenderMode: 0
|
||||
m_Camera: {fileID: 0}
|
||||
m_PlaneDistance: 100
|
||||
m_PixelPerfect: 0
|
||||
m_ReceivesEvents: 1
|
||||
m_OverrideSorting: 0
|
||||
m_OverridePixelPerfect: 0
|
||||
m_SortingBucketNormalizedSize: 0
|
||||
m_VertexColorAlwaysGammaSpace: 1
|
||||
m_AdditionalShaderChannelsFlag: 31
|
||||
m_UpdateRectTransformForStandalone: 0
|
||||
m_SortingLayerID: 0
|
||||
m_SortingOrder: 0
|
||||
m_TargetDisplay: 0
|
||||
--- !u!114 &7369916918447278109
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 7794214711916059980}
|
||||
m_GameObject: {fileID: 3529610083755649764}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: bd17b8b605f2ba540bac156b5cf5ac77, type: 3}
|
||||
m_Script: {fileID: 11500000, guid: dc42784cf147c0c48a680349fa168899, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
m_Material: {fileID: 0}
|
||||
m_Color: {r: 1, g: 1, b: 1, a: 1}
|
||||
m_RaycastTarget: 1
|
||||
m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
|
||||
m_Maskable: 1
|
||||
m_OnCullStateChanged:
|
||||
m_PersistentCalls:
|
||||
m_Calls: []
|
||||
m_text: Cao NI Ma
|
||||
m_isRightToLeft: 0
|
||||
m_fontAsset: {fileID: 11400000, guid: 8f586378b4e144a9851e7b34d9b748ee, type: 2}
|
||||
m_sharedMaterial: {fileID: 2180264, guid: 8f586378b4e144a9851e7b34d9b748ee, type: 2}
|
||||
m_fontSharedMaterials: []
|
||||
m_fontMaterial: {fileID: 0}
|
||||
m_fontMaterials: []
|
||||
m_fontColor32:
|
||||
m_IgnoreReversedGraphics: 1
|
||||
m_BlockingObjects: 0
|
||||
m_BlockingMask:
|
||||
serializedVersion: 2
|
||||
rgba: 4278190080
|
||||
m_fontColor: {r: 0, g: 0, b: 0, a: 1}
|
||||
m_enableVertexGradient: 0
|
||||
m_colorMode: 3
|
||||
m_fontColorGradient:
|
||||
topLeft: {r: 1, g: 1, b: 1, a: 1}
|
||||
topRight: {r: 1, g: 1, b: 1, a: 1}
|
||||
bottomLeft: {r: 1, g: 1, b: 1, a: 1}
|
||||
bottomRight: {r: 1, g: 1, b: 1, a: 1}
|
||||
m_fontColorGradientPreset: {fileID: 0}
|
||||
m_spriteAsset: {fileID: 0}
|
||||
m_tintAllSprites: 0
|
||||
m_StyleSheet: {fileID: 0}
|
||||
m_TextStyleHashCode: -1183493901
|
||||
m_overrideHtmlColors: 0
|
||||
m_faceColor:
|
||||
serializedVersion: 2
|
||||
rgba: 4294967295
|
||||
m_fontSize: 36
|
||||
m_fontSizeBase: 36
|
||||
m_fontWeight: 400
|
||||
m_enableAutoSizing: 0
|
||||
m_fontSizeMin: 18
|
||||
m_fontSizeMax: 72
|
||||
m_fontStyle: 0
|
||||
m_HorizontalAlignment: 1
|
||||
m_VerticalAlignment: 256
|
||||
m_textAlignment: 65535
|
||||
m_characterSpacing: 0
|
||||
m_wordSpacing: 0
|
||||
m_lineSpacing: 0
|
||||
m_lineSpacingMax: 0
|
||||
m_paragraphSpacing: 0
|
||||
m_charWidthMaxAdj: 0
|
||||
m_enableWordWrapping: 0
|
||||
m_wordWrappingRatios: 0.4
|
||||
m_overflowMode: 0
|
||||
m_linkedTextComponent: {fileID: 0}
|
||||
parentLinkedComponent: {fileID: 0}
|
||||
m_enableKerning: 1
|
||||
m_enableExtraPadding: 0
|
||||
checkPaddingRequired: 0
|
||||
m_isRichText: 1
|
||||
m_parseCtrlCharacters: 1
|
||||
m_isOrthographic: 1
|
||||
m_isCullingEnabled: 0
|
||||
m_horizontalMapping: 0
|
||||
m_verticalMapping: 0
|
||||
m_uvLineOffset: 0
|
||||
m_geometrySortingOrder: 0
|
||||
m_IsTextObjectScaleStatic: 0
|
||||
m_VertexBufferAutoSizeReduction: 0
|
||||
m_useMaxVisibleDescender: 1
|
||||
m_pageToDisplay: 1
|
||||
m_margin: {x: 0, y: 0, z: 0, w: 0}
|
||||
m_isUsingLegacyAnimationComponent: 0
|
||||
m_isVolumetricText: 0
|
||||
m_hasFontAssetChanged: 0
|
||||
m_baseMaterial: {fileID: 0}
|
||||
m_maskOffset: {x: 0, y: 0, z: 0, w: 0}
|
||||
m_localizationID: 0
|
||||
m_localizationKey:
|
||||
m_Bits: 4294967295
|
||||
--- !u!114 &2727887776950860579
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 3529610083755649764}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 5552ea6a0a020954781d0ca2e1944512, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
_controllers:
|
||||
- Name: Controller
|
||||
Length: 2
|
||||
_recorders:
|
||||
- {fileID: 7000239766839884593}
|
||||
@ -13,6 +13,8 @@ GameObject:
|
||||
- component: {fileID: 1839643559262572090}
|
||||
- component: {fileID: 1590262444720639052}
|
||||
- component: {fileID: 8270483239104722155}
|
||||
- component: {fileID: 2333725313425952563}
|
||||
- component: {fileID: 4311531008057825966}
|
||||
m_Layer: 5
|
||||
m_Name: UILoadUpdateWindow
|
||||
m_TagString: Untagged
|
||||
@ -40,7 +42,7 @@ RectTransform:
|
||||
- {fileID: 6056289203651508232}
|
||||
- {fileID: 8277505350050533328}
|
||||
- {fileID: 434786077437263718}
|
||||
- {fileID: 8914933720877746316}
|
||||
- {fileID: 6450832602823646900}
|
||||
m_Father: {fileID: 0}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
m_AnchorMin: {x: 0, y: 0}
|
||||
@ -123,19 +125,191 @@ MonoBehaviour:
|
||||
- {fileID: 2070341851612434847}
|
||||
- {fileID: 3075848250350631692}
|
||||
- {fileID: 6906437026743455523}
|
||||
- {fileID: 1733702088294755917}
|
||||
mImgTestList:
|
||||
- {fileID: 4753672883913794088}
|
||||
- {fileID: 19703495912376014}
|
||||
- {fileID: 4185039174538723206}
|
||||
- {fileID: 1057851142068418248}
|
||||
- {fileID: 6926886040523368586}
|
||||
- {fileID: 4911364472039860152}
|
||||
mCanvasGroupTestList:
|
||||
- {fileID: 8659972988376294145}
|
||||
- {fileID: 188176691515685520}
|
||||
- {fileID: 4563810544014715074}
|
||||
- {fileID: 2060993977344163427}
|
||||
- {fileID: 2080853874740608963}
|
||||
mObjLobb: {fileID: 5770000422433218588}
|
||||
- {fileID: 9040300679633372597}
|
||||
--- !u!114 &2333725313425952563
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 526598954257632073}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 5552ea6a0a020954781d0ca2e1944512, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
_controllers:
|
||||
- Name: Controller0
|
||||
Length: 2
|
||||
_recorders: []
|
||||
--- !u!114 &4311531008057825966
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 526598954257632073}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: f7859f2b76d69e044b4e966f48a3607d, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
AnimationNodes:
|
||||
- rid: 6739296021839609879
|
||||
references:
|
||||
version: 2
|
||||
RefIds:
|
||||
- rid: 6739296021839609879
|
||||
type: {class: EntryNode, ns: AlicizaX.AnimationFlow.Runtime, asm: AlicizaX.AnimationFlow.Runtime}
|
||||
data:
|
||||
elapsedTime: 0
|
||||
Childs: []
|
||||
State: 0
|
||||
nodePos: {x: 208, y: 82}
|
||||
uuid: 052a4cdb-50cb-44b3-876f-90639a028620
|
||||
LoopCount: 1
|
||||
Name:
|
||||
--- !u!1 &1733702088294755917
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 6450832602823646900}
|
||||
- component: {fileID: 7414234782880229079}
|
||||
- component: {fileID: 4911364472039860152}
|
||||
- component: {fileID: 9040300679633372597}
|
||||
m_Layer: 5
|
||||
m_Name: '*Obj#Img#CanvasGroup@Test*4 1'
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!224 &6450832602823646900
|
||||
RectTransform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1733702088294755917}
|
||||
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||
m_LocalPosition: {x: 0, y: 0, z: 0}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children: []
|
||||
m_Father: {fileID: 2553447206821208227}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
m_AnchorMin: {x: 0.5, y: 0.5}
|
||||
m_AnchorMax: {x: 0.5, y: 0.5}
|
||||
m_AnchoredPosition: {x: 0, y: 0}
|
||||
m_SizeDelta: {x: 100, y: 100}
|
||||
m_Pivot: {x: 0.5, y: 0.5}
|
||||
--- !u!222 &7414234782880229079
|
||||
CanvasRenderer:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1733702088294755917}
|
||||
m_CullTransparentMesh: 1
|
||||
--- !u!114 &4911364472039860152
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1733702088294755917}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: af0993b503fa4dd1adf519458df05486, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
m_Material: {fileID: 2100000, guid: 624af9784554f4047997278dfbb22e47, type: 2}
|
||||
m_Color: {r: 1, g: 1, b: 1, a: 1}
|
||||
m_RaycastTarget: 1
|
||||
m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
|
||||
m_Maskable: 1
|
||||
m_OnCullStateChanged:
|
||||
m_PersistentCalls:
|
||||
m_Calls: []
|
||||
m_Sprite: {fileID: 0}
|
||||
m_Type: 0
|
||||
m_PreserveAspect: 0
|
||||
m_FillCenter: 1
|
||||
m_FillMethod: 4
|
||||
m_FillAmount: 1
|
||||
m_FillClockwise: 1
|
||||
m_FillOrigin: 0
|
||||
m_UseSpriteMesh: 0
|
||||
m_PixelsPerUnitMultiplier: 1
|
||||
m_ColorType: 0
|
||||
m_GradientColor:
|
||||
serializedVersion: 2
|
||||
key0: {r: 0, g: 0, b: 0, a: 1}
|
||||
key1: {r: 1, g: 1, b: 1, a: 1}
|
||||
key2: {r: 0, g: 0, b: 0, a: 0}
|
||||
key3: {r: 0, g: 0, b: 0, a: 0}
|
||||
key4: {r: 0, g: 0, b: 0, a: 0}
|
||||
key5: {r: 0, g: 0, b: 0, a: 0}
|
||||
key6: {r: 0, g: 0, b: 0, a: 0}
|
||||
key7: {r: 0, g: 0, b: 0, a: 0}
|
||||
ctime0: 0
|
||||
ctime1: 65535
|
||||
ctime2: 0
|
||||
ctime3: 0
|
||||
ctime4: 0
|
||||
ctime5: 0
|
||||
ctime6: 0
|
||||
ctime7: 0
|
||||
atime0: 0
|
||||
atime1: 65535
|
||||
atime2: 0
|
||||
atime3: 0
|
||||
atime4: 0
|
||||
atime5: 0
|
||||
atime6: 0
|
||||
atime7: 0
|
||||
m_Mode: 0
|
||||
m_ColorSpace: -1
|
||||
m_NumColorKeys: 2
|
||||
m_NumAlphaKeys: 2
|
||||
m_Direction: 0
|
||||
m_OriginFlipMode: 0
|
||||
m_FlipMode: 0
|
||||
m_FlipWithCopy: 1
|
||||
m_FlipEdgeHorizontal: 2
|
||||
m_FlipEdgeVertical: 5
|
||||
m_FlipFillCenter: 3
|
||||
m_FlipDirection: 3
|
||||
--- !u!225 &9040300679633372597
|
||||
CanvasGroup:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1733702088294755917}
|
||||
m_Enabled: 1
|
||||
m_Alpha: 1
|
||||
m_Interactable: 1
|
||||
m_BlocksRaycasts: 1
|
||||
m_IgnoreParentGroups: 0
|
||||
--- !u!1 &2070341851612434847
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
@ -288,7 +462,7 @@ RectTransform:
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 2129114196574798884}
|
||||
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||
m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
|
||||
m_LocalPosition: {x: 0, y: 0, z: 0}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_ConstrainProportionsScale: 0
|
||||
@ -297,8 +471,8 @@ RectTransform:
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
m_AnchorMin: {x: 0.5, y: 0.5}
|
||||
m_AnchorMax: {x: 0.5, y: 0.5}
|
||||
m_AnchoredPosition: {x: -80, y: 103}
|
||||
m_SizeDelta: {x: 160, y: 30}
|
||||
m_AnchoredPosition: {x: -22.212296, y: 84.000015}
|
||||
m_SizeDelta: {x: 275.5753, y: 68}
|
||||
m_Pivot: {x: 0.5, y: 0.5}
|
||||
--- !u!222 &4316998201337448832
|
||||
CanvasRenderer:
|
||||
@ -355,8 +529,8 @@ MonoBehaviour:
|
||||
m_faceColor:
|
||||
serializedVersion: 2
|
||||
rgba: 4294967295
|
||||
m_fontSize: 36
|
||||
m_fontSizeBase: 36
|
||||
m_fontSize: 35.31
|
||||
m_fontSizeBase: 35.31
|
||||
m_fontWeight: 400
|
||||
m_enableAutoSizing: 0
|
||||
m_fontSizeMin: 18
|
||||
@ -904,37 +1078,6 @@ CanvasGroup:
|
||||
m_Interactable: 1
|
||||
m_BlocksRaycasts: 1
|
||||
m_IgnoreParentGroups: 0
|
||||
--- !u!1 &5770000422433218588
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 8914933720877746316}
|
||||
m_Layer: 0
|
||||
m_Name: Obj@Lobb
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!4 &8914933720877746316
|
||||
Transform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 5770000422433218588}
|
||||
serializedVersion: 2
|
||||
m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
|
||||
m_LocalPosition: {x: -4.1347046, y: -12.957336, z: -1.5050803}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children: []
|
||||
m_Father: {fileID: 2553447206821208227}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
--- !u!1 &5796037856908370449
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
113
Client/Assets/Bundles/UI/Window/dsadsa.prefab
Normal file
113
Client/Assets/Bundles/UI/Window/dsadsa.prefab
Normal file
@ -0,0 +1,113 @@
|
||||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!1 &6254794749973378618
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 6727890108811658598}
|
||||
- component: {fileID: 5336894464968040355}
|
||||
- component: {fileID: 5438494893605038134}
|
||||
m_Layer: 0
|
||||
m_Name: Image
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!224 &6727890108811658598
|
||||
RectTransform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 6254794749973378618}
|
||||
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||
m_LocalPosition: {x: 0, y: 0, z: 0}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children: []
|
||||
m_Father: {fileID: 8014054370398145502}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
m_AnchorMin: {x: 0, y: 0}
|
||||
m_AnchorMax: {x: 1, y: 1}
|
||||
m_AnchoredPosition: {x: 0, y: 0}
|
||||
m_SizeDelta: {x: 0, y: 0}
|
||||
m_Pivot: {x: 0.5, y: 0.5}
|
||||
--- !u!222 &5336894464968040355
|
||||
CanvasRenderer:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 6254794749973378618}
|
||||
m_CullTransparentMesh: 1
|
||||
--- !u!114 &5438494893605038134
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 6254794749973378618}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
m_Material: {fileID: 0}
|
||||
m_Color: {r: 1, g: 0, b: 0, a: 1}
|
||||
m_RaycastTarget: 1
|
||||
m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
|
||||
m_Maskable: 1
|
||||
m_OnCullStateChanged:
|
||||
m_PersistentCalls:
|
||||
m_Calls: []
|
||||
m_Sprite: {fileID: 0}
|
||||
m_Type: 0
|
||||
m_PreserveAspect: 0
|
||||
m_FillCenter: 1
|
||||
m_FillMethod: 4
|
||||
m_FillAmount: 1
|
||||
m_FillClockwise: 1
|
||||
m_FillOrigin: 0
|
||||
m_UseSpriteMesh: 0
|
||||
m_PixelsPerUnitMultiplier: 1
|
||||
--- !u!1 &6546920197169975982
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 8014054370398145502}
|
||||
m_Layer: 0
|
||||
m_Name: dsadsa
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!224 &8014054370398145502
|
||||
RectTransform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 6546920197169975982}
|
||||
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||
m_LocalPosition: {x: 0, y: 0, z: 0}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children:
|
||||
- {fileID: 6727890108811658598}
|
||||
m_Father: {fileID: 0}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
m_AnchorMin: {x: 0.5, y: 0.5}
|
||||
m_AnchorMax: {x: 0.5, y: 0.5}
|
||||
m_AnchoredPosition: {x: 0, y: 0}
|
||||
m_SizeDelta: {x: 800, y: 600}
|
||||
m_Pivot: {x: 0.5, y: 0.5}
|
||||
7
Client/Assets/Bundles/UI/Window/dsadsa.prefab.meta
Normal file
7
Client/Assets/Bundles/UI/Window/dsadsa.prefab.meta
Normal file
@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9302214e576d0a649b660ead4c9537e6
|
||||
PrefabImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
1433
Client/Assets/Default.wlt
Normal file
1433
Client/Assets/Default.wlt
Normal file
File diff suppressed because it is too large
Load Diff
7
Client/Assets/Default.wlt.meta
Normal file
7
Client/Assets/Default.wlt.meta
Normal file
@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 002e269efc288864b9fe6b6c51d15935
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -480,6 +480,10 @@ PrefabInstance:
|
||||
propertyPath: m_Name
|
||||
value: UIRoot
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 6941144161958937340, guid: 9368ff38b2090b2468f8358242026e4b, type: 3}
|
||||
propertyPath: m_BlockingMask.m_Bits
|
||||
value: 4294967295
|
||||
objectReference: {fileID: 0}
|
||||
m_RemovedComponents: []
|
||||
m_RemovedGameObjects: []
|
||||
m_AddedGameObjects:
|
||||
|
||||
@ -11,5 +11,16 @@ namespace Game.UI
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
#region Controller
|
||||
public UXController.ControllerHandle CtlLevel { get; private set; }
|
||||
public override void Awake()
|
||||
{
|
||||
base.Awake();
|
||||
var controller = gameObject.GetComponent<UXController>();
|
||||
CtlLevel = controller.GetController("ctlLevel");
|
||||
}
|
||||
#endregion
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -28,21 +28,28 @@ namespace Game.UI
|
||||
public UXTextMeshPro TextGameTitle => mTextGameTitle;
|
||||
|
||||
[SerializeField]
|
||||
private GameObject[] mObjTestList = new GameObject[5];
|
||||
private GameObject[] mObjTestList = new GameObject[6];
|
||||
public GameObject[] ObjTestList => mObjTestList;
|
||||
|
||||
[SerializeField]
|
||||
private UXImage[] mImgTestList = new UXImage[5];
|
||||
private UXImage[] mImgTestList = new UXImage[6];
|
||||
public UXImage[] ImgTestList => mImgTestList;
|
||||
|
||||
[SerializeField]
|
||||
private CanvasGroup[] mCanvasGroupTestList = new CanvasGroup[5];
|
||||
private CanvasGroup[] mCanvasGroupTestList = new CanvasGroup[6];
|
||||
public CanvasGroup[] CanvasGroupTestList => mCanvasGroupTestList;
|
||||
|
||||
[SerializeField]
|
||||
private GameObject mObjLobb;
|
||||
public GameObject ObjLobb => mObjLobb;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Controller
|
||||
public UXController.ControllerHandle Controller0 { get; private set; }
|
||||
public override void Awake()
|
||||
{
|
||||
base.Awake();
|
||||
var controller = gameObject.GetComponent<UXController>();
|
||||
Controller0 = controller.GetController("Controller0");
|
||||
}
|
||||
#endregion
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,12 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using AlicizaX.UI;
|
||||
using UnityEngine;
|
||||
|
||||
public class TestScrollViewHolder : ViewHolder
|
||||
{
|
||||
public override void BindViewData<T>(T data)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3f0ca321f54751b458b8e51698dab5a8
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -1,5 +1,7 @@
|
||||
using System.Collections.Generic;
|
||||
using AlicizaX;
|
||||
using AlicizaX.Resource.Runtime;
|
||||
using AlicizaX.UI;
|
||||
using AlicizaX.UI.Runtime;
|
||||
using Cysharp.Threading.Tasks;
|
||||
using Game.UI;
|
||||
|
||||
Binary file not shown.
Binary file not shown.
File diff suppressed because one or more lines are too long
@ -1 +1 @@
|
||||
Subproject commit d68d1a6d013f0dfd56689a23fbbe0d1dd15bf35d
|
||||
Subproject commit 4317656c64bd9321a6f21ba6aff671d95bd0ed82
|
||||
@ -1 +1 @@
|
||||
Subproject commit 33922c9521ea1cc3552a50750cc7ce3762d11383
|
||||
Subproject commit 0045d5a7e12a76bc3218ae6d3a6eb38e7882d78e
|
||||
@ -1 +1 @@
|
||||
Subproject commit 850dfb7af6d04781430b7a03453ae2758f283061
|
||||
Subproject commit dfef3f519986499082cf9fa0a3efed246678bd95
|
||||
8
Client/Packages/com.alicizax.uxtool/Editor.meta
Normal file
8
Client/Packages/com.alicizax.uxtool/Editor.meta
Normal file
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: bd291d07a2484ac49af2ac1ced61963f
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,26 @@
|
||||
{
|
||||
"name": "AlicizaUXTool.Editor",
|
||||
"rootNamespace": "",
|
||||
"references": [
|
||||
"GUID:760f1778adc613f49a4394fb41ff0bbc",
|
||||
"GUID:5d6fc8d2af5717244b925e7db309edd1",
|
||||
"GUID:fb064c8bf96bac94e90d2f39090daa94",
|
||||
"GUID:1619e00706139ce488ff80c0daeea8e7",
|
||||
"GUID:4d1926c9df5b052469a1c63448b7609a",
|
||||
"GUID:99a2a63c2a1143c4ba448165a98a5108",
|
||||
"GUID:8d62da4aabd2a19419c7378d23ea5849",
|
||||
"GUID:313320016f75fd24e9885858267ed35c",
|
||||
"GUID:189d55e03d78888459720d730f4d2424"
|
||||
],
|
||||
"includePlatforms": [
|
||||
"Editor"
|
||||
],
|
||||
"excludePlatforms": [],
|
||||
"allowUnsafeCode": true,
|
||||
"overrideReferences": false,
|
||||
"precompiledReferences": [],
|
||||
"autoReferenced": true,
|
||||
"defineConstraints": [],
|
||||
"versionDefines": [],
|
||||
"noEngineReferences": false
|
||||
}
|
||||
@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f7a54d99aea1f544194a344d7b71c22b
|
||||
AssemblyDefinitionImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,249 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using AlicizaX.UI;
|
||||
using AlicizaX.UI.Editor;
|
||||
using AlicizaX.UI.Runtime;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
using YooAsset.Editor;
|
||||
|
||||
namespace Aliciza.UXTool
|
||||
{
|
||||
public class AlicizaUXUIGeneratorRuleHelper : IUIGeneratorRuleHelper
|
||||
{
|
||||
public string GetPrivateComponentByNameRule(string regexName, string componentName, EBindType bindType)
|
||||
{
|
||||
var endPrefix = bindType == EBindType.ListCom ? "List" : string.Empty;
|
||||
var common = UIGenerateConfiguration.Instance.UIGenerateCommonData;
|
||||
var endNameIndex = componentName.IndexOf(common.ComCheckEndName, StringComparison.Ordinal);
|
||||
|
||||
var componentSuffix = endNameIndex >= 0 ? componentName.Substring(endNameIndex + 1) : componentName;
|
||||
return $"m{regexName}{componentSuffix}{endPrefix}";
|
||||
}
|
||||
|
||||
public string GetPublicComponentByNameRule(string variableName)
|
||||
{
|
||||
if (string.IsNullOrEmpty(variableName)) return variableName;
|
||||
return variableName.Length > 1 ? variableName.Substring(1) : variableName;
|
||||
}
|
||||
|
||||
public string GetClassGenerateName(GameObject targetObject, UIScriptGenerateData scriptGenerateData)
|
||||
{
|
||||
var config = UIGenerateConfiguration.Instance.UIGenerateCommonData;
|
||||
var prefix = config.GeneratePrefix ?? "ui";
|
||||
return $"{prefix}_{targetObject.name}";
|
||||
}
|
||||
|
||||
public string GetUIResourceSavePath(GameObject targetObject, UIScriptGenerateData scriptGenerateData)
|
||||
{
|
||||
if (targetObject == null) return $"\"{nameof(targetObject)}\"";
|
||||
|
||||
var defaultPath = targetObject.name;
|
||||
var assetPath = UIGenerateQuick.GetPrefabAssetPath(targetObject);
|
||||
|
||||
if (string.IsNullOrEmpty(assetPath) || !assetPath.StartsWith("Assets/", StringComparison.Ordinal))
|
||||
return defaultPath;
|
||||
|
||||
assetPath = assetPath.Replace('\\', '/');
|
||||
return scriptGenerateData.LoadType switch
|
||||
{
|
||||
EUIResLoadType.Resources => GetResourcesPath(assetPath, scriptGenerateData, defaultPath),
|
||||
EUIResLoadType.AssetBundle => GetAssetBundlePath(assetPath, scriptGenerateData, defaultPath),
|
||||
_ => defaultPath
|
||||
};
|
||||
}
|
||||
|
||||
private static string GetResourcesPath(string assetPath, UIScriptGenerateData scriptGenerateData, string defaultPath)
|
||||
{
|
||||
var resourcesRoot = scriptGenerateData.UIPrefabRootPath;
|
||||
var relPath = GetResourcesRelativePath(assetPath, resourcesRoot);
|
||||
|
||||
if (relPath == null)
|
||||
{
|
||||
Debug.LogWarning($"[UI生成] 资源 {assetPath} 不在配置的 Resources 根目录下: {resourcesRoot}");
|
||||
return defaultPath;
|
||||
}
|
||||
|
||||
return relPath;
|
||||
}
|
||||
|
||||
private static string GetAssetBundlePath(string assetPath, UIScriptGenerateData scriptGenerateData, string defaultPath)
|
||||
{
|
||||
try
|
||||
{
|
||||
var defaultPackage = AssetBundleCollectorSettingData.Setting.GetPackage("DefaultPackage");
|
||||
if (defaultPackage?.EnableAddressable == true)
|
||||
return defaultPath;
|
||||
}
|
||||
catch
|
||||
{
|
||||
// 忽略异常,继续处理
|
||||
}
|
||||
|
||||
var bundleRoot = scriptGenerateData.UIPrefabRootPath;
|
||||
if (!assetPath.StartsWith(bundleRoot, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
Debug.LogWarning($"[UI生成] 资源 {assetPath} 不在配置的 AssetBundle 根目录下: {bundleRoot}");
|
||||
return defaultPath;
|
||||
}
|
||||
|
||||
return Path.ChangeExtension(assetPath, null);
|
||||
}
|
||||
|
||||
private static string GetResourcesRelativePath(string assetPath, string resourcesRoot)
|
||||
{
|
||||
if (string.IsNullOrEmpty(assetPath) || string.IsNullOrEmpty(resourcesRoot)) return null;
|
||||
|
||||
assetPath = assetPath.Replace('\\', '/');
|
||||
resourcesRoot = resourcesRoot.Replace('\\', '/');
|
||||
|
||||
if (!assetPath.StartsWith(resourcesRoot, StringComparison.OrdinalIgnoreCase))
|
||||
return null;
|
||||
|
||||
var relPath = assetPath.Substring(resourcesRoot.Length).TrimStart('/');
|
||||
return Path.ChangeExtension(relPath, null);
|
||||
}
|
||||
|
||||
public void WriteUIScriptContent(GameObject targetObject, string className, string scriptContent, UIScriptGenerateData scriptGenerateData)
|
||||
{
|
||||
if (string.IsNullOrEmpty(className)) throw new ArgumentNullException(nameof(className));
|
||||
if (scriptContent == null) throw new ArgumentNullException(nameof(scriptContent));
|
||||
if (scriptGenerateData == null) throw new ArgumentNullException(nameof(scriptGenerateData));
|
||||
|
||||
var scriptFolderPath = scriptGenerateData.GenerateHolderCodePath;
|
||||
var scriptFilePath = Path.Combine(scriptFolderPath, $"{className}.cs");
|
||||
|
||||
Directory.CreateDirectory(scriptFolderPath);
|
||||
UXController controller = targetObject.GetComponent<UXController>();
|
||||
|
||||
if (controller != null && controller.Controllers.Count > 0)
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.Append("\t\t#region Controller");
|
||||
sb.AppendLine();
|
||||
|
||||
foreach (var ctl in controller.Controllers)
|
||||
{
|
||||
string upperName = ctl.Name[0].ToString().ToUpper() + ctl.Name.Substring(1);
|
||||
sb.AppendLine($"\t\tpublic UXController.ControllerHandle {upperName} {{ get; private set; }}");
|
||||
}
|
||||
|
||||
sb.AppendLine("\t\tpublic override void Awake()");
|
||||
sb.AppendLine("\t\t{");
|
||||
sb.AppendLine("\t\t\tbase.Awake();");
|
||||
sb.AppendLine("\t\t\tvar controller = gameObject.GetComponent<UXController>();");
|
||||
foreach (var ctl in controller.Controllers)
|
||||
{
|
||||
string upperName = ctl.Name[0].ToString().ToUpper() + ctl.Name.Substring(1);
|
||||
sb.AppendLine($"\t\t\t{upperName} = controller.GetController(\"{ctl.Name}\");");
|
||||
}
|
||||
|
||||
sb.AppendLine("\t\t}");
|
||||
sb.AppendLine("\t\t#endregion");
|
||||
scriptContent = scriptContent.Replace("#Controller#", sb.ToString());
|
||||
}
|
||||
else
|
||||
{
|
||||
scriptContent = scriptContent.Replace("#Controller#", string.Empty);
|
||||
}
|
||||
|
||||
|
||||
if (File.Exists(scriptFilePath) && IsContentUnchanged(scriptFilePath, scriptContent))
|
||||
{
|
||||
UIScriptGeneratorHelper.BindUIScript();
|
||||
return;
|
||||
}
|
||||
|
||||
File.WriteAllText(scriptFilePath, scriptContent, Encoding.UTF8);
|
||||
AssetDatabase.Refresh();
|
||||
}
|
||||
|
||||
private static bool IsContentUnchanged(string filePath, string newContent)
|
||||
{
|
||||
var oldText = File.ReadAllText(filePath, Encoding.UTF8);
|
||||
return oldText.Equals(newContent, StringComparison.Ordinal);
|
||||
}
|
||||
|
||||
public bool CheckCanGenerate(GameObject targetObject, UIScriptGenerateData scriptGenerateData)
|
||||
{
|
||||
if (targetObject == null || scriptGenerateData == null) return false;
|
||||
|
||||
var assetPath = UIGenerateQuick.GetPrefabAssetPath(targetObject);
|
||||
if (string.IsNullOrEmpty(assetPath) || !assetPath.StartsWith("Assets/", StringComparison.Ordinal))
|
||||
return false;
|
||||
|
||||
assetPath = assetPath.Replace('\\', '/');
|
||||
var isValidPath = assetPath.StartsWith(scriptGenerateData.UIPrefabRootPath, StringComparison.OrdinalIgnoreCase);
|
||||
|
||||
if (!isValidPath)
|
||||
{
|
||||
Debug.LogWarning($"UI存储位置与配置生成规则不符合 请检查对应配置的UIPrefabRootPath\n[AssetPath]{assetPath}\n[ConfigPath]{scriptGenerateData.UIPrefabRootPath}");
|
||||
}
|
||||
|
||||
return isValidPath;
|
||||
}
|
||||
|
||||
public string GetReferenceNamespace(List<UIBindData> uiBindDatas)
|
||||
{
|
||||
var namespaceSet = new HashSet<string>(StringComparer.Ordinal) { "UnityEngine" };
|
||||
|
||||
if (uiBindDatas?.Any(d => d.BindType == EBindType.ListCom) == true)
|
||||
{
|
||||
namespaceSet.Add("System.Collections.Generic");
|
||||
}
|
||||
|
||||
uiBindDatas?
|
||||
.Where(bindData => bindData?.Objs?.FirstOrDefault() != null)
|
||||
.Select(bindData => bindData.GetFirstOrDefaultType().Namespace)
|
||||
.Where(ns => !string.IsNullOrEmpty(ns))
|
||||
.ToList()
|
||||
.ForEach(ns => namespaceSet.Add(ns));
|
||||
|
||||
return string.Join(Environment.NewLine, namespaceSet.Select(ns => $"using {ns};"));
|
||||
}
|
||||
|
||||
public string GetVariableContent(List<UIBindData> uiBindDatas)
|
||||
{
|
||||
if (uiBindDatas == null || uiBindDatas.Count == 0) return string.Empty;
|
||||
|
||||
var variableBuilder = new StringBuilder();
|
||||
var variables = uiBindDatas
|
||||
.Where(b => b != null && !string.IsNullOrEmpty(b.Name))
|
||||
.Select(b => GenerateVariableDeclaration(b))
|
||||
.Where(declaration => !string.IsNullOrEmpty(declaration));
|
||||
|
||||
return string.Join("\n\n", variables);
|
||||
}
|
||||
|
||||
private string GenerateVariableDeclaration(UIBindData bindData)
|
||||
{
|
||||
var variableName = bindData.Name;
|
||||
var publicName = GetPublicComponentByNameRule(variableName);
|
||||
var firstType = bindData.GetFirstOrDefaultType();
|
||||
var typeName = firstType?.Name ?? "Component";
|
||||
|
||||
var declaration = new StringBuilder();
|
||||
declaration.AppendLine("\t\t[SerializeField]");
|
||||
|
||||
switch (bindData.BindType)
|
||||
{
|
||||
case EBindType.None:
|
||||
case EBindType.Widget:
|
||||
declaration.AppendLine($"\t\tprivate {typeName} {variableName};");
|
||||
declaration.Append($"\t\tpublic {typeName} {publicName} => {variableName};");
|
||||
break;
|
||||
|
||||
case EBindType.ListCom:
|
||||
var count = Math.Max(0, bindData.Objs?.Count ?? 0);
|
||||
declaration.AppendLine($"\t\tprivate {typeName}[] {variableName} = new {typeName}[{count}];");
|
||||
declaration.Append($"\t\tpublic {typeName}[] {publicName} => {variableName};");
|
||||
break;
|
||||
}
|
||||
|
||||
return declaration.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 48bb3b6017052894fbcebf49a41e6b61
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
3
Client/Packages/com.alicizax.uxtool/Editor/Base.meta
Normal file
3
Client/Packages/com.alicizax.uxtool/Editor/Base.meta
Normal file
@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 971b3a6d43d84485bc82a92f677c431d
|
||||
timeCreated: 1763975420
|
||||
@ -0,0 +1,98 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using UnityEditorInternal;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Aliciza.UXTool
|
||||
{
|
||||
internal class ScriptableSingleton<T> : ScriptableObject where T : ScriptableObject
|
||||
{
|
||||
private static T s_Instance;
|
||||
|
||||
public static T Instance
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!s_Instance)
|
||||
{
|
||||
LoadOrCreate();
|
||||
}
|
||||
|
||||
return s_Instance;
|
||||
}
|
||||
}
|
||||
|
||||
public static T LoadOrCreate()
|
||||
{
|
||||
string filePath = GetFilePath();
|
||||
if (!string.IsNullOrEmpty(filePath))
|
||||
{
|
||||
var arr = InternalEditorUtility.LoadSerializedFileAndForget(filePath);
|
||||
s_Instance = arr.Length > 0 ? arr[0] as T : s_Instance ?? CreateInstance<T>();
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogError($"save location of {nameof(ScriptableSingleton<T>)} is invalid");
|
||||
}
|
||||
|
||||
return s_Instance;
|
||||
}
|
||||
|
||||
public static void Save(bool saveAsText = true)
|
||||
{
|
||||
if (!s_Instance)
|
||||
{
|
||||
Debug.LogError("Cannot save ScriptableSingleton: no instance!");
|
||||
return;
|
||||
}
|
||||
|
||||
string filePath = GetFilePath();
|
||||
if (!string.IsNullOrEmpty(filePath))
|
||||
{
|
||||
string directoryName = Path.GetDirectoryName(filePath);
|
||||
if (!Directory.Exists(directoryName))
|
||||
{
|
||||
Directory.CreateDirectory(directoryName);
|
||||
}
|
||||
|
||||
UnityEngine.Object[] obj = new T[1] { s_Instance };
|
||||
InternalEditorUtility.SaveToSerializedFileAndForget(obj, filePath, saveAsText);
|
||||
}
|
||||
}
|
||||
|
||||
protected static string GetFilePath()
|
||||
{
|
||||
return typeof(T).GetCustomAttributes(inherit: true)
|
||||
.Where(v => v is FilePathAttribute)
|
||||
.Cast<FilePathAttribute>()
|
||||
.FirstOrDefault()
|
||||
?.filepath;
|
||||
}
|
||||
}
|
||||
|
||||
[AttributeUsage(AttributeTargets.Class)]
|
||||
public class FilePathAttribute : Attribute
|
||||
{
|
||||
internal string filepath;
|
||||
|
||||
/// <summary>
|
||||
/// 单例存放路径
|
||||
/// </summary>
|
||||
/// <param name="path">相对 Project 路径</param>
|
||||
public FilePathAttribute(string path)
|
||||
{
|
||||
if (string.IsNullOrEmpty(path))
|
||||
{
|
||||
throw new ArgumentException("Invalid relative path (it is empty)");
|
||||
}
|
||||
|
||||
if (path[0] == '/')
|
||||
{
|
||||
path = path.Substring(1);
|
||||
}
|
||||
|
||||
filepath = path;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 611899e70b4f424091a58af4e485db0b
|
||||
timeCreated: 1763975423
|
||||
8
Client/Packages/com.alicizax.uxtool/Editor/Common.meta
Normal file
8
Client/Packages/com.alicizax.uxtool/Editor/Common.meta
Normal file
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 11e0c553755e49e9bb95b35bd924cdc6
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: dc242d8f56ba469cac5f90603b46b1a8
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,23 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Aliciza.UXTool
|
||||
{
|
||||
public static partial class ContextMenuUtils
|
||||
{
|
||||
public static class Menu
|
||||
{
|
||||
public static void BuildPrefabTabMenuItem(string guid)
|
||||
{
|
||||
ResetMenu();
|
||||
BuildContextMenu("关闭右侧全部", false,null);
|
||||
BuildContextMenu("关闭左侧全部", false, null);
|
||||
BuildContextMenu("关闭全部", false, null);
|
||||
BuildContextMenu("关闭当前", false, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 835d2b9493e24712925e98a7a3da62a2
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,72 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Aliciza.UXTool
|
||||
{
|
||||
public static partial class ContextMenuUtils
|
||||
{
|
||||
private static GenericMenu contextMenu = new GenericMenu(); // 保存当前菜单
|
||||
private static HashSet<string> menuItems = new HashSet<string>(); // 用于跟踪菜单项,防止重复
|
||||
private static bool menuBuilt = false; // 标记是否已构建菜单
|
||||
|
||||
// 清空现有菜单和菜单项,重新构建菜单
|
||||
static void ResetMenu()
|
||||
{
|
||||
contextMenu = new GenericMenu();
|
||||
menuItems.Clear();
|
||||
menuBuilt = false;
|
||||
}
|
||||
|
||||
// 判断菜单是否为空
|
||||
public static bool IsMenuEmpty()
|
||||
{
|
||||
// 判断contextMenu是否有任何项,若没有,则菜单为空
|
||||
return contextMenu.GetItemCount() == 0; // 如果没有菜单项,返回 true
|
||||
}
|
||||
|
||||
// 显示菜单
|
||||
public static void ShowContextMenu()
|
||||
{
|
||||
if (!menuBuilt) BuildContextMenu(); // 如果菜单尚未构建,则构建菜单
|
||||
if (!IsMenuEmpty()) // 如果菜单项不为空,则显示菜单
|
||||
{
|
||||
contextMenu.ShowAsContext();
|
||||
}
|
||||
ResetMenu(); // 显示完菜单后重置
|
||||
}
|
||||
|
||||
// 构建普通菜单项
|
||||
public static void BuildContextMenu(string label, bool check, GenericMenu.MenuFunction clickEvent)
|
||||
{
|
||||
if (menuItems.Contains(label)) return; // 防止重复添加相同的菜单项
|
||||
contextMenu.AddItem(new GUIContent(label), check, clickEvent);
|
||||
menuItems.Add(label);
|
||||
menuBuilt = true;
|
||||
}
|
||||
|
||||
// 构建带参数的菜单项
|
||||
public static void BuildContextMenuWithArgs(string label, bool check, GenericMenu.MenuFunction2 clickEvent, object arg1)
|
||||
{
|
||||
if (menuItems.Contains(label)) return; // 防止重复添加相同的菜单项
|
||||
contextMenu.AddItem(new GUIContent(label), check, clickEvent, arg1);
|
||||
menuItems.Add(label);
|
||||
menuBuilt = true;
|
||||
}
|
||||
|
||||
// 添加分隔符
|
||||
public static void BuildSeparator()
|
||||
{
|
||||
contextMenu.AddSeparator(string.Empty); // 添加一个通用的分隔符
|
||||
menuBuilt = true;
|
||||
}
|
||||
|
||||
// 为了确保每次显示菜单之前都进行初始化
|
||||
static void BuildContextMenu()
|
||||
{
|
||||
// 在这里可以添加额外的默认菜单项(如果有需要的话)
|
||||
menuBuilt = true; // 表示菜单已经构建
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7ede2a32054642119314d6875e9d8615
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d974406a2b96e2049970825025bdaa38
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,67 @@
|
||||
#if UNITY_EDITOR
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
|
||||
namespace Aliciza.UXTool
|
||||
{
|
||||
public class FindContainerLogic
|
||||
{
|
||||
public static bool ObjectFit(GameObject obj)
|
||||
{
|
||||
if (obj == null) return false;
|
||||
//Graphic[] components = obj.GetComponents<Graphic>();
|
||||
//if (components == null || components.Length == 0) return false;
|
||||
return obj.activeInHierarchy && obj.GetComponent<RectTransform>() != null && obj.GetComponent<StageEngine>() == null && obj != PrefabStageUtils.StageRoot.gameObject;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 只选中一个非 Root的节点时,拖动出来的节点应该和该节点同层级
|
||||
/// 未选中或者选中 根Canvas 节点,拖动出来的节点都在 根Canvas 子节点层级
|
||||
/// 选中多个时 拖动出来的节点在 根Canvas 子节点层级
|
||||
/// </summary>
|
||||
public static Transform GetObjectParent(GameObject[] selection)
|
||||
{
|
||||
var prefabStage = PrefabStageUtils.GetCurrentPrefabStage();
|
||||
if (prefabStage != null)
|
||||
{
|
||||
//Prefab编辑模式下,需要额外区分是否是Canvas (Environment)
|
||||
if (selection.Length == 1
|
||||
&& !selection[0].name.Equals("Canvas (Environment)")
|
||||
&& selection[0].transform != prefabStage.prefabContentsRoot.transform)
|
||||
{
|
||||
return selection[0].transform.parent.transform;
|
||||
}
|
||||
else
|
||||
{
|
||||
return prefabStage.prefabContentsRoot.transform;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (selection.Length == 1)
|
||||
{
|
||||
if (selection[0].transform == selection[0].transform.root)
|
||||
{
|
||||
return selection[0].transform.root;
|
||||
}
|
||||
else
|
||||
{
|
||||
return selection[0].transform.parent.transform;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (Object.FindObjectsOfType<Canvas>().Length == 0)
|
||||
{
|
||||
new GameObject("Canvas", typeof(Canvas), typeof(CanvasScaler), typeof(GraphicRaycaster));
|
||||
}
|
||||
|
||||
return Object.FindObjectsOfType<Canvas>()[0].transform;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 67cc1404d938c9942896d12f3832cff5
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3e53dd6d150f78d4d9239f98eef08fb2
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,91 @@
|
||||
#if UNITY_EDITOR
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
namespace Aliciza.UXTool
|
||||
{
|
||||
//所有需要执行撤销的操作 在这里定义Commond,通过Execute执行Undo记录
|
||||
//目的是为了不让Undo. 代码乱飞
|
||||
public abstract class UXUndoCommand
|
||||
{
|
||||
public abstract void Execute();
|
||||
}
|
||||
|
||||
public class QuickCreateCommand : UXUndoCommand
|
||||
{
|
||||
private GameObject m_quickObj;
|
||||
|
||||
public QuickCreateCommand(GameObject qucikObj)
|
||||
{
|
||||
m_quickObj = qucikObj;
|
||||
}
|
||||
|
||||
public override void Execute()
|
||||
{
|
||||
Undo.IncrementCurrentGroup();
|
||||
Undo.RegisterCreatedObjectUndo(m_quickObj, "Create" + m_quickObj.name);
|
||||
Undo.IncrementCurrentGroup();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public class CombineCommand : UXUndoCommand
|
||||
{
|
||||
private GameObject m_combineRoot;
|
||||
private RectTransform[] m_combineObjects;
|
||||
|
||||
public CombineCommand(GameObject combineRoot, RectTransform[] combineObjects)
|
||||
{
|
||||
m_combineRoot = combineRoot;
|
||||
m_combineObjects = combineObjects;
|
||||
}
|
||||
|
||||
public override void Execute()
|
||||
{
|
||||
//var id = Undo.GetCurrentGroup();
|
||||
Undo.IncrementCurrentGroup();
|
||||
|
||||
Undo.RegisterCreatedObjectUndo(m_combineRoot, "Combine Operation");
|
||||
foreach (var rect in m_combineObjects)
|
||||
{
|
||||
Undo.SetTransformParent(rect.transform, m_combineRoot.transform, "Combine Operation");
|
||||
}
|
||||
|
||||
Undo.IncrementCurrentGroup();
|
||||
}
|
||||
}
|
||||
|
||||
public class AlignCommand : UXUndoCommand
|
||||
{
|
||||
private RectTransform[] m_alignObjects;
|
||||
public AlignCommand(RectTransform[] alignObjects)
|
||||
{
|
||||
m_alignObjects = alignObjects;
|
||||
}
|
||||
|
||||
public override void Execute()
|
||||
{
|
||||
Undo.IncrementCurrentGroup();
|
||||
Undo.RecordObjects(m_alignObjects, "Align Operation");
|
||||
}
|
||||
|
||||
}
|
||||
// public class LocationLineCommand : UXUndoCommand
|
||||
// {
|
||||
// private static LocationLinesData m_LinesData;
|
||||
// //private static TextAsset m_datajson;
|
||||
// private string undoName;
|
||||
// public LocationLineCommand(LocationLinesData linesData, string OperationName)
|
||||
// {
|
||||
// m_LinesData = linesData;
|
||||
// undoName = OperationName;
|
||||
// //m_datajson = AssetDatabase.LoadAssetAtPath<TextAsset>(ThunderFireUIToolConfig.LocationLinesDataPath);
|
||||
// }
|
||||
// public override void Execute()
|
||||
// {
|
||||
// Undo.IncrementCurrentGroup();
|
||||
// Undo.RecordObject(m_LinesData, undoName);
|
||||
// //Undo.RecordObject(m_datajson, undoName);
|
||||
// }
|
||||
// }
|
||||
}
|
||||
#endif
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ee529be6285730e48a42ee2573e5fd8a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 51515c3febc44da9bc96e216fb4f2c52
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
811
Client/Packages/com.alicizax.uxtool/Editor/Common/Utils/Utils.cs
Normal file
811
Client/Packages/com.alicizax.uxtool/Editor/Common/Utils/Utils.cs
Normal file
@ -0,0 +1,811 @@
|
||||
#if UNITY_EDITOR
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
using UnityEditor.SceneManagement;
|
||||
using System.Reflection;
|
||||
using System;
|
||||
using System.IO;
|
||||
using Object = UnityEngine.Object;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using UnityEngine.UIElements;
|
||||
using UnityEditor.UIElements;
|
||||
#if UNITY_2021_3_OR_NEWER
|
||||
using PrefabStageUtility = UnityEditor.SceneManagement.PrefabStageUtility;
|
||||
|
||||
#else
|
||||
using PrefabStageUtility = UnityEditor.Experimental.SceneManagement.PrefabStageUtility;
|
||||
#endif
|
||||
|
||||
namespace Aliciza.UXTool
|
||||
{
|
||||
public static partial class Utils
|
||||
{
|
||||
#if !UNITY_2021_3_OR_NEWER
|
||||
private static readonly float m_WindowToolbarHeight = 21f;
|
||||
private static readonly float m_StageHandlingFixedHeight = 25f;
|
||||
private static readonly float m_TabHeight = 19f;
|
||||
#endif
|
||||
|
||||
#region Reflection
|
||||
|
||||
//反射方法 性能消耗较大 调用后尽量缓存结果
|
||||
/// <summary>
|
||||
/// 有可能引起window重新生成实例 暂时废弃
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
[Obsolete]
|
||||
public static EditorWindow GetGameView()
|
||||
{
|
||||
var type = typeof(Editor).Assembly.GetType("UnityEditor.GameView");
|
||||
var gameview = EditorWindow.GetWindow(type);
|
||||
return gameview;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取GameView对象
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public static object GetMainPlayModeView()
|
||||
{
|
||||
var playModeViewType = System.Type.GetType("UnityEditor.PlayModeView,UnityEditor");
|
||||
var GetMainPlayModeView = playModeViewType.GetMethod("GetMainPlayModeView", BindingFlags.NonPublic | BindingFlags.Static);
|
||||
var view = GetMainPlayModeView.Invoke(null, null);
|
||||
return view;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取所有的GameView对象
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public static Object[] GetPlayViews()
|
||||
{
|
||||
Assembly assembly = typeof(EditorWindow).Assembly;
|
||||
Type type = assembly.GetType("UnityEditor.GameView");
|
||||
return UnityEngine.Resources.FindObjectsOfTypeAll(type);
|
||||
}
|
||||
|
||||
public static EditorWindow GetHierarchyWindow()
|
||||
{
|
||||
var HierarchyViewType = System.Type.GetType("UnityEditor.SceneHierarchyWindow,UnityEditor");
|
||||
|
||||
//var GetSceneHierarchyWindow = HierarchyViewType.GetMethod("GetSceneHierarchyWindowToFocusForNewGameObjects", BindingFlags.NonPublic | BindingFlags.Static);
|
||||
//var hierarchyWindow = GetSceneHierarchyWindow.Invoke(null, null);
|
||||
|
||||
object hierarchyWindow = GetPropertyValue(HierarchyViewType, "lastInteractedHierarchyWindow");
|
||||
EditorWindow window = hierarchyWindow as EditorWindow;
|
||||
|
||||
return window;
|
||||
}
|
||||
|
||||
public static SceneView GetSceneView()
|
||||
{
|
||||
return SceneView.currentDrawingSceneView;
|
||||
}
|
||||
|
||||
public static EditorWindow GetEditorWindow(string EditorWindowClassName)
|
||||
{
|
||||
var assembly = typeof(EditorWindow).Assembly;
|
||||
var type = assembly.GetType(EditorWindowClassName);
|
||||
var editorWindow = EditorWindow.GetWindow(type);
|
||||
return editorWindow;
|
||||
}
|
||||
|
||||
public static Editor GetEditor(Object[] targets, string EditorClassName)
|
||||
{
|
||||
var assembly = typeof(Editor).Assembly;
|
||||
var type = assembly.GetType(EditorClassName);
|
||||
var editor = Editor.CreateEditor(targets, type);
|
||||
return editor;
|
||||
}
|
||||
|
||||
public static Editor GetEditor(Object target, string EditorClassName)
|
||||
{
|
||||
var assembly = typeof(Editor).Assembly;
|
||||
var type = assembly.GetType(EditorClassName);
|
||||
var editor = Editor.CreateEditor(target, type);
|
||||
return editor;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取Editor中的类方法
|
||||
/// </summary>
|
||||
/// <param name="editorClassType"></param>
|
||||
/// <param name="methodName"></param>
|
||||
/// <param name="paraCount">要获取的method的参数个数,用于区分重载方法</param>
|
||||
/// <returns></returns>
|
||||
public static List<MethodInfo> GetEditorMethod(Type editorClassType, string methodName, int paraCount = -1)
|
||||
{
|
||||
List<MethodInfo> m = new List<MethodInfo>();
|
||||
|
||||
MethodInfo[] methods = editorClassType.GetMethods(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static);
|
||||
foreach (var method in methods)
|
||||
{
|
||||
if (method.Name == methodName && method.GetParameters().Length == paraCount)
|
||||
{
|
||||
m.Add(method);
|
||||
}
|
||||
}
|
||||
|
||||
return m;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取没有重载的Editor方法
|
||||
/// </summary>
|
||||
/// <param name="editorClassType"></param>
|
||||
/// <param name="methodName"></param>
|
||||
/// <returns></returns>
|
||||
public static MethodInfo GetEditorMethod(Type editorClassType, string methodName)
|
||||
{
|
||||
MethodInfo[] methods = editorClassType.GetMethods(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static | BindingFlags.Instance);
|
||||
foreach (var method in methods)
|
||||
{
|
||||
if (method.Name == methodName)
|
||||
{
|
||||
return method;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 通过反射调用非静态方法
|
||||
/// </summary>
|
||||
/// <param name="obj">调用对象,若调用静态函数请使用typeof(className)</param>
|
||||
/// <param name="methodName">方法名称</param>
|
||||
/// <param name="parameters">方法的参数,默认为null,表示不传参</param>
|
||||
/// <returns>非静态方法的返回值</returns>
|
||||
public static object InvokeMethod(object obj, string methodName, object[] parameters = null)
|
||||
{
|
||||
if (parameters == null)
|
||||
{
|
||||
return obj.GetType().GetMethod(methodName, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance).Invoke(obj, null);
|
||||
}
|
||||
|
||||
Type[] types = new Type[parameters.Length];
|
||||
for (int i = 0; i < parameters.Length; i++)
|
||||
{
|
||||
types[i] = parameters[i].GetType();
|
||||
}
|
||||
|
||||
return obj.GetType().GetMethod(methodName, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance, null, types, null).Invoke(obj, parameters);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 通过反射调用静态方法
|
||||
/// </summary>
|
||||
/// <param name="type">调用静态函数请使用typeof(className)</param>
|
||||
/// <param name="methodName">方法名称</param>
|
||||
/// <param name="parameters">方法的参数,默认为null,表示不传参</param>
|
||||
/// <returns>静态方法的返回值</returns>
|
||||
public static object InvokeMethod(Type type, string methodName, object[] parameters = null)
|
||||
{
|
||||
if (parameters == null)
|
||||
{
|
||||
return type.GetMethod(methodName, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static).Invoke(null, null);
|
||||
}
|
||||
|
||||
Type[] types = new Type[parameters.Length];
|
||||
for (int i = 0; i < parameters.Length; i++)
|
||||
{
|
||||
types[i] = parameters[i].GetType();
|
||||
}
|
||||
|
||||
return type.GetMethod(methodName, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static, null, types, null).Invoke(null, parameters);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 通过反射获取非静态属性的值
|
||||
/// </summary>
|
||||
/// <param name="obj">调用对象,若调用静态属性请使用typeof(className)</param>
|
||||
/// <param name="propertyName">属性名称</param>
|
||||
/// <param name="index">索引属性的参数,默认为null,表示获取非索引属性</param>
|
||||
/// <returns>非静态属性的值</returns>
|
||||
public static object GetPropertyValue(object obj, string propertyName, object[] index = null)
|
||||
{
|
||||
if (index == null)
|
||||
{
|
||||
return obj.GetType().GetProperty(propertyName, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance).GetValue(obj);
|
||||
}
|
||||
|
||||
Type[] types = new Type[index.Length];
|
||||
for (int i = 0; i < index.Length; i++)
|
||||
{
|
||||
types[i] = index[i].GetType();
|
||||
}
|
||||
|
||||
return obj.GetType().GetProperty(propertyName, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance, null, null, types, null).GetValue(obj, index);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 通过反射获取静态属性的值
|
||||
/// </summary>
|
||||
/// <param name="obj">调用静态属性请使用typeof(className)</param>
|
||||
/// <param name="propertyName">属性名称</param>
|
||||
/// <param name="index">索引属性的参数,默认为null,表示获取非索引属性</param>
|
||||
/// <returns>静态属性的值</returns>
|
||||
public static object GetPropertyValue(Type type, string propertyName, object[] index = null)
|
||||
{
|
||||
if (index == null)
|
||||
{
|
||||
return type.GetProperty(propertyName, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static).GetValue(null);
|
||||
}
|
||||
|
||||
Type[] types = new Type[index.Length];
|
||||
for (int i = 0; i < index.Length; i++)
|
||||
{
|
||||
types[i] = index[i].GetType();
|
||||
}
|
||||
|
||||
return type.GetProperty(propertyName, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static, null, null, types, null).GetValue(null, index);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 通过反射获取非静态变量的值
|
||||
/// </summary>
|
||||
/// <param name="obj">调用对象,若调用静态变量请使用typeof(className)</param>
|
||||
/// <param name="fieldName">变量名称</param>
|
||||
/// <returns>非静态变量的值</returns>
|
||||
public static object GetFieldValue(object obj, string fieldName)
|
||||
{
|
||||
return obj.GetType().GetField(fieldName, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance).GetValue(obj);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 通过反射获取静态变量的值
|
||||
/// </summary>
|
||||
/// <param name="obj">调用静态变量请使用typeof(className)</param>
|
||||
/// <param name="fieldName">变量名称</param>
|
||||
/// <returns>静态变量的值</returns>
|
||||
public static object GetFieldValue(Type type, string fieldName)
|
||||
{
|
||||
return type.GetField(fieldName, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static).GetValue(null);
|
||||
}
|
||||
|
||||
public static Rect GetSceneViewCameraRect()
|
||||
{
|
||||
var type = typeof(SceneView);
|
||||
PropertyInfo info = type.GetProperty("cameraRect", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static);
|
||||
object r = info.GetValue(SceneView.lastActiveSceneView, null);
|
||||
Rect rect = (Rect)r;
|
||||
return rect;
|
||||
}
|
||||
|
||||
public static float GetSceneViewOffest()
|
||||
{
|
||||
#if !UNITY_2021_3_OR_NEWER
|
||||
//if (SceneView.sceneViews.Count == 0)
|
||||
//{
|
||||
// return 19 + 25;
|
||||
//}
|
||||
//
|
||||
//SceneView sceneView = (SceneView)SceneView.sceneViews[0];
|
||||
////SceneView sceneView = SceneView.currentDrawingSceneView;
|
||||
//
|
||||
//var sceneviewtype = typeof(SceneView);
|
||||
//PropertyInfo toolbarHeightInfo = sceneviewtype.GetProperty("toolbarHeight", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static);
|
||||
//float toolbarHeight = (float)toolbarHeightInfo.GetValue(sceneView, null);
|
||||
//
|
||||
//
|
||||
//var dockareaType = typeof(Editor).Assembly.GetType("UnityEditor.DockArea");
|
||||
//FieldInfo kTabHeightInfo = dockareaType.GetField("kTabHeight", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static);
|
||||
//float kTabHeight = (float)kTabHeightInfo.GetValue(null);
|
||||
//
|
||||
//return kTabHeight + toolbarHeight;
|
||||
return m_TabHeight + GetSceneViewToolbarHeight();
|
||||
#else
|
||||
var prefabStage = PrefabStageUtility.GetCurrentPrefabStage();
|
||||
if (prefabStage != null)
|
||||
{
|
||||
return 19 + 46;
|
||||
}
|
||||
else
|
||||
{
|
||||
return 19 + 25;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
public static float GetSceneViewToolbarHeight()
|
||||
{
|
||||
#if !UNITY_2021_3_OR_NEWER
|
||||
//var sceneviewtype = typeof(SceneView);
|
||||
//PropertyInfo toolbarHeightInfo = sceneviewtype.GetProperty("toolbarHeight", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static);
|
||||
//float toolbarHeight = (float)toolbarHeightInfo.GetValue(SceneView.lastActiveSceneView, null);
|
||||
//return toolbarHeight;
|
||||
var prefabStage = PrefabStageUtility.GetCurrentPrefabStage();
|
||||
if (prefabStage != null)
|
||||
{
|
||||
return m_WindowToolbarHeight + m_StageHandlingFixedHeight;
|
||||
}
|
||||
else
|
||||
{
|
||||
return m_WindowToolbarHeight;
|
||||
}
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region GameView
|
||||
|
||||
public static Vector2 GetGameViewSize()
|
||||
{
|
||||
MethodInfo GetSizeOfMainGameView = GetEditorMethod(Type.GetType("UnityEditor.GameView,UnityEditor"), "GetSizeOfMainGameView");
|
||||
if (GetSizeOfMainGameView != null)
|
||||
{
|
||||
var Res = GetSizeOfMainGameView.Invoke(null, null);
|
||||
return (Vector2)Res;
|
||||
}
|
||||
|
||||
return Vector2.zero;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Prefab
|
||||
|
||||
public static void OpenPrefab(string prefabPath)
|
||||
{
|
||||
PrefabStageUtility.OpenPrefab(prefabPath);
|
||||
List<MethodInfo> m = GetEditorMethod(typeof(PrefabStageUtility), "OpenPrefab", 1);
|
||||
m[0].Invoke(null, new object[] { prefabPath });
|
||||
}
|
||||
|
||||
public static void SetCursor(Texture2D texture)
|
||||
{
|
||||
List<MethodInfo> m = GetEditorMethod(typeof(EditorGUIUtility), "SetCurrentViewCursor", 3);
|
||||
m[0].Invoke(null, new object[] { texture, new Vector2(16, 16), MouseCursor.CustomCursor });
|
||||
}
|
||||
|
||||
public static void ClearCurrentViewCursor()
|
||||
{
|
||||
List<MethodInfo> m = GetEditorMethod(typeof(EditorGUIUtility), "ClearCurrentViewCursor", 0);
|
||||
m[0].Invoke(null, null);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Editor State
|
||||
|
||||
public static void ExitPrefabStage()
|
||||
{
|
||||
StageUtility.GoToMainStage();
|
||||
}
|
||||
|
||||
public static void EnterPlayMode()
|
||||
{
|
||||
EditorApplication.isPlaying = true;
|
||||
PlayerPrefs.SetString("previewStage", "true");
|
||||
}
|
||||
|
||||
public static void StopPlayMode()
|
||||
{
|
||||
EditorApplication.isPlaying = false;
|
||||
PlayerPrefs.SetString("previewStage", "false");
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Selecion
|
||||
|
||||
/// <summary>
|
||||
/// 获得Hierarchy中选中且激活的对象。
|
||||
/// </summary>
|
||||
public static GameObject selectionActiveGameObject
|
||||
{
|
||||
get
|
||||
{
|
||||
if (UXSelectionUtil.activeGameObject == null) return null;
|
||||
return UXSelectionUtil.activeGameObject.activeInHierarchy ? UXSelectionUtil.activeGameObject : null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取当前选中对象
|
||||
/// 若选中超过1个以上的对象返回False
|
||||
/// </summary>
|
||||
/// <param name="obj">返回选中对象</param>
|
||||
/// <returns>是否成功获取</returns>
|
||||
public static bool TryGetSelectObject(out GameObject obj)
|
||||
{
|
||||
obj = null;
|
||||
if (selectionActiveGameObject == null || UXSelectionUtil.gameObjects.Length > 1)
|
||||
return false;
|
||||
|
||||
obj = selectionActiveGameObject;
|
||||
//这里要避免选中Prefab中用于预览的Canvas
|
||||
var rect = obj.transform as RectTransform;
|
||||
bool isCanvasEnvironment = obj.transform.parent == null && obj.name == "Canvas(Environment)";
|
||||
return obj != null && !isCanvasEnvironment;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取当前选中对象的RectTransform
|
||||
/// 若选中超过1个以上的对象返回False
|
||||
/// </summary>
|
||||
/// <param name="rect">返回选中对象Rect</param>
|
||||
/// <returns>是否成功获取</returns>
|
||||
public static bool TryGetSelectionRectTransform(out RectTransform rect)
|
||||
{
|
||||
rect = null;
|
||||
if (selectionActiveGameObject == null || UXSelectionUtil.gameObjects.Length > 1)
|
||||
return false;
|
||||
rect = selectionActiveGameObject.transform as RectTransform;
|
||||
//这里要避免选中Prefab中用于预览的Canvas
|
||||
bool isCanvasEnvironment = rect.parent == null && rect.name == "CanvasEnvironment";
|
||||
return rect != null && !isCanvasEnvironment;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取全部选中对象RrectTransform
|
||||
/// </summary>
|
||||
/// <returns>返回列表</returns>
|
||||
public static List<RectTransform> GetAllSelectionRectTransform()
|
||||
{
|
||||
List<RectTransform> rects = new List<RectTransform>();
|
||||
GameObject[] objects = UXSelectionUtil.gameObjects;
|
||||
foreach (var obj in objects)
|
||||
{
|
||||
RectTransform rect = obj.GetComponent<RectTransform>();
|
||||
if (rect != null)
|
||||
rects.Add(rect);
|
||||
}
|
||||
|
||||
return rects;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取当前选中对象的path
|
||||
/// 若选中超过1个以上的对象返回False
|
||||
/// </summary>
|
||||
/// <returns>是否成功获取</returns>
|
||||
public static bool TryGetSelectionPath(out string path)
|
||||
{
|
||||
path = "";
|
||||
if (Selection.assetGUIDs.Length == 1)
|
||||
{
|
||||
path = AssetDatabase.GUIDToAssetPath(Selection.assetGUIDs[0]);
|
||||
}
|
||||
|
||||
return !string.IsNullOrEmpty(path);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public static List<string> GetAllSelectionPath()
|
||||
{
|
||||
return Selection.assetGUIDs.Select(AssetDatabase.GUIDToAssetPath).ToList();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Texutre&Icon
|
||||
|
||||
//生成和缓存 preview图片
|
||||
static Dictionary<string, Texture> m_PreviewDict = new Dictionary<string, Texture>();
|
||||
static Dictionary<string, Texture2D> m_PreviewDict2D = new Dictionary<string, Texture2D>();
|
||||
|
||||
public static Texture GetAssetsPreviewTexture(string guid, int previewSize = 79)
|
||||
{
|
||||
if (!File.Exists(AssetDatabase.GUIDToAssetPath(guid))) return null;
|
||||
|
||||
if (!m_PreviewDict.TryGetValue(guid, out var tex))
|
||||
{
|
||||
tex = GenAssetsPreviewTexture(guid, previewSize);
|
||||
if (tex != null)
|
||||
{
|
||||
m_PreviewDict[guid] = tex;
|
||||
}
|
||||
}
|
||||
|
||||
if (tex == null)
|
||||
{
|
||||
tex = GenAssetsPreviewTexture(guid, previewSize);
|
||||
if (tex != null)
|
||||
m_PreviewDict[guid] = tex;
|
||||
}
|
||||
|
||||
return tex;
|
||||
}
|
||||
|
||||
public static Texture UpdatePreviewTexture(string guid, int previewSize = 79)
|
||||
{
|
||||
var tex = GenAssetsPreviewTexture(guid, previewSize);
|
||||
if (tex != null)
|
||||
m_PreviewDict[guid] = tex;
|
||||
|
||||
return tex;
|
||||
}
|
||||
|
||||
public static Texture GetAssetsNewPreviewTexture(string guid, int previewSize = 79)
|
||||
{
|
||||
Texture tex = GenAssetsPreviewTexture(guid, previewSize);
|
||||
if (tex != null)
|
||||
{
|
||||
m_PreviewDict[guid] = tex;
|
||||
}
|
||||
|
||||
return tex;
|
||||
}
|
||||
|
||||
public static Texture2D GetAssetsPreviewTexture2D(string guid, int previewSize = 79)
|
||||
{
|
||||
if (!m_PreviewDict2D.TryGetValue(guid, out var tex))
|
||||
{
|
||||
Texture tex1 = GetAssetsPreviewTexture(guid, previewSize);
|
||||
if (tex1 != null)
|
||||
{
|
||||
tex = TextureToTexture2D(tex1);
|
||||
m_PreviewDict2D[guid] = tex;
|
||||
}
|
||||
}
|
||||
|
||||
return tex;
|
||||
}
|
||||
|
||||
private static Texture2D TextureToTexture2D(Texture texture)
|
||||
{
|
||||
Texture2D texture2D = new Texture2D(texture.width, texture.height, TextureFormat.RGBA32, false);
|
||||
RenderTexture currentRT = RenderTexture.active;
|
||||
RenderTexture renderTexture = RenderTexture.GetTemporary(texture.width, texture.height, 32);
|
||||
Graphics.Blit(texture, renderTexture);
|
||||
|
||||
RenderTexture.active = renderTexture;
|
||||
texture2D.ReadPixels(new Rect(0, 0, renderTexture.width, renderTexture.height), 0, 0);
|
||||
texture2D.Apply();
|
||||
|
||||
RenderTexture.active = currentRT;
|
||||
RenderTexture.ReleaseTemporary(renderTexture);
|
||||
|
||||
return texture2D;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 生成prefab的预览图,
|
||||
/// </summary>
|
||||
/// <param name="obj"></param>
|
||||
/// <param name="previewSize"></param>
|
||||
/// <returns></returns>
|
||||
public static Texture GenAssetsPreviewTexture(string guid, int previewSize = 79)
|
||||
{
|
||||
// if (EditorApplication.isPlaying)
|
||||
// {
|
||||
// return null;
|
||||
// }
|
||||
|
||||
string path = AssetDatabase.GUIDToAssetPath(guid);
|
||||
GameObject obj = AssetDatabase.LoadAssetAtPath<GameObject>(path);
|
||||
|
||||
GameObject canvas = new GameObject("UXRenderCanvas", typeof(Canvas));
|
||||
GameObject cameraObj = new GameObject("UXRenderCamera", typeof(Camera));
|
||||
canvas.transform.position = new Vector3(10000, 10000, 10000);
|
||||
canvas.GetComponent<RectTransform>().sizeDelta = new Vector2(Screen.currentResolution.width, Screen.currentResolution.height);
|
||||
|
||||
GameObject go = GameObject.Instantiate(obj, canvas.transform);
|
||||
|
||||
Bounds bound = GetBounds(go);
|
||||
|
||||
cameraObj.transform.position = new Vector3((bound.max.x + bound.min.x) / 2, (bound.max.y + bound.min.y) / 2, (bound.max.z + bound.min.z) / 2 - 100);
|
||||
cameraObj.transform.LookAt(cameraObj.transform.position);
|
||||
|
||||
Camera camera = cameraObj.GetComponent<Camera>();
|
||||
camera.cameraType = CameraType.SceneView;
|
||||
camera.orthographic = true;
|
||||
camera.clearFlags = CameraClearFlags.SolidColor;
|
||||
camera.backgroundColor = new Color(0, 0, 0, 0f);
|
||||
|
||||
float width = bound.max.x - bound.min.x;
|
||||
float height = bound.max.y - bound.min.y;
|
||||
float max_camera_size = (width > height ? width : height) + 10;
|
||||
camera.orthographicSize = max_camera_size / 2;
|
||||
|
||||
RenderTexture rt = RenderTexture.GetTemporary(previewSize, previewSize, 24);
|
||||
camera.targetTexture = rt;
|
||||
camera.RenderDontRestore();
|
||||
|
||||
RenderTexture tex = new RenderTexture(previewSize, previewSize, 0, RenderTextureFormat.Default);
|
||||
Graphics.Blit(rt, tex);
|
||||
|
||||
//Texture2D tex = new Texture2D(previewSize, previewSize, TextureFormat.ARGB32, false);
|
||||
//tex.ReadPixels(new Rect(0, 0, previewSize, previewSize), 0, 0);
|
||||
//tex.Apply();
|
||||
|
||||
RenderTexture.active = null;
|
||||
camera.targetTexture = null;
|
||||
rt.Release();
|
||||
RenderTexture.ReleaseTemporary(rt);
|
||||
|
||||
Object.DestroyImmediate(canvas);
|
||||
Object.DestroyImmediate(cameraObj);
|
||||
|
||||
return tex;
|
||||
}
|
||||
|
||||
public static Bounds GetBounds(GameObject obj)
|
||||
{
|
||||
Vector3 Min = new Vector3(99999, 99999, 99999);
|
||||
Vector3 Max = new Vector3(-99999, -99999, -99999);
|
||||
MeshRenderer[] renders = obj.GetComponentsInChildren<MeshRenderer>();
|
||||
if (renders.Length > 0)
|
||||
{
|
||||
for (int i = 0; i < renders.Length; i++)
|
||||
{
|
||||
if (renders[i].bounds.min.x < Min.x)
|
||||
Min.x = renders[i].bounds.min.x;
|
||||
if (renders[i].bounds.min.y < Min.y)
|
||||
Min.y = renders[i].bounds.min.y;
|
||||
if (renders[i].bounds.min.z < Min.z)
|
||||
Min.z = renders[i].bounds.min.z;
|
||||
|
||||
if (renders[i].bounds.max.x > Max.x)
|
||||
Max.x = renders[i].bounds.max.x;
|
||||
if (renders[i].bounds.max.y > Max.y)
|
||||
Max.y = renders[i].bounds.max.y;
|
||||
if (renders[i].bounds.max.z > Max.z)
|
||||
Max.z = renders[i].bounds.max.z;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
RectTransform[] rectTrans = obj.GetComponentsInChildren<RectTransform>();
|
||||
Vector3[] corner = new Vector3[4];
|
||||
for (int i = 0; i < rectTrans.Length; i++)
|
||||
{
|
||||
//获取节点的四个角的世界坐标,分别按顺序为左下左上,右上右下
|
||||
rectTrans[i].GetWorldCorners(corner);
|
||||
if (corner[0].x < Min.x)
|
||||
Min.x = corner[0].x;
|
||||
if (corner[0].y < Min.y)
|
||||
Min.y = corner[0].y;
|
||||
if (corner[0].z < Min.z)
|
||||
Min.z = corner[0].z;
|
||||
|
||||
if (corner[2].x > Max.x)
|
||||
Max.x = corner[2].x;
|
||||
if (corner[2].y > Max.y)
|
||||
Max.y = corner[2].y;
|
||||
if (corner[2].z > Max.z)
|
||||
Max.z = corner[2].z;
|
||||
}
|
||||
}
|
||||
|
||||
Vector3 center = (Min + Max) / 2;
|
||||
Vector3 size = new Vector3(Max.x - Min.x, Max.y - Min.y, Max.z - Min.z);
|
||||
return new Bounds(center, size);
|
||||
}
|
||||
|
||||
public static void DrawGreenRect(int instanceID, Rect selectionRect, string text)
|
||||
{
|
||||
GameObject go = EditorUtility.InstanceIDToObject(instanceID) as GameObject;
|
||||
Rect rect = new Rect(selectionRect)
|
||||
{
|
||||
width = selectionRect.width + (PrefabUtility.IsAnyPrefabInstanceRoot(go) ? 0 : 20)
|
||||
};
|
||||
EditorGUI.DrawRect(rect, new Color(0.157f, 0.157f, 0.157f, 1f));
|
||||
GUI.Label(selectionRect, PrefabUtility.GetIconForGameObject(go));
|
||||
GUI.Label(new Rect(selectionRect) { x = selectionRect.x + 20 },
|
||||
text, new GUIStyle() { normal = { textColor = Color.green } });
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Regex
|
||||
|
||||
public static bool CheckHasAlphanumeric(string input)
|
||||
{
|
||||
const string pattern = "^[a-zA-Z0-9_]+$";
|
||||
return Regex.IsMatch(input, pattern);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
// #region Panel
|
||||
// public static string SelectFolder(bool needUnderAssets = true)
|
||||
// {
|
||||
// string folderPath = PlayerPrefs.GetString("LastParticleCheckPath");
|
||||
// string path = EditorUtility.OpenFolderPanel(EditorLocalization.GetLocalization(EditorLocalizationStorage.Def_选择路径), folderPath, "");
|
||||
// if (path != "")
|
||||
// {
|
||||
// int index = path.IndexOf("Assets");
|
||||
// if (index != -1 || !needUnderAssets)
|
||||
// {
|
||||
// PlayerPrefs.SetString("LastParticleCheckPath", path);
|
||||
// if (needUnderAssets)
|
||||
// {
|
||||
// path = path.Substring(index);
|
||||
// }
|
||||
// return path + "/";
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// EditorUtility.DisplayDialog("messageBox",
|
||||
// EditorLocalization.GetLocalization(EditorLocalizationStorage.Def_目录不在Assets下Tip),
|
||||
// EditorLocalization.GetLocalization(EditorLocalizationStorage.Def_确定));
|
||||
// }
|
||||
// }
|
||||
// return null;
|
||||
// }
|
||||
//
|
||||
// public static string SelectFile()
|
||||
// {
|
||||
// string filePath = PlayerPrefs.GetString("LastParticleCheckPath");
|
||||
// string path = EditorUtility.OpenFilePanel(EditorLocalization.GetLocalization(EditorLocalizationStorage.Def_选择文件), filePath, "");
|
||||
// if (path != "")
|
||||
// {
|
||||
// int index = path.IndexOf("Assets");
|
||||
// if (index != -1)
|
||||
// {
|
||||
// PlayerPrefs.SetString("LastParticleCheckPath", path);
|
||||
// return path.Substring(index);
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// EditorUtility.DisplayDialog("messageBox",
|
||||
// EditorLocalization.GetLocalization(EditorLocalizationStorage.Def_目录不在Assets下Tip),
|
||||
// EditorLocalization.GetLocalization(EditorLocalizationStorage.Def_确定));
|
||||
// }
|
||||
// }
|
||||
// return null;
|
||||
// }
|
||||
//
|
||||
// public static string SaveFile(string defaultName = "", string extension = "")
|
||||
// {
|
||||
// string filePath = PlayerPrefs.GetString("LastParticleCheckPath");
|
||||
// string path = EditorUtility.SaveFilePanel(EditorLocalization.GetLocalization(EditorLocalizationStorage.Def_保存), filePath, defaultName, extension);
|
||||
// if (path != "")
|
||||
// {
|
||||
// int index = path.IndexOf("Assets");
|
||||
// if (index != -1)
|
||||
// {
|
||||
// PlayerPrefs.SetString("LastParticleCheckPath", path);
|
||||
// return path.Substring(index);
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// EditorUtility.DisplayDialog("messageBox",
|
||||
// EditorLocalization.GetLocalization(EditorLocalizationStorage.Def_目录不在Assets下Tip),
|
||||
// EditorLocalization.GetLocalization(EditorLocalizationStorage.Def_确定));
|
||||
// }
|
||||
// }
|
||||
// return null;
|
||||
// }
|
||||
// #endregion
|
||||
|
||||
public static int EnumPopupEx(Rect rect, string label, Type type, int enumValueIndex, string[] labels)
|
||||
{
|
||||
int[] ints = (int[])Enum.GetValues(type);
|
||||
string[] strings = Enum.GetNames(type);
|
||||
if (labels.Length != ints.Length)
|
||||
{
|
||||
return EditorGUI.IntPopup(rect, label, enumValueIndex, strings, ints);
|
||||
}
|
||||
else
|
||||
{
|
||||
return EditorGUI.IntPopup(rect, label, enumValueIndex, labels, ints);
|
||||
}
|
||||
}
|
||||
|
||||
public static int EnumPopupLayoutEx(string label, Type type, int enumValueIndex, string[] labels)
|
||||
{
|
||||
int[] ints = (int[])Enum.GetValues(type);
|
||||
string[] strings = Enum.GetNames(type);
|
||||
if (labels.Length != ints.Length)
|
||||
{
|
||||
return EditorGUILayout.IntPopup(label, enumValueIndex, strings, ints);
|
||||
}
|
||||
else
|
||||
{
|
||||
return EditorGUILayout.IntPopup(label, enumValueIndex, labels, ints);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a90c428d74014a66a7e8c0c3faacd176
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
8
Client/Packages/com.alicizax.uxtool/Editor/Config.meta
Normal file
8
Client/Packages/com.alicizax.uxtool/Editor/Config.meta
Normal file
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b38dd3e8fb89442baf34c39c99abf56d
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,37 @@
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using UnityEditor;
|
||||
|
||||
namespace Aliciza.UXTool
|
||||
{
|
||||
[System.Serializable]
|
||||
[FilePath("ProjectSettings/UXPrefabTabsConfig.asset")]
|
||||
internal class UXPrefabTabsConfig : ScriptableSingleton<UXPrefabTabsConfig>
|
||||
{
|
||||
public List<string> tabs = new();
|
||||
|
||||
public void SyncTabs()
|
||||
{
|
||||
var dirty = false;
|
||||
for (int i = 0; i < tabs.Count; i++)
|
||||
{
|
||||
string assetPath = AssetDatabase.GUIDToAssetPath(tabs[i]);
|
||||
#if UNITY_6000_OR_NEWER
|
||||
if (string.IsNullOrEmpty(assetPath) || !AssetDatabase.AssetPathExists(assetPath))
|
||||
{
|
||||
tabs.RemoveAt(i);
|
||||
dirty = true;
|
||||
}
|
||||
#else
|
||||
if (string.IsNullOrEmpty(assetPath) || !File.Exists(assetPath))
|
||||
{
|
||||
tabs.RemoveAt(i);
|
||||
dirty = true;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
if (dirty) Save();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c9f1c3d755f245759b4595b65b5e576e
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
8
Client/Packages/com.alicizax.uxtool/Editor/Def.meta
Normal file
8
Client/Packages/com.alicizax.uxtool/Editor/Def.meta
Normal file
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f206d2f1a2fd4f81b95496801251b88c
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,14 @@
|
||||
using System.IO;
|
||||
|
||||
namespace Aliciza.UXTool
|
||||
{
|
||||
public static class Def_UXGUIPath
|
||||
{
|
||||
public static readonly string ConfigPath = "Packages/com.alicizax.uxtool/Editor/UXGUI/Res/Config";
|
||||
|
||||
public static readonly string ComponentRes = "Packages/com.alicizax.uxtool/Editor/UXGUI/Res/Component/";
|
||||
|
||||
public static readonly string UIResRootPath = "Assets/Bundles/UI";
|
||||
public static readonly string DefaultLayoutPath = "Assets/Default.wlt";
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8ef1696f4c864c8c840cb2e637e72189
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
8
Client/Packages/com.alicizax.uxtool/Editor/UXGUI.meta
Normal file
8
Client/Packages/com.alicizax.uxtool/Editor/UXGUI.meta
Normal file
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3f498c2f64bbd1e4589a9c8058380495
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e45fc341a3c39f547b39cce778b1c136
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9768513809263534088a2f32ed7c34f5
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,92 @@
|
||||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!1 &6407046066269117143
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 6186567549948268087}
|
||||
- component: {fileID: 2482112926894068853}
|
||||
- component: {fileID: 7955690355910783094}
|
||||
- component: {fileID: 9148593535310968807}
|
||||
m_Layer: 5
|
||||
m_Name: UIStage
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!224 &6186567549948268087
|
||||
RectTransform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 6407046066269117143}
|
||||
m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
|
||||
m_LocalPosition: {x: 0, y: 0, z: 0}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children: []
|
||||
m_Father: {fileID: 0}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
m_AnchorMin: {x: 0, y: 0}
|
||||
m_AnchorMax: {x: 1, y: 1}
|
||||
m_AnchoredPosition: {x: 0, y: 0}
|
||||
m_SizeDelta: {x: 0, y: 0}
|
||||
m_Pivot: {x: 0.5, y: 0.5}
|
||||
--- !u!223 &2482112926894068853
|
||||
Canvas:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 6407046066269117143}
|
||||
m_Enabled: 1
|
||||
serializedVersion: 3
|
||||
m_RenderMode: 1
|
||||
m_Camera: {fileID: 0}
|
||||
m_PlaneDistance: 100
|
||||
m_PixelPerfect: 0
|
||||
m_ReceivesEvents: 1
|
||||
m_OverrideSorting: 0
|
||||
m_OverridePixelPerfect: 0
|
||||
m_SortingBucketNormalizedSize: 0
|
||||
m_VertexColorAlwaysGammaSpace: 1
|
||||
m_AdditionalShaderChannelsFlag: 27
|
||||
m_UpdateRectTransformForStandalone: 0
|
||||
m_SortingLayerID: 0
|
||||
m_SortingOrder: 0
|
||||
m_TargetDisplay: 0
|
||||
--- !u!114 &7955690355910783094
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 6407046066269117143}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: dc42784cf147c0c48a680349fa168899, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
m_IgnoreReversedGraphics: 1
|
||||
m_BlockingObjects: 0
|
||||
m_BlockingMask:
|
||||
serializedVersion: 2
|
||||
m_Bits: 4294967295
|
||||
--- !u!114 &9148593535310968807
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 6407046066269117143}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 21d71bc4606dce64fb65869d11d782df, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0d29cbf8bba5fb34a87e59a5b6ead9a9
|
||||
PrefabImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,155 @@
|
||||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!1 &1398347742421458328
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 4677800691646643621}
|
||||
- component: {fileID: 5154831573443284405}
|
||||
- component: {fileID: 4279721120777329907}
|
||||
m_Layer: 0
|
||||
m_Name: View
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!224 &4677800691646643621
|
||||
RectTransform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1398347742421458328}
|
||||
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||
m_LocalPosition: {x: 0, y: 0, z: 0}
|
||||
m_LocalScale: {x: 0, y: 0, z: 0}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children:
|
||||
- {fileID: 1431218276088536921}
|
||||
m_Father: {fileID: 0}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
m_AnchorMin: {x: 0, y: 0}
|
||||
m_AnchorMax: {x: 1, y: 1}
|
||||
m_AnchoredPosition: {x: 0, y: 0}
|
||||
m_SizeDelta: {x: 0, y: 0}
|
||||
m_Pivot: {x: 0, y: 0}
|
||||
--- !u!223 &5154831573443284405
|
||||
Canvas:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1398347742421458328}
|
||||
m_Enabled: 1
|
||||
serializedVersion: 3
|
||||
m_RenderMode: 0
|
||||
m_Camera: {fileID: 0}
|
||||
m_PlaneDistance: 100
|
||||
m_PixelPerfect: 0
|
||||
m_ReceivesEvents: 1
|
||||
m_OverrideSorting: 0
|
||||
m_OverridePixelPerfect: 0
|
||||
m_SortingBucketNormalizedSize: 0
|
||||
m_VertexColorAlwaysGammaSpace: 1
|
||||
m_AdditionalShaderChannelsFlag: 0
|
||||
m_UpdateRectTransformForStandalone: 0
|
||||
m_SortingLayerID: 0
|
||||
m_SortingOrder: 0
|
||||
m_TargetDisplay: 0
|
||||
--- !u!114 &4279721120777329907
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1398347742421458328}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: dc42784cf147c0c48a680349fa168899, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
m_IgnoreReversedGraphics: 1
|
||||
m_BlockingObjects: 0
|
||||
m_BlockingMask:
|
||||
serializedVersion: 2
|
||||
m_Bits: 4294967295
|
||||
--- !u!1 &8524026683550576887
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 1431218276088536921}
|
||||
- component: {fileID: 4882821455657707705}
|
||||
- component: {fileID: 2892262279528891506}
|
||||
m_Layer: 0
|
||||
m_Name: Image
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!224 &1431218276088536921
|
||||
RectTransform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 8524026683550576887}
|
||||
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||
m_LocalPosition: {x: 0, y: 0, z: 0}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children: []
|
||||
m_Father: {fileID: 4677800691646643621}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
m_AnchorMin: {x: 0, y: 0}
|
||||
m_AnchorMax: {x: 1, y: 1}
|
||||
m_AnchoredPosition: {x: 0, y: 0}
|
||||
m_SizeDelta: {x: 0, y: 0}
|
||||
m_Pivot: {x: 0.5, y: 0.5}
|
||||
--- !u!222 &4882821455657707705
|
||||
CanvasRenderer:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 8524026683550576887}
|
||||
m_CullTransparentMesh: 1
|
||||
--- !u!114 &2892262279528891506
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 8524026683550576887}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
m_Material: {fileID: 0}
|
||||
m_Color: {r: 0.3962264, g: 0.3962264, b: 0.3962264, a: 1}
|
||||
m_RaycastTarget: 1
|
||||
m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
|
||||
m_Maskable: 1
|
||||
m_OnCullStateChanged:
|
||||
m_PersistentCalls:
|
||||
m_Calls: []
|
||||
m_Sprite: {fileID: 0}
|
||||
m_Type: 0
|
||||
m_PreserveAspect: 0
|
||||
m_FillCenter: 1
|
||||
m_FillMethod: 4
|
||||
m_FillAmount: 1
|
||||
m_FillClockwise: 1
|
||||
m_FillOrigin: 0
|
||||
m_UseSpriteMesh: 0
|
||||
m_PixelsPerUnitMultiplier: 1
|
||||
@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3b558d5df13733448af9e24b0c779294
|
||||
PrefabImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0fdf05c5b631e734ba52458209687d6a
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4216e337fd7940249a555a2f0543a2da
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8db0bb683a284f3192668f83cf1cdf98
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 34949e2402051a244b0ba35d0dba372a
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,987 @@
|
||||
#if UNITY_EDITOR
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
using System.Collections.Generic;
|
||||
using UnityEditor.SceneManagement;
|
||||
using UnityEngine.SceneManagement;
|
||||
using UnityEngine.UIElements;
|
||||
using System.Linq;
|
||||
using UnityEngine.UI;
|
||||
|
||||
namespace Aliciza.UXTool
|
||||
{
|
||||
public static class SceneViewContextMenu
|
||||
{
|
||||
// 右键状态和拖动状态
|
||||
private static bool rightMouseDown = false;
|
||||
private static bool hasDrag = false;
|
||||
|
||||
// 右键菜单的委托,允许外部插件扩展菜单
|
||||
public delegate void AddContextMenuFunc();
|
||||
|
||||
public static AddContextMenuFunc addContextMenuFunc;
|
||||
|
||||
// 用于复制/粘贴RectTransform数据的缓存
|
||||
private static RectTransformCache clipboard = null;
|
||||
|
||||
[InitializeOnLoadMethod]
|
||||
public static void Init()
|
||||
{
|
||||
// 在SceneView中注册事件回调
|
||||
if (UXDesinUtil.InDesign)
|
||||
{
|
||||
SceneView.duringSceneGui += OnSceneGUI;
|
||||
}
|
||||
else
|
||||
{
|
||||
SceneView.duringSceneGui -= OnSceneGUI;
|
||||
}
|
||||
}
|
||||
|
||||
static void OnSceneGUI(SceneView sceneView)
|
||||
{
|
||||
if (PrefabStageUtils.InEmptyStage) return;
|
||||
|
||||
// 如果不是右键点击,直接返回
|
||||
if (Event.current == null || Event.current.button != 1)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// 右键按下,初始化状态
|
||||
if (Event.current.type == EventType.MouseDown)
|
||||
{
|
||||
hasDrag = false;
|
||||
rightMouseDown = true;
|
||||
return;
|
||||
}
|
||||
|
||||
// 拖动过程中标记状态
|
||||
if (Event.current.type == EventType.MouseDrag && rightMouseDown)
|
||||
{
|
||||
hasDrag = true;
|
||||
return;
|
||||
}
|
||||
|
||||
// 右键松开时弹出菜单
|
||||
if (Event.current.type == EventType.MouseUp && rightMouseDown && !hasDrag)
|
||||
{
|
||||
HandleRightClick();
|
||||
Event.current.Use(); // 防止事件进一步传播
|
||||
}
|
||||
}
|
||||
|
||||
// 右键菜单生成和显示的核心方法
|
||||
private static void HandleRightClick()
|
||||
{
|
||||
// 生成右键菜单项
|
||||
GenerateRectTransformMenu();
|
||||
GenerateSelectObjectMenu();
|
||||
|
||||
// 调用外部扩展菜单
|
||||
addContextMenuFunc?.Invoke();
|
||||
|
||||
// 显示菜单
|
||||
ContextMenuUtils.ShowContextMenu(); // 调用修改后的 ShowContextMenu
|
||||
|
||||
// 重置状态
|
||||
rightMouseDown = false;
|
||||
}
|
||||
|
||||
#region RectTransform 操作菜单
|
||||
|
||||
private static void GenerateRectTransformMenu()
|
||||
{
|
||||
if (UXSelectionUtil.gameObjects == null || UXSelectionUtil.gameObjects.Length == 0) return;
|
||||
if (!(UXSelectionUtil.gameObjects[0].transform is RectTransform))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
GameObject[] selected = UXSelectionUtil.gameObjects;
|
||||
|
||||
// 添加分隔符
|
||||
ContextMenuUtils.BuildSeparator();
|
||||
|
||||
// 添加通用菜单项
|
||||
AddCommonItem();
|
||||
|
||||
// 布局与对齐
|
||||
ContextMenuUtils.BuildSeparator();
|
||||
ContextMenuUtils.BuildContextMenu("对齐/Left", false, () => AlignSelected(AlignType.Left));
|
||||
ContextMenuUtils.BuildContextMenu("对齐/Center (水平)", false, () => AlignSelected(AlignType.CenterHorizontal));
|
||||
ContextMenuUtils.BuildContextMenu("对齐/Right", false, () => AlignSelected(AlignType.Right));
|
||||
ContextMenuUtils.BuildContextMenu("对齐/Top", false, () => AlignSelected(AlignType.Top));
|
||||
ContextMenuUtils.BuildContextMenu("对齐/Middle (垂直)", false, () => AlignSelected(AlignType.CenterVertical));
|
||||
ContextMenuUtils.BuildContextMenu("对齐/Bottom", false, () => AlignSelected(AlignType.Bottom));
|
||||
|
||||
ContextMenuUtils.BuildSeparator();
|
||||
ContextMenuUtils.BuildContextMenu("分发/水平分发", false, () => DistributeSelected(DistributeAxis.Horizontal));
|
||||
ContextMenuUtils.BuildContextMenu("分发/垂直分发", false, () => DistributeSelected(DistributeAxis.Vertical));
|
||||
|
||||
ContextMenuUtils.BuildSeparator();
|
||||
ContextMenuUtils.BuildContextMenu("大小/匹配第一个尺寸", false, () => MatchSizeToFirst());
|
||||
ContextMenuUtils.BuildContextMenu("像素对齐/位置取整", false, () => PixelSnapSelected());
|
||||
ContextMenuUtils.BuildContextMenu("重置/位置与旋转与缩放", false, () => ResetTransformSelected());
|
||||
|
||||
ContextMenuUtils.BuildSeparator();
|
||||
ContextMenuUtils.BuildContextMenu("锚点/设为四角(保持位置)", false, () => SetAnchorsToCornersKeepPos());
|
||||
ContextMenuUtils.BuildContextMenu("锚点/居中锚点(保持位置)", false, () => SetAnchorsToCenterKeepPos());
|
||||
|
||||
ContextMenuUtils.BuildSeparator();
|
||||
ContextMenuUtils.BuildContextMenu("临时操作/复制 RectTransform", false, () => CopyRectTransform());
|
||||
ContextMenuUtils.BuildContextMenu("临时操作/粘贴 RectTransform", clipboard == null, () => PasteRectTransform());
|
||||
|
||||
ContextMenuUtils.BuildSeparator();
|
||||
ContextMenuUtils.BuildContextMenu("对象/向前移动(置为最后Sibling)", false, () => BringToFront());
|
||||
ContextMenuUtils.BuildContextMenu("对象/发送到最底层(置为第0)", false, () => SendToBack());
|
||||
ContextMenuUtils.BuildContextMenu("对象/创建容器并包裹", false, () => CreateContainerAroundSelection());
|
||||
ContextMenuUtils.BuildContextMenu("对象/复制(场景内)", false, () => DuplicateSelection());
|
||||
ContextMenuUtils.BuildContextMenu("对象/重命名选中", false, () => RenameSelection());
|
||||
ContextMenuUtils.BuildContextMenu("对象/选择父对象", false, () => SelectParent());
|
||||
ContextMenuUtils.BuildContextMenu("对象/聚焦(场景视图)", false, () => FrameSelection());
|
||||
|
||||
ContextMenuUtils.BuildSeparator();
|
||||
ContextMenuUtils.BuildContextMenu("Graphic/切换 RaycastTarget", false, () => ToggleRaycastTarget());
|
||||
ContextMenuUtils.BuildContextMenu("排序/按名称排序同级子项", false, () => SortSiblingsByName());
|
||||
|
||||
// 如果可以组合RectTransform,则添加组合项
|
||||
ContextMenuUtils.BuildSeparator();
|
||||
if (CombineWidgetLogic.CanCombine(UXSelectionUtil.gameObjects))
|
||||
{
|
||||
ContextMenuUtils.BuildContextMenu("组合", false, () => CombineWidgetLogic.GenCombineRootRect(UXSelectionUtil.gameObjects));
|
||||
}
|
||||
else
|
||||
{
|
||||
ContextMenuUtils.BuildContextMenu("组合", true, null); // Disabled item
|
||||
}
|
||||
}
|
||||
|
||||
private static void SetAnchors(GameObject[] selectedObjects)
|
||||
{
|
||||
// 这里是设置锚点的逻辑,示例函数
|
||||
Debug.Log("设置锚点的功能...");
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private static void AddCommonItem()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
#region UI 选择菜单
|
||||
|
||||
private static void GenerateSelectObjectMenu()
|
||||
{
|
||||
var prefabStage = PrefabStageUtils.GetCurrentPrefabStage();
|
||||
List<RectTransform> inSceneObjs = GetAllUIObjects(prefabStage);
|
||||
|
||||
// 获取鼠标位置并转换为屏幕坐标(GUI坐标调整)
|
||||
Camera camera = SceneView.currentDrawingSceneView.camera;
|
||||
Vector2 mousePosGui = Event.current.mousePosition;
|
||||
Vector2 mousePos = new Vector2(mousePosGui.x, camera.pixelHeight - mousePosGui.y);
|
||||
|
||||
// 排序UI对象
|
||||
SortedList<string, RectTransform> sortedUIObjects = SortUIObjects(inSceneObjs);
|
||||
|
||||
// 将UI对象添加到菜单中
|
||||
foreach (string key in sortedUIObjects.Keys)
|
||||
{
|
||||
RectTransform obj = sortedUIObjects[key];
|
||||
if (RectTransformUtility.RectangleContainsScreenPoint(obj, mousePos, camera))
|
||||
{
|
||||
ContextMenuUtils.BuildContextMenu(obj.name, false, () => UXSelectionUtil.activeGameObject = obj.gameObject);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 获取场景中的所有UI对象
|
||||
private static List<RectTransform> GetAllUIObjects(PrefabStage prefabStage)
|
||||
{
|
||||
List<RectTransform> uiObjects = new List<RectTransform>();
|
||||
|
||||
if (prefabStage != null)
|
||||
{
|
||||
RectTransform[] allObjects = prefabStage.prefabContentsRoot.GetComponentsInChildren<RectTransform>(true);
|
||||
foreach (RectTransform obj in allObjects)
|
||||
{
|
||||
if (FindContainerLogic.ObjectFit(obj.gameObject))
|
||||
{
|
||||
uiObjects.Add(obj);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Scene scene = SceneManager.GetActiveScene();
|
||||
GameObject[] rootObjects = scene.GetRootGameObjects();
|
||||
foreach (GameObject rootObj in rootObjects)
|
||||
{
|
||||
RectTransform[] childRects = rootObj.GetComponentsInChildren<RectTransform>(true);
|
||||
foreach (RectTransform rect in childRects)
|
||||
{
|
||||
if (FindContainerLogic.ObjectFit(rect.gameObject))
|
||||
{
|
||||
uiObjects.Add(rect);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return uiObjects;
|
||||
}
|
||||
|
||||
// 排序UI对象
|
||||
private static SortedList<string, RectTransform> SortUIObjects(List<RectTransform> uiObjects)
|
||||
{
|
||||
List<string> uiNames = new List<string>();
|
||||
foreach (var rect in uiObjects)
|
||||
{
|
||||
uiNames.Add(GetTransformHierarchyString("", rect));
|
||||
}
|
||||
|
||||
// 按照名称排序
|
||||
SortedList<string, RectTransform> sortedList = new SortedList<string, RectTransform>(new DuplicateKeyComparer());
|
||||
for (int i = 0; i < uiObjects.Count; i++)
|
||||
{
|
||||
// 确保 key 唯一:包含索引
|
||||
string key = uiNames[i] + "_" + i;
|
||||
sortedList.Add(key, uiObjects[i]);
|
||||
}
|
||||
|
||||
return sortedList;
|
||||
}
|
||||
|
||||
// 获取UI对象的层级字符串
|
||||
private static string GetTransformHierarchyString(string prefix, Transform trans)
|
||||
{
|
||||
string str = string.IsNullOrEmpty(prefix) ? trans.GetSiblingIndex().ToString() : $"{trans.GetSiblingIndex()}.{prefix}";
|
||||
if (trans.parent != null)
|
||||
{
|
||||
return GetTransformHierarchyString(str, trans.parent);
|
||||
}
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 辅助类与功能实现
|
||||
|
||||
// 自定义比较器,用于排序UI元素
|
||||
private class DuplicateKeyComparer : IComparer<string>
|
||||
{
|
||||
public int Compare(string x, string y)
|
||||
{
|
||||
return -string.Compare(x, y); // 逆序排序
|
||||
}
|
||||
}
|
||||
|
||||
private enum AlignType
|
||||
{
|
||||
Left,
|
||||
CenterHorizontal,
|
||||
Right,
|
||||
Top,
|
||||
CenterVertical,
|
||||
Bottom
|
||||
}
|
||||
|
||||
private enum DistributeAxis
|
||||
{
|
||||
Horizontal,
|
||||
Vertical
|
||||
}
|
||||
|
||||
private class RectTransformCache
|
||||
{
|
||||
public Vector2 anchoredPosition;
|
||||
public Vector2 sizeDelta;
|
||||
public Vector2 anchorMin;
|
||||
public Vector2 anchorMax;
|
||||
public Vector2 pivot;
|
||||
public Quaternion rotation;
|
||||
public Vector3 localScale;
|
||||
}
|
||||
|
||||
private static void CopyRectTransform()
|
||||
{
|
||||
if (UXSelectionUtil.gameObjects == null || UXSelectionUtil.gameObjects.Length == 0) return;
|
||||
var rt = UXSelectionUtil.gameObjects[0].GetComponent<RectTransform>();
|
||||
if (rt == null) return;
|
||||
clipboard = new RectTransformCache()
|
||||
{
|
||||
anchoredPosition = rt.anchoredPosition,
|
||||
sizeDelta = rt.sizeDelta,
|
||||
anchorMin = rt.anchorMin,
|
||||
anchorMax = rt.anchorMax,
|
||||
pivot = rt.pivot,
|
||||
rotation = rt.localRotation,
|
||||
localScale = rt.localScale
|
||||
};
|
||||
Debug.Log("已复制 RectTransform 数据到剪贴板");
|
||||
}
|
||||
|
||||
private static void PasteRectTransform()
|
||||
{
|
||||
if (clipboard == null || UXSelectionUtil.gameObjects == null) return;
|
||||
foreach (var go in UXSelectionUtil.gameObjects)
|
||||
{
|
||||
var rt = go.GetComponent<RectTransform>();
|
||||
if (rt == null) continue;
|
||||
Undo.RecordObject(rt, "Paste RectTransform");
|
||||
rt.anchorMin = clipboard.anchorMin;
|
||||
rt.anchorMax = clipboard.anchorMax;
|
||||
rt.pivot = clipboard.pivot;
|
||||
rt.sizeDelta = clipboard.sizeDelta;
|
||||
rt.localRotation = clipboard.rotation;
|
||||
rt.localScale = clipboard.localScale;
|
||||
|
||||
// 保持世界位置:计算当前 world pos,然后应用 anchoredPosition 使其看起来相同
|
||||
Vector3 worldPos = rt.position;
|
||||
rt.anchoredPosition = clipboard.anchoredPosition;
|
||||
rt.position = worldPos;
|
||||
|
||||
EditorUtility.SetDirty(rt);
|
||||
}
|
||||
}
|
||||
|
||||
private static void AlignSelected(AlignType align)
|
||||
{
|
||||
var gos = UXSelectionUtil.gameObjects?.Where(g => g != null).ToArray();
|
||||
if (gos == null || gos.Length <= 1) return;
|
||||
// 统一以第一个为目标
|
||||
RectTransform[] rts = gos.Select(g => g.GetComponent<RectTransform>()).Where(r => r != null).ToArray();
|
||||
if (rts.Length <= 1) return;
|
||||
|
||||
// 计算所有对象在世界空间的边界
|
||||
switch (align)
|
||||
{
|
||||
case AlignType.Left:
|
||||
{
|
||||
float target = rts.Min(rt => GetWorldLeft(rt));
|
||||
foreach (var rt in rts) MoveWorldLeftTo(rt, target);
|
||||
}
|
||||
break;
|
||||
case AlignType.Right:
|
||||
{
|
||||
float target = rts.Max(rt => GetWorldRight(rt));
|
||||
foreach (var rt in rts) MoveWorldRightTo(rt, target);
|
||||
}
|
||||
break;
|
||||
case AlignType.CenterHorizontal:
|
||||
{
|
||||
float minX = rts.Min(rt => GetWorldLeft(rt));
|
||||
float maxX = rts.Max(rt => GetWorldRight(rt));
|
||||
float centerX = (minX + maxX) / 2f;
|
||||
foreach (var rt in rts) MoveWorldCenterXTo(rt, centerX);
|
||||
}
|
||||
break;
|
||||
case AlignType.Top:
|
||||
{
|
||||
float target = rts.Max(rt => GetWorldTop(rt));
|
||||
foreach (var rt in rts) MoveWorldTopTo(rt, target);
|
||||
}
|
||||
break;
|
||||
case AlignType.Bottom:
|
||||
{
|
||||
float target = rts.Min(rt => GetWorldBottom(rt));
|
||||
foreach (var rt in rts) MoveWorldBottomTo(rt, target);
|
||||
}
|
||||
break;
|
||||
case AlignType.CenterVertical:
|
||||
{
|
||||
float minY = rts.Min(rt => GetWorldBottom(rt));
|
||||
float maxY = rts.Max(rt => GetWorldTop(rt));
|
||||
float centerY = (minY + maxY) / 2f;
|
||||
foreach (var rt in rts) MoveWorldCenterYTo(rt, centerY);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private static float GetWorldLeft(RectTransform rt)
|
||||
{
|
||||
var corners = new Vector3[4];
|
||||
rt.GetWorldCorners(corners);
|
||||
return corners[0].x; // bottom-left.x
|
||||
}
|
||||
|
||||
private static float GetWorldRight(RectTransform rt)
|
||||
{
|
||||
var corners = new Vector3[4];
|
||||
rt.GetWorldCorners(corners);
|
||||
return corners[2].x; // top-right.x
|
||||
}
|
||||
|
||||
private static float GetWorldTop(RectTransform rt)
|
||||
{
|
||||
var corners = new Vector3[4];
|
||||
rt.GetWorldCorners(corners);
|
||||
return corners[1].y; // top-left.y
|
||||
}
|
||||
|
||||
private static float GetWorldBottom(RectTransform rt)
|
||||
{
|
||||
var corners = new Vector3[4];
|
||||
rt.GetWorldCorners(corners);
|
||||
return corners[0].y; // bottom-left.y
|
||||
}
|
||||
|
||||
// 将世界空间的位移转换为父空间的局部位移,并应用到 rectTransform.localPosition
|
||||
private static void ApplyWorldDeltaToRectTransform(RectTransform rt, Vector3 worldDelta, string undoName)
|
||||
{
|
||||
if (rt == null || rt.parent == null) return;
|
||||
Undo.RecordObject(rt, undoName);
|
||||
|
||||
// 将世界向量转换为父对象的局部向量(旋转会被考虑,但位置不重要因为这是增量)
|
||||
Vector3 localDelta = rt.parent.InverseTransformVector(worldDelta);
|
||||
|
||||
// 应用到 localPosition(而不是直接修改 world position),这样对于 UI 更稳定
|
||||
rt.localPosition += localDelta;
|
||||
|
||||
EditorUtility.SetDirty(rt);
|
||||
}
|
||||
|
||||
private static void MoveWorldLeftTo(RectTransform rt, float worldX)
|
||||
{
|
||||
var corners = new Vector3[4];
|
||||
rt.GetWorldCorners(corners);
|
||||
float curLeft = corners[0].x;
|
||||
float delta = worldX - curLeft;
|
||||
if (Mathf.Approximately(delta, 0f)) return;
|
||||
ApplyWorldDeltaToRectTransform(rt, new Vector3(delta, 0, 0), "Align Left");
|
||||
}
|
||||
|
||||
private static void MoveWorldRightTo(RectTransform rt, float worldX)
|
||||
{
|
||||
var corners = new Vector3[4];
|
||||
rt.GetWorldCorners(corners);
|
||||
float curRight = corners[2].x;
|
||||
float delta = worldX - curRight;
|
||||
if (Mathf.Approximately(delta, 0f)) return;
|
||||
ApplyWorldDeltaToRectTransform(rt, new Vector3(delta, 0, 0), "Align Right");
|
||||
}
|
||||
|
||||
private static void MoveWorldTopTo(RectTransform rt, float worldY)
|
||||
{
|
||||
var corners = new Vector3[4];
|
||||
rt.GetWorldCorners(corners);
|
||||
float curTop = corners[1].y;
|
||||
float delta = worldY - curTop;
|
||||
if (Mathf.Approximately(delta, 0f)) return;
|
||||
ApplyWorldDeltaToRectTransform(rt, new Vector3(0, delta, 0), "Align Top");
|
||||
}
|
||||
|
||||
private static void MoveWorldBottomTo(RectTransform rt, float worldY)
|
||||
{
|
||||
var corners = new Vector3[4];
|
||||
rt.GetWorldCorners(corners);
|
||||
float curBottom = corners[0].y;
|
||||
float delta = worldY - curBottom;
|
||||
if (Mathf.Approximately(delta, 0f)) return;
|
||||
ApplyWorldDeltaToRectTransform(rt, new Vector3(0, delta, 0), "Align Bottom");
|
||||
}
|
||||
|
||||
private static void MoveWorldCenterXTo(RectTransform rt, float worldX)
|
||||
{
|
||||
var corners = new Vector3[4];
|
||||
rt.GetWorldCorners(corners);
|
||||
float curCenterX = (corners[0].x + corners[2].x) / 2f;
|
||||
float delta = worldX - curCenterX;
|
||||
if (Mathf.Approximately(delta, 0f)) return;
|
||||
ApplyWorldDeltaToRectTransform(rt, new Vector3(delta, 0, 0), "Align Center X");
|
||||
}
|
||||
|
||||
private static void MoveWorldCenterYTo(RectTransform rt, float worldY)
|
||||
{
|
||||
var corners = new Vector3[4];
|
||||
rt.GetWorldCorners(corners);
|
||||
float curCenterY = (corners[0].y + corners[2].y) / 2f;
|
||||
float delta = worldY - curCenterY;
|
||||
if (Mathf.Approximately(delta, 0f)) return;
|
||||
ApplyWorldDeltaToRectTransform(rt, new Vector3(0, delta, 0), "Align Center Y");
|
||||
}
|
||||
|
||||
private static void DistributeSelected(DistributeAxis axis)
|
||||
{
|
||||
var gos = UXSelectionUtil.gameObjects?.Where(g => g != null).ToArray();
|
||||
if (gos == null || gos.Length <= 2) return; // 两个对象没必要
|
||||
RectTransform[] rts = gos.Select(g => g.GetComponent<RectTransform>()).Where(r => r != null).ToArray();
|
||||
if (rts.Length <= 2) return;
|
||||
|
||||
// 按中心排序
|
||||
if (axis == DistributeAxis.Horizontal)
|
||||
{
|
||||
var ordered = rts.OrderBy(rt => GetWorldCenter(rt).x).ToArray();
|
||||
float left = GetWorldLeft(ordered.First());
|
||||
float right = GetWorldRight(ordered.Last());
|
||||
float totalWidth = ordered.Sum(rt => GetWidthWorld(rt));
|
||||
float space = (right - left - totalWidth) / (ordered.Length - 1);
|
||||
float curX = left;
|
||||
for (int i = 0; i < ordered.Length; i++)
|
||||
{
|
||||
float w = GetWidthWorld(ordered[i]);
|
||||
float targetCenter = curX + w / 2f;
|
||||
MoveWorldCenterXTo(ordered[i], targetCenter);
|
||||
curX += w + space;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var ordered = rts.OrderBy(rt => GetWorldCenter(rt).y).ToArray();
|
||||
float bottom = GetWorldBottom(ordered.First());
|
||||
float top = GetWorldTop(ordered.Last());
|
||||
float totalHeight = ordered.Sum(rt => GetHeightWorld(rt));
|
||||
float space = (top - bottom - totalHeight) / (ordered.Length - 1);
|
||||
float curY = bottom;
|
||||
for (int i = 0; i < ordered.Length; i++)
|
||||
{
|
||||
float h = GetHeightWorld(ordered[i]);
|
||||
float targetCenter = curY + h / 2f;
|
||||
MoveWorldCenterYTo(ordered[i], targetCenter);
|
||||
curY += h + space;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static Vector2 GetWorldCenter(RectTransform rt)
|
||||
{
|
||||
var corners = new Vector3[4];
|
||||
rt.GetWorldCorners(corners);
|
||||
return (corners[0] + corners[2]) / 2f;
|
||||
}
|
||||
|
||||
private static float GetWidthWorld(RectTransform rt)
|
||||
{
|
||||
var corners = new Vector3[4];
|
||||
rt.GetWorldCorners(corners);
|
||||
return Mathf.Abs(corners[2].x - corners[0].x);
|
||||
}
|
||||
|
||||
private static float GetHeightWorld(RectTransform rt)
|
||||
{
|
||||
var corners = new Vector3[4];
|
||||
rt.GetWorldCorners(corners);
|
||||
return Mathf.Abs(corners[1].y - corners[0].y);
|
||||
}
|
||||
|
||||
private static void MatchSizeToFirst()
|
||||
{
|
||||
var gos = UXSelectionUtil.gameObjects;
|
||||
if (gos == null || gos.Length <= 1) return;
|
||||
var first = gos[0].GetComponent<RectTransform>();
|
||||
if (first == null) return;
|
||||
for (int i = 1; i < gos.Length; i++)
|
||||
{
|
||||
var rt = gos[i].GetComponent<RectTransform>();
|
||||
if (rt == null) continue;
|
||||
Undo.RecordObject(rt, "Match Size");
|
||||
rt.sizeDelta = first.sizeDelta;
|
||||
EditorUtility.SetDirty(rt);
|
||||
}
|
||||
}
|
||||
|
||||
private static void PixelSnapSelected()
|
||||
{
|
||||
foreach (var go in UXSelectionUtil.gameObjects ?? new GameObject[0])
|
||||
{
|
||||
var rt = go.GetComponent<RectTransform>();
|
||||
if (rt == null) continue;
|
||||
Undo.RecordObject(rt, "Pixel Snap");
|
||||
Vector2 ap = rt.anchoredPosition;
|
||||
ap.x = Mathf.Round(ap.x);
|
||||
ap.y = Mathf.Round(ap.y);
|
||||
rt.anchoredPosition = ap;
|
||||
EditorUtility.SetDirty(rt);
|
||||
}
|
||||
}
|
||||
|
||||
private static void ResetTransformSelected()
|
||||
{
|
||||
foreach (var go in UXSelectionUtil.gameObjects ?? new GameObject[0])
|
||||
{
|
||||
var rt = go.GetComponent<RectTransform>();
|
||||
if (rt == null) continue;
|
||||
Undo.RecordObject(rt, "Reset Transform");
|
||||
rt.anchoredPosition = Vector2.zero;
|
||||
rt.localRotation = Quaternion.identity;
|
||||
rt.localScale = Vector3.one;
|
||||
EditorUtility.SetDirty(rt);
|
||||
}
|
||||
}
|
||||
|
||||
private static void SetAnchorsToCornersKeepPos()
|
||||
{
|
||||
foreach (var go in UXSelectionUtil.gameObjects ?? new GameObject[0])
|
||||
{
|
||||
var rt = go.GetComponent<RectTransform>();
|
||||
if (rt == null) continue;
|
||||
Undo.RecordObject(rt, "Set Anchors to Corners Keep Position");
|
||||
|
||||
Transform parent = rt.parent;
|
||||
if (parent == null) continue;
|
||||
RectTransform parentRT = parent as RectTransform;
|
||||
if (parentRT == null) continue;
|
||||
|
||||
Vector3[] corners = new Vector3[4];
|
||||
rt.GetWorldCorners(corners);
|
||||
Vector3[] parentCorners = new Vector3[4];
|
||||
parentRT.GetWorldCorners(parentCorners);
|
||||
|
||||
Vector2 newAnchorMin = new Vector2(
|
||||
Mathf.InverseLerp(parentCorners[0].x, parentCorners[2].x, corners[0].x),
|
||||
Mathf.InverseLerp(parentCorners[0].y, parentCorners[2].y, corners[0].y));
|
||||
Vector2 newAnchorMax = new Vector2(
|
||||
Mathf.InverseLerp(parentCorners[0].x, parentCorners[2].x, corners[2].x),
|
||||
Mathf.InverseLerp(parentCorners[0].y, parentCorners[2].y, corners[2].y));
|
||||
|
||||
Vector2 oldAnchored = rt.anchoredPosition;
|
||||
rt.anchorMin = newAnchorMin;
|
||||
rt.anchorMax = newAnchorMax;
|
||||
rt.anchoredPosition = oldAnchored; // 尝试保留视觉位置
|
||||
|
||||
EditorUtility.SetDirty(rt);
|
||||
}
|
||||
}
|
||||
|
||||
private static void SetAnchorsToCenterKeepPos()
|
||||
{
|
||||
foreach (var go in UXSelectionUtil.gameObjects ?? new GameObject[0])
|
||||
{
|
||||
var rt = go.GetComponent<RectTransform>();
|
||||
if (rt == null) continue;
|
||||
Undo.RecordObject(rt, "Set Anchors to Center Keep Position");
|
||||
Transform parent = rt.parent;
|
||||
if (parent == null) continue;
|
||||
RectTransform parentRT = parent as RectTransform;
|
||||
if (parentRT == null) continue;
|
||||
|
||||
Vector3[] corners = new Vector3[4];
|
||||
rt.GetWorldCorners(corners);
|
||||
Vector3[] parentCorners = new Vector3[4];
|
||||
parentRT.GetWorldCorners(parentCorners);
|
||||
|
||||
float centerX = (corners[0].x + corners[2].x) / 2f;
|
||||
float centerY = (corners[0].y + corners[2].y) / 2f;
|
||||
|
||||
Vector2 anchor = new Vector2(
|
||||
Mathf.InverseLerp(parentCorners[0].x, parentCorners[2].x, centerX),
|
||||
Mathf.InverseLerp(parentCorners[0].y, parentCorners[2].y, centerY));
|
||||
|
||||
Vector2 oldAnchored = rt.anchoredPosition;
|
||||
rt.anchorMin = anchor;
|
||||
rt.anchorMax = anchor;
|
||||
rt.anchoredPosition = oldAnchored;
|
||||
|
||||
EditorUtility.SetDirty(rt);
|
||||
}
|
||||
}
|
||||
|
||||
private static void BringToFront()
|
||||
{
|
||||
foreach (var go in UXSelectionUtil.gameObjects ?? new GameObject[0])
|
||||
{
|
||||
Transform parent = go.transform.parent;
|
||||
Undo.RecordObject(go.transform, "Bring To Front");
|
||||
if (parent != null)
|
||||
{
|
||||
go.transform.SetSiblingIndex(parent.childCount - 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
go.transform.SetSiblingIndex(go.transform.GetSiblingIndex());
|
||||
}
|
||||
|
||||
EditorUtility.SetDirty(go);
|
||||
}
|
||||
}
|
||||
|
||||
private static void SendToBack()
|
||||
{
|
||||
foreach (var go in UXSelectionUtil.gameObjects ?? new GameObject[0])
|
||||
{
|
||||
Transform parent = go.transform.parent;
|
||||
Undo.RecordObject(go.transform, "Send To Back");
|
||||
go.transform.SetSiblingIndex(0);
|
||||
EditorUtility.SetDirty(go);
|
||||
}
|
||||
}
|
||||
|
||||
private static void CreateContainerAroundSelection()
|
||||
{
|
||||
var gos = UXSelectionUtil.gameObjects?.Where(g => g != null).ToArray();
|
||||
if (gos == null || gos.Length == 0) return;
|
||||
// 只允许相同父级
|
||||
Transform parent = gos[0].transform.parent;
|
||||
if (gos.Any(g => g.transform.parent != parent))
|
||||
{
|
||||
Debug.LogWarning("创建容器要求所有对象在同一父级下");
|
||||
return;
|
||||
}
|
||||
|
||||
// 计算包围盒(世界坐标)
|
||||
var rts = gos.Select(g => g.GetComponent<RectTransform>()).Where(r => r != null).ToArray();
|
||||
if (rts.Length == 0) return;
|
||||
|
||||
Vector3 worldMin = new Vector3(float.MaxValue, float.MaxValue, float.MaxValue);
|
||||
Vector3 worldMax = new Vector3(float.MinValue, float.MinValue, float.MinValue);
|
||||
foreach (var rt in rts)
|
||||
{
|
||||
Vector3[] corners = new Vector3[4];
|
||||
rt.GetWorldCorners(corners);
|
||||
worldMin = Vector3.Min(worldMin, corners[0]);
|
||||
worldMax = Vector3.Max(worldMax, corners[2]);
|
||||
}
|
||||
|
||||
// 创建容器
|
||||
GameObject root = UIBuilderUtil.CreateUIObj("Container");
|
||||
Undo.RegisterCreatedObjectUndo(root, "Create Container");
|
||||
var rootRT = root.GetComponent<RectTransform>();
|
||||
root.transform.SetParent(parent);
|
||||
root.transform.SetSiblingIndex(rts.Min(r => r.GetSiblingIndex()));
|
||||
rootRT.localScale = Vector3.one;
|
||||
|
||||
// 将世界包围盒转换为父本地坐标
|
||||
RectTransform parentRT = parent as RectTransform;
|
||||
if (parentRT == null)
|
||||
{
|
||||
Debug.LogWarning("父对象不是 RectTransform,无法创建容器");
|
||||
return;
|
||||
}
|
||||
|
||||
Vector3 localMin = parentRT.InverseTransformPoint(worldMin);
|
||||
Vector3 localMax = parentRT.InverseTransformPoint(worldMax);
|
||||
Vector2 size = new Vector2(localMax.x - localMin.x, localMax.y - localMin.y);
|
||||
Vector2 center = (localMin + localMax) / 2f;
|
||||
|
||||
rootRT.sizeDelta = size;
|
||||
rootRT.anchoredPosition = center;
|
||||
|
||||
// 将选中对象设置为容器子对象并保持位置
|
||||
foreach (var g in gos)
|
||||
{
|
||||
Undo.SetTransformParent(g.transform, root.transform, "Wrap In Container");
|
||||
}
|
||||
|
||||
Selection.activeGameObject = root;
|
||||
EditorUtility.SetDirty(rootRT);
|
||||
}
|
||||
|
||||
private static void DuplicateSelection()
|
||||
{
|
||||
var gos = UXSelectionUtil.gameObjects?.Where(g => g != null).ToArray();
|
||||
if (gos == null || gos.Length == 0) return;
|
||||
List<GameObject> newOnes = new List<GameObject>();
|
||||
foreach (var g in gos)
|
||||
{
|
||||
GameObject dup = Object.Instantiate(g, g.transform.parent);
|
||||
dup.name = g.name + " Copy";
|
||||
Undo.RegisterCreatedObjectUndo(dup, "Duplicate UI Element");
|
||||
dup.transform.SetSiblingIndex(g.transform.GetSiblingIndex() + 1);
|
||||
newOnes.Add(dup);
|
||||
}
|
||||
|
||||
Selection.objects = newOnes.ToArray();
|
||||
}
|
||||
|
||||
private static void RenameSelection()
|
||||
{
|
||||
if (UXSelectionUtil.gameObjects == null || UXSelectionUtil.gameObjects.Length == 0) return;
|
||||
Selection.activeGameObject = UXSelectionUtil.gameObjects[0];
|
||||
// 调用编辑器的重命名命令
|
||||
EditorApplication.ExecuteMenuItem("Edit/Rename");
|
||||
}
|
||||
|
||||
private static void SelectParent()
|
||||
{
|
||||
if (UXSelectionUtil.gameObjects == null || UXSelectionUtil.gameObjects.Length == 0) return;
|
||||
var go = UXSelectionUtil.gameObjects[0];
|
||||
if (go.transform.parent != null)
|
||||
{
|
||||
Selection.activeGameObject = go.transform.parent.gameObject;
|
||||
}
|
||||
}
|
||||
|
||||
private static void FrameSelection()
|
||||
{
|
||||
SceneView.lastActiveSceneView?.FrameSelected();
|
||||
}
|
||||
|
||||
private static void ToggleRaycastTarget()
|
||||
{
|
||||
foreach (var go in UXSelectionUtil.gameObjects ?? new GameObject[0])
|
||||
{
|
||||
var g = go.GetComponent<Graphic>();
|
||||
if (g == null) continue;
|
||||
Undo.RecordObject(g, "Toggle RaycastTarget");
|
||||
g.raycastTarget = !g.raycastTarget;
|
||||
EditorUtility.SetDirty(g);
|
||||
}
|
||||
}
|
||||
|
||||
private static void SortSiblingsByName()
|
||||
{
|
||||
var gos = UXSelectionUtil.gameObjects?.Where(g => g != null).ToArray();
|
||||
if (gos == null || gos.Length == 0) return;
|
||||
Transform parent = gos[0].transform.parent;
|
||||
if (parent == null) return;
|
||||
|
||||
var children = parent.Cast<Transform>().Select(t => t.gameObject).OrderBy(o => o.name).ToArray();
|
||||
for (int i = 0; i < children.Length; i++)
|
||||
{
|
||||
Undo.RecordObject(children[i].transform, "Sort By Name");
|
||||
children[i].transform.SetSiblingIndex(i);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
// CombineWidgetLogic 放在同一文件以便完整交付
|
||||
public static class CombineWidgetLogic
|
||||
{
|
||||
/// <summary>
|
||||
/// 组合节点
|
||||
/// </summary>
|
||||
/// <param name="rects">需要组合的对象 需要所有对象在同一层级</param>
|
||||
/// <returns>组合之后的父节点</returns>
|
||||
public static GameObject GenCombineRootRect(List<RectTransform> rects)
|
||||
{
|
||||
if (rects == null || rects.Count == 0) return null;
|
||||
|
||||
// 确保所有在同一父级
|
||||
Transform parent = rects[0].transform.parent;
|
||||
if (rects.Any(r => r.transform.parent != parent))
|
||||
{
|
||||
Debug.LogWarning("所有对象必须在同一父级下才能组合");
|
||||
return null;
|
||||
}
|
||||
|
||||
// 计算世界包围盒
|
||||
Vector3 worldMin = Vector3.positiveInfinity;
|
||||
Vector3 worldMax = Vector3.negativeInfinity;
|
||||
foreach (var rt in rects)
|
||||
{
|
||||
Vector3[] corners = new Vector3[4];
|
||||
rt.GetWorldCorners(corners);
|
||||
worldMin = Vector3.Min(worldMin, corners[0]);
|
||||
worldMax = Vector3.Max(worldMax, corners[2]);
|
||||
}
|
||||
|
||||
// 创建容器
|
||||
GameObject root = UIBuilderUtil.CreateUIObj("CombineRoot");
|
||||
RectTransform rootRT = root.GetComponent<RectTransform>();
|
||||
root.transform.SetParent(parent);
|
||||
rootRT.localScale = Vector3.one;
|
||||
|
||||
// 将世界包围盒转换为父本地坐标
|
||||
RectTransform parentRT = parent as RectTransform;
|
||||
if (parentRT == null)
|
||||
{
|
||||
Debug.LogWarning("父对象不是 RectTransform,无法创建组合容器");
|
||||
return null;
|
||||
}
|
||||
|
||||
Vector3 localMin = parentRT.InverseTransformPoint(worldMin);
|
||||
Vector3 localMax = parentRT.InverseTransformPoint(worldMax);
|
||||
Vector2 size = new Vector2(localMax.x - localMin.x, localMax.y - localMin.y);
|
||||
Vector2 center = (localMin + localMax) / 2f;
|
||||
|
||||
rootRT.sizeDelta = size;
|
||||
rootRT.anchoredPosition = center;
|
||||
|
||||
// 按原顺序设置为子对象
|
||||
rects.Sort((a, b) => a.GetSiblingIndex() - b.GetSiblingIndex());
|
||||
foreach (var rt in rects)
|
||||
{
|
||||
Undo.SetTransformParent(rt.transform, root.transform, "Combine Widgets");
|
||||
}
|
||||
|
||||
Selection.activeGameObject = root;
|
||||
return root;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 组合节点
|
||||
/// </summary>
|
||||
/// <param name="rects"></param>
|
||||
/// <returns></returns>
|
||||
public static GameObject GenCombineRootRect(GameObject[] objs)
|
||||
{
|
||||
List<RectTransform> rects = objs.ToList().Select(a => a.GetComponent<RectTransform>()).Where(r => r != null).ToList();
|
||||
|
||||
return GenCombineRootRect(rects);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="rects"></param>
|
||||
/// <returns></returns>
|
||||
private static bool AllHaveSameParent(GameObject[] objs)
|
||||
{
|
||||
if (objs.Length == 1)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
Transform parent = objs[0].transform.parent;
|
||||
for (int i = 1; i < objs.Length; i++)
|
||||
{
|
||||
if (objs[i].transform.parent != parent)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static bool AllHaveRectTransform(GameObject[] objs)
|
||||
{
|
||||
for (int i = 0; i < objs.Length; i++)
|
||||
{
|
||||
if (objs[i].GetComponent<RectTransform>() == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static bool CanCombine(GameObject[] objs)
|
||||
{
|
||||
if (objs == null || objs.Length <= 1)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SameLevelUI = false;
|
||||
if (AllHaveSameParent(objs))
|
||||
{
|
||||
SameLevelUI = true;
|
||||
}
|
||||
|
||||
bool UIInPrefab = false;
|
||||
foreach (GameObject go in objs)
|
||||
{
|
||||
string assetPath = PrefabUtility.GetPrefabAssetPathOfNearestInstanceRoot(go);
|
||||
if (!string.IsNullOrEmpty(assetPath))
|
||||
{
|
||||
UIInPrefab = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (SameLevelUI && !UIInPrefab)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: deac8fa4e6a7d8348b7fd4f2e06676b2
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ae357567eebc45eb8847bd1d2ad0eab8
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,107 @@
|
||||
using AlicizaX.UI;
|
||||
using AlicizaX.UI.Runtime;
|
||||
using UnityEditor;
|
||||
using UnityEditor.SceneManagement;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
namespace Aliciza.UXTool
|
||||
{
|
||||
public class UXControllerAddWindow : EditorWindow
|
||||
{
|
||||
private UXController _ux;
|
||||
private TextField _nameField;
|
||||
private IntegerField _lengthField;
|
||||
|
||||
public static void ShowWindow(UXController ux)
|
||||
{
|
||||
var w = CreateInstance<UXControllerAddWindow>();
|
||||
w.titleContent = new GUIContent("添加控制器");
|
||||
w._ux = ux;
|
||||
w.position = new Rect(Screen.width / 2f - 150f, Screen.height / 2f - 60f, 320, 120);
|
||||
w.ShowUtility();
|
||||
}
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
var root = rootVisualElement;
|
||||
root.style.paddingLeft = 8;
|
||||
root.style.paddingTop = 8;
|
||||
root.style.paddingRight = 8;
|
||||
root.style.paddingBottom = 8;
|
||||
|
||||
var title = new Label("添加控制器");
|
||||
title.style.unityFontStyleAndWeight = FontStyle.Bold;
|
||||
title.style.marginBottom = 6;
|
||||
root.Add(title);
|
||||
|
||||
_nameField = new TextField("Name");
|
||||
_nameField.value = _ux != null ? $"Controller{_ux.Controllers.Count}" : "Controller";
|
||||
root.Add(_nameField);
|
||||
|
||||
_lengthField = new IntegerField("Length");
|
||||
_lengthField.value = 2;
|
||||
root.Add(_lengthField);
|
||||
|
||||
var row = new VisualElement();
|
||||
row.style.flexDirection = FlexDirection.Row;
|
||||
row.style.justifyContent = Justify.FlexEnd;
|
||||
row.style.marginTop = 8;
|
||||
|
||||
var add = new Button(() =>
|
||||
{
|
||||
if (_ux != null)
|
||||
{
|
||||
Undo.RecordObject(_ux, "Add Controller");
|
||||
// 使用 SerializedObject 能保证序列化
|
||||
var so = new SerializedObject(_ux);
|
||||
var controllersProp = so.FindProperty("_controllers");
|
||||
int newIndex = controllersProp.arraySize;
|
||||
controllersProp.InsertArrayElementAtIndex(newIndex);
|
||||
var el = controllersProp.GetArrayElementAtIndex(newIndex);
|
||||
var nameProp = el.FindPropertyRelative("Name");
|
||||
var lengthProp = el.FindPropertyRelative("Length");
|
||||
nameProp.stringValue = _nameField.value;
|
||||
lengthProp.intValue = Mathf.Max(1, _lengthField.value);
|
||||
so.ApplyModifiedProperties();
|
||||
EditorUtility.SetDirty(_ux);
|
||||
}
|
||||
else
|
||||
{
|
||||
// 如果外部没有提供 UXController,尝试在 prefab root 上添加
|
||||
var prefabStage = PrefabStageUtility.GetCurrentPrefabStage();
|
||||
if (prefabStage != null && prefabStage.prefabContentsRoot != null)
|
||||
{
|
||||
var comp = Undo.AddComponent<UXController>(prefabStage.prefabContentsRoot);
|
||||
EditorUtility.SetDirty(prefabStage.prefabContentsRoot);
|
||||
if (comp != null)
|
||||
{
|
||||
Undo.RecordObject(comp, "Add Controller");
|
||||
var so = new SerializedObject(comp);
|
||||
var controllersProp = so.FindProperty("_controllers");
|
||||
controllersProp.InsertArrayElementAtIndex(0);
|
||||
var el = controllersProp.GetArrayElementAtIndex(0);
|
||||
var nameProp = el.FindPropertyRelative("Name");
|
||||
var lengthProp = el.FindPropertyRelative("Length");
|
||||
nameProp.stringValue = _nameField.value;
|
||||
lengthProp.intValue = Mathf.Max(1, _lengthField.value);
|
||||
so.ApplyModifiedProperties();
|
||||
EditorUtility.SetDirty(comp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Close();
|
||||
});
|
||||
add.text = "添加";
|
||||
row.Add(add);
|
||||
|
||||
var cancel = new Button(() => Close());
|
||||
cancel.text = "取消";
|
||||
cancel.style.marginLeft = 8;
|
||||
row.Add(cancel);
|
||||
|
||||
root.Add(row);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 67b9bfb10302d6d43aaee21321c53818
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,136 @@
|
||||
using System;
|
||||
using System.Drawing;
|
||||
using AlicizaX.UI;
|
||||
using AlicizaX.UI.Runtime;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UIElements;
|
||||
using FontStyle = UnityEngine.FontStyle;
|
||||
|
||||
namespace Aliciza.UXTool
|
||||
{
|
||||
public class UXControllerEditWindow : EditorWindow
|
||||
{
|
||||
private UXController _ux;
|
||||
private int _index;
|
||||
private TextField _nameField;
|
||||
private IntegerField _lengthField;
|
||||
|
||||
public static void ShowWindow(UXController ux, int index, string currentName, int currentLength)
|
||||
{
|
||||
var w = CreateInstance<UXControllerEditWindow>();
|
||||
w.titleContent = new GUIContent("编辑控制器");
|
||||
w._ux = ux;
|
||||
w._index = index;
|
||||
w.position = new Rect(Screen.width / 2f - 150f, Screen.height / 2f - 60f, 320, 120);
|
||||
w.ShowUtility();
|
||||
}
|
||||
|
||||
private void CreateGUI()
|
||||
{
|
||||
var root = rootVisualElement;
|
||||
root.style.paddingLeft = 8;
|
||||
root.style.paddingTop = 8;
|
||||
root.style.paddingRight = 8;
|
||||
root.style.paddingBottom = 8;
|
||||
|
||||
var title = new Label($"编辑控制器 [{_index}]");
|
||||
title.style.unityFontStyleAndWeight = FontStyle.Bold;
|
||||
title.style.marginBottom = 6;
|
||||
root.Add(title);
|
||||
|
||||
_nameField = new TextField("Name");
|
||||
if (_ux != null && _index >= 0 && _index < _ux.Controllers.Count) _nameField.value = _ux.Controllers[_index].Name;
|
||||
root.Add(_nameField);
|
||||
|
||||
_lengthField = new IntegerField("Length");
|
||||
if (_ux != null && _index >= 0 && _index < _ux.Controllers.Count)
|
||||
{
|
||||
_lengthField.value = _ux.Controllers[_index].Length;
|
||||
}
|
||||
|
||||
_lengthField.value = Mathf.Max(1, _lengthField.value);
|
||||
root.Add(_lengthField);
|
||||
|
||||
var row = new VisualElement();
|
||||
row.style.flexDirection = FlexDirection.Row;
|
||||
row.style.justifyContent = Justify.FlexEnd;
|
||||
row.style.marginTop = 8;
|
||||
|
||||
var save = new Button(() =>
|
||||
{
|
||||
if (_ux != null && _index >= 0 && !string.IsNullOrEmpty(_nameField.value))
|
||||
{
|
||||
var so = new SerializedObject(_ux);
|
||||
var controllersProp = so.FindProperty("_controllers");
|
||||
if (controllersProp != null)
|
||||
{
|
||||
Undo.RecordObject(_ux, "Edit Controller");
|
||||
var el = controllersProp.GetArrayElementAtIndex(_index);
|
||||
var nameProp = el.FindPropertyRelative("Name");
|
||||
string oldValue = nameProp.stringValue;
|
||||
var lengthProp = el.FindPropertyRelative("Length");
|
||||
nameProp.stringValue = _nameField.value;
|
||||
lengthProp.intValue = Mathf.Max(1, _lengthField.value);
|
||||
so.ApplyModifiedProperties();
|
||||
|
||||
var recorders = so.FindProperty("_recorders");
|
||||
|
||||
for (int i = 0; i < recorders.arraySize; i++)
|
||||
{
|
||||
var recorderProp = recorders.GetArrayElementAtIndex(i);
|
||||
var recorderSo = new SerializedObject(recorderProp.objectReferenceValue);
|
||||
var stateEntriesProp = recorderSo.FindProperty("_stateEntries");
|
||||
for (int j = 0; j < stateEntriesProp.arraySize; j++)
|
||||
{
|
||||
var entry = stateEntriesProp.GetArrayElementAtIndex(j);
|
||||
var controllerNameProp = entry.FindPropertyRelative("ControllerName");
|
||||
if (controllerNameProp.stringValue.Equals(oldValue))
|
||||
{
|
||||
controllerNameProp.stringValue = _nameField.value;
|
||||
}
|
||||
}
|
||||
|
||||
recorderSo.ApplyModifiedProperties();
|
||||
}
|
||||
|
||||
EditorUtility.SetDirty(_ux);
|
||||
}
|
||||
}
|
||||
|
||||
Close();
|
||||
});
|
||||
save.text = "保存";
|
||||
row.Add(save);
|
||||
|
||||
var delete = new Button(() =>
|
||||
{
|
||||
if (_ux != null && _index >= 0)
|
||||
{
|
||||
var so = new SerializedObject(_ux);
|
||||
var controllersProp = so.FindProperty("_controllers");
|
||||
if (controllersProp != null)
|
||||
{
|
||||
Undo.RecordObject(_ux, "Edit Controller");
|
||||
controllersProp.DeleteArrayElementAtIndex(_index);
|
||||
so.ApplyModifiedProperties();
|
||||
EditorUtility.SetDirty(_ux);
|
||||
}
|
||||
}
|
||||
|
||||
Close();
|
||||
});
|
||||
delete.text = "删除";
|
||||
row.Add(delete);
|
||||
|
||||
var cancel = new Button(() => Close());
|
||||
cancel.text = "取消";
|
||||
cancel.style.marginLeft = 8;
|
||||
|
||||
row.Add(cancel);
|
||||
|
||||
|
||||
root.Add(row);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2c4d51bc600e38a4f9100a50edc69771
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,433 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using Aliciza.UXTool;
|
||||
using UnityEditor;
|
||||
using UnityEditor.Experimental.SceneManagement;
|
||||
using UnityEditor.SceneManagement;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UIElements;
|
||||
using UnityEditor.Callbacks;
|
||||
using AlicizaX.UI;
|
||||
using AlicizaX.UI.Runtime;
|
||||
|
||||
public static class UXControllerSceneOverlayManager
|
||||
{
|
||||
private static readonly Dictionary<int, UXControllerOverlayVE> s_map = new Dictionary<int, UXControllerOverlayVE>();
|
||||
|
||||
[InitializeOnLoadMethod]
|
||||
public static void Initialize()
|
||||
{
|
||||
UXDesinUtil.OnEnterDesignMode += RegisterEvents;
|
||||
UXDesinUtil.OnExitDesignMode += UnRegisterEvents;
|
||||
|
||||
if (UXDesinUtil.InDesign)
|
||||
{
|
||||
RegisterEvents();
|
||||
}
|
||||
else
|
||||
{
|
||||
UnRegisterEvents();
|
||||
}
|
||||
}
|
||||
|
||||
private static void RegisterEvents()
|
||||
{
|
||||
SceneView.duringSceneGui += OnSceneGui;
|
||||
EditorApplication.update += EditorUpdate;
|
||||
EditorApplication.playModeStateChanged += OnPlayModeStateChanged;
|
||||
}
|
||||
|
||||
private static void UnRegisterEvents()
|
||||
{
|
||||
SceneView.duringSceneGui -= OnSceneGui;
|
||||
EditorApplication.update -= EditorUpdate;
|
||||
EditorApplication.playModeStateChanged -= OnPlayModeStateChanged;
|
||||
|
||||
RemoveAllOverlays();
|
||||
}
|
||||
|
||||
private static void RemoveAllOverlays()
|
||||
{
|
||||
var views = SceneView.sceneViews;
|
||||
foreach (var obj in views)
|
||||
{
|
||||
var sv = obj as SceneView;
|
||||
if (sv == null) continue;
|
||||
try
|
||||
{
|
||||
var root = sv.rootVisualElement;
|
||||
if (root == null) continue;
|
||||
var existing = root.Q<VisualElement>("ux-controller-ve");
|
||||
if (existing != null)
|
||||
{
|
||||
root.Remove(existing);
|
||||
}
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
// 清理字典
|
||||
s_map.Clear();
|
||||
}
|
||||
|
||||
private static void OnPlayModeStateChanged(PlayModeStateChange state)
|
||||
{
|
||||
SceneView.RepaintAll();
|
||||
}
|
||||
|
||||
private static void OnSceneGui(SceneView sv)
|
||||
{
|
||||
if (sv == null) return;
|
||||
if (PrefabStageUtils.InEmptyStage) return;
|
||||
// ensure overlay exists for this SceneView
|
||||
var root = sv.rootVisualElement;
|
||||
if (root == null) return;
|
||||
|
||||
int id = sv.GetInstanceID();
|
||||
if (!s_map.ContainsKey(id))
|
||||
{
|
||||
// avoid duplicate q by name
|
||||
var existing = root.Q<VisualElement>("ux-controller-ve");
|
||||
if (existing != null)
|
||||
{
|
||||
// if already present (from another vm), keep it and track
|
||||
var ve = existing as UXControllerOverlayVE;
|
||||
if (ve != null) s_map[id] = ve;
|
||||
}
|
||||
else
|
||||
{
|
||||
var ve = new UXControllerOverlayVE();
|
||||
ve.name = "ux-controller-ve";
|
||||
// place at top so it overlays scene view content
|
||||
ve.style.position = Position.Absolute;
|
||||
ve.style.left = 0;
|
||||
ve.style.top = 0;
|
||||
ve.style.width = Length.Percent(100);
|
||||
// z-index: bring to front
|
||||
// ve.style.unityZIndex = 1000;
|
||||
root.Add(ve);
|
||||
s_map[id] = ve;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void EditorUpdate()
|
||||
{
|
||||
// Refresh all overlays (they check for changes internally and early out)
|
||||
foreach (var kv in s_map)
|
||||
{
|
||||
var ve = kv.Value;
|
||||
if (ve != null) ve.EditorUpdate();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class UXControllerOverlayVE : VisualElement
|
||||
{
|
||||
private VisualElement _root;
|
||||
private VisualElement _controllerList;
|
||||
private Button _addBtn;
|
||||
private PrefabStage _currentPrefabStage;
|
||||
private UXController _currentUX;
|
||||
private int _lastControllersHash = 0;
|
||||
|
||||
public UXControllerOverlayVE()
|
||||
{
|
||||
style.flexDirection = FlexDirection.Column;
|
||||
style.paddingLeft = 6;
|
||||
style.paddingTop = 6;
|
||||
style.paddingRight = 6;
|
||||
style.paddingBottom = 6;
|
||||
style.width = Length.Percent(100);
|
||||
style.unityBackgroundImageTintColor = Color.white;
|
||||
style.height = Length.Percent(5);
|
||||
|
||||
|
||||
style.backgroundColor = new StyleColor(new Color(0.12f, 0.12f, 0.12f, 0.95f));
|
||||
style.borderTopWidth = 1;
|
||||
style.borderBottomWidth = 1;
|
||||
style.borderLeftWidth = 1;
|
||||
style.borderRightWidth = 1;
|
||||
style.borderTopColor = new StyleColor(new Color(0, 0, 0, 0.6f));
|
||||
style.borderBottomColor = new StyleColor(new Color(0, 0, 0, 0.6f));
|
||||
style.borderLeftColor = new StyleColor(new Color(0, 0, 0, 0.6f));
|
||||
style.borderRightColor = new StyleColor(new Color(0, 0, 0, 0.6f));
|
||||
style.paddingBottom = 8;
|
||||
style.paddingTop = 6;
|
||||
style.paddingLeft = 6;
|
||||
style.paddingRight = 6;
|
||||
|
||||
_addBtn = new Button(() =>
|
||||
{
|
||||
if (_currentUX != null)
|
||||
{
|
||||
UXControllerAddWindow.ShowWindow(_currentUX);
|
||||
}
|
||||
else
|
||||
{
|
||||
var prefabStage = PrefabStageUtility.GetCurrentPrefabStage();
|
||||
if (prefabStage != null && prefabStage.prefabContentsRoot != null)
|
||||
{
|
||||
Undo.AddComponent<UXController>(prefabStage.prefabContentsRoot);
|
||||
EditorUtility.SetDirty(prefabStage.prefabContentsRoot);
|
||||
}
|
||||
}
|
||||
|
||||
Refresh();
|
||||
});
|
||||
|
||||
_addBtn.style.width = 22;
|
||||
_addBtn.style.height = 22;
|
||||
_addBtn.style.backgroundColor = new StyleColor(new Color(0.26f, 0.26f, 0.26f));
|
||||
_addBtn.style.marginRight = 0;
|
||||
_addBtn.style.borderBottomLeftRadius = 4;
|
||||
_addBtn.style.borderBottomRightRadius = 4;
|
||||
_addBtn.style.borderTopLeftRadius = 4;
|
||||
_addBtn.style.borderTopRightRadius = 4;
|
||||
_addBtn.style.alignSelf = Align.Center;
|
||||
_addBtn.style.justifyContent = Justify.Center;
|
||||
_addBtn.tooltip = "添加新的控制器";
|
||||
|
||||
var cLabel = new Label("+");
|
||||
cLabel.style.unityFontStyleAndWeight = FontStyle.Bold;
|
||||
cLabel.style.unityTextAlign = TextAnchor.MiddleCenter;
|
||||
cLabel.style.color = new StyleColor(Color.white);
|
||||
cLabel.style.fontSize = 12;
|
||||
cLabel.style.flexGrow = 0;
|
||||
_addBtn.Add(cLabel);
|
||||
|
||||
|
||||
var scroll = new ScrollView(ScrollViewMode.Horizontal);
|
||||
scroll.style.flexDirection = FlexDirection.Row;
|
||||
scroll.style.paddingLeft = 0;
|
||||
scroll.style.paddingRight = 2;
|
||||
scroll.style.paddingBottom = 0;
|
||||
scroll.style.paddingTop = 0;
|
||||
scroll.horizontalScrollerVisibility = ScrollerVisibility.Hidden;
|
||||
scroll.verticalScrollerVisibility = ScrollerVisibility.Hidden;
|
||||
|
||||
_controllerList = new VisualElement();
|
||||
_controllerList.style.flexDirection = FlexDirection.Row;
|
||||
_controllerList.style.flexWrap = Wrap.Wrap;
|
||||
_controllerList.style.alignItems = Align.FlexStart;
|
||||
|
||||
scroll.Add(_controllerList);
|
||||
scroll.Add(_addBtn);
|
||||
this.Add(scroll);
|
||||
|
||||
Refresh();
|
||||
}
|
||||
|
||||
public void EditorUpdate()
|
||||
{
|
||||
// called by manager on EditorApplication.update
|
||||
var prefabStage = PrefabStageUtility.GetCurrentPrefabStage();
|
||||
if (prefabStage != _currentPrefabStage)
|
||||
{
|
||||
_currentPrefabStage = prefabStage;
|
||||
Refresh();
|
||||
return;
|
||||
}
|
||||
|
||||
if (_currentUX != null)
|
||||
{
|
||||
int h = ComputeControllersHash(_currentUX);
|
||||
if (h != _lastControllersHash)
|
||||
{
|
||||
_lastControllersHash = h;
|
||||
Refresh();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private int ComputeControllersHash(UXController ux)
|
||||
{
|
||||
if (ux == null) return 0;
|
||||
unchecked
|
||||
{
|
||||
int hash = 17;
|
||||
var list = ux.Controllers;
|
||||
if (list != null)
|
||||
{
|
||||
foreach (var cd in list)
|
||||
{
|
||||
hash = hash * 23 + (cd?.Name?.GetHashCode() ?? 0);
|
||||
hash = hash * 23 + (cd?.Length.GetHashCode() ?? 0);
|
||||
hash = hash * 23 + (cd?.CurrentIndex.GetHashCode() ?? 0);
|
||||
}
|
||||
}
|
||||
|
||||
return hash;
|
||||
}
|
||||
}
|
||||
|
||||
public void Refresh()
|
||||
{
|
||||
_controllerList.Clear();
|
||||
|
||||
var prefabStage = PrefabStageUtility.GetCurrentPrefabStage();
|
||||
_currentPrefabStage = prefabStage;
|
||||
_currentUX = null;
|
||||
if (prefabStage != null && prefabStage.prefabContentsRoot != null)
|
||||
{
|
||||
_currentUX = prefabStage.prefabContentsRoot.GetComponent<UXController>();
|
||||
}
|
||||
|
||||
if (PrefabStageUtils.InEmptyStage)
|
||||
{
|
||||
_addBtn.style.display = DisplayStyle.None;
|
||||
return;
|
||||
}
|
||||
|
||||
_addBtn.style.display = DisplayStyle.Flex;
|
||||
if (_currentUX == null)
|
||||
{
|
||||
var empty = new Label("当前 Prefab 未包含 UXController。点击右侧按钮可添加。");
|
||||
empty.style.unityFontStyleAndWeight = FontStyle.Italic;
|
||||
empty.style.color = new StyleColor(new Color(0.75f, 0.75f, 0.75f));
|
||||
empty.style.marginLeft = 6;
|
||||
empty.style.marginTop = 6;
|
||||
_controllerList.Add(empty);
|
||||
_lastControllersHash = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
var controllers = _currentUX.Controllers;
|
||||
if (controllers == null) return;
|
||||
|
||||
for (int i = 0; i < controllers.Count; i++)
|
||||
{
|
||||
var cd = controllers[i];
|
||||
var card = CreateControllerCard(cd, i);
|
||||
_controllerList.Add(card);
|
||||
}
|
||||
|
||||
_lastControllersHash = ComputeControllersHash(_currentUX);
|
||||
}
|
||||
|
||||
private VisualElement CreateControllerCard(UXController.ControllerData cd, int index)
|
||||
{
|
||||
var card = new VisualElement();
|
||||
card.style.flexDirection = FlexDirection.Row;
|
||||
card.style.alignItems = Align.Center;
|
||||
card.style.paddingLeft = 6;
|
||||
card.style.paddingRight = 6;
|
||||
card.style.paddingTop = 4;
|
||||
card.style.paddingBottom = 4;
|
||||
card.style.backgroundColor = new StyleColor(new Color(0.13f, 0.13f, 0.13f));
|
||||
card.style.borderTopLeftRadius = 6;
|
||||
card.style.borderBottomLeftRadius = 6;
|
||||
card.style.borderTopRightRadius = 6;
|
||||
card.style.borderBottomRightRadius = 6;
|
||||
card.style.minHeight = 28;
|
||||
card.style.marginRight = 4;
|
||||
card.style.marginBottom = 6;
|
||||
|
||||
// icon (C with underline)
|
||||
var cBox = new VisualElement();
|
||||
cBox.style.width = 22;
|
||||
cBox.style.height = 22;
|
||||
cBox.style.backgroundColor = new StyleColor(new Color(0.26f, 0.26f, 0.26f));
|
||||
cBox.style.marginRight = 0;
|
||||
cBox.style.borderBottomLeftRadius = 4;
|
||||
cBox.style.borderBottomRightRadius = 4;
|
||||
cBox.style.borderTopLeftRadius = 4;
|
||||
cBox.style.borderTopRightRadius = 4;
|
||||
cBox.style.alignSelf = Align.Center;
|
||||
cBox.style.justifyContent = Justify.Center;
|
||||
|
||||
var cLabel = new Label("C");
|
||||
cLabel.style.unityFontStyleAndWeight = FontStyle.Bold;
|
||||
cLabel.style.unityTextAlign = TextAnchor.MiddleCenter;
|
||||
cLabel.style.color = new StyleColor(Color.white);
|
||||
cLabel.style.fontSize = 12;
|
||||
cLabel.style.flexGrow = 0;
|
||||
cBox.Add(cLabel);
|
||||
|
||||
var underline = new VisualElement();
|
||||
underline.style.position = Position.Absolute;
|
||||
underline.style.height = 2;
|
||||
underline.style.width = 14;
|
||||
underline.style.left = 4;
|
||||
underline.style.bottom = 3;
|
||||
underline.style.backgroundColor = new StyleColor(Color.white);
|
||||
|
||||
cBox.Add(underline);
|
||||
|
||||
card.Add(cBox);
|
||||
|
||||
|
||||
var nameBtn = new Button(() => { UXControllerEditWindow.ShowWindow(_currentUX, index, cd.Name, cd.Length); });
|
||||
nameBtn.text = cd.Name;
|
||||
nameBtn.style.unityTextAlign = TextAnchor.MiddleLeft;
|
||||
nameBtn.style.width = 120;
|
||||
nameBtn.style.height = 22;
|
||||
nameBtn.style.marginRight = 6;
|
||||
|
||||
card.Add(nameBtn);
|
||||
|
||||
|
||||
var indicesContainer = new VisualElement();
|
||||
indicesContainer.style.flexDirection = FlexDirection.Row;
|
||||
indicesContainer.style.alignItems = Align.Center;
|
||||
|
||||
int length = Math.Max(1, cd.Length);
|
||||
for (int idx = 0; idx < length; idx++)
|
||||
{
|
||||
int capturedIdx = idx;
|
||||
bool isSelected = (cd.CurrentIndex == idx);
|
||||
|
||||
var idxBtn = new Button(() =>
|
||||
{
|
||||
SetControllerIndexViaReflection(_currentUX, index, capturedIdx);
|
||||
Refresh(); // force update visual state
|
||||
});
|
||||
idxBtn.text = idx.ToString();
|
||||
idxBtn.style.width = 22;
|
||||
idxBtn.style.height = 18;
|
||||
idxBtn.style.unityTextAlign = TextAnchor.MiddleCenter;
|
||||
idxBtn.style.paddingLeft = 0;
|
||||
idxBtn.style.paddingRight = 0;
|
||||
idxBtn.style.paddingTop = 0;
|
||||
idxBtn.style.paddingBottom = 0;
|
||||
idxBtn.style.fontSize = 11;
|
||||
idxBtn.style.borderTopLeftRadius = 3;
|
||||
idxBtn.style.borderTopRightRadius = 3;
|
||||
idxBtn.style.borderBottomLeftRadius = 3;
|
||||
idxBtn.style.borderBottomRightRadius = 3;
|
||||
idxBtn.style.marginRight = 4;
|
||||
|
||||
if (isSelected)
|
||||
{
|
||||
idxBtn.style.backgroundColor = new StyleColor(new Color(0.9f, 0.55f, 0.1f));
|
||||
idxBtn.style.color = new StyleColor(Color.white);
|
||||
}
|
||||
else
|
||||
{
|
||||
idxBtn.style.backgroundColor = new StyleColor(new Color(0.18f, 0.18f, 0.18f));
|
||||
idxBtn.style.color = new StyleColor(Color.white);
|
||||
}
|
||||
|
||||
indicesContainer.Add(idxBtn);
|
||||
}
|
||||
|
||||
card.Add(indicesContainer);
|
||||
|
||||
return card;
|
||||
}
|
||||
|
||||
private static void SetControllerIndexViaReflection(UXController ux, int controllerIndex, int selectedIndex)
|
||||
{
|
||||
if (ux == null) return;
|
||||
var t = ux.GetType();
|
||||
var mi = t.GetMethod("SetControllerIndex", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);
|
||||
if (mi != null)
|
||||
{
|
||||
mi.Invoke(ux, new object[] { controllerIndex, selectedIndex });
|
||||
EditorUtility.SetDirty(ux);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3dc3451006fe98b47bffcc2c2a872d91
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d7e417a455885fb468c5aa9c679b6628
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 460b36db23659db4cb26ee8ba3234ad9
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8268cda376c28d14db254b78f6c56df7
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,166 @@
|
||||
#if UNITY_EDITOR
|
||||
using UnityEditor;
|
||||
using UnityEngine.UIElements;
|
||||
using System.IO;
|
||||
using UnityEngine;
|
||||
using System;
|
||||
|
||||
namespace Aliciza.UXTool
|
||||
{
|
||||
public class PrefabSingleTab : VisualElement
|
||||
{
|
||||
public Button visual;
|
||||
private string m_guid;
|
||||
private static int m_maxwidth = PrefabTabs.m_maxWidth;
|
||||
private static int m_minwidth = PrefabTabs.m_minWidth;
|
||||
private static int m_maxcharacters = PrefabTabs.m_maxCharacters;
|
||||
private static int m_mincharacters = PrefabTabs.m_minCharacters;
|
||||
|
||||
public PrefabSingleTab(FileInfo info, string guid, int prefabcounts, bool isclose, int width)
|
||||
{
|
||||
// 计算每个 tab 的推荐宽度(防空)
|
||||
SceneView sceneView = SceneView.lastActiveSceneView;
|
||||
int prewidth = 100;
|
||||
if (sceneView != null && prefabcounts > 0)
|
||||
{
|
||||
prewidth = Mathf.Max(1, (int)sceneView.position.width / Math.Max(1, prefabcounts));
|
||||
}
|
||||
|
||||
// 根 Button(用于作为 tab 容器并响应 click)
|
||||
visual = new Button();
|
||||
visual.name = "Tab";
|
||||
|
||||
// 基本样式(尽量还原 UXML 的样式)
|
||||
visual.style.height = 20;
|
||||
visual.style.flexDirection = FlexDirection.Row;
|
||||
visual.style.alignItems = Align.Center;
|
||||
visual.style.justifyContent = Justify.FlexStart;
|
||||
visual.style.unityTextAlign = TextAnchor.MiddleCenter;
|
||||
visual.style.backgroundColor = new StyleColor(new Color(60f / 255f, 60f / 255f, 60f / 255f, 1f));
|
||||
// margin、padding、border 等默认即可
|
||||
|
||||
// Label
|
||||
Label label = new Label();
|
||||
label.name = "Label";
|
||||
label.style.paddingLeft = 5;
|
||||
label.style.whiteSpace = WhiteSpace.NoWrap;
|
||||
label.style.unityTextAlign = TextAnchor.MiddleCenter;
|
||||
label.style.alignSelf = Align.Center;
|
||||
// label 默认宽度稍后设置为 nowwidth
|
||||
|
||||
// 关闭按钮
|
||||
Button close = new Button();
|
||||
close.name = "Close";
|
||||
close.style.width = 20;
|
||||
close.style.height = 20;
|
||||
close.style.alignSelf = Align.Center;
|
||||
close.style.marginLeft = 3;
|
||||
close.style.marginRight = 3;
|
||||
|
||||
close.style.backgroundImage = new StyleBackground(EditorGUIUtility.IconContent("d_winbtn_mac_close_a").image as Texture2D);
|
||||
close.style.backgroundSize = new BackgroundSize(new Length(80, LengthUnit.Percent), new Length(80, LengthUnit.Percent));
|
||||
close.style.backgroundColor = Color.clear;
|
||||
close.style.borderLeftWidth = 0;
|
||||
close.style.borderRightWidth = 0;
|
||||
|
||||
visual.Add(label);
|
||||
visual.Add(close);
|
||||
|
||||
int nowwidth = m_maxwidth;
|
||||
if (isclose)
|
||||
{
|
||||
label.style.width = width;
|
||||
nowwidth = width;
|
||||
}
|
||||
else
|
||||
{
|
||||
int computed = Math.Min(Math.Max(m_minwidth, prewidth), m_maxwidth);
|
||||
label.style.width = computed;
|
||||
nowwidth = computed;
|
||||
}
|
||||
|
||||
int nowCharacterscounts = m_maxcharacters;
|
||||
if (nowwidth < m_maxwidth)
|
||||
nowCharacterscounts = Math.Max(m_maxcharacters - (int)((m_maxwidth - nowwidth) / 10), m_mincharacters);
|
||||
|
||||
m_guid = guid;
|
||||
string fileName = Path.GetFileNameWithoutExtension(info.Name);
|
||||
label.text = SetTextWithEllipsis(nowCharacterscounts, fileName);
|
||||
|
||||
// 事件绑定(与原逻辑一致)
|
||||
close.clickable.activators.Add(new ManipulatorActivationFilter { button = MouseButton.LeftMouse });
|
||||
close.clicked += OnClose;
|
||||
close.RegisterCallback<MouseEnterEvent, VisualElement>(OnHoverClose, close);
|
||||
close.RegisterCallback<MouseLeaveEvent, VisualElement>(OnHoverClose, close);
|
||||
|
||||
visual.RegisterCallback<MouseEnterEvent>(OnHoverChange);
|
||||
visual.RegisterCallback<MouseLeaveEvent>(OnHoverChange);
|
||||
|
||||
visual.clickable.activators.Add(new ManipulatorActivationFilter { button = MouseButton.LeftMouse });
|
||||
visual.clicked += OnClick;
|
||||
visual.RegisterCallback<MouseDownEvent>(Callback);
|
||||
|
||||
// 将 visual 作为当前元素的一部分(方便外部访问 this.visual)
|
||||
this.Add(visual);
|
||||
}
|
||||
|
||||
private void Callback(MouseDownEvent e)
|
||||
{
|
||||
if (e.button == 1)
|
||||
{
|
||||
ContextMenuUtils.Menu.BuildPrefabTabMenuItem(m_guid);
|
||||
ContextMenuUtils.ShowContextMenu();
|
||||
}
|
||||
}
|
||||
|
||||
private void OnHoverChange(EventBase e)
|
||||
{
|
||||
if (m_guid == PrefabTabs.SelectedGuid) return;
|
||||
if (e.eventTypeId == MouseEnterEvent.TypeId())
|
||||
{
|
||||
visual.style.backgroundColor = new StyleColor(new Color(78f / 255f, 78f / 255f, 78f / 255f, 1f));
|
||||
}
|
||||
else if (e.eventTypeId == MouseLeaveEvent.TypeId())
|
||||
{
|
||||
visual.style.backgroundColor = new StyleColor(new Color(60f / 255f, 60f / 255f, 60f / 255f, 1f));
|
||||
}
|
||||
}
|
||||
|
||||
private void OnHoverClose(EventBase e, VisualElement close)
|
||||
{
|
||||
// if (e.eventTypeId == MouseEnterEvent.TypeId())
|
||||
// {
|
||||
// close.style.backgroundColor = new StyleColor(new Color(60f / 255f, 60f / 255f, 60f / 255f, 1f));
|
||||
// }
|
||||
// else if (e.eventTypeId == MouseLeaveEvent.TypeId())
|
||||
// {
|
||||
// close.style.backgroundColor = new StyleColor(new Color(60f / 255f, 60f / 255f, 60f / 255f, 0f));
|
||||
// }
|
||||
}
|
||||
|
||||
private void OnClick()
|
||||
{
|
||||
PrefabTabs.OpenTab(m_guid, true);
|
||||
}
|
||||
|
||||
private void OnClose()
|
||||
{
|
||||
PrefabTabs.CloseTab(m_guid);
|
||||
}
|
||||
|
||||
private string SetTextWithEllipsis(int count, string name)
|
||||
{
|
||||
if (name.Length <= count)
|
||||
{
|
||||
return name;
|
||||
}
|
||||
else
|
||||
{
|
||||
string ans = name.Substring(0, Math.Max(0, count - 3));
|
||||
ans += "...";
|
||||
return ans;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 99285ec27ea549d99d16c4fd764c42a9
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,250 @@
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UIElements;
|
||||
using System.Linq;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Collections.Generic;
|
||||
using UnityEditor.SceneManagement;
|
||||
using UXScroller = UnityEngine.UIElements.Scroller;
|
||||
|
||||
namespace Aliciza.UXTool
|
||||
{
|
||||
public static class PrefabTabs
|
||||
{
|
||||
public static readonly int m_maxCharacters = 20;
|
||||
public static readonly int m_minCharacters = 13;
|
||||
public static readonly int m_maxWidth = 150;
|
||||
public static readonly int m_minWidth = 100;
|
||||
|
||||
public static VisualElement prefabTabsPanel;
|
||||
private static ScrollView TabsList;
|
||||
private static List<string> m_tabs;
|
||||
private static string m_selectedTab;
|
||||
private static SceneView sceneView = SceneView.lastActiveSceneView;
|
||||
private static int width;
|
||||
|
||||
public static string SelectedGuid
|
||||
{
|
||||
get { return m_selectedTab; }
|
||||
set
|
||||
{
|
||||
m_selectedTab = value;
|
||||
EditorPrefs.SetString("AlicizaUXTool_SelectedTab", value); // 记录当前选中的Tab
|
||||
}
|
||||
}
|
||||
|
||||
[InitializeOnLoadMethod]
|
||||
public static void Initialize()
|
||||
{
|
||||
UXDesinUtil.OnEnterDesignMode += RegisterEvents;
|
||||
UXDesinUtil.OnExitDesignMode += UnRegisterEvents;
|
||||
|
||||
if (UXDesinUtil.InDesign)
|
||||
{
|
||||
RegisterEvents();
|
||||
}
|
||||
else
|
||||
{
|
||||
UnRegisterEvents();
|
||||
}
|
||||
}
|
||||
|
||||
private static void RegisterEvents()
|
||||
{
|
||||
PrefabStageUtils.OnEditRootReplaced += OnEditRootReplaced;
|
||||
EditorApplication.delayCall += OnEditorLoaded;
|
||||
UXPrefabTabsConfig.Instance.SyncTabs();
|
||||
}
|
||||
|
||||
private static void UnRegisterEvents()
|
||||
{
|
||||
PrefabStageUtils.OnEditRootReplaced -= OnEditRootReplaced;
|
||||
EditorApplication.delayCall -= OnEditorLoaded;
|
||||
}
|
||||
|
||||
private static void OnEditorLoaded()
|
||||
{
|
||||
if (UXDesinUtil.InDesign)
|
||||
{
|
||||
CreateGUI();
|
||||
RefreshTabs(false, 0);
|
||||
OpenOrFallbackToLastValidTab();
|
||||
}
|
||||
}
|
||||
|
||||
private static void OnEditRootReplaced(string assetPath)
|
||||
{
|
||||
string guid = AssetDatabase.AssetPathToGUID(assetPath);
|
||||
SelectedGuid = guid;
|
||||
RefreshTabs(false, 0);
|
||||
}
|
||||
|
||||
private static void CreateGUI()
|
||||
{
|
||||
sceneView = SceneView.lastActiveSceneView;
|
||||
|
||||
if (sceneView == null) return;
|
||||
|
||||
prefabTabsPanel = new VisualElement();
|
||||
prefabTabsPanel.name = "Tabs";
|
||||
prefabTabsPanel.style.height = new StyleLength(new Length(100, LengthUnit.Percent));
|
||||
prefabTabsPanel.style.position = Position.Relative;
|
||||
prefabTabsPanel.style.top = 0;
|
||||
prefabTabsPanel.style.right = 0;
|
||||
prefabTabsPanel.style.left = 0;
|
||||
prefabTabsPanel.style.backgroundColor = new Color(48f / 255f, 48f / 255f, 48f / 255f); // rgb(48, 48, 48)
|
||||
prefabTabsPanel.style.flexGrow = 0;
|
||||
prefabTabsPanel.style.paddingRight = 0;
|
||||
prefabTabsPanel.style.marginRight = 160;
|
||||
|
||||
TabsList = new ScrollView(ScrollViewMode.Horizontal);
|
||||
TabsList.name = "TabsList";
|
||||
TabsList.horizontalScrollerVisibility = ScrollerVisibility.AlwaysVisible;
|
||||
TabsList.verticalScrollerVisibility = ScrollerVisibility.Hidden;
|
||||
|
||||
|
||||
prefabTabsPanel.Add(TabsList);
|
||||
|
||||
prefabTabsPanel.style.flexGrow = 1;
|
||||
prefabTabsPanel.style.height = Length.Percent(50);
|
||||
prefabTabsPanel.contentContainer.RegisterCallback<MouseLeaveEvent>((e) =>
|
||||
{
|
||||
Vector2 old = TabsList.scrollOffset;
|
||||
TabsList.scrollOffset = old;
|
||||
width = m_tabs.Count == 0 ? m_minWidth : Math.Min(Math.Max(m_minWidth, (int)sceneView.position.width / m_tabs.Count), m_maxWidth);
|
||||
RefreshTabs(false, 0);
|
||||
});
|
||||
UXScroller scroller = TabsList.horizontalScroller;
|
||||
scroller.Remove(scroller.lowButton);
|
||||
scroller.Remove(scroller.highButton);
|
||||
scroller.slider.style.height = 4;
|
||||
scroller.slider.style.marginBottom = 0;
|
||||
scroller.slider.style.paddingBottom = 0;
|
||||
scroller.slider.style.marginLeft = scroller.slider.style.marginRight = 0;
|
||||
scroller.style.height = 5;
|
||||
scroller.style.bottom = 0;
|
||||
scroller.style.marginBottom = 0;
|
||||
|
||||
var parent = sceneView.rootVisualElement.parent.parent.parent.parent.parent.Q<IMGUIContainer>();
|
||||
if (parent != null) parent.Add(prefabTabsPanel);
|
||||
parent.style.flexShrink = 1;
|
||||
parent.style.width = Length.Percent(100);
|
||||
parent.style.flexGrow = 0.08f;
|
||||
parent.style.height = Length.Percent(5);
|
||||
|
||||
m_tabs = UXPrefabTabsConfig.Instance.tabs;
|
||||
m_selectedTab = EditorPrefs.GetString("AlicizaUXTool_SelectedTab", string.Empty);
|
||||
if (m_tabs.Find(p => p == m_selectedTab) == null) m_selectedTab = string.Empty;
|
||||
RefreshTabs(false, 0); // 初始化时刷新 Tabs
|
||||
}
|
||||
|
||||
public static void RefreshTabs(bool isclose, int width)
|
||||
{
|
||||
if (prefabTabsPanel == null) return;
|
||||
#if UNITY_2021_3_OR_NEWER
|
||||
prefabTabsPanel.style.top = 0;
|
||||
#else
|
||||
prefabTabsPanel.style.top = string.IsNullOrEmpty(m_selectedTab) ? 21 : 46;
|
||||
#endif
|
||||
bool flag = TabsList.contentContainer.childCount == m_tabs.Count;
|
||||
TabsList.Clear();
|
||||
Button selectButton = null;
|
||||
foreach (var item in m_tabs)
|
||||
{
|
||||
string path = AssetDatabase.GUIDToAssetPath(item);
|
||||
if (path != "" && File.Exists(path))
|
||||
{
|
||||
var tab = new PrefabSingleTab(new FileInfo(path), item, m_tabs.Count, isclose, width);
|
||||
if (item == m_selectedTab)
|
||||
{
|
||||
tab.visual.style.backgroundColor = new Color(95f / 255, 95f / 255, 95f / 255, 1);
|
||||
selectButton = tab.visual;
|
||||
}
|
||||
else
|
||||
{
|
||||
tab.visual.style.backgroundColor = new Color(60f / 255, 60f / 255, 60f / 255, 1);
|
||||
}
|
||||
|
||||
TabsList.Add(tab.visual);
|
||||
}
|
||||
}
|
||||
|
||||
if (selectButton != null)
|
||||
{
|
||||
if (flag)
|
||||
{
|
||||
selectButton.RegisterCallback<GeometryChangedEvent, VisualElement>(FirstLayoutCallback, selectButton);
|
||||
}
|
||||
else
|
||||
{
|
||||
TabsList.contentContainer.RegisterCallback<GeometryChangedEvent, VisualElement>(FirstLayoutCallback, selectButton);
|
||||
}
|
||||
}
|
||||
|
||||
UXPrefabTabsConfig.Instance.tabs = m_tabs; // 保存当前 Tabs 状态
|
||||
UXPrefabTabsConfig.Save(); // 保存到磁盘
|
||||
}
|
||||
|
||||
private static void OpenOrFallbackToLastValidTab()
|
||||
{
|
||||
// 尝试打开当前选中的GUID
|
||||
if (!string.IsNullOrEmpty(m_selectedTab) && m_tabs.Contains(m_selectedTab))
|
||||
{
|
||||
PrefabStageUtils.SwitchStage(AssetDatabase.GUIDToAssetPath(m_selectedTab));
|
||||
}
|
||||
else
|
||||
{
|
||||
// 移除不存在的GUID并选中最后一个有效的
|
||||
m_tabs.RemoveAll(guid =>
|
||||
{
|
||||
string path = AssetDatabase.GUIDToAssetPath(guid);
|
||||
return string.IsNullOrEmpty(path) || !File.Exists(path);
|
||||
});
|
||||
|
||||
if (m_tabs.Count > 0)
|
||||
{
|
||||
m_selectedTab = m_tabs.Last();
|
||||
PrefabStageUtils.SwitchStage(AssetDatabase.GUIDToAssetPath(m_selectedTab));
|
||||
}
|
||||
else
|
||||
{
|
||||
m_selectedTab = string.Empty;
|
||||
PrefabStageUtils.OpenDefaultStage();
|
||||
}
|
||||
}
|
||||
|
||||
RefreshTabs(false, 0);
|
||||
}
|
||||
|
||||
|
||||
public static void OpenTab(string guid, bool replaceRoot)
|
||||
{
|
||||
SelectedGuid = guid;
|
||||
if (replaceRoot)
|
||||
{
|
||||
PrefabStageUtils.SwitchStage(AssetDatabase.GUIDToAssetPath(guid));
|
||||
}
|
||||
|
||||
if (!m_tabs.Contains(guid))
|
||||
{
|
||||
m_tabs.Add(guid);
|
||||
width = Math.Min(Math.Max(m_minWidth, (int)sceneView.position.width / m_tabs.Count), m_maxWidth);
|
||||
}
|
||||
|
||||
RefreshTabs(false, 0);
|
||||
}
|
||||
|
||||
public static void CloseTab(string guid)
|
||||
{
|
||||
m_tabs.Remove(guid);
|
||||
OpenOrFallbackToLastValidTab();
|
||||
}
|
||||
|
||||
private static void FirstLayoutCallback(GeometryChangedEvent evt, VisualElement v)
|
||||
{
|
||||
if (!TabsList.Contains(v)) return;
|
||||
TabsList.ScrollTo(v);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ddadf2863f864c74b994822b0dfcec71
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b118a0eb1d94cad4abc3fbda06d94698
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: dc4810274e14449aa8d6e0f9ef1ad21a
|
||||
timeCreated: 1763717108
|
||||
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6387ac1bfd9741d9a73c7b7247100b17
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,517 @@
|
||||
#if UNITY_EDITOR
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
namespace Aliciza.UXTool
|
||||
{
|
||||
public static class ResolutionController
|
||||
{
|
||||
private const string EditorPrefsKey = "ResolutionViewInitialized";
|
||||
|
||||
private static bool hasInitialized
|
||||
{
|
||||
get { return EditorPrefs.GetBool(EditorPrefsKey, false); }
|
||||
set { EditorPrefs.SetBool(EditorPrefsKey, value); }
|
||||
}
|
||||
|
||||
// per-SceneView UI instances (避免重复添加同一 VisualElement 到多个父节点)
|
||||
private static readonly Dictionary<int, VisualElement> roots = new Dictionary<int, VisualElement>();
|
||||
private static readonly Dictionary<int, IMGUIContainer> containers = new Dictionary<int, IMGUIContainer>();
|
||||
private static readonly HashSet<int> guiSubscribedIds = new HashSet<int>();
|
||||
|
||||
// 反射相关
|
||||
private static MethodInfo gameViewSizePopupMethod;
|
||||
private static PropertyInfo selectedSizeIndexProperty;
|
||||
private static object gameViewInstance;
|
||||
private static int selectedSizeIndex = -1;
|
||||
private static object currentSizeGroupTypeObj;
|
||||
|
||||
// 事件订阅标志
|
||||
private static bool subscribedToEditorLoadEvents = false;
|
||||
private static bool subscribedToUpdate = false;
|
||||
|
||||
// 布局参数(需要时可调整)
|
||||
private const float GuiWidth = 160f;
|
||||
private const float RootWidth = 180f;
|
||||
private const float RootHeight = 22f;
|
||||
private const float OffsetTop = -22f;
|
||||
private const float OffsetRight = -20f;
|
||||
|
||||
[InitializeOnLoadMethod]
|
||||
public static void Initialize()
|
||||
{
|
||||
// 在初始化时恢复(确保干净状态),并注册自己的事件
|
||||
RestoreOriginalToolbar();
|
||||
UXDesinUtil.OnEnterDesignMode += RegisterEvents;
|
||||
UXDesinUtil.OnExitDesignMode += UnRegisterEvents;
|
||||
|
||||
if (UXDesinUtil.InDesign)
|
||||
{
|
||||
RegisterEvents();
|
||||
}
|
||||
else
|
||||
{
|
||||
UnRegisterEvents();
|
||||
}
|
||||
}
|
||||
|
||||
private static void RegisterEvents()
|
||||
{
|
||||
if (subscribedToEditorLoadEvents) return;
|
||||
EditorApplication.delayCall += OnEditorLoadDelay;
|
||||
AssemblyReloadEvents.beforeAssemblyReload += OnBeforeAssemblyReload;
|
||||
AssemblyReloadEvents.afterAssemblyReload += OnAfterAssemblyReload;
|
||||
subscribedToEditorLoadEvents = true;
|
||||
}
|
||||
|
||||
private static void UnRegisterEvents()
|
||||
{
|
||||
if (!subscribedToEditorLoadEvents) return;
|
||||
EditorApplication.delayCall -= OnEditorLoadDelay;
|
||||
AssemblyReloadEvents.beforeAssemblyReload -= OnBeforeAssemblyReload;
|
||||
AssemblyReloadEvents.afterAssemblyReload -= OnAfterAssemblyReload;
|
||||
subscribedToEditorLoadEvents = false;
|
||||
}
|
||||
|
||||
private static void OnEditorLoadDelay()
|
||||
{
|
||||
// EnsureInitialized 是幂等的
|
||||
if (!hasInitialized)
|
||||
{
|
||||
EnsureInitialized();
|
||||
}
|
||||
|
||||
// 仅在设计模式下启动 per-frame 更新
|
||||
if (UXDesinUtil.InDesign)
|
||||
{
|
||||
UpdateSceneViewUI(); // 立即尝试添加
|
||||
if (!subscribedToUpdate)
|
||||
{
|
||||
EditorApplication.update += UpdateSceneViewUI;
|
||||
subscribedToUpdate = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// 不在设计模式则移除所有 UI
|
||||
Cleanup();
|
||||
}
|
||||
}
|
||||
|
||||
private static void OnBeforeAssemblyReload()
|
||||
{
|
||||
Cleanup();
|
||||
}
|
||||
|
||||
private static void OnAfterAssemblyReload()
|
||||
{
|
||||
// 延迟调用以等待编辑器恢复
|
||||
EditorApplication.delayCall += OnEditorLoadDelay;
|
||||
}
|
||||
|
||||
private static void EnsureInitialized()
|
||||
{
|
||||
if (hasInitialized) return;
|
||||
|
||||
bool ok = InitWindowData();
|
||||
if (!ok)
|
||||
{
|
||||
hasInitialized = false;
|
||||
return;
|
||||
}
|
||||
|
||||
// 不再在此创建单一 Root;Root 在 AddRootToSceneView 时为每个 SceneView 创建
|
||||
hasInitialized = true;
|
||||
}
|
||||
|
||||
private static bool InitWindowData()
|
||||
{
|
||||
try
|
||||
{
|
||||
// 尝试通过你项目已有的 Utils 获取主 GameView
|
||||
try
|
||||
{
|
||||
// 如果项目提供了 Utils.GetMainPlayModeView(),保留调用(你原代码中提到)
|
||||
var utilsType = Type.GetType("Utils");
|
||||
if (utilsType != null)
|
||||
{
|
||||
var method = utilsType.GetMethod("GetMainPlayModeView", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static);
|
||||
if (method != null)
|
||||
{
|
||||
gameViewInstance = method.Invoke(null, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
// 忽略 utils 获取失败,继续使用反射查找
|
||||
}
|
||||
|
||||
// 如果为空,尝试从类型查找已存在的 GameView 实例
|
||||
if (gameViewInstance == null)
|
||||
{
|
||||
var gvType = typeof(Editor).Assembly.GetType("UnityEditor.GameView");
|
||||
if (gvType != null)
|
||||
{
|
||||
var arr = Resources.FindObjectsOfTypeAll(gvType);
|
||||
if (arr != null && arr.Length > 0)
|
||||
gameViewInstance = arr[0];
|
||||
}
|
||||
}
|
||||
|
||||
if (gameViewInstance == null)
|
||||
return false;
|
||||
|
||||
var editorGUILayoutType = Type.GetType("UnityEditor.EditorGUILayout,UnityEditor")
|
||||
?? typeof(Editor).Assembly.GetType("UnityEditor.EditorGUILayout");
|
||||
if (editorGUILayoutType != null)
|
||||
{
|
||||
gameViewSizePopupMethod = editorGUILayoutType.GetMethod("GameViewSizePopup", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static);
|
||||
}
|
||||
|
||||
var gameViewType = typeof(Editor).Assembly.GetType("UnityEditor.GameView");
|
||||
if (gameViewType == null) return false;
|
||||
|
||||
selectedSizeIndexProperty = gameViewType.GetProperty("selectedSizeIndex", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
|
||||
if (selectedSizeIndexProperty == null) return false;
|
||||
|
||||
var currentSizeGroupProp = gameViewType.GetProperty("currentSizeGroupType", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance);
|
||||
if (currentSizeGroupProp != null)
|
||||
{
|
||||
var getMethod = currentSizeGroupProp.GetGetMethod(nonPublic: true);
|
||||
if (getMethod != null)
|
||||
{
|
||||
if (getMethod.IsStatic)
|
||||
currentSizeGroupTypeObj = currentSizeGroupProp.GetValue(null);
|
||||
else
|
||||
currentSizeGroupTypeObj = currentSizeGroupProp.GetValue(gameViewInstance);
|
||||
}
|
||||
}
|
||||
|
||||
// 初始索引读取
|
||||
selectedSizeIndex = (int)selectedSizeIndexProperty.GetValue(gameViewInstance);
|
||||
|
||||
return true;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.LogWarning("[ResolutionController] InitWindowData failed: " + e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static void UpdateSceneViewUI()
|
||||
{
|
||||
try
|
||||
{
|
||||
// 仅当 UXDesinUtil 在设计模式时显示
|
||||
if (!UXDesinUtil.InDesign)
|
||||
{
|
||||
if (hasInitialized)
|
||||
RestoreOriginalToolbar();
|
||||
return;
|
||||
}
|
||||
|
||||
EnsureInitialized();
|
||||
|
||||
if (!hasInitialized)
|
||||
return;
|
||||
|
||||
// 为所有可用 SceneView 保证 UI(多 SceneView 情况下,每个 SceneView 一个实例,避免重复添加同一个 VE)
|
||||
foreach (SceneView sceneView in SceneView.sceneViews)
|
||||
{
|
||||
if (sceneView == null) continue;
|
||||
AddRootToSceneView(sceneView);
|
||||
}
|
||||
|
||||
// 触发 repaint
|
||||
var last = SceneView.lastActiveSceneView;
|
||||
if (last != null) last.Repaint();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.LogWarning("[ResolutionController] UpdateSceneViewUI exception: " + e);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 为指定的 SceneView 创建并添加 Root + IMGUIContainer(如果尚未添加)。
|
||||
/// 使用 sceneView.GetInstanceID() 作为 key,避免重复创建/添加。
|
||||
/// </summary>
|
||||
private static void AddRootToSceneView(SceneView sceneView)
|
||||
{
|
||||
if (sceneView == null) return;
|
||||
|
||||
int id = sceneView.GetInstanceID();
|
||||
|
||||
try
|
||||
{
|
||||
var rootVisual = sceneView.rootVisualElement;
|
||||
if (rootVisual == null) return;
|
||||
|
||||
// 如果已经为该 SceneView 创建过 root,则确保它还在该 SceneView 下
|
||||
if (roots.ContainsKey(id))
|
||||
{
|
||||
var existingRoot = roots[id];
|
||||
if (existingRoot == null || existingRoot.parent == null)
|
||||
{
|
||||
// 如果被移除则重加
|
||||
if (existingRoot != null)
|
||||
{
|
||||
rootVisual.Add(existingRoot);
|
||||
}
|
||||
else
|
||||
{
|
||||
// 如果被 GC 或置 null,则删掉记录并重新创建
|
||||
roots.Remove(id);
|
||||
if (containers.ContainsKey(id))
|
||||
containers.Remove(id);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// 若 rootVisual 中已有同名元素(来自旧版本或其他插件残留),不要重复添加
|
||||
const string rootName = "ResolutionController_Root";
|
||||
var found = rootVisual.Q(rootName);
|
||||
if (found != null)
|
||||
{
|
||||
// 已经存在一个元素(可能来自之前的装载),把它当作该 SceneView 的 root
|
||||
roots[id] = found;
|
||||
// 如果需要,确保里面有 IMGUIContainer(若无,创建一个)
|
||||
var gui = found.Q<IMGUIContainer>("IMGUIContainer_ResolutionController");
|
||||
if (gui == null)
|
||||
{
|
||||
var newGui = CreateAndAttachIMGUI(found, id);
|
||||
containers[id] = newGui;
|
||||
SubscribeGuiHandlerIfNeeded(id, newGui);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// 创建新的 root + imGUIContainer 实例并加入到 sceneView.rootVisualElement
|
||||
var newRoot = new VisualElement();
|
||||
newRoot.name = rootName;
|
||||
newRoot.style.position = Position.Absolute;
|
||||
newRoot.style.top = OffsetTop;
|
||||
newRoot.style.right = OffsetRight;
|
||||
newRoot.style.width = RootWidth;
|
||||
newRoot.style.height = RootHeight;
|
||||
newRoot.style.overflow = Overflow.Visible;
|
||||
|
||||
var newGuiContainer = new IMGUIContainer();
|
||||
newGuiContainer.name = "IMGUIContainer_ResolutionController";
|
||||
newGuiContainer.style.width = RootWidth;
|
||||
newGuiContainer.style.height = RootHeight;
|
||||
|
||||
newRoot.Add(newGuiContainer);
|
||||
rootVisual.Add(newRoot);
|
||||
|
||||
roots[id] = newRoot;
|
||||
containers[id] = newGuiContainer;
|
||||
|
||||
SubscribeGuiHandlerIfNeeded(id, newGuiContainer);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.LogWarning("[ResolutionController] AddRootToSceneView failed: " + e);
|
||||
}
|
||||
}
|
||||
|
||||
private static IMGUIContainer CreateAndAttachIMGUI(VisualElement root, int id)
|
||||
{
|
||||
var gui = new IMGUIContainer();
|
||||
gui.name = "IMGUIContainer_ResolutionController";
|
||||
gui.style.width = RootWidth;
|
||||
gui.style.height = RootHeight;
|
||||
root.Add(gui);
|
||||
return gui;
|
||||
}
|
||||
|
||||
private static void SubscribeGuiHandlerIfNeeded(int id, IMGUIContainer gui)
|
||||
{
|
||||
if (gui == null) return;
|
||||
if (guiSubscribedIds.Contains(id)) return;
|
||||
gui.onGUIHandler += () => OnGUIHandlerForId(id);
|
||||
guiSubscribedIds.Add(id);
|
||||
}
|
||||
|
||||
private static void UnsubscribeGuiHandlerIfNeeded(int id)
|
||||
{
|
||||
if (!guiSubscribedIds.Contains(id)) return;
|
||||
if (containers.TryGetValue(id, out var gui) && gui != null)
|
||||
{
|
||||
// 无法直接移除匿名 lambda,因此改为移除所有并重新创建时控制(但为保险尝试移除单个引用)
|
||||
try
|
||||
{
|
||||
gui.onGUIHandler -= () => OnGUIHandlerForId(id);
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
guiSubscribedIds.Remove(id);
|
||||
}
|
||||
|
||||
private static void OnGUIHandlerForId(int id)
|
||||
{
|
||||
try
|
||||
{
|
||||
GUILayoutOption[] ops = new GUILayoutOption[] { GUILayout.Width(GuiWidth) };
|
||||
|
||||
if (gameViewSizePopupMethod != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
// 兼容不同签名
|
||||
gameViewSizePopupMethod.Invoke(null, new object[] { currentSizeGroupTypeObj, selectedSizeIndex, gameViewInstance, EditorStyles.toolbarPopup, ops });
|
||||
}
|
||||
catch (TargetParameterCountException)
|
||||
{
|
||||
try
|
||||
{
|
||||
gameViewSizePopupMethod.Invoke(null, new object[] { selectedSizeIndex, gameViewInstance, EditorStyles.toolbarPopup, ops });
|
||||
}
|
||||
catch
|
||||
{
|
||||
// 忽略
|
||||
}
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// 忽略单次调用错误
|
||||
}
|
||||
}
|
||||
|
||||
OnGUI();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.LogWarning("[ResolutionController] OnGUIHandler exception: " + e);
|
||||
}
|
||||
}
|
||||
|
||||
private static void OnGUI()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (gameViewInstance == null || selectedSizeIndexProperty == null) return;
|
||||
|
||||
int nowIndex = (int)selectedSizeIndexProperty.GetValue(gameViewInstance);
|
||||
if (nowIndex != selectedSizeIndex)
|
||||
{
|
||||
selectedSizeIndex = nowIndex;
|
||||
// 如果需要可在此触发回调或通知其他系统
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.LogWarning("[ResolutionController] OnGUI exception: " + e);
|
||||
}
|
||||
}
|
||||
|
||||
private static void RestoreOriginalToolbar()
|
||||
{
|
||||
try
|
||||
{
|
||||
// 取消每个 sceneView 的 gui handler 并移除 roots
|
||||
foreach (var kv in new List<int>(roots.Keys))
|
||||
{
|
||||
int id = kv;
|
||||
if (containers.TryGetValue(id, out var gui) && gui != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
// 尝试移除我们的 handler(如果用了 lambda,直接移除可能无效,但我们会移除整个元素)
|
||||
gui.onGUIHandler = null;
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
if (roots.TryGetValue(id, out var root) && root != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (root.parent != null)
|
||||
root.parent.Remove(root);
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.LogWarning("[ResolutionController] RestoreOriginalToolbar failed: " + e);
|
||||
}
|
||||
finally
|
||||
{
|
||||
// 保持状态清理
|
||||
hasInitialized = false;
|
||||
guiSubscribedIds.Clear();
|
||||
containers.Clear();
|
||||
roots.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
private static void Cleanup()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (subscribedToUpdate)
|
||||
{
|
||||
EditorApplication.update -= UpdateSceneViewUI;
|
||||
subscribedToUpdate = false;
|
||||
}
|
||||
|
||||
// 移除所有 gui handlers 并删除 UI
|
||||
foreach (var id in new List<int>(containers.Keys))
|
||||
{
|
||||
try
|
||||
{
|
||||
var gui = containers[id];
|
||||
if (gui != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
gui.onGUIHandler = null;
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
foreach (var kv in new List<int>(roots.Keys))
|
||||
{
|
||||
var root = roots[kv];
|
||||
try
|
||||
{
|
||||
if (root != null && root.parent != null)
|
||||
{
|
||||
root.parent.Remove(root);
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
guiSubscribedIds.Clear();
|
||||
containers.Clear();
|
||||
roots.Clear();
|
||||
|
||||
gameViewSizePopupMethod = null;
|
||||
selectedSizeIndexProperty = null;
|
||||
gameViewInstance = null;
|
||||
currentSizeGroupTypeObj = null;
|
||||
|
||||
hasInitialized = false;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.LogWarning("[ResolutionController] Cleanup exception: " + e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 90b75fdc99d74744aa927bf3c3fe26c5
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: eac5a1940a9ac9747a8304c5021714b2
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,140 @@
|
||||
using System.Linq;
|
||||
using Aliciza.UXTool;
|
||||
using AlicizaX.AnimationFlow.Runtime;
|
||||
using AlicizaX.Localization.Editor;
|
||||
using AlicizaX.Localization.Runtime;
|
||||
using AlicizaX.UI.Editor;
|
||||
using Paps.UnityToolbarExtenderUIToolkit;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
namespace Aliciza.UXTool
|
||||
{
|
||||
[MainToolbarElement("ToolBarUXAnimation", alignment: ToolbarAlign.Right, order: 1)]
|
||||
public class ToolBarUXAnimation : IMGUIContainer
|
||||
{
|
||||
private GUIContent btnAnimation;
|
||||
|
||||
public void InitializeElement()
|
||||
{
|
||||
btnAnimation = EditorGUIUtility.TrTextContentWithIcon("动画编辑", "", "AnimationClip Icon");
|
||||
UXDesinUtil.OnEnterDesignMode += OnEntryDesignMode;
|
||||
UXDesinUtil.OnExitDesignMode += OnExitDesignMode;
|
||||
if (UXDesinUtil.InDesign)
|
||||
OnEntryDesignMode();
|
||||
else
|
||||
{
|
||||
OnExitDesignMode();
|
||||
}
|
||||
}
|
||||
|
||||
private void OnExitDesignMode()
|
||||
{
|
||||
onGUIHandler = null;
|
||||
}
|
||||
|
||||
private void OnEntryDesignMode()
|
||||
{
|
||||
onGUIHandler = MyGUIMethod;
|
||||
}
|
||||
|
||||
private void MyGUIMethod()
|
||||
{
|
||||
GUILayout.BeginHorizontal();
|
||||
if (GUILayout.Button(btnAnimation, EditorStyles.toolbarButton, GUILayout.MaxWidth(120)))
|
||||
{
|
||||
var gameObject = PrefabStageUtils.StageRoot.gameObject;
|
||||
if (!gameObject.TryGetComponent(typeof(AnimationFlow), out Component flow))
|
||||
{
|
||||
gameObject.AddComponent(typeof(AnimationFlow));
|
||||
}
|
||||
Selection.activeGameObject = PrefabStageUtils.StageRoot.gameObject;
|
||||
UnityEditor.EditorApplication.ExecuteMenuItem("Window/AnimationGraph");
|
||||
}
|
||||
|
||||
GUILayout.Space(5);
|
||||
GUILayout.EndHorizontal();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[MainToolbarElement("ToolBarExportUIScriptCode", alignment: ToolbarAlign.Right, order: 1)]
|
||||
public class ToolBarExportUIScriptCode : IMGUIContainer
|
||||
{
|
||||
private GUIContent exportBtn;
|
||||
|
||||
public void InitializeElement()
|
||||
{
|
||||
exportBtn = EditorGUIUtility.TrTextContentWithIcon("导出代码", "", "cs Script Icon");
|
||||
UXDesinUtil.OnEnterDesignMode += OnEntryDesignMode;
|
||||
UXDesinUtil.OnExitDesignMode += OnExitDesignMode;
|
||||
if (UXDesinUtil.InDesign)
|
||||
OnEntryDesignMode();
|
||||
else
|
||||
{
|
||||
OnExitDesignMode();
|
||||
}
|
||||
}
|
||||
|
||||
private void OnExitDesignMode()
|
||||
{
|
||||
onGUIHandler = null;
|
||||
}
|
||||
|
||||
private void OnEntryDesignMode()
|
||||
{
|
||||
onGUIHandler = MyGUIMethod;
|
||||
}
|
||||
|
||||
private void MyGUIMethod()
|
||||
{
|
||||
GUILayout.BeginHorizontal();
|
||||
if (GUILayout.Button(exportBtn, EditorStyles.toolbarButton, GUILayout.MaxWidth(120)))
|
||||
{
|
||||
Selection.activeGameObject = PrefabStageUtils.StageRoot.gameObject;
|
||||
UIGenerateQuick.UIGenerateBind();
|
||||
}
|
||||
|
||||
GUILayout.Space(5);
|
||||
GUILayout.EndHorizontal();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
[MainToolbarElement(id: "ToolBarExitDesign", alignment: ToolbarAlign.Right, order: 3)]
|
||||
public class ToolBarExitDesign : IMGUIContainer
|
||||
{
|
||||
private GUIContent entryBtnContent;
|
||||
|
||||
|
||||
public void InitializeElement()
|
||||
{
|
||||
entryBtnContent = EditorGUIUtility.TrTextContentWithIcon("Exit Design", "", EditorGUIUtility.IconContent("d_BuildSettings.Standalone").image);
|
||||
onGUIHandler = MyGUIMethod;
|
||||
}
|
||||
|
||||
private void MyGUIMethod()
|
||||
{
|
||||
GUILayout.BeginHorizontal();
|
||||
|
||||
if (UXDesinUtil.InDesign)
|
||||
{
|
||||
entryBtnContent.text = "退出UI编辑器";
|
||||
if (GUILayout.Button(entryBtnContent, EditorStyles.toolbarButton, GUILayout.MaxWidth(125)))
|
||||
{
|
||||
UXDesinUtil.ExitUIDesinger();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
entryBtnContent.text = "进入UI编辑器";
|
||||
if (GUILayout.Button(entryBtnContent, EditorStyles.toolbarButton, GUILayout.MaxWidth(125)))
|
||||
{
|
||||
UXDesinUtil.OpenUIDesinger();
|
||||
}
|
||||
}
|
||||
|
||||
GUILayout.EndHorizontal();
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 009e07b906b2e7a4297b6a5b04a38ad4
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,229 @@
|
||||
using UnityEditor;
|
||||
using UnityEditor.Overlays;
|
||||
using UnityEditor.Toolbars;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Aliciza.UXTool
|
||||
{
|
||||
[Overlay(typeof(SceneView), "Align")]
|
||||
public class AlignToolbarOverlay : ToolbarOverlay
|
||||
{
|
||||
AlignToolbarOverlay() : base(
|
||||
AlignTopLeft.id,
|
||||
AlignTopCenter.id,
|
||||
AlignTopRight.id,
|
||||
AlignBottomLeft.id,
|
||||
AlignBottomCenter.id,
|
||||
AlignBottomRight.id,
|
||||
AlignHorizontalLeft.id,
|
||||
AlignHorizontalCenter.id,
|
||||
AlignHorizontalRight.id
|
||||
)
|
||||
{
|
||||
}
|
||||
|
||||
// 12 个按钮的初始化
|
||||
[EditorToolbarElement(AlignTopLeft.id, typeof(SceneView))]
|
||||
class AlignTopLeft : EditorToolbarButton
|
||||
{
|
||||
public const string id = "AlignToolbarOverlay/AlignTopLeft";
|
||||
|
||||
public AlignTopLeft()
|
||||
{
|
||||
text = "";
|
||||
icon = EditorGUIUtility.IconContent("d_align_vertically_top_active").image as Texture2D; // 使用带 _active 后缀的图标
|
||||
tooltip = "顶部左对齐";
|
||||
clicked += () => OnAlign(AlignType.TopLeft);
|
||||
}
|
||||
}
|
||||
|
||||
[EditorToolbarElement(AlignTopCenter.id, typeof(SceneView))]
|
||||
class AlignTopCenter : EditorToolbarButton
|
||||
{
|
||||
public const string id = "AlignToolbarOverlay/AlignTopCenter";
|
||||
|
||||
public AlignTopCenter()
|
||||
{
|
||||
text = "";
|
||||
icon = EditorGUIUtility.IconContent("d_align_vertically_center_active").image as Texture2D; // 使用带 _active 后缀的图标
|
||||
tooltip = "顶部居中对齐";
|
||||
clicked += () => OnAlign(AlignType.TopCenter);
|
||||
}
|
||||
}
|
||||
|
||||
[EditorToolbarElement(AlignTopRight.id, typeof(SceneView))]
|
||||
class AlignTopRight : EditorToolbarButton
|
||||
{
|
||||
public const string id = "AlignToolbarOverlay/AlignTopRight";
|
||||
|
||||
public AlignTopRight()
|
||||
{
|
||||
text = "";
|
||||
icon = EditorGUIUtility.IconContent("d_align_vertically_top_active").image as Texture2D; // 使用带 _active 后缀的图标
|
||||
tooltip = "顶部右对齐";
|
||||
clicked += () => OnAlign(AlignType.TopRight);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
[EditorToolbarElement(AlignBottomLeft.id, typeof(SceneView))]
|
||||
class AlignBottomLeft : EditorToolbarButton
|
||||
{
|
||||
public const string id = "AlignToolbarOverlay/AlignBottomLeft";
|
||||
|
||||
public AlignBottomLeft()
|
||||
{
|
||||
text = "";
|
||||
icon = EditorGUIUtility.IconContent("d_align_vertically_bottom_active").image as Texture2D; // 使用带 _active 后缀的图标
|
||||
tooltip = "左下对齐";
|
||||
clicked += () => OnAlign(AlignType.BottomLeft);
|
||||
}
|
||||
}
|
||||
|
||||
[EditorToolbarElement(AlignBottomCenter.id, typeof(SceneView))]
|
||||
class AlignBottomCenter : EditorToolbarButton
|
||||
{
|
||||
public const string id = "AlignToolbarOverlay/AlignBottomCenter";
|
||||
|
||||
public AlignBottomCenter()
|
||||
{
|
||||
text = "";
|
||||
icon = EditorGUIUtility.IconContent("d_align_vertically_center_active").image as Texture2D; // 使用带 _active 后缀的图标
|
||||
tooltip = "底部居中对齐";
|
||||
clicked += () => OnAlign(AlignType.BottomCenter);
|
||||
}
|
||||
}
|
||||
|
||||
[EditorToolbarElement(AlignBottomRight.id, typeof(SceneView))]
|
||||
class AlignBottomRight : EditorToolbarButton
|
||||
{
|
||||
public const string id = "AlignToolbarOverlay/AlignBottomRight";
|
||||
|
||||
public AlignBottomRight()
|
||||
{
|
||||
text = "";
|
||||
icon = EditorGUIUtility.IconContent("d_align_vertically_bottom_active").image as Texture2D; // 使用带 _active 后缀的图标
|
||||
tooltip = "右下对齐";
|
||||
clicked += () => OnAlign(AlignType.BottomRight);
|
||||
}
|
||||
}
|
||||
|
||||
[EditorToolbarElement(AlignHorizontalLeft.id, typeof(SceneView))]
|
||||
class AlignHorizontalLeft : EditorToolbarButton
|
||||
{
|
||||
public const string id = "AlignToolbarOverlay/AlignHorizontalLeft";
|
||||
|
||||
public AlignHorizontalLeft()
|
||||
{
|
||||
text = "";
|
||||
icon = EditorGUIUtility.IconContent("d_align_horizontally_left_active").image as Texture2D; // 使用带 _active 后缀的图标
|
||||
tooltip = "水平左对齐";
|
||||
clicked += () => OnAlign(AlignType.HorizontalLeft);
|
||||
}
|
||||
}
|
||||
|
||||
[EditorToolbarElement(AlignHorizontalCenter.id, typeof(SceneView))]
|
||||
class AlignHorizontalCenter : EditorToolbarButton
|
||||
{
|
||||
public const string id = "AlignToolbarOverlay/AlignHorizontalCenter";
|
||||
|
||||
public AlignHorizontalCenter()
|
||||
{
|
||||
text = "";
|
||||
icon = EditorGUIUtility.IconContent("d_align_horizontally_center_active").image as Texture2D; // 使用带 _active 后缀的图标
|
||||
tooltip = "水平居中对齐";
|
||||
clicked += () => OnAlign(AlignType.HorizontalCenter);
|
||||
}
|
||||
}
|
||||
|
||||
[EditorToolbarElement(AlignHorizontalRight.id, typeof(SceneView))]
|
||||
class AlignHorizontalRight : EditorToolbarButton
|
||||
{
|
||||
public const string id = "AlignToolbarOverlay/AlignHorizontalRight";
|
||||
|
||||
public AlignHorizontalRight()
|
||||
{
|
||||
text = "";
|
||||
icon = EditorGUIUtility.IconContent("d_align_horizontally_right_active").image as Texture2D; // 使用带 _active 后缀的图标
|
||||
tooltip = "水平右对齐";
|
||||
clicked += () => OnAlign(AlignType.HorizontalRight);
|
||||
}
|
||||
}
|
||||
|
||||
// 枚举定义所有对齐类型
|
||||
public enum AlignType
|
||||
{
|
||||
TopLeft,
|
||||
TopCenter,
|
||||
TopRight,
|
||||
BottomLeft,
|
||||
BottomCenter,
|
||||
BottomRight,
|
||||
HorizontalLeft,
|
||||
HorizontalCenter,
|
||||
HorizontalRight
|
||||
}
|
||||
|
||||
// 对齐方法
|
||||
private static void OnAlign(AlignType alignType)
|
||||
{
|
||||
GameObject selectedObject = Selection.activeGameObject;
|
||||
|
||||
if (selectedObject == null) return;
|
||||
|
||||
RectTransform rectTransform = selectedObject.GetComponent<RectTransform>();
|
||||
if (rectTransform == null) return;
|
||||
Undo.RecordObject(rectTransform, alignType.ToString());
|
||||
// 简化对齐逻辑,设置锚点和位置
|
||||
switch (alignType)
|
||||
{
|
||||
case AlignType.TopLeft:
|
||||
rectTransform.anchorMin = new Vector2(0, 1);
|
||||
rectTransform.anchorMax = new Vector2(0, 1);
|
||||
rectTransform.anchoredPosition = Vector2.zero;
|
||||
break;
|
||||
case AlignType.TopCenter:
|
||||
rectTransform.anchorMin = new Vector2(0.5f, 1);
|
||||
rectTransform.anchorMax = new Vector2(0.5f, 1);
|
||||
rectTransform.anchoredPosition = Vector2.zero;
|
||||
break;
|
||||
case AlignType.TopRight:
|
||||
rectTransform.anchorMin = new Vector2(1, 1);
|
||||
rectTransform.anchorMax = new Vector2(1, 1);
|
||||
rectTransform.anchoredPosition = Vector2.zero;
|
||||
break;
|
||||
case AlignType.BottomLeft:
|
||||
rectTransform.anchorMin = new Vector2(0, 0);
|
||||
rectTransform.anchorMax = new Vector2(0, 0);
|
||||
rectTransform.anchoredPosition = Vector2.zero;
|
||||
break;
|
||||
case AlignType.BottomCenter:
|
||||
rectTransform.anchorMin = new Vector2(0.5f, 0);
|
||||
rectTransform.anchorMax = new Vector2(0.5f, 0);
|
||||
rectTransform.anchoredPosition = Vector2.zero;
|
||||
break;
|
||||
case AlignType.BottomRight:
|
||||
rectTransform.anchorMin = new Vector2(1, 0);
|
||||
rectTransform.anchorMax = new Vector2(1, 0);
|
||||
rectTransform.anchoredPosition = Vector2.zero;
|
||||
break;
|
||||
case AlignType.HorizontalLeft:
|
||||
rectTransform.anchorMin = new Vector2(0, 0.5f);
|
||||
rectTransform.anchorMax = new Vector2(0, 0.5f);
|
||||
rectTransform.anchoredPosition = Vector2.zero;
|
||||
break;
|
||||
case AlignType.HorizontalCenter:
|
||||
rectTransform.anchorMin = new Vector2(0.5f, 0.5f);
|
||||
rectTransform.anchorMax = new Vector2(0.5f, 0.5f);
|
||||
rectTransform.anchoredPosition = Vector2.zero;
|
||||
break;
|
||||
case AlignType.HorizontalRight:
|
||||
rectTransform.anchorMin = new Vector2(1, 0.5f);
|
||||
rectTransform.anchorMax = new Vector2(1, 0.5f);
|
||||
rectTransform.anchoredPosition = Vector2.zero;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1ff49d8ffdac41d47aab4fbbcffb8f53
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 50354e13d8f6ebf46bf3250c156fe5b7
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,351 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
using UnityEngine.UIElements;
|
||||
using Button = UnityEngine.UIElements.Button;
|
||||
using Object = UnityEngine.Object;
|
||||
|
||||
namespace Aliciza.UXTool
|
||||
{
|
||||
public enum UXComponentType
|
||||
{
|
||||
Window,
|
||||
Widget,
|
||||
Base
|
||||
}
|
||||
|
||||
|
||||
public partial class UXCreateWindowVisualAsset : VisualElement
|
||||
{
|
||||
// 这是你原先的基础 prefab(作为模板)
|
||||
private const string PrefabPath = "Packages/com.alicizax.uxtool/Editor/UXGUI/Res/Component/View.prefab";
|
||||
|
||||
private readonly PopupField<UXComponentType> createTypePopupField;
|
||||
private readonly Button btnConfirm;
|
||||
private readonly TextField resTextField;
|
||||
private readonly Vector2IntField resVectorField;
|
||||
private readonly Label pathLabel;
|
||||
|
||||
private Action _createCallBack;
|
||||
private string _folderPath;
|
||||
|
||||
public void SetCallBack(string path, Action callback)
|
||||
{
|
||||
_createCallBack = callback;
|
||||
_folderPath = path;
|
||||
UpdatePathLabel();
|
||||
}
|
||||
|
||||
public UXCreateWindowVisualAsset()
|
||||
{
|
||||
style.flexGrow = 1;
|
||||
style.flexShrink = 0;
|
||||
style.height = Length.Percent(100);
|
||||
style.width = Length.Percent(100);
|
||||
|
||||
UXComponentType[] createTypes = new[]
|
||||
{
|
||||
UXComponentType.Window,
|
||||
UXComponentType.Widget,
|
||||
UXComponentType.Base
|
||||
};
|
||||
|
||||
// 组件名称
|
||||
resTextField = new TextField();
|
||||
resTextField.label = "组件名称";
|
||||
resTextField.style.unityFontStyleAndWeight = FontStyle.Bold;
|
||||
resTextField.style.paddingBottom = 5;
|
||||
resTextField.RegisterValueChangedCallback(evt => UpdatePathLabel());
|
||||
|
||||
// 类型下拉
|
||||
createTypePopupField = new PopupField<UXComponentType>();
|
||||
createTypePopupField.name = "createTypePopupField";
|
||||
createTypePopupField.label = "组件类型";
|
||||
createTypePopupField.choices = createTypes.ToList();
|
||||
createTypePopupField.style.unityFontStyleAndWeight = FontStyle.Bold;
|
||||
createTypePopupField.index = 0;
|
||||
createTypePopupField.style.paddingBottom = 5;
|
||||
createTypePopupField.RegisterValueChangedCallback(evt =>
|
||||
{
|
||||
UpdateResolutionVisibility(evt.newValue);
|
||||
UpdatePathLabel();
|
||||
});
|
||||
|
||||
resVectorField = new Vector2IntField();
|
||||
resVectorField.label = "组件大小";
|
||||
resVectorField.value = new Vector2Int(800, 600);
|
||||
resVectorField.style.paddingBottom = 5;
|
||||
resVectorField.style.unityFontStyleAndWeight = FontStyle.Bold;
|
||||
resVectorField.RegisterValueChangedCallback(evt =>
|
||||
{
|
||||
// 强制为非负值(如果需要可以改为 >=1)
|
||||
var v = evt.newValue;
|
||||
v.x = Mathf.Max(0, v.x);
|
||||
v.y = Mathf.Max(0, v.y);
|
||||
// 如果用户输入负数,修正回去并刷新字段
|
||||
if (v != evt.newValue)
|
||||
resVectorField.SetValueWithoutNotify(v);
|
||||
|
||||
UpdatePathLabel();
|
||||
});
|
||||
|
||||
// 路径显示
|
||||
pathLabel = new Label();
|
||||
pathLabel.style.unityFontStyleAndWeight = FontStyle.Bold;
|
||||
pathLabel.style.marginTop = 6;
|
||||
pathLabel.text = GetFullPrefabPathText();
|
||||
|
||||
// 创建按钮
|
||||
btnConfirm = new Button(OnBtnConfirmClick);
|
||||
btnConfirm.style.height = new Length(30, LengthUnit.Pixel);
|
||||
btnConfirm.text = "创建";
|
||||
btnConfirm.style.unityFontStyleAndWeight = FontStyle.Bold;
|
||||
|
||||
// Add in desired order
|
||||
Add(resTextField);
|
||||
Add(createTypePopupField);
|
||||
Add(resVectorField);
|
||||
Add(pathLabel);
|
||||
Add(btnConfirm);
|
||||
|
||||
UpdateResolutionVisibility(createTypePopupField.value);
|
||||
}
|
||||
|
||||
private void UpdateResolutionVisibility(UXComponentType type)
|
||||
{
|
||||
if (type == UXComponentType.Window)
|
||||
{
|
||||
resVectorField.style.display = DisplayStyle.None;
|
||||
}
|
||||
else
|
||||
{
|
||||
resVectorField.style.display = DisplayStyle.Flex;
|
||||
}
|
||||
}
|
||||
|
||||
private string GetFullPrefabPathText()
|
||||
{
|
||||
var name = resTextField != null ? resTextField.value?.Trim() : "";
|
||||
var folder = _folderPath ?? "";
|
||||
if (string.IsNullOrEmpty(folder))
|
||||
return "<路径未设置>";
|
||||
if (string.IsNullOrEmpty(name))
|
||||
return folder;
|
||||
// ensure folder uses forward slashes
|
||||
var f = folder.Replace("\\", "/").TrimEnd('/');
|
||||
return $"{f}/{name}.prefab";
|
||||
}
|
||||
|
||||
private void UpdatePathLabel()
|
||||
{
|
||||
if (pathLabel != null)
|
||||
pathLabel.text = GetFullPrefabPathText();
|
||||
}
|
||||
|
||||
private void OnBtnConfirmClick()
|
||||
{
|
||||
// 基本校验
|
||||
string componentName = resTextField.value?.Trim();
|
||||
if (string.IsNullOrEmpty(componentName))
|
||||
{
|
||||
EditorUtility.DisplayDialog("错误", "请输入组件名称。", "确定");
|
||||
return;
|
||||
}
|
||||
|
||||
UXComponentType type = createTypePopupField.value;
|
||||
|
||||
Vector2Int res = resVectorField.value;
|
||||
int width = res.x;
|
||||
int height = res.y;
|
||||
|
||||
if (type != UXComponentType.Window)
|
||||
{
|
||||
if (width <= 0 || height <= 0)
|
||||
{
|
||||
EditorUtility.DisplayDialog("错误", "请设置有效的分辨率(宽度和高度必须为正整数)。", "确定");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(_folderPath))
|
||||
{
|
||||
EditorUtility.DisplayDialog("错误", "_folderPath 未设置,请先设置保存目录。", "确定");
|
||||
return;
|
||||
}
|
||||
|
||||
// 确保文件夹存在(基于 AssetDatabase)
|
||||
string folder = _folderPath.Replace("\\", "/").TrimEnd('/');
|
||||
EnsureFolderExists(folder);
|
||||
|
||||
// 先确认模板 prefab 可以加载
|
||||
var templatePrefab = AssetDatabase.LoadAssetAtPath<GameObject>(PrefabPath);
|
||||
if (templatePrefab == null)
|
||||
{
|
||||
EditorUtility.DisplayDialog("错误", $"无法加载基础 prefab:{PrefabPath}", "确定");
|
||||
return;
|
||||
}
|
||||
|
||||
// 使用 PrefabUtility.LoadPrefabContents 在 prefab asset 上直接编辑(不会在场景产生临时实例)
|
||||
GameObject prefabContentsRoot = null;
|
||||
try
|
||||
{
|
||||
prefabContentsRoot = PrefabUtility.LoadPrefabContents(PrefabPath);
|
||||
if (prefabContentsRoot == null)
|
||||
{
|
||||
EditorUtility.DisplayDialog("错误", $"无法通过 LoadPrefabContents 加载 prefab: {PrefabPath}", "确定");
|
||||
return;
|
||||
}
|
||||
|
||||
// 修改 prefabContentsRoot(模板的根)
|
||||
prefabContentsRoot.name = componentName;
|
||||
|
||||
// 找到根或第一个 RectTransform(通常模板根是 RectTransform)
|
||||
RectTransform rect = prefabContentsRoot.GetComponent<RectTransform>();
|
||||
if (rect == null)
|
||||
{
|
||||
rect = prefabContentsRoot.GetComponentInChildren<RectTransform>();
|
||||
}
|
||||
|
||||
if (rect == null)
|
||||
{
|
||||
EditorUtility.DisplayDialog("错误", "Prefab 模板中未找到 RectTransform,无法设置尺寸。", "确定");
|
||||
return;
|
||||
}
|
||||
|
||||
// 根据类型调整
|
||||
if (type == UXComponentType.Window)
|
||||
{
|
||||
// 全屏拉伸
|
||||
rect.localScale = Vector3.one;
|
||||
rect.localPosition = Vector3.zero;
|
||||
rect.pivot = new Vector2(0.5f, 0.5f);
|
||||
rect.anchorMin = Vector2.zero;
|
||||
rect.anchorMax = Vector2.one;
|
||||
rect.offsetMin = Vector2.zero;
|
||||
rect.offsetMax = Vector2.zero;
|
||||
rect.anchoredPosition = Vector2.zero;
|
||||
|
||||
// 如果存在 Canvas,确保 RenderMode 为 ScreenSpaceOverlay 并且 localScale 为 1
|
||||
var canvas = prefabContentsRoot.GetComponent<Canvas>() ?? prefabContentsRoot.GetComponentInChildren<Canvas>();
|
||||
if (canvas != null)
|
||||
{
|
||||
canvas.renderMode = RenderMode.ScreenSpaceOverlay;
|
||||
#if UNITY_2019_1_OR_NEWER
|
||||
// worldCamera 设为 null 以避免意外引用
|
||||
canvas.worldCamera = null;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (type == UXComponentType.Base)
|
||||
{
|
||||
// 移除 GraphicRaycaster 与 Canvas(如果存在)
|
||||
var gr = prefabContentsRoot.GetComponent<GraphicRaycaster>();
|
||||
if (gr != null) Object.DestroyImmediate(gr);
|
||||
else
|
||||
{
|
||||
var grChild = prefabContentsRoot.GetComponentInChildren<GraphicRaycaster>();
|
||||
if (grChild != null) Object.DestroyImmediate(grChild);
|
||||
}
|
||||
|
||||
var canvas = prefabContentsRoot.GetComponent<Canvas>();
|
||||
if (canvas != null) Object.DestroyImmediate(canvas);
|
||||
else
|
||||
{
|
||||
var canvasChild = prefabContentsRoot.GetComponentInChildren<Canvas>();
|
||||
if (canvasChild != null) Object.DestroyImmediate(canvasChild);
|
||||
}
|
||||
}
|
||||
|
||||
rect.localScale = Vector3.one;
|
||||
rect.pivot = new Vector2(0.5f, 0.5f);
|
||||
rect.anchorMin = rect.anchorMax = new Vector2(0.5f, 0.5f);
|
||||
rect.anchoredPosition = Vector2.zero;
|
||||
rect.sizeDelta = new Vector2(width, height);
|
||||
}
|
||||
|
||||
string prefabPath = $"{folder}/{componentName}.prefab";
|
||||
prefabPath = prefabPath.Replace("\\", "/");
|
||||
|
||||
// 如果已存在,询问是否覆盖
|
||||
var existing = AssetDatabase.LoadAssetAtPath<GameObject>(prefabPath);
|
||||
if (existing != null)
|
||||
{
|
||||
bool overwrite = EditorUtility.DisplayDialog("覆盖确认",
|
||||
$"已存在 {componentName}.prefab,是否覆盖?",
|
||||
"覆盖",
|
||||
"取消");
|
||||
if (!overwrite)
|
||||
{
|
||||
// 取消:卸载 prefabContents,并返回
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// 保存为新的 prefab asset(将 prefabContentsRoot 内容保存到目标 prefabPath)
|
||||
GameObject saved = PrefabUtility.SaveAsPrefabAsset(prefabContentsRoot, prefabPath);
|
||||
if (saved == null)
|
||||
{
|
||||
EditorUtility.DisplayDialog("失败", $"保存 prefab 失败:{prefabPath}", "确定");
|
||||
}
|
||||
else
|
||||
{
|
||||
AssetDatabase.SaveAssets();
|
||||
AssetDatabase.Refresh();
|
||||
_createCallBack?.Invoke();
|
||||
// 尝试关闭创建窗口(如果存在)
|
||||
var wnd = EditorWindow.GetWindow<UXComponentCreateWindow>();
|
||||
if (wnd != null) wnd.Close();
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.LogError($"创建 prefab 时发生异常: {ex}");
|
||||
EditorUtility.DisplayDialog("异常", $"创建 prefab 时发生异常,请查看控制台。", "确定");
|
||||
}
|
||||
finally
|
||||
{
|
||||
// 一定要卸载 prefab contents(如果加载成功)
|
||||
if (prefabContentsRoot != null)
|
||||
PrefabUtility.UnloadPrefabContents(prefabContentsRoot);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 确保 AssetDatabase 中的文件夹存在(以 "Assets" 为根),会逐级创建缺失的子文件夹。
|
||||
/// 传入示例: "Assets/MyFolder/SubFolder"
|
||||
/// </summary>
|
||||
private void EnsureFolderExists(string folderPath)
|
||||
{
|
||||
if (string.IsNullOrEmpty(folderPath))
|
||||
throw new ArgumentNullException(nameof(folderPath));
|
||||
|
||||
var path = folderPath.Replace("\\", "/").TrimEnd('/');
|
||||
if (AssetDatabase.IsValidFolder(path))
|
||||
return;
|
||||
|
||||
string[] parts = path.Split('/');
|
||||
if (parts.Length == 0 || parts[0] != "Assets")
|
||||
{
|
||||
Debug.LogError("folderPath 必须以 'Assets' 开头。");
|
||||
return;
|
||||
}
|
||||
|
||||
string cur = "Assets";
|
||||
for (int i = 1; i < parts.Length; i++)
|
||||
{
|
||||
string next = cur + "/" + parts[i];
|
||||
if (!AssetDatabase.IsValidFolder(next))
|
||||
{
|
||||
AssetDatabase.CreateFolder(cur, parts[i]);
|
||||
}
|
||||
|
||||
cur = next;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 82c75a05bd05409783674012d80a4f1a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,35 @@
|
||||
using System;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
namespace Aliciza.UXTool
|
||||
{
|
||||
public class UXComponentCreateWindow : EditorWindow
|
||||
{
|
||||
public void SetCreateData(string path, System.Action callback)
|
||||
{
|
||||
uxCreateWindowVisualAsset.SetCallBack(path, callback);
|
||||
}
|
||||
|
||||
private UXCreateWindowVisualAsset uxCreateWindowVisualAsset;
|
||||
|
||||
private void CreateGUI()
|
||||
{
|
||||
uxCreateWindowVisualAsset = new UXCreateWindowVisualAsset();
|
||||
rootVisualElement.Add(uxCreateWindowVisualAsset);
|
||||
}
|
||||
}
|
||||
|
||||
public static class UXComponentCreateWindowHelper
|
||||
{
|
||||
public static void ShowWindow(string path, Action createCallBack)
|
||||
{
|
||||
UXComponentCreateWindow window = EditorWindow.GetWindow<UXComponentCreateWindow>(true, "创建组件");
|
||||
window.maxSize = new Vector2(400, 150);
|
||||
window.minSize = new Vector2(400, 150);
|
||||
window.Show();
|
||||
window.SetCreateData(path, createCallBack);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b6545b205586412ca32dd1bebd68f6e7
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,68 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
/// <summary>
|
||||
/// 在多 SceneView 窗口下安全地添加/移除临时回调。
|
||||
/// 原理:Templist 存放需在下一帧执行的回调,OnSceneGUI 将会把 Templist 转移到 list 来执行。
|
||||
/// </summary>
|
||||
public class UXCustomSceneView
|
||||
{
|
||||
static private List<Action<SceneView>> list = new List<Action<SceneView>>();
|
||||
static private List<Action<SceneView>> Templist = new List<Action<SceneView>>();
|
||||
|
||||
[InitializeOnLoadMethod]
|
||||
static void Init()
|
||||
{
|
||||
list.Clear();
|
||||
Templist.Clear();
|
||||
SceneView.duringSceneGui -= OnSceneGUI;
|
||||
SceneView.duringSceneGui += OnSceneGUI;
|
||||
}
|
||||
|
||||
static public void AddDelegate(Action<SceneView> method)
|
||||
{
|
||||
if (method == null) return;
|
||||
// 去重
|
||||
if (!Templist.Contains(method))
|
||||
Templist.Add(method);
|
||||
}
|
||||
|
||||
static public void RemoveDelegate(Action<SceneView> method)
|
||||
{
|
||||
if (method == null) return;
|
||||
var idx = Templist.FindIndex(i => i == method);
|
||||
if (idx >= 0) Templist.RemoveAt(idx);
|
||||
}
|
||||
|
||||
static public void OnSceneGUI(SceneView sceneView)
|
||||
{
|
||||
// 执行上一帧注册的 list
|
||||
foreach (Action<SceneView> method in list)
|
||||
{
|
||||
try
|
||||
{
|
||||
method.Invoke(sceneView);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.LogException(e);
|
||||
}
|
||||
}
|
||||
|
||||
list.Clear();
|
||||
|
||||
// 将 Templist 拷贝到 list,并在下一帧执行,这样可以避免在枚举时修改集合的问题
|
||||
foreach (Action<SceneView> method in Templist)
|
||||
{
|
||||
if (!list.Contains(method))
|
||||
list.Add(method);
|
||||
}
|
||||
}
|
||||
|
||||
static public void ClearDelegate()
|
||||
{
|
||||
Templist.Clear();
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8f3197943fdad5143b94a26ef9c0eacb
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 777a45b1b56a4ef6866011a0f739705a
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7634273f5c12e874bb902025a97852e1
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user