This commit is contained in:
陈思海 2026-02-28 17:09:15 +08:00
parent 960842327d
commit 8547e57f55
138 changed files with 368 additions and 13180 deletions

View File

@ -307,7 +307,10 @@ MonoBehaviour:
_controllers:
- Name: Controller0
Length: 2
_recorders: []
- Name: ctlChange
Length: 3
_recorders:
- {fileID: 5562764555442211541}
--- !u!114 &4311531008057825966
MonoBehaviour:
m_ObjectHideFlags: 0
@ -658,7 +661,8 @@ GameObject:
- component: {fileID: 7425209180716806519}
- component: {fileID: 162949476695556915}
- component: {fileID: 3482583127883842067}
- component: {fileID: 950606262313515452}
- component: {fileID: 6667574876542659940}
- component: {fileID: 5562764555442211541}
m_Layer: 5
m_Name: ScrollView@TestList
m_TagString: Untagged
@ -738,7 +742,6 @@ MonoBehaviour:
m_EditorClassIdentifier:
direction: 0
alignment: 2
content: {fileID: 5086806237606523596}
spacing: {x: 0, y: 5}
padding: {x: 0, y: 0}
scroll: 1
@ -747,20 +750,21 @@ MonoBehaviour:
wheelSpeed: 10
templates:
- {fileID: 3596253569820580282}
_scrollerTypeName: AlicizaX.UI.Scroller
_scroller: {fileID: 950606262313515452}
_showScrollBar: 0
_scrollbar: {fileID: 0}
content: {fileID: 5086806237606523596}
showScrollBar: 0
scrollbar: {fileID: 0}
_layoutManagerTypeName: AlicizaX.UI.LinearLayoutManager
_layoutManager:
rid: 6739296565164244992
layoutManager:
rid: 6739296679894712320
_scrollerTypeName: AlicizaX.UI.Scroller
scroller: {fileID: 6667574876542659940}
references:
version: 2
RefIds:
- rid: 6739296565164244992
- rid: 6739296679894712320
type: {class: LinearLayoutManager, ns: AlicizaX.UI, asm: AlicizaX.UI.Extension}
data:
--- !u!114 &950606262313515452
--- !u!114 &6667574876542659940
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
@ -772,6 +776,32 @@ MonoBehaviour:
m_Script: {fileID: 11500000, guid: 7b7de4cb3a1546e4a9ade6b8dbf8af92, type: 3}
m_Name:
m_EditorClassIdentifier:
dragStopTime: 0
--- !u!114 &5562764555442211541
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 3365040644835594956}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 35829408994740a08c32ac2f519442f0, type: 3}
m_Name:
m_EditorClassIdentifier:
_id: 62478209
_controller: {fileID: 2333725313425952563}
_stateEntries:
- State:
rid: 6739296679894712321
ControllerName: Controller0
ControllerIndex: 1
references:
version: 2
RefIds:
- rid: 6739296679894712321
type: {class: GameObjectPropertyStateBase, ns: AlicizaX.UI, asm: AlicizaX.UI.Extension}
data:
--- !u!1 &5796037856908370449
GameObject:
m_ObjectHideFlags: 0

View File

@ -278,7 +278,6 @@ MonoBehaviour:
- {fileID: 460664918}
- {fileID: 810504353}
wheels: {fileID: 810504353}
timeScale: 1
--- !u!4 &182366649
Transform:
m_ObjectHideFlags: 0
@ -2820,13 +2819,16 @@ MonoBehaviour:
logAllocations: 1
logFiltered: 0
logIgnored: 0
filterAllocations: []
filterAllocations:
- PrimeTween.Runtime
- PrimeTweenDemo
ignoreAllocations:
- RaycastHit.get_collider()
- Component.get_transform()
- Component.get_gameObject()
- MeasureMemoryAllocations
- DebugInfo
- Slider.set_value
--- !u!114 &1755549107
MonoBehaviour:
m_ObjectHideFlags: 0

File diff suppressed because it is too large Load Diff

View File

@ -10,25 +10,5 @@ namespace PrimeTweenDemo {
public abstract class Animatable : Clickable {
public abstract Sequence Animate(bool toEndValue);
}
#if UNITY_EDITOR
[UnityEditor.CustomEditor(typeof(Clickable), true), UnityEditor.CanEditMultipleObjects]
internal class InspectorWithButton : UnityEditor.Editor {
GUIStyle boldButtonStyle;
public override void OnInspectorGUI() {
DrawDefaultInspector();
GUILayout.Space(8);
if (boldButtonStyle == null) {
boldButtonStyle = new GUIStyle(GUI.skin.button) { fontStyle = FontStyle.Bold };
}
if (GUILayout.Button("Play Animation", boldButtonStyle)) {
foreach (var t in targets) {
(t as Clickable).OnClick();
}
}
}
}
#endif
}
#endif

View File

@ -18,7 +18,8 @@ namespace PrimeTweenDemo {
Sequence PlayFlipAnimation() {
if (!sequence.isAlive) {
const float jumpDuration = 0.3f;
sequence = Tween.LocalPositionZ(animationAnchor, 0.2f, jumpDuration)
sequence = Sequence.Create()
.Chain(Tween.LocalPositionZ(animationAnchor, 0.2f, jumpDuration))
.Chain(Tween.LocalEulerAngles(animationAnchor, Vector3.zero, new Vector3(0, 360, 0), 0.9f, Ease.InOutBack))
.Chain(Tween.LocalPositionZ(animationAnchor, 0, jumpDuration));
}

View File

@ -14,7 +14,6 @@ namespace PrimeTweenDemo {
[SerializeField] TypewriterAnimatorExample typewriterAnimatorExample;
[SerializeField] Animatable[] animatables;
[SerializeField] Wheels wheels;
[SerializeField, Range(0.5f, 5f)] float timeScale = 1;
bool isAnimatingWithCoroutineOrAsync;
public Sequence animateAllSequence;
@ -59,8 +58,6 @@ namespace PrimeTweenDemo {
}
void Update() {
Time.timeScale = timeScale;
animateAllPartsButton.GetComponent<Image>().enabled = !isAnimatingWithCoroutineOrAsync;
animateAllPartsButton.GetComponentInChildren<Text>().enabled = !isAnimatingWithCoroutineOrAsync;

View File

@ -17,8 +17,9 @@ namespace PrimeTweenDemo {
return Sequence.Create();
}
isClosed = _isClosed;
var sequence = Sequence.Create();
var rotationTween = Tween.LocalRotation(animationAnchor, _isClosed ? new Vector3(0, -90) : Vector3.zero, 0.7f, Ease.InOutElastic);
var sequence = Sequence.Create(rotationTween);
sequence.Group(rotationTween);
if (_isClosed) {
sequence.Group(cameraController.Shake(0.5f));
}

View File

@ -9,7 +9,7 @@ namespace PrimeTweenDemo {
void OnEnable() {
models = GetComponentsInChildren<MeshRenderer>();
foreach (var mr in models) {
_ = mr.material; // copy shared material
mr.sharedMaterial = new Material(mr.sharedMaterial); // copy shared material
}
}
}

View File

@ -9,21 +9,25 @@ namespace PrimeTweenDemo {
[SerializeField] CameraProjectionMatrixAnimation cameraProjectionMatrixAnimation;
[CanBeNull] public HighlightableElement current { get; private set; }
#if UNITY_2019_1_OR_NEWER && !PHYSICS_MODULE_INSTALLED
void Awake() {
#if UNITY_2019_1_OR_NEWER && !PHYSICS_MODULE_INSTALLED
Debug.LogError("Please install the package needed for Physics.Raycast(): 'Package Manager/Packages/Built-in/Physics' (com.unity.modules.physics).");
#endif
Debug.LogError("Please install the package needed for Physics.Raycast(): 'Package Manager/Packages/Built-in/Physics' (com.unity.modules.physics).");
}
#endif
void Update() {
if (cameraProjectionMatrixAnimation.IsAnimating) {
return;
}
if (InputController.touchSupported && !InputController.Get()) {
if (Application.isMobilePlatform && InputController.touchSupported && !InputController.Get()) {
SetCurrentHighlighted(null);
return;
}
var ray = mainCamera.ScreenPointToRay(InputController.screenPosition);
var screenPosition = InputController.screenPosition;
if (!new Rect(0f, 0f, Screen.width, Screen.height).Contains(screenPosition)) {
return;
}
var ray = mainCamera.ScreenPointToRay(screenPosition);
var highlightableElement = RaycastHighlightableElement(ray);
SetCurrentHighlighted(highlightableElement);
@ -59,7 +63,7 @@ namespace PrimeTweenDemo {
static void AnimateHighlightedElement([NotNull] HighlightableElement highlightable, bool isHighlighted) {
Tween.LocalPositionZ(highlightable.highlightAnchor, isHighlighted ? 0.08f : 0, 0.3f);
foreach (var model in highlightable.models) {
Tween.MaterialColor(model.material, emissionColorPropId, isHighlighted ? Color.white * 0.25f : Color.black, 0.2f, Ease.OutQuad);
Tween.MaterialColor(model.sharedMaterial, emissionColorPropId, isHighlighted ? Color.white * 0.25f : Color.black, 0.2f, Ease.OutQuad);
}
}
}

View File

@ -26,11 +26,6 @@ namespace PrimeTweenDemo {
readonly List<int[]> filteredPaths = new List<int[]>();
int lastProcessedFrame = -1;
void Awake() {
filterAllocations.Add("PrimeTween.Runtime");
filterAllocations.Add("PrimeTweenDemo");
}
void OnEnable() {
ProfilerDriver.ClearAllFrames();
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 515936d411314273be8baba85fc2068a
timeCreated: 1755085955

View File

@ -0,0 +1,32 @@
using UnityEngine;
using UnityEngine.Events;
namespace PrimeTweenDemo {
// p0 todo replace with AnimateOnClick and serialize TweenAnimation instead? no, because I need to reference all animations from one place to animate them all
// p0 todo create Demo Pro. With which version of Unity?
public class OnClick : MonoBehaviour {
[SerializeField] public UnityEvent onClick = new UnityEvent();
void Update() {
if (InputController.GetDown()) {
Vector2 screenPos = InputController.screenPosition;
var ray = Camera.main.ScreenPointToRay(screenPos);
if (Physics.Raycast(ray, out var hit) && IsChild(hit.transform, transform)) {
// Debug.Log("onClick", this);
onClick.Invoke();
}
}
}
static bool IsChild(Transform t, Transform other) {
Transform parent = t.parent;
while (parent != null) {
if (parent == other) {
return true;
}
parent = parent.parent;
}
return false;
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 26e5afea6b0e469b8f640089915afdd2
timeCreated: 1755085967

View File

@ -9,7 +9,7 @@ namespace PrimeTweenDemo {
float currentSpeed;
void Awake() {
_ = roadModel.material; // copy shared material
roadModel.sharedMaterial = new Material(roadModel.sharedMaterial); // copy shared material
}
public override Sequence Animate(bool isAnimating) {
@ -22,7 +22,7 @@ namespace PrimeTweenDemo {
}
void Update() {
roadModel.material.mainTextureOffset += new Vector2(-1f, 1f) * currentSpeed * Time.deltaTime;
roadModel.sharedMaterial.mainTextureOffset += new Vector2(-1f, 1f) * currentSpeed * Time.deltaTime;
}
}
}

View File

@ -25,8 +25,8 @@ namespace PrimeTweenDemo {
sequence.Stop();
}
var tweenSettings = new TweenSettings(0.4f, Ease.OutBack, endDelay: 0.1f);
sequence = Tween.LocalPosition(animationAnchor, midPos, tweenSettings)
.Chain(Tween.LocalPosition(animationAnchor, _isClosed ? closedPos : openedPos, tweenSettings));
sequence = Tween.LocalPosition(animationAnchor, new TweenSettings<Vector3>(midPos, tweenSettings))
.Chain(Tween.LocalPosition(animationAnchor, new TweenSettings<Vector3>(_isClosed ? closedPos : openedPos, tweenSettings)));
return sequence;
}
}

View File

@ -1,17 +0,0 @@
using PrimeTween;
using UnityEngine;
namespace PrimeTweenDemo
{
public class TestAnimation:MonoBehaviour
{
[SerializeField] Transform animationAnchor;
public bool _isClosed;
public void Test()
{
var rotationTween = Tween.LocalRotation(animationAnchor, _isClosed ? new Vector3(0, -90) : Vector3.zero, 0.7f, Ease.InOutElastic);
var sequence = Sequence.Create(rotationTween);
}
}
}

View File

@ -1,3 +0,0 @@
fileFormatVersion: 2
guid: 52c8fdb241904ec08d0206d641fd84cc
timeCreated: 1747275178

View File

@ -2,27 +2,20 @@
%TAG !u! tag:unity3d.com,2011:
--- !u!21 &2100000
Material:
serializedVersion: 8
serializedVersion: 6
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_Name: Exterior
m_Shader: {fileID: 46, guid: 0000000000000000f000000000000000, type: 0}
m_Parent: {fileID: 0}
m_ModifiedSerializedProperties: 0
m_ValidKeywords:
- _EMISSION
- _GLOSSYREFLECTIONS_OFF
- _SPECULARHIGHLIGHTS_OFF
m_InvalidKeywords: []
m_ShaderKeywords: _EMISSION
m_LightmapFlags: 1
m_EnableInstancingVariants: 0
m_DoubleSidedGI: 0
m_CustomRenderQueue: -1
stringTagMap: {}
disabledShaderPasses: []
m_LockedProperties:
m_SavedProperties:
serializedVersion: 3
m_TexEnvs:
@ -62,7 +55,6 @@ Material:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
m_Ints: []
m_Floats:
- _BumpScale: 1
- _Cutoff: 0.5
@ -83,5 +75,3 @@ Material:
m_Colors:
- _Color: {r: 1, g: 1, b: 1, a: 1}
- _EmissionColor: {r: 0, g: 0, b: 0, a: 1}
m_BuildTextureStacks: []
m_AllowLocking: 1

View File

@ -2,27 +2,20 @@
%TAG !u! tag:unity3d.com,2011:
--- !u!21 &2100000
Material:
serializedVersion: 8
serializedVersion: 6
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_Name: Wheel
m_Shader: {fileID: 46, guid: 0000000000000000f000000000000000, type: 0}
m_Parent: {fileID: 0}
m_ModifiedSerializedProperties: 0
m_ValidKeywords:
- _EMISSION
- _GLOSSYREFLECTIONS_OFF
- _SPECULARHIGHLIGHTS_OFF
m_InvalidKeywords: []
m_ShaderKeywords: _EMISSION
m_LightmapFlags: 1
m_EnableInstancingVariants: 0
m_DoubleSidedGI: 0
m_CustomRenderQueue: -1
stringTagMap: {}
disabledShaderPasses: []
m_LockedProperties:
m_SavedProperties:
serializedVersion: 3
m_TexEnvs:
@ -62,7 +55,6 @@ Material:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
m_Ints: []
m_Floats:
- _BumpScale: 1
- _Cutoff: 0.5
@ -83,5 +75,3 @@ Material:
m_Colors:
- _Color: {r: 1, g: 1, b: 1, a: 1}
- _EmissionColor: {r: 0, g: 0, b: 0, a: 1}
m_BuildTextureStacks: []
m_AllowLocking: 1

View File

@ -2,27 +2,20 @@
%TAG !u! tag:unity3d.com,2011:
--- !u!21 &2100000
Material:
serializedVersion: 8
serializedVersion: 6
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_Name: interior
m_Shader: {fileID: 46, guid: 0000000000000000f000000000000000, type: 0}
m_Parent: {fileID: 0}
m_ModifiedSerializedProperties: 0
m_ValidKeywords:
- _EMISSION
- _GLOSSYREFLECTIONS_OFF
- _SPECULARHIGHLIGHTS_OFF
m_InvalidKeywords: []
m_ShaderKeywords: _EMISSION
m_LightmapFlags: 1
m_EnableInstancingVariants: 0
m_DoubleSidedGI: 0
m_CustomRenderQueue: -1
stringTagMap: {}
disabledShaderPasses: []
m_LockedProperties:
m_SavedProperties:
serializedVersion: 3
m_TexEnvs:
@ -62,7 +55,6 @@ Material:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
m_Ints: []
m_Floats:
- _BumpScale: 1
- _Cutoff: 0.5
@ -83,5 +75,3 @@ Material:
m_Colors:
- _Color: {r: 1, g: 1, b: 1, a: 1}
- _EmissionColor: {r: 0, g: 0, b: 0, a: 1}
m_BuildTextureStacks: []
m_AllowLocking: 1

View File

@ -23,8 +23,7 @@ Material:
m_CustomRenderQueue: -1
stringTagMap:
RenderType: Opaque
disabledShaderPasses:
- MOTIONVECTORS
disabledShaderPasses: []
m_LockedProperties:
m_SavedProperties:
serializedVersion: 3
@ -125,7 +124,6 @@ Material:
- _EmissionColor: {r: 0, g: 0, b: 0, a: 1}
- _SpecColor: {r: 0.2, g: 0.2, b: 0.2, a: 1}
m_BuildTextureStacks: []
m_AllowLocking: 1
--- !u!114 &7263079360766289332
MonoBehaviour:
m_ObjectHideFlags: 11
@ -138,4 +136,4 @@ MonoBehaviour:
m_Script: {fileID: 11500000, guid: d0353a89b1f911e48b9e16bdc9f2e058, type: 3}
m_Name:
m_EditorClassIdentifier:
version: 9
version: 7

View File

@ -12,7 +12,7 @@ MonoBehaviour:
m_Script: {fileID: 11500000, guid: d0353a89b1f911e48b9e16bdc9f2e058, type: 3}
m_Name:
m_EditorClassIdentifier:
version: 9
version: 7
--- !u!21 &2100000
Material:
serializedVersion: 8
@ -36,8 +36,7 @@ Material:
m_CustomRenderQueue: -1
stringTagMap:
RenderType: Opaque
disabledShaderPasses:
- MOTIONVECTORS
disabledShaderPasses: []
m_LockedProperties:
m_SavedProperties:
serializedVersion: 3
@ -138,4 +137,3 @@ Material:
- _EmissionColor: {r: 0, g: 0, b: 0, a: 1}
- _SpecColor: {r: 0.2, g: 0.2, b: 0.2, a: 1}
m_BuildTextureStacks: []
m_AllowLocking: 1

View File

@ -25,7 +25,6 @@ Material:
disabledShaderPasses:
- DepthOnly
- SHADOWCASTER
- MOTIONVECTORS
m_LockedProperties:
m_SavedProperties:
serializedVersion: 3
@ -126,7 +125,6 @@ Material:
- _EmissionColor: {r: 0, g: 0, b: 0, a: 1}
- _SpecColor: {r: 0.2, g: 0.2, b: 0.2, a: 1}
m_BuildTextureStacks: []
m_AllowLocking: 1
--- !u!114 &3728866138871552857
MonoBehaviour:
m_ObjectHideFlags: 11
@ -139,4 +137,4 @@ MonoBehaviour:
m_Script: {fileID: 11500000, guid: d0353a89b1f911e48b9e16bdc9f2e058, type: 3}
m_Name:
m_EditorClassIdentifier:
version: 9
version: 7

View File

@ -23,8 +23,7 @@ Material:
m_CustomRenderQueue: -1
stringTagMap:
RenderType: Opaque
disabledShaderPasses:
- MOTIONVECTORS
disabledShaderPasses: []
m_LockedProperties:
m_SavedProperties:
serializedVersion: 3
@ -125,7 +124,6 @@ Material:
- _EmissionColor: {r: 0, g: 0, b: 0, a: 1}
- _SpecColor: {r: 0.2, g: 0.2, b: 0.2, a: 1}
m_BuildTextureStacks: []
m_AllowLocking: 1
--- !u!114 &6538925729129028096
MonoBehaviour:
m_ObjectHideFlags: 11
@ -138,4 +136,4 @@ MonoBehaviour:
m_Script: {fileID: 11500000, guid: d0353a89b1f911e48b9e16bdc9f2e058, type: 3}
m_Name:
m_EditorClassIdentifier:
version: 9
version: 7

View File

@ -23,8 +23,7 @@ Material:
m_CustomRenderQueue: -1
stringTagMap:
RenderType: Opaque
disabledShaderPasses:
- MOTIONVECTORS
disabledShaderPasses: []
m_LockedProperties:
m_SavedProperties:
serializedVersion: 3
@ -125,7 +124,6 @@ Material:
- _EmissionColor: {r: 0, g: 0, b: 0, a: 1}
- _SpecColor: {r: 0.2, g: 0.2, b: 0.2, a: 1}
m_BuildTextureStacks: []
m_AllowLocking: 1
--- !u!114 &8482762829682952528
MonoBehaviour:
m_ObjectHideFlags: 11
@ -138,4 +136,4 @@ MonoBehaviour:
m_Script: {fileID: 11500000, guid: d0353a89b1f911e48b9e16bdc9f2e058, type: 3}
m_Name:
m_EditorClassIdentifier:
version: 9
version: 7

View File

@ -208,7 +208,7 @@ DefaultImporter:
};
}
internal const string version = "1.3.1";
internal const string version = "1.3.7";
}
internal static class FixedUpdateParameterMigration {

View File

@ -214,6 +214,10 @@ PrefabInstance:
propertyPath: m_LocalEulerAnglesHint.z
value: 0
objectReference: {fileID: 0}
- target: {fileID: 1599215827984154130, guid: 8e4808bf30e54a8439a661bfa35ee982, type: 3}
propertyPath: _language
value: ChineseSimplified
objectReference: {fileID: 0}
- target: {fileID: 5472542677182214335, guid: 8e4808bf30e54a8439a661bfa35ee982, type: 3}
propertyPath: m_Name
value: Entry

View File

@ -1 +1,11 @@
{"bindings":[{"action":"input.map.player/input.action.movement","id":"15d59094-85d1-408d-b565-724532c15263","path":"<Keyboard>/upArrow","interactions":"null","processors":"null"}]}
{
"bindings": [
{
"action": "input.map.player/input.action.movement",
"id": "15d59094-85d1-408d-b565-724532c15263",
"path": "<Keyboard>/upArrow",
"interactions": "null",
"processors": "null"
}
]
}

View File

@ -1,332 +0,0 @@
{
"FileVersion": "2025.9.30",
"EnableAddressable": true,
"SupportExtensionless": true,
"LocationToLower": false,
"IncludeAssetGUID": false,
"ReplaceAssetPathWithAddress": false,
"OutputNameStyle": 0,
"BuildBundleType": 1,
"BuildPipeline": "EditorSimulateBuildPipeline",
"PackageName": "DefaultPackage",
"PackageVersion": "Simulate",
"PackageNote": "2025/12/25 13:43:46",
"AssetList": [
{
"Address": "Click",
"AssetPath": "Assets/Bundles/Audios/UI/Click.wav",
"AssetGUID": "",
"AssetTags": [
"Audios"
],
"BundleID": 0,
"DependBundleIDs": []
},
{
"Address": "Hover",
"AssetPath": "Assets/Bundles/Audios/UI/Hover.wav",
"AssetGUID": "",
"AssetTags": [
"Audios"
],
"BundleID": 0,
"DependBundleIDs": []
},
{
"Address": "tables_tbachievement",
"AssetPath": "Assets/Bundles/Configs/bytes/tables_tbachievement.bytes",
"AssetGUID": "",
"AssetTags": [
"WEBGL_PRELOAD",
"Configs"
],
"BundleID": 2,
"DependBundleIDs": []
},
{
"Address": "LocalizationTable",
"AssetPath": "Assets/Bundles/Configs/LocalizationTable.asset",
"AssetGUID": "",
"AssetTags": [
"WEBGL_PRELOAD",
"Configs"
],
"BundleID": 1,
"DependBundleIDs": []
},
{
"Address": "AlicizaX.Framework.Runtime.dll",
"AssetPath": "Assets/Bundles/DLL/AlicizaX.Framework.Runtime.dll.bytes",
"AssetGUID": "",
"AssetTags": [
"DLL"
],
"BundleID": 3,
"DependBundleIDs": []
},
{
"Address": "GameBase.dll",
"AssetPath": "Assets/Bundles/DLL/GameBase.dll.bytes",
"AssetGUID": "",
"AssetTags": [
"DLL"
],
"BundleID": 3,
"DependBundleIDs": []
},
{
"Address": "GameLib.dll",
"AssetPath": "Assets/Bundles/DLL/GameLib.dll.bytes",
"AssetGUID": "",
"AssetTags": [
"DLL"
],
"BundleID": 3,
"DependBundleIDs": []
},
{
"Address": "GameLogic.dll",
"AssetPath": "Assets/Bundles/DLL/GameLogic.dll.bytes",
"AssetGUID": "",
"AssetTags": [
"DLL"
],
"BundleID": 3,
"DependBundleIDs": []
},
{
"Address": "GameProto.dll",
"AssetPath": "Assets/Bundles/DLL/GameProto.dll.bytes",
"AssetGUID": "",
"AssetTags": [
"DLL"
],
"BundleID": 3,
"DependBundleIDs": []
},
{
"Address": "mscorlib.dll",
"AssetPath": "Assets/Bundles/DLL/mscorlib.dll.bytes",
"AssetGUID": "",
"AssetTags": [
"DLL"
],
"BundleID": 3,
"DependBundleIDs": []
},
{
"Address": "System.Core.dll",
"AssetPath": "Assets/Bundles/DLL/System.Core.dll.bytes",
"AssetGUID": "",
"AssetTags": [
"DLL"
],
"BundleID": 3,
"DependBundleIDs": []
},
{
"Address": "UniTask.Runtime.dll",
"AssetPath": "Assets/Bundles/DLL/UniTask.Runtime.dll.bytes",
"AssetGUID": "",
"AssetTags": [
"DLL"
],
"BundleID": 3,
"DependBundleIDs": []
},
{
"Address": "Unity.InputSystem.dll",
"AssetPath": "Assets/Bundles/DLL/Unity.InputSystem.dll.bytes",
"AssetGUID": "",
"AssetTags": [
"DLL"
],
"BundleID": 3,
"DependBundleIDs": []
},
{
"Address": "Map1000",
"AssetPath": "Assets/Bundles/Scenes/Map1000.unity",
"AssetGUID": "",
"AssetTags": [
"Scenes"
],
"BundleID": 4,
"DependBundleIDs": []
},
{
"Address": "MyShaderVariants",
"AssetPath": "Assets/Bundles/ShaderVariants/MyShaderVariants.shadervariants",
"AssetGUID": "",
"AssetTags": [
"ShaderVariants"
],
"BundleID": 9,
"DependBundleIDs": []
},
{
"Address": "Hotkey Component",
"AssetPath": "Assets/Bundles/UI/Component/Hotkey Component.prefab",
"AssetGUID": "",
"AssetTags": [
"UI"
],
"BundleID": 5,
"DependBundleIDs": []
},
{
"Address": "dsadsa",
"AssetPath": "Assets/Bundles/UI/Window/dsadsa.prefab",
"AssetGUID": "",
"AssetTags": [
"UI"
],
"BundleID": 6,
"DependBundleIDs": []
},
{
"Address": "UICardWidget",
"AssetPath": "Assets/Bundles/UI/Window/UICardWidget.prefab",
"AssetGUID": "",
"AssetTags": [
"UI"
],
"BundleID": 7,
"DependBundleIDs": []
},
{
"Address": "UILoadUpdateWindow",
"AssetPath": "Assets/Bundles/UI/Window/UILoadUpdateWindow.prefab",
"AssetGUID": "",
"AssetTags": [
"UI"
],
"BundleID": 8,
"DependBundleIDs": []
}
],
"BundleList": [
{
"BundleName": "assets_bundles_audios_ui.bundle",
"UnityCRC": 0,
"FileHash": "77cdb65b7cfb324f9e511e95ee2e394f",
"FileCRC": 0,
"FileSize": 35951,
"Encrypted": false,
"Tags": [
"Audios"
],
"DependBundleIDs": []
},
{
"BundleName": "assets_bundles_configs.bundle",
"UnityCRC": 0,
"FileHash": "3a378d308429cd99b280d880a27af478",
"FileCRC": 0,
"FileSize": 5280,
"Encrypted": false,
"Tags": [
"WEBGL_PRELOAD",
"Configs"
],
"DependBundleIDs": []
},
{
"BundleName": "assets_bundles_configs_bytes.bundle",
"UnityCRC": 0,
"FileHash": "589b0dd91da2eee9c4e3146135170d34",
"FileCRC": 0,
"FileSize": 281,
"Encrypted": false,
"Tags": [
"WEBGL_PRELOAD",
"Configs"
],
"DependBundleIDs": []
},
{
"BundleName": "assets_bundles_dll.bundle",
"UnityCRC": 0,
"FileHash": "ba45bd2617fcfa1e64a95d7741dc8012",
"FileCRC": 0,
"FileSize": 4342272,
"Encrypted": false,
"Tags": [
"DLL"
],
"DependBundleIDs": []
},
{
"BundleName": "assets_bundles_scenes_map1000.bundle",
"UnityCRC": 0,
"FileHash": "4530edb970dd229fed5dd3259fee4ece",
"FileCRC": 0,
"FileSize": 12277,
"Encrypted": false,
"Tags": [
"Scenes"
],
"DependBundleIDs": []
},
{
"BundleName": "assets_bundles_ui_component_hotkey_component.bundle",
"UnityCRC": 0,
"FileHash": "bc3db7c732b0a641c72c594787b6027a",
"FileCRC": 0,
"FileSize": 24744,
"Encrypted": false,
"Tags": [
"UI"
],
"DependBundleIDs": []
},
{
"BundleName": "assets_bundles_ui_window_dsadsa.bundle",
"UnityCRC": 0,
"FileHash": "b63a46898c5fc9d7c7ddf1c887ca0642",
"FileCRC": 0,
"FileSize": 3325,
"Encrypted": false,
"Tags": [
"UI"
],
"DependBundleIDs": []
},
{
"BundleName": "assets_bundles_ui_window_uicardwidget.bundle",
"UnityCRC": 0,
"FileHash": "abe9622ac433e5d039830d047bb7d28f",
"FileCRC": 0,
"FileSize": 6852,
"Encrypted": false,
"Tags": [
"UI"
],
"DependBundleIDs": []
},
{
"BundleName": "assets_bundles_ui_window_uiloadupdatewindow.bundle",
"UnityCRC": 0,
"FileHash": "82253a65841b8f1db612149ec8c9a317",
"FileCRC": 0,
"FileSize": 54180,
"Encrypted": false,
"Tags": [
"UI"
],
"DependBundleIDs": []
},
{
"BundleName": "unityshaders.bundle",
"UnityCRC": 0,
"FileHash": "c9f21eee9f3febcafa65a26802cf31f0",
"FileCRC": 0,
"FileSize": 3518,
"Encrypted": false,
"Tags": [
"ShaderVariants"
],
"DependBundleIDs": []
}
]
}

@ -1 +1 @@
Subproject commit 80d3a482b77520f2018673cc24c9b4e0bcfb0056
Subproject commit 0a5ef9135ccb240227cf655e2251a63bf2d5da7a

@ -1 +1 @@
Subproject commit ca46f80d674efe7410e3988512eeb97e3d40b9ff
Subproject commit f92e91920dc4c31b41e36e3fa40058d4e6aace34

View File

@ -1,8 +0,0 @@
fileFormatVersion: 2
guid: bd291d07a2484ac49af2ac1ced61963f
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,26 +0,0 @@
{
"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
}

View File

@ -1,7 +0,0 @@
fileFormatVersion: 2
guid: f7a54d99aea1f544194a344d7b71c22b
AssemblyDefinitionImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,249 +0,0 @@
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 AlicizaX.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();
}
}
}

View File

@ -1,11 +0,0 @@
fileFormatVersion: 2
guid: 48bb3b6017052894fbcebf49a41e6b61
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,3 +0,0 @@
fileFormatVersion: 2
guid: 971b3a6d43d84485bc82a92f677c431d
timeCreated: 1763975420

View File

@ -1,98 +0,0 @@
using System;
using System.IO;
using System.Linq;
using UnityEditorInternal;
using UnityEngine;
namespace AlicizaX.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;
}
}
}

View File

@ -1,3 +0,0 @@
fileFormatVersion: 2
guid: 611899e70b4f424091a58af4e485db0b
timeCreated: 1763975423

View File

@ -1,8 +0,0 @@
fileFormatVersion: 2
guid: 11e0c553755e49e9bb95b35bd924cdc6
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,8 +0,0 @@
fileFormatVersion: 2
guid: dc242d8f56ba469cac5f90603b46b1a8
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,23 +0,0 @@
using System;
using System.Collections.Generic;
using System.IO;
using UnityEditor;
using UnityEngine;
namespace AlicizaX.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);
}
}
}
}

View File

@ -1,11 +0,0 @@
fileFormatVersion: 2
guid: 835d2b9493e24712925e98a7a3da62a2
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,72 +0,0 @@
using System;
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
namespace AlicizaX.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; // 表示菜单已经构建
}
}
}

View File

@ -1,11 +0,0 @@
fileFormatVersion: 2
guid: 7ede2a32054642119314d6875e9d8615
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,8 +0,0 @@
fileFormatVersion: 2
guid: d974406a2b96e2049970825025bdaa38
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,67 +0,0 @@
#if UNITY_EDITOR
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
namespace AlicizaX.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

View File

@ -1,11 +0,0 @@
fileFormatVersion: 2
guid: 67cc1404d938c9942896d12f3832cff5
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,8 +0,0 @@
fileFormatVersion: 2
guid: 3e53dd6d150f78d4d9239f98eef08fb2
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,91 +0,0 @@
#if UNITY_EDITOR
using UnityEngine;
using UnityEditor;
namespace AlicizaX.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

View File

@ -1,11 +0,0 @@
fileFormatVersion: 2
guid: ee529be6285730e48a42ee2573e5fd8a
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,8 +0,0 @@
fileFormatVersion: 2
guid: 51515c3febc44da9bc96e216fb4f2c52
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,811 +0,0 @@
#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 AlicizaX.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(UnityEditor.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 UnityEditor.Editor GetEditor(Object[] targets, string EditorClassName)
{
var assembly = typeof(UnityEditor.Editor).Assembly;
var type = assembly.GetType(EditorClassName);
var editor = UnityEditor.Editor.CreateEditor(targets, type);
return editor;
}
public static UnityEditor.Editor GetEditor(Object target, string EditorClassName)
{
var assembly = typeof(UnityEditor.Editor).Assembly;
var type = assembly.GetType(EditorClassName);
var editor = UnityEditor.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

View File

@ -1,11 +0,0 @@
fileFormatVersion: 2
guid: a90c428d74014a66a7e8c0c3faacd176
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,8 +0,0 @@
fileFormatVersion: 2
guid: b38dd3e8fb89442baf34c39c99abf56d
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,37 +0,0 @@
using System.Collections.Generic;
using System.IO;
using UnityEditor;
namespace AlicizaX.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();
}
}
}

View File

@ -1,11 +0,0 @@
fileFormatVersion: 2
guid: c9f1c3d755f245759b4595b65b5e576e
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,8 +0,0 @@
fileFormatVersion: 2
guid: f206d2f1a2fd4f81b95496801251b88c
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,14 +0,0 @@
using System.IO;
namespace AlicizaX.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";
}
}

View File

@ -1,11 +0,0 @@
fileFormatVersion: 2
guid: 8ef1696f4c864c8c840cb2e637e72189
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,8 +0,0 @@
fileFormatVersion: 2
guid: 3f498c2f64bbd1e4589a9c8058380495
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,8 +0,0 @@
fileFormatVersion: 2
guid: e45fc341a3c39f547b39cce778b1c136
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,8 +0,0 @@
fileFormatVersion: 2
guid: 9768513809263534088a2f32ed7c34f5
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,92 +0,0 @@
%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:

View File

@ -1,7 +0,0 @@
fileFormatVersion: 2
guid: 0d29cbf8bba5fb34a87e59a5b6ead9a9
PrefabImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,155 +0,0 @@
%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

View File

@ -1,7 +0,0 @@
fileFormatVersion: 2
guid: 3b558d5df13733448af9e24b0c779294
PrefabImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,8 +0,0 @@
fileFormatVersion: 2
guid: 0fdf05c5b631e734ba52458209687d6a
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,7 +0,0 @@
fileFormatVersion: 2
guid: 4216e337fd7940249a555a2f0543a2da
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,8 +0,0 @@
fileFormatVersion: 2
guid: 8db0bb683a284f3192668f83cf1cdf98
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,8 +0,0 @@
fileFormatVersion: 2
guid: 34949e2402051a244b0ba35d0dba372a
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,987 +0,0 @@
#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 AlicizaX.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
{
UnityEngine.SceneManagement.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

View File

@ -1,11 +0,0 @@
fileFormatVersion: 2
guid: deac8fa4e6a7d8348b7fd4f2e06676b2
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,8 +0,0 @@
fileFormatVersion: 2
guid: ae357567eebc45eb8847bd1d2ad0eab8
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,107 +0,0 @@
using AlicizaX.UI;
using AlicizaX.UI.Runtime;
using UnityEditor;
using UnityEditor.SceneManagement;
using UnityEngine;
using UnityEngine.UIElements;
namespace AlicizaX.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);
}
}
}

View File

@ -1,11 +0,0 @@
fileFormatVersion: 2
guid: 67b9bfb10302d6d43aaee21321c53818
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,136 +0,0 @@
using System;
using System.Drawing;
using AlicizaX.UI;
using AlicizaX.UI.Runtime;
using UnityEditor;
using UnityEngine;
using UnityEngine.UIElements;
using FontStyle = UnityEngine.FontStyle;
namespace AlicizaX.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);
}
}
}

View File

@ -1,11 +0,0 @@
fileFormatVersion: 2
guid: 2c4d51bc600e38a4f9100a50edc69771
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,433 +0,0 @@
using System;
using System.Collections.Generic;
using System.Reflection;
using AlicizaX.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);
}
}
}

View File

@ -1,11 +0,0 @@
fileFormatVersion: 2
guid: 3dc3451006fe98b47bffcc2c2a872d91
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,8 +0,0 @@
fileFormatVersion: 2
guid: d7e417a455885fb468c5aa9c679b6628
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,11 +0,0 @@
fileFormatVersion: 2
guid: 460b36db23659db4cb26ee8ba3234ad9
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,8 +0,0 @@
fileFormatVersion: 2
guid: 8268cda376c28d14db254b78f6c56df7
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,166 +0,0 @@
#if UNITY_EDITOR
using UnityEditor;
using UnityEngine.UIElements;
using System.IO;
using UnityEngine;
using System;
namespace AlicizaX.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

View File

@ -1,11 +0,0 @@
fileFormatVersion: 2
guid: 99285ec27ea549d99d16c4fd764c42a9
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,250 +0,0 @@
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 AlicizaX.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);
}
}
}

View File

@ -1,11 +0,0 @@
fileFormatVersion: 2
guid: ddadf2863f864c74b994822b0dfcec71
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,8 +0,0 @@
fileFormatVersion: 2
guid: b118a0eb1d94cad4abc3fbda06d94698
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

Some files were not shown because too many files have changed in this diff Show More