From 635754f0729292a8518f5507599bbdc1cc855269 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=99=88=E6=80=9D=E6=B5=B7?= <1464576565@qq.com> Date: Wed, 1 Apr 2026 13:20:06 +0800 Subject: [PATCH] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E4=BD=BF=E7=94=A8=E6=96=87?= =?UTF-8?q?=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Client/.tmp_api_editor.txt | 632 +++++ Client/.tmp_api_ui.txt | 1156 ++++++++ Client/Assets/Books.meta | 8 + Client/Assets/Books/EditorExtension.meta | 8 + .../EditorExtension/AtlasPostprocessor.md | 83 + .../AtlasPostprocessor.md.meta | 7 + .../Assets/Books/EditorExtension/BaseTools.md | 87 + .../Books/EditorExtension/BaseTools.md.meta | 7 + .../EditorExtension/CustomToolbarItems.md | 55 + .../CustomToolbarItems.md.meta | 7 + .../Assets/Books/EditorExtension/HybridCLR.md | 74 + .../Books/EditorExtension/HybridCLR.md.meta | 7 + .../Books/EditorExtension/ReferenceFinder.md | 103 + .../EditorExtension/ReferenceFinder.md.meta | 7 + .../Books/EditorExtension/TexturePacker.md | 42 + .../EditorExtension/TexturePacker.md.meta | 7 + .../Books/EditorExtension/ToolbarExtender.md | 98 + .../EditorExtension/ToolbarExtender.md.meta | 7 + Client/Assets/Books/Framework.meta | 8 + Client/Assets/Books/Framework/Core.meta | 8 + Client/Assets/Books/Framework/Core/Event.md | 182 ++ .../Assets/Books/Framework/Core/Event.md.meta | 7 + .../Assets/Books/Framework/Core/Foundation.md | 186 ++ .../Books/Framework/Core/Foundation.md.meta | 7 + .../Books/Framework/Core/GameObjectPool.md | 98 + .../Framework/Core/GameObjectPool.md.meta | 7 + .../Assets/Books/Framework/Core/ObjectPool.md | 204 ++ .../Books/Framework/Core/ObjectPool.md.meta | 7 + Client/Assets/Books/Framework/Editor.meta | 8 + .../Books/Framework/Editor/EditorTools.md | 157 ++ .../Framework/Editor/EditorTools.md.meta | 7 + Client/Assets/Books/Framework/Plugins.meta | 8 + .../Assets/Books/Framework/Plugins/Plugins.md | 54 + .../Books/Framework/Plugins/Plugins.md.meta | 7 + Client/Assets/Books/Framework/QuickStart.md | 134 + .../Assets/Books/Framework/QuickStart.md.meta | 7 + Client/Assets/Books/Framework/Runtime.meta | 8 + .../Assets/Books/Framework/Runtime/Audio.md | 94 + .../Books/Framework/Runtime/Audio.md.meta | 7 + .../Books/Framework/Runtime/Debugger.md | 68 + .../Books/Framework/Runtime/Debugger.md.meta | 7 + .../Books/Framework/Runtime/Localization.md | 97 + .../Framework/Runtime/Localization.md.meta | 7 + .../Books/Framework/Runtime/Procedure.md | 103 + .../Books/Framework/Runtime/Procedure.md.meta | 7 + .../Books/Framework/Runtime/Resource.md | 673 +++++ .../Books/Framework/Runtime/Resource.md.meta | 7 + .../Assets/Books/Framework/Runtime/Scene.md | 477 ++++ .../Books/Framework/Runtime/Scene.md.meta | 7 + .../Assets/Books/Framework/Runtime/Timer.md | 672 +++++ .../Books/Framework/Runtime/Timer.md.meta | 7 + Client/Assets/Books/Framework/Runtime/UI.md | 552 ++++ .../Assets/Books/Framework/Runtime/UI.md.meta | 7 + Client/Assets/Books/UIExtension.meta | 8 + Client/Assets/Books/UIExtension/InputGlyph.md | 94 + .../Books/UIExtension/InputGlyph.md.meta | 7 + .../Assets/Books/UIExtension/RecyclerView.md | 2402 +++++++++++++++++ .../Books/UIExtension/RecyclerView.md.meta | 7 + Client/Assets/Books/UIExtension/UXButton.md | 22 + .../Assets/Books/UIExtension/UXButton.md.meta | 7 + .../Assets/Books/UIExtension/UXController.md | 51 + .../Books/UIExtension/UXController.md.meta | 7 + .../Assets/Books/UIExtension/UXDraggable.md | 16 + .../Books/UIExtension/UXDraggable.md.meta | 7 + .../Assets/Books/UIExtension/UXGroupToggle.md | 22 + .../Books/UIExtension/UXGroupToggle.md.meta | 7 + Client/Assets/Books/UIExtension/UXHelper.md | 31 + .../Assets/Books/UIExtension/UXHelper.md.meta | 7 + Client/Assets/Books/UIExtension/UXHotkey.md | 24 + .../Assets/Books/UIExtension/UXHotkey.md.meta | 7 + Client/Assets/Books/UIExtension/UXImage.md | 41 + .../Assets/Books/UIExtension/UXImage.md.meta | 7 + .../Assets/Books/UIExtension/UXNavigation.md | 37 + .../Books/UIExtension/UXNavigation.md.meta | 7 + .../Assets/Books/UIExtension/UXSelectable.md | 22 + .../Books/UIExtension/UXSelectable.md.meta | 7 + Client/Assets/Books/UIExtension/UXText.md | 16 + .../Assets/Books/UIExtension/UXText.md.meta | 7 + .../GameLogic/UI/TestScrollItemRender.cs | 26 +- .../TextMesh Pro/Resources/TMP Settings.asset | 2 +- .../Simulate/DefaultPackage_Simulate.bytes | Bin 1594 -> 1593 bytes .../Simulate/DefaultPackage_Simulate.hash | 2 +- .../Simulate/DefaultPackage_Simulate.json | 2 +- .../Packages/com.alicizax.unity.ui.extension | 2 +- Client/UserSettings/Layouts/default-2022.dwlt | 98 +- 85 files changed, 9231 insertions(+), 62 deletions(-) create mode 100644 Client/.tmp_api_editor.txt create mode 100644 Client/.tmp_api_ui.txt create mode 100644 Client/Assets/Books.meta create mode 100644 Client/Assets/Books/EditorExtension.meta create mode 100644 Client/Assets/Books/EditorExtension/AtlasPostprocessor.md create mode 100644 Client/Assets/Books/EditorExtension/AtlasPostprocessor.md.meta create mode 100644 Client/Assets/Books/EditorExtension/BaseTools.md create mode 100644 Client/Assets/Books/EditorExtension/BaseTools.md.meta create mode 100644 Client/Assets/Books/EditorExtension/CustomToolbarItems.md create mode 100644 Client/Assets/Books/EditorExtension/CustomToolbarItems.md.meta create mode 100644 Client/Assets/Books/EditorExtension/HybridCLR.md create mode 100644 Client/Assets/Books/EditorExtension/HybridCLR.md.meta create mode 100644 Client/Assets/Books/EditorExtension/ReferenceFinder.md create mode 100644 Client/Assets/Books/EditorExtension/ReferenceFinder.md.meta create mode 100644 Client/Assets/Books/EditorExtension/TexturePacker.md create mode 100644 Client/Assets/Books/EditorExtension/TexturePacker.md.meta create mode 100644 Client/Assets/Books/EditorExtension/ToolbarExtender.md create mode 100644 Client/Assets/Books/EditorExtension/ToolbarExtender.md.meta create mode 100644 Client/Assets/Books/Framework.meta create mode 100644 Client/Assets/Books/Framework/Core.meta create mode 100644 Client/Assets/Books/Framework/Core/Event.md create mode 100644 Client/Assets/Books/Framework/Core/Event.md.meta create mode 100644 Client/Assets/Books/Framework/Core/Foundation.md create mode 100644 Client/Assets/Books/Framework/Core/Foundation.md.meta create mode 100644 Client/Assets/Books/Framework/Core/GameObjectPool.md create mode 100644 Client/Assets/Books/Framework/Core/GameObjectPool.md.meta create mode 100644 Client/Assets/Books/Framework/Core/ObjectPool.md create mode 100644 Client/Assets/Books/Framework/Core/ObjectPool.md.meta create mode 100644 Client/Assets/Books/Framework/Editor.meta create mode 100644 Client/Assets/Books/Framework/Editor/EditorTools.md create mode 100644 Client/Assets/Books/Framework/Editor/EditorTools.md.meta create mode 100644 Client/Assets/Books/Framework/Plugins.meta create mode 100644 Client/Assets/Books/Framework/Plugins/Plugins.md create mode 100644 Client/Assets/Books/Framework/Plugins/Plugins.md.meta create mode 100644 Client/Assets/Books/Framework/QuickStart.md create mode 100644 Client/Assets/Books/Framework/QuickStart.md.meta create mode 100644 Client/Assets/Books/Framework/Runtime.meta create mode 100644 Client/Assets/Books/Framework/Runtime/Audio.md create mode 100644 Client/Assets/Books/Framework/Runtime/Audio.md.meta create mode 100644 Client/Assets/Books/Framework/Runtime/Debugger.md create mode 100644 Client/Assets/Books/Framework/Runtime/Debugger.md.meta create mode 100644 Client/Assets/Books/Framework/Runtime/Localization.md create mode 100644 Client/Assets/Books/Framework/Runtime/Localization.md.meta create mode 100644 Client/Assets/Books/Framework/Runtime/Procedure.md create mode 100644 Client/Assets/Books/Framework/Runtime/Procedure.md.meta create mode 100644 Client/Assets/Books/Framework/Runtime/Resource.md create mode 100644 Client/Assets/Books/Framework/Runtime/Resource.md.meta create mode 100644 Client/Assets/Books/Framework/Runtime/Scene.md create mode 100644 Client/Assets/Books/Framework/Runtime/Scene.md.meta create mode 100644 Client/Assets/Books/Framework/Runtime/Timer.md create mode 100644 Client/Assets/Books/Framework/Runtime/Timer.md.meta create mode 100644 Client/Assets/Books/Framework/Runtime/UI.md create mode 100644 Client/Assets/Books/Framework/Runtime/UI.md.meta create mode 100644 Client/Assets/Books/UIExtension.meta create mode 100644 Client/Assets/Books/UIExtension/InputGlyph.md create mode 100644 Client/Assets/Books/UIExtension/InputGlyph.md.meta create mode 100644 Client/Assets/Books/UIExtension/RecyclerView.md create mode 100644 Client/Assets/Books/UIExtension/RecyclerView.md.meta create mode 100644 Client/Assets/Books/UIExtension/UXButton.md create mode 100644 Client/Assets/Books/UIExtension/UXButton.md.meta create mode 100644 Client/Assets/Books/UIExtension/UXController.md create mode 100644 Client/Assets/Books/UIExtension/UXController.md.meta create mode 100644 Client/Assets/Books/UIExtension/UXDraggable.md create mode 100644 Client/Assets/Books/UIExtension/UXDraggable.md.meta create mode 100644 Client/Assets/Books/UIExtension/UXGroupToggle.md create mode 100644 Client/Assets/Books/UIExtension/UXGroupToggle.md.meta create mode 100644 Client/Assets/Books/UIExtension/UXHelper.md create mode 100644 Client/Assets/Books/UIExtension/UXHelper.md.meta create mode 100644 Client/Assets/Books/UIExtension/UXHotkey.md create mode 100644 Client/Assets/Books/UIExtension/UXHotkey.md.meta create mode 100644 Client/Assets/Books/UIExtension/UXImage.md create mode 100644 Client/Assets/Books/UIExtension/UXImage.md.meta create mode 100644 Client/Assets/Books/UIExtension/UXNavigation.md create mode 100644 Client/Assets/Books/UIExtension/UXNavigation.md.meta create mode 100644 Client/Assets/Books/UIExtension/UXSelectable.md create mode 100644 Client/Assets/Books/UIExtension/UXSelectable.md.meta create mode 100644 Client/Assets/Books/UIExtension/UXText.md create mode 100644 Client/Assets/Books/UIExtension/UXText.md.meta diff --git a/Client/.tmp_api_editor.txt b/Client/.tmp_api_editor.txt new file mode 100644 index 0000000..fecc691 --- /dev/null +++ b/Client/.tmp_api_editor.txt @@ -0,0 +1,632 @@ +###FILE G:\UnityProject\Aliciza\Client\Packages\com.alicizax.unity.editor.extension\Editor\HybridCLR\BuildDLLCommand.cs +NAMESPACE +L13 public static class BuildDLLCommand +L16 public static string AssemblyTextAssetPath = Application.dataPath + "/" + "Bundles/DLL"; +L22 public static void Disable() +L32 public static void Enable() +L41 public static void BuildAndCopyDlls() +L49 public static void GenerateAll() +L54 public static void BuildAndCopyDlls(BuildTarget target) +L60 public static void CopyAOTHotUpdateDlls(BuildTarget target) +L67 public static void CopyAOTAssembliesToAssetPath() +L129 public static void CopyHotUpdateAssembliesToAssetPath() + +###FILE G:\UnityProject\Aliciza\Client\Packages\com.alicizax.unity.editor.extension\Editor\Postprocessor\Atlas\AtlasConfiguration.cs +NAMESPACE +L7 public class AtlasConfiguration : AlicizaX.Editor.Setting.ScriptableSingleton +L15 public TextureImporterFormat iosFormat = TextureImporterFormat.ASTC_5x5; +L16 public TextureImporterFormat webglFormat = TextureImporterFormat.ASTC_6x6; +L19 public bool enableRotation = true; +L20 public int blockOffset = 1; +L21 public bool tightPacking = true; +L24 public bool autoGenerate = true; +L25 public bool enableLogging = true; +L26 public bool enableV2 = true; + +###FILE G:\UnityProject\Aliciza\Client\Packages\com.alicizax.unity.editor.extension\Editor\Postprocessor\Atlas\AtlasEditorWindow.cs +NAMESPACE +L6 public class AtlasConfigWindow : EditorWindow +L9 public static void ShowWindow() + +###FILE G:\UnityProject\Aliciza\Client\Packages\com.alicizax.unity.editor.extension\Editor\Postprocessor\Atlas\EditorSpriteSaveInfo.cs +NAMESPACE +L11 public static class EditorSpriteSaveInfo +L34 public static void ConvertToSprite(string assetPath) +L73 public static void OnImportSprite(string assetPath) +L96 public static void OnDeleteSprite(string assetPath) +L114 public static void ForceGenerateAll() +L122 public static void ClearCache() +L129 public static void MarkParentAtlasesDirty(string assetPath) + +###FILE G:\UnityProject\Aliciza\Client\Packages\com.alicizax.unity.editor.extension\Editor\Postprocessor\Atlas\SpritePostprocessor.cs +NAMESPACE +L5 public class SpritePostprocessor : AssetPostprocessor + +###FILE G:\UnityProject\Aliciza\Client\Packages\com.alicizax.unity.editor.extension\Editor\ReferenceFinder\AssetTreeView.cs +NAMESPACE TEngine.Editor +L13 public AssetViewItem assetRoot; +L15 public AssetTreeView(TreeViewState state, MultiColumnHeader multicolumnHeader) : base(state, multicolumnHeader) +L39 public void SortExpandItem() +L53 public static MultiColumnHeaderState CreateDefaultMultiColumnHeaderState(float treeViewWidth, bool isDepend) + +###FILE G:\UnityProject\Aliciza\Client\Packages\com.alicizax.unity.editor.extension\Editor\ReferenceFinder\AssetViewItem.cs +NAMESPACE TEngine.Editor +L7 public ReferenceFinderData.AssetDescription data; + +###FILE G:\UnityProject\Aliciza\Client\Packages\com.alicizax.unity.editor.extension\Editor\ReferenceFinder\ClickColumn.cs +NAMESPACE TEngine.Editor +L9 public delegate void SortInColumn(); +L11 public static Dictionary SortWithIndex = new Dictionary +L17 public ClickColumn(MultiColumnHeaderState state) : base(state) => canSort = true; +L30 public static void SortByName() => SortHelper.SortByName(); +L32 public static void SortByPath() => SortHelper.SortByPath(); + +###FILE G:\UnityProject\Aliciza\Client\Packages\com.alicizax.unity.editor.extension\Editor\ReferenceFinder\DragAreaGetObject.cs +NAMESPACE TEngine.Editor +L8 public static Object[] GetObjects(string meg = null) + +###FILE G:\UnityProject\Aliciza\Client\Packages\com.alicizax.unity.editor.extension\Editor\ReferenceFinder\ListInfo.cs +NAMESPACE TEngine.Editor +L5 public int Count; +L6 public string Name; +L7 public string Type; + +###FILE G:\UnityProject\Aliciza\Client\Packages\com.alicizax.unity.editor.extension\Editor\ReferenceFinder\ReferenceFinderData.cs +NAMESPACE TEngine.Editor +L18 public enum AssetState : byte +L27 public const int MinThreadCount = 8; +L48 public string[] allAssets; +L49 public Dictionary assetDict = new Dictionary(); +L51 public void CollectDependenciesInfo() +L115 public void ReadAssetInfo() +L137 public void GetAsset(string dataPath, string assetPath) +L198 public bool ReadFromCache() +L296 public void UpdateAssetState(string guid) +L318 public static string GetInfoByState(AssetState state) +L373 public void ClearCache() => _dictCache.Clear(); +L375 public string GetRefCount(AssetDescription desc, AssetDescription parentDesc) +L409 public string assetDependencyHashString; +L410 public List dependencies = new List(); +L411 public string name = ""; +L412 public string path = ""; +L413 public List references = new List(); +L414 public AssetState state = AssetState.Normal; + +###FILE G:\UnityProject\Aliciza\Client\Packages\com.alicizax.unity.editor.extension\Editor\ReferenceFinder\ResourceReferenceInfo.cs +NAMESPACE TEngine.Editor +L13 public static readonly ReferenceFinderData Data = new ReferenceFinderData(); +L19 public bool needUpdateAssetTree; +L20 public bool needUpdateState = true; +L21 public List selectedAssetGuid = new List(); +L30 public AssetTreeView mAssetTreeView; +L44 public static void FindRef() +L202 public void DrawOptionBar() + +###FILE G:\UnityProject\Aliciza\Client\Packages\com.alicizax.unity.editor.extension\Editor\ReferenceFinder\SortConfig.cs +NAMESPACE TEngine.Editor +L7 public static readonly Dictionary SortTypeChangeByNameHandler = new Dictionary +L14 public static readonly Dictionary SortTypeChangeByPathHandler = new Dictionary +L21 public static readonly Dictionary SortTypeGroup = new Dictionary +L30 public const short TYPE_BY_NAME_GROUP = 2; +L31 public const short TYPE_BY_PATH_GROUP = 1; + +###FILE G:\UnityProject\Aliciza\Client\Packages\com.alicizax.unity.editor.extension\Editor\ReferenceFinder\SortHelper.cs +NAMESPACE TEngine.Editor +L8 public delegate int SortCompare(string lString, string rString); +L10 public static readonly HashSet SortedGuid = new HashSet(); +L11 public static readonly Dictionary SortedAsset = new Dictionary(); +L12 public static SortType CurSortType = SortType.None; +L13 public static SortType PathType = SortType.None; +L14 public static SortType NameType = SortType.None; +L16 public static readonly Dictionary CompareFunction = new Dictionary +L24 public static void Init() +L30 public static void ChangeSortType(short sortGroup, Dictionary handler, ref SortType recoverType) +L45 public static void SortByName() => ChangeSortType(SortConfig.TYPE_BY_NAME_GROUP, SortConfig.SortTypeChangeByNameHandler, ref NameType); +L47 public static void SortByPath() => ChangeSortType(SortConfig.TYPE_BY_PATH_GROUP, SortConfig.SortTypeChangeByPathHandler, ref PathType); +L49 public static void SortChild(ReferenceFinderData.AssetDescription data) +L77 public static void NormalSort(List strList) +L83 public static void FastSort(List strList) +L95 public static int CompareWithName(string lString, string rString) +L101 public static int CompareWithNameDesc(string lString, string rString) => 0 - CompareWithName(lString, rString); +L103 public static int CompareWithPath(string lString, string rString) +L109 public static int CompareWithPathDesc(string lString, string rString) => 0 - CompareWithPath(lString, rString); + +###FILE G:\UnityProject\Aliciza\Client\Packages\com.alicizax.unity.editor.extension\Editor\ReferenceFinder\SortType.cs +NAMESPACE TEngine.Editor +L3 public enum SortType + +###FILE G:\UnityProject\Aliciza\Client\Packages\com.alicizax.unity.editor.extension\Editor\TexturePacker\UnityTexturePackEditor.cs +NAMESPACE +L8 public class UnityTexturePackerEditor : EditorWindow +L22 public static void ShowWindow() +L154 public string name; +L155 public string sourcePath; +L156 public int x; +L157 public int y; +L158 public int w; +L159 public int h; +L160 public int sourceW; +L161 public int sourceH; +L166 public string path; +L167 public bool isReadable; +L168 public TextureImporterType type; +L169 public TextureImporterCompression compression; +L170 public int maxSize; + +###FILE G:\UnityProject\Aliciza\Client\Packages\com.alicizax.unity.editor.extension\Editor\Toolbar\ControlPanelWindow\MainToolbarControlPanelWindow.cs +NAMESPACE Paps.UnityToolbarExtenderUIToolkit +L9 public class MainToolbarControlPanelWindow : EditorWindow +L32 public static void OpenWindow() + +###FILE G:\UnityProject\Aliciza\Client\Packages\com.alicizax.unity.editor.extension\Editor\Toolbar\ControlPanelWindow\MainToolbarElementController.cs +NAMESPACE Paps.UnityToolbarExtenderUIToolkit +L23 public string Id { get; } +L24 public VisualElement ControlledVisualElement { get; } +L25 public bool HoldsAGroup => _foldout != null; +L26 public bool HoldsANativeElement { get; private set; } +L28 public MainToolbarElementController(OverridableElement overridableElement, +L64 public bool ContainsSubController(string id) + +###FILE G:\UnityProject\Aliciza\Client\Packages\com.alicizax.unity.editor.extension\Editor\Toolbar\ControlPanelWindow\OrganizationalFoldableContainer.cs +NAMESPACE Paps.UnityToolbarExtenderUIToolkit +L15 public OrganizationalFoldableContainer(string containerId, string foldoutText) +L41 public void SetControllers(IEnumerable controllers) + +###FILE G:\UnityProject\Aliciza\Client\Packages\com.alicizax.unity.editor.extension\Editor\Toolbar\ControlPanelWindow\OverridableElement.cs +NAMESPACE Paps.UnityToolbarExtenderUIToolkit +L7 public string Id { get; } +L8 public VisualElement VisualElement { get; } +L9 public bool IsNative { get; } +L11 public OverridableElement(string id, VisualElement visualElement, bool isNative) + +###FILE G:\UnityProject\Aliciza\Client\Packages\com.alicizax.unity.editor.extension\Editor\Toolbar\Extensions\EditorWindowExtensions.cs +NAMESPACE Paps.UnityToolbarExtenderUIToolkit +L6 public static class EditorWindowExtensions +L8 public static void ShowAsDropdownForMainToolbar(this EditorWindow window, Rect activatorRect, Vector2 size) + +###FILE G:\UnityProject\Aliciza\Client\Packages\com.alicizax.unity.editor.extension\Editor\Toolbar\GroupDefinitions\GroupDefinition.cs +NAMESPACE Paps.UnityToolbarExtenderUIToolkit +L7 public string GroupId { get; } +L8 public string GroupName { get; } +L9 public ToolbarAlign Alignment { get; } +L10 public int Order { get; } +L11 public string[] ToolbarElementsIds { get; } +L13 public GroupDefinition(string groupId, string groupName, ToolbarAlign alignment, int order, string[] toolbarElementsIds) +L22 public bool AreEquals(GroupDefinition other) + +###FILE G:\UnityProject\Aliciza\Client\Packages\com.alicizax.unity.editor.extension\Editor\Toolbar\GroupDefinitions\GroupDropdownWindowPopup.cs +NAMESPACE Paps.UnityToolbarExtenderUIToolkit +L12 public void Initialize(VisualElement[] groupElements) + +###FILE G:\UnityProject\Aliciza\Client\Packages\com.alicizax.unity.editor.extension\Editor\Toolbar\GroupDefinitions\GroupDropdownWindowPopupManager.cs +NAMESPACE Paps.UnityToolbarExtenderUIToolkit +L124 public static void Show(Rect activatorRect, VisualElement[] elements) + +###FILE G:\UnityProject\Aliciza\Client\Packages\com.alicizax.unity.editor.extension\Editor\Toolbar\GroupDefinitions\GroupElement.cs +NAMESPACE Paps.UnityToolbarExtenderUIToolkit +L10 public VisualElement[] GroupedElements => _groupedElements.ToArray(); +L12 public GroupElement(string name) +L19 public void Initialize(VisualElement[] groupedElements) + +###FILE G:\UnityProject\Aliciza\Client\Packages\com.alicizax.unity.editor.extension\Editor\Toolbar\GroupDefinitions\GroupPopupSubWindowAttribute.cs +NAMESPACE Paps.UnityToolbarExtenderUIToolkit +L6 public class GroupPopupSubWindowAttribute : Attribute + +###FILE G:\UnityProject\Aliciza\Client\Packages\com.alicizax.unity.editor.extension\Editor\Toolbar\GroupDefinitions\IGroupDefinitionRepository.cs +NAMESPACE Paps.UnityToolbarExtenderUIToolkit +L5 public GroupDefinition[] GetAll(); + +###FILE G:\UnityProject\Aliciza\Client\Packages\com.alicizax.unity.editor.extension\Editor\Toolbar\GroupDefinitions\MainToolbarElementDropdownAttribute.cs +NAMESPACE Paps.UnityToolbarExtenderUIToolkit + +###FILE G:\UnityProject\Aliciza\Client\Packages\com.alicizax.unity.editor.extension\Editor\Toolbar\GroupDefinitions\MainToolbarElementDropdownDrawer.cs +NAMESPACE Paps.UnityToolbarExtenderUIToolkit +L16 public override VisualElement CreatePropertyGUI(SerializedProperty property) + +###FILE G:\UnityProject\Aliciza\Client\Packages\com.alicizax.unity.editor.extension\Editor\Toolbar\GroupDefinitions\ScriptableGroupDefinition.cs +NAMESPACE Paps.UnityToolbarExtenderUIToolkit +L6 public sealed class ScriptableGroupDefinition : ScriptableObject +L23 public string GroupId => _groupId; +L24 public string GroupName => _groupName; +L25 public ToolbarAlign Alignment => _alignment; +L26 public int Order => _order; +L27 public string[] ToolbarElementsIds => _toolbarElementsIds == null ? new string[0] : _toolbarElementsIds; + +###FILE G:\UnityProject\Aliciza\Client\Packages\com.alicizax.unity.editor.extension\Editor\Toolbar\GroupDefinitions\ScriptableGroupDefinitionHelper.cs +NAMESPACE Paps.UnityToolbarExtenderUIToolkit +L18 public static void Refresh() +L33 public static IEnumerable GetUnusedMainToolbarElementIds(IEnumerable allIds) +L44 public static IEnumerable GetGroupIds() +L49 public static IEnumerable GetEligibleGroupChildsFor(string groupId) + +###FILE G:\UnityProject\Aliciza\Client\Packages\com.alicizax.unity.editor.extension\Editor\Toolbar\GroupDefinitions\ScriptableObjectGroupDefinitionRepository.cs +NAMESPACE Paps.UnityToolbarExtenderUIToolkit +L8 public GroupDefinition[] GetAll() + +###FILE G:\UnityProject\Aliciza\Client\Packages\com.alicizax.unity.editor.extension\Editor\Toolbar\Helpers\GlobalActions.cs +NAMESPACE Paps.UnityToolbarExtenderUIToolkit +L8 public static void ResetOverrides() +L14 public static void ResetOverridesIfUserAccepts() +L25 public static void ShowDialog(string title, string message, string okMessage, + +###FILE G:\UnityProject\Aliciza\Client\Packages\com.alicizax.unity.editor.extension\Editor\Toolbar\Helpers\Icons.cs +NAMESPACE Paps.UnityToolbarExtenderUIToolkit +L15 public static Texture VisibilityOnOverrideIcon => VISIBILITY_ON_OVERRIDE_ICON_LAZY.Value; +L16 public static Texture VisibilityOffOverrideIcon => VISIBILITY_OFF_OVERRIDE_ICON_LAZY.Value; + +###FILE G:\UnityProject\Aliciza\Client\Packages\com.alicizax.unity.editor.extension\Editor\Toolbar\Helpers\MenuItems.cs +NAMESPACE Paps.UnityToolbarExtenderUIToolkit +L8 public static void Refresh() +L14 public static void OpenControlPanel() + +###FILE G:\UnityProject\Aliciza\Client\Packages\com.alicizax.unity.editor.extension\Editor\Toolbar\Helpers\RefreshToolbarAutomaticExtenderOnSave.cs +NAMESPACE Paps.UnityToolbarExtenderUIToolkit + +###FILE G:\UnityProject\Aliciza\Client\Packages\com.alicizax.unity.editor.extension\Editor\Toolbar\Helpers\ServicesAndRepositories.cs +NAMESPACE Paps.UnityToolbarExtenderUIToolkit +L5 public static IMainToolbarElementOverrideRepository MainToolbarElementOverridesRepository = +L8 public static IGroupDefinitionRepository GroupDefinitionRepository = +L11 public static IMainToolbarElementRepository MainToolbarElementRepository = +L14 public static IValueSerializer ValueSerializer = +L17 public static IMainToolbarElementVariableSerializer MainToolbarElementVariableSerializer = +L20 public static IMainToolbarElementVariableRepository MainToolbarElementVariableRepository = + +###FILE G:\UnityProject\Aliciza\Client\Packages\com.alicizax.unity.editor.extension\Editor\Toolbar\Helpers\ToolInfo.cs +NAMESPACE Paps.UnityToolbarExtenderUIToolkit +L5 public const string COMPANY_NAME = "Tools/Extension/Toolbar"; +L6 public const string EDITOR_MENU_BASE = COMPANY_NAME + "/" ; + +###FILE G:\UnityProject\Aliciza\Client\Packages\com.alicizax.unity.editor.extension\Editor\Toolbar\Helpers\UnityNativeElementsIds.cs +NAMESPACE Paps.UnityToolbarExtenderUIToolkit +L47 public const string TOOLBAR_PRODUCT_CAPTION = "ToolbarProductCaption"; +L48 public const string ACCOUNT_DROPDOWN_ID = "AccountDropdown"; +L49 public const string CLOUD_BUTTON_ID = "CloudButton"; +L50 public const string VERSION_CONTROL_ID = "VersionControlButton"; +L51 public const string STORE_BUTTON_ID = "StoreButton"; +L54 public const string PLAY_BUTTON_ID = "PlayButton"; +L55 public const string PAUSE_BUTTON_ID = "PauseButton"; +L56 public const string FRAME_STEP_BUTTON_ID = "FrameStepButton"; +L59 public const string LAYOUT_DROPDOWN_ID = "LayoutDropdown"; +L60 public const string LAYERS_DROPDOWN_ID = "LayersDropdown"; +L61 public const string SEARCH_BUTTON_ID = "SearchButton"; +L62 public const string MODES_DROPDOWN_ID = "ModesDropdown"; +L63 public const string PREVIEW_PACKAGES_IN_USE_DROPDOWN_ID = "PreviewPackagesInUseDropdown"; +L64 public const string UNDO_BUTTON_ID = "UndoButton"; +L65 public const string MULTIPLAYER_ROLE_DROPDOWN = "MultiplayerRoleDropdown"; +L105 public static string IdOf(VisualElement visualElement) + +###FILE G:\UnityProject\Aliciza\Client\Packages\com.alicizax.unity.editor.extension\Editor\Toolbar\Helpers\UserSettingsPrefs.cs +NAMESPACE Paps.UnityToolbarExtenderUIToolkit +L9 public static class UserSettingsPrefs +L26 public static void SetInt(string key, int value) +L32 public static void SetFloat(string key, float value) +L38 public static void SetDouble(string key, double value) +L44 public static void SetBool(string key, bool value) +L50 public static void SetString(string key, string value) +L56 public static string GetString(string key, string defaultValue = null) +L64 public static bool GetBool(string key, bool defaultValue = false) +L72 public static int GetInt(string key, int defaultValue = 0) +L80 public static float GetFloat(string key, float defaultValue = 0.0f) +L88 public static double GetDouble(string key, double defaultValue = 0.0) + +###FILE G:\UnityProject\Aliciza\Client\Packages\com.alicizax.unity.editor.extension\Editor\Toolbar\Override\IMainToolbarElementOverrideRepository.cs +NAMESPACE Paps.UnityToolbarExtenderUIToolkit +L5 public MainToolbarElementOverride? Get(string elementId); +L6 public MainToolbarElementOverride[] GetAll(); +L7 public void Save(MainToolbarElementOverride elementOverride); +L8 public void Clear(); + +###FILE G:\UnityProject\Aliciza\Client\Packages\com.alicizax.unity.editor.extension\Editor\Toolbar\Override\MainToolbarElementOverride.cs +NAMESPACE Paps.UnityToolbarExtenderUIToolkit +L5 public string ElementId { get; } +L6 public bool Visible { get; } +L8 public MainToolbarElementOverride(string elementId, bool visible) + +###FILE G:\UnityProject\Aliciza\Client\Packages\com.alicizax.unity.editor.extension\Editor\Toolbar\Override\UserSettingsFileMainToolbarElementOverrideRepository.cs +NAMESPACE Paps.UnityToolbarExtenderUIToolkit +L16 public string ElementId; +L17 public bool Visible; +L22 public UserSettingsFileMainToolbarElementOverrideRepository() +L27 public void Clear() +L33 public MainToolbarElementOverride? Get(string elementId) +L41 public MainToolbarElementOverride[] GetAll() +L46 public void Save(MainToolbarElementOverride elementOverride) + +###FILE G:\UnityProject\Aliciza\Client\Packages\com.alicizax.unity.editor.extension\Editor\Toolbar\RecommendedStyles\ButtonRecommendedStyle.cs +NAMESPACE Paps.UnityToolbarExtenderUIToolkit +L9 public ButtonRecommendedStyle(Button button) + +###FILE G:\UnityProject\Aliciza\Client\Packages\com.alicizax.unity.editor.extension\Editor\Toolbar\RecommendedStyles\ColorFieldRecommendedStyle.cs +NAMESPACE Paps.UnityToolbarExtenderUIToolkit +L10 public ColorFieldRecommendedStyle(ColorField colorField) + +###FILE G:\UnityProject\Aliciza\Client\Packages\com.alicizax.unity.editor.extension\Editor\Toolbar\RecommendedStyles\DropdownFieldRecommendedStyle.cs +NAMESPACE Paps.UnityToolbarExtenderUIToolkit +L9 public DropdownFieldRecommendedStyle(DropdownField dropdownField) + +###FILE G:\UnityProject\Aliciza\Client\Packages\com.alicizax.unity.editor.extension\Editor\Toolbar\RecommendedStyles\EditorToolbarDropdownRecommendedStyle.cs +NAMESPACE Paps.UnityToolbarExtenderUIToolkit +L10 public EditorToolbarDropdownRecommendedStyle(EditorToolbarDropdown dropdown) + +###FILE G:\UnityProject\Aliciza\Client\Packages\com.alicizax.unity.editor.extension\Editor\Toolbar\RecommendedStyles\EditorToolbarToggleRecommendedStyle.cs +NAMESPACE Paps.UnityToolbarExtenderUIToolkit +L14 public EditorToolbarToggleRecommendedStyle(EditorToolbarToggle toolbarToggle) + +###FILE G:\UnityProject\Aliciza\Client\Packages\com.alicizax.unity.editor.extension\Editor\Toolbar\RecommendedStyles\EnumFieldRecommendedStyle.cs +NAMESPACE Paps.UnityToolbarExtenderUIToolkit +L9 public EnumFieldRecommendedStyle(EnumField enumField) + +###FILE G:\UnityProject\Aliciza\Client\Packages\com.alicizax.unity.editor.extension\Editor\Toolbar\RecommendedStyles\EnumFlagsFieldRecommendedStyle.cs +NAMESPACE Paps.UnityToolbarExtenderUIToolkit +L10 public EnumFlagsFieldRecommendedStyle(EnumFlagsField enumField) + +###FILE G:\UnityProject\Aliciza\Client\Packages\com.alicizax.unity.editor.extension\Editor\Toolbar\RecommendedStyles\FloatFieldRecommendedStyle.cs +NAMESPACE Paps.UnityToolbarExtenderUIToolkit +L11 public FloatFieldRecommendedStyle(FloatField floatField) + +###FILE G:\UnityProject\Aliciza\Client\Packages\com.alicizax.unity.editor.extension\Editor\Toolbar\RecommendedStyles\IntegerFieldRecommendedStyle.cs +NAMESPACE Paps.UnityToolbarExtenderUIToolkit +L12 public IntegerFieldRecommendedStyle(IntegerField integerField) + +###FILE G:\UnityProject\Aliciza\Client\Packages\com.alicizax.unity.editor.extension\Editor\Toolbar\RecommendedStyles\LayerFieldRecommendedStyle.cs +NAMESPACE Paps.UnityToolbarExtenderUIToolkit +L10 public LayerFieldRecommendedStyle(LayerField layerField) + +###FILE G:\UnityProject\Aliciza\Client\Packages\com.alicizax.unity.editor.extension\Editor\Toolbar\RecommendedStyles\ObjectFieldRecommendedStyle.cs +NAMESPACE Paps.UnityToolbarExtenderUIToolkit +L10 public ObjectFieldRecommendedStyle(ObjectField objectField) + +###FILE G:\UnityProject\Aliciza\Client\Packages\com.alicizax.unity.editor.extension\Editor\Toolbar\RecommendedStyles\RecommendedStyle.cs +NAMESPACE Paps.UnityToolbarExtenderUIToolkit +L5 public void Apply(bool isInsideGroup) + +###FILE G:\UnityProject\Aliciza\Client\Packages\com.alicizax.unity.editor.extension\Editor\Toolbar\RecommendedStyles\RecommendedStyles.cs +NAMESPACE Paps.UnityToolbarExtenderUIToolkit +L15 public static void SetElements(RecommendedStyleVisualElement[] elements) + +###FILE G:\UnityProject\Aliciza\Client\Packages\com.alicizax.unity.editor.extension\Editor\Toolbar\RecommendedStyles\RecommendedStyleVisualElement.cs +NAMESPACE Paps.UnityToolbarExtenderUIToolkit +L7 public VisualElement VisualElement { get; } +L8 public bool IsInsideGroup { get; } +L10 public RecommendedStyleVisualElement(VisualElement visualElement, bool isInsideGroup) + +###FILE G:\UnityProject\Aliciza\Client\Packages\com.alicizax.unity.editor.extension\Editor\Toolbar\RecommendedStyles\SliderRecommendedStyle.cs +NAMESPACE Paps.UnityToolbarExtenderUIToolkit +L11 public SliderRecommendedStyle(Slider slider) + +###FILE G:\UnityProject\Aliciza\Client\Packages\com.alicizax.unity.editor.extension\Editor\Toolbar\RecommendedStyles\TagFieldRecommendedStyle.cs +NAMESPACE Paps.UnityToolbarExtenderUIToolkit +L10 public TagFieldRecommendedStyle(TagField tagField) + +###FILE G:\UnityProject\Aliciza\Client\Packages\com.alicizax.unity.editor.extension\Editor\Toolbar\RecommendedStyles\TextFieldRecommendedStyle.cs +NAMESPACE Paps.UnityToolbarExtenderUIToolkit +L11 public TextFieldRecommendedStyle(TextField textField) + +###FILE G:\UnityProject\Aliciza\Client\Packages\com.alicizax.unity.editor.extension\Editor\Toolbar\RecommendedStyles\ToggleRecommendedStyle.cs +NAMESPACE Paps.UnityToolbarExtenderUIToolkit +L9 public ToggleRecommendedStyle(Toggle toggle) + +###FILE G:\UnityProject\Aliciza\Client\Packages\com.alicizax.unity.editor.extension\Editor\Toolbar\RecommendedStyles\Vector2FieldRecommendedStyle.cs +NAMESPACE Paps.UnityToolbarExtenderUIToolkit +L12 public Vector2FieldRecommendedStyle(Vector2Field vector2Field) + +###FILE G:\UnityProject\Aliciza\Client\Packages\com.alicizax.unity.editor.extension\Editor\Toolbar\RecommendedStyles\Vector3FieldRecommendedStyle.cs +NAMESPACE Paps.UnityToolbarExtenderUIToolkit +L11 public Vector3FieldRecommendedStyle(Vector3Field vector3Field) + +###FILE G:\UnityProject\Aliciza\Client\Packages\com.alicizax.unity.editor.extension\Editor\Toolbar\SerializableValues\ElementVariables.cs +NAMESPACE Paps.UnityToolbarExtenderUIToolkit +L7 public MainToolbarElement MainToolbarElement; +L8 public FieldVariable[] Fields; +L9 public PropertyVariable[] Properties; +L11 public bool DidChange() +L24 public void UpdateValues() + +###FILE G:\UnityProject\Aliciza\Client\Packages\com.alicizax.unity.editor.extension\Editor\Toolbar\SerializableValues\FieldVariable.cs +NAMESPACE Paps.UnityToolbarExtenderUIToolkit +L7 public readonly FieldInfo Field; +L9 public FieldVariable(MainToolbarElement element, FieldInfo field, IValueSerializer valueSerializer, SerializeAttribute attribute) +L15 public override object Get() +L20 public override void Set(object value) + +###FILE G:\UnityProject\Aliciza\Client\Packages\com.alicizax.unity.editor.extension\Editor\Toolbar\SerializableValues\IMainToolbarElementVariableRepository.cs +NAMESPACE Paps.UnityToolbarExtenderUIToolkit +L5 public void Set(SerializableElement serializableElement); +L6 public void SetAll(SerializableElement[] serializableElements); +L7 public SerializableElement[] GetAll(); +L8 public void Save(); + +###FILE G:\UnityProject\Aliciza\Client\Packages\com.alicizax.unity.editor.extension\Editor\Toolbar\SerializableValues\IMainToolbarElementVariableSerializer.cs +NAMESPACE Paps.UnityToolbarExtenderUIToolkit +L5 public string Serialize(SerializableElementGroup serializableElementGroup); +L6 public SerializableElementGroup Deserialize(string serializedElementGroup); + +###FILE G:\UnityProject\Aliciza\Client\Packages\com.alicizax.unity.editor.extension\Editor\Toolbar\SerializableValues\IValueSerializer.cs +NAMESPACE Paps.UnityToolbarExtenderUIToolkit + +###FILE G:\UnityProject\Aliciza\Client\Packages\com.alicizax.unity.editor.extension\Editor\Toolbar\SerializableValues\MainToolbarElementVariableWatcher.cs +NAMESPACE Paps.UnityToolbarExtenderUIToolkit +L18 public MainToolbarElementVariableWatcher(IMainToolbarElementVariableRepository repository, IValueSerializer valueSerializer) +L24 public void RestoreValues(MainToolbarElement[] elements) +L77 public void Update() + +###FILE G:\UnityProject\Aliciza\Client\Packages\com.alicizax.unity.editor.extension\Editor\Toolbar\SerializableValues\PropertyVariable.cs +NAMESPACE Paps.UnityToolbarExtenderUIToolkit +L8 public readonly PropertyInfo Property; +L10 public PropertyVariable(MainToolbarElement element, PropertyInfo property, IValueSerializer valueSerializer, SerializeAttribute attribute) +L16 public override object Get() +L21 public override void Set(object value) + +###FILE G:\UnityProject\Aliciza\Client\Packages\com.alicizax.unity.editor.extension\Editor\Toolbar\SerializableValues\SerializableElement.cs +NAMESPACE Paps.UnityToolbarExtenderUIToolkit +L5 public string ElementFullTypeName; +L6 public SerializableVariable[] Variables = new SerializableVariable[0]; + +###FILE G:\UnityProject\Aliciza\Client\Packages\com.alicizax.unity.editor.extension\Editor\Toolbar\SerializableValues\SerializableElementGroup.cs +NAMESPACE Paps.UnityToolbarExtenderUIToolkit +L7 public Dictionary SerializableElements = new Dictionary(); + +###FILE G:\UnityProject\Aliciza\Client\Packages\com.alicizax.unity.editor.extension\Editor\Toolbar\SerializableValues\SerializableVariable.cs +NAMESPACE Paps.UnityToolbarExtenderUIToolkit +L7 public ValueHolderType Type; +L8 public string Key; +L9 public Type ValueType; +L10 public object Value; + +###FILE G:\UnityProject\Aliciza\Client\Packages\com.alicizax.unity.editor.extension\Editor\Toolbar\SerializableValues\SerializeAttribute.cs +NAMESPACE Paps.UnityToolbarExtenderUIToolkit +L6 public class SerializeAttribute : Attribute +L8 public string SerializationKey { get; } +L10 public SerializeAttribute(string serializationKey = null) + +###FILE G:\UnityProject\Aliciza\Client\Packages\com.alicizax.unity.editor.extension\Editor\Toolbar\SerializableValues\UnitySerializationMainToolbarElementVariableSerializer.cs +NAMESPACE Paps.UnityToolbarExtenderUIToolkit +L13 public Dictionary SerializableElements; +L18 public string ElementFullTypeName; +L19 public SerializableVariableDTO[] Variables; +L24 public ValueHolderType Type; +L25 public string Key; +L26 public string SerializedValue; +L27 public string SerializedValueTypeFullyQualifiedName; +L33 public UnitySerializationMainToolbarElementVariableSerializer(IValueSerializer valuesSerializer) +L41 public string Serialize(SerializableElementGroup serializableElementGroup) +L48 public SerializableElementGroup Deserialize(string serializedElementGroup) + +###FILE G:\UnityProject\Aliciza\Client\Packages\com.alicizax.unity.editor.extension\Editor\Toolbar\SerializableValues\UnitySerializationValueSerializer.cs +NAMESPACE Paps.UnityToolbarExtenderUIToolkit +L7 public T Deserialize(string serializedValue) +L12 public string Serialize(T value) + +###FILE G:\UnityProject\Aliciza\Client\Packages\com.alicizax.unity.editor.extension\Editor\Toolbar\SerializableValues\UserSettingsFileMainToolbarElementVariableRepository.cs +NAMESPACE Paps.UnityToolbarExtenderUIToolkit +L26 public UserSettingsFileMainToolbarElementVariableRepository(IMainToolbarElementVariableSerializer serializer) +L31 public SerializableElement[] GetAll() +L36 public void Set(SerializableElement serializableElement) +L41 public void SetAll(SerializableElement[] serializableElements) +L57 public void Save() + +###FILE G:\UnityProject\Aliciza\Client\Packages\com.alicizax.unity.editor.extension\Editor\Toolbar\SerializableValues\ValueHolderType.cs +NAMESPACE Paps.UnityToolbarExtenderUIToolkit + +###FILE G:\UnityProject\Aliciza\Client\Packages\com.alicizax.unity.editor.extension\Editor\Toolbar\SerializableValues\Variable.cs +NAMESPACE Paps.UnityToolbarExtenderUIToolkit +L24 public MainToolbarElement Element { get; } +L25 public Type ValueType { get; } +L26 public SerializeAttribute Attribute { get; } +L28 public Variable(MainToolbarElement element, Type valueType, object initialValue, IValueSerializer valueSerializer, SerializeAttribute attribute) +L38 public abstract object Get(); +L40 public abstract void Set(object value); +L42 public bool DidChange() +L63 public void UpdateValue() + +###FILE G:\UnityProject\Aliciza\Client\Packages\com.alicizax.unity.editor.extension\Editor\Toolbar\ByAttributeMainToolbarElementRepository.cs +NAMESPACE Paps.UnityToolbarExtenderUIToolkit +L11 public MainToolbarElement[] GetAll() + +###FILE G:\UnityProject\Aliciza\Client\Packages\com.alicizax.unity.editor.extension\Editor\Toolbar\IMainToolbarElementRepository.cs +NAMESPACE Paps.UnityToolbarExtenderUIToolkit +L5 public MainToolbarElement[] GetAll(); + +###FILE G:\UnityProject\Aliciza\Client\Packages\com.alicizax.unity.editor.extension\Editor\Toolbar\MainToolbar.cs +NAMESPACE Paps.UnityToolbarExtenderUIToolkit +L10 public static class MainToolbar +L21 public static event Action OnInitialized; +L22 public static event Action OnRefresh; +L24 public static VisualElement UnityToolbarRoot { get; private set; } +L26 public static VisualElement LeftContainer { get; private set; } +L27 public static VisualElement CenterContainer { get; private set; } +L28 public static VisualElement RightContainer { get; private set; } +L29 public static VisualElement PlayModeButtonsContainer { get; private set; } +L31 public static bool IsAvailable => _innerToolbarObject != null; + +###FILE G:\UnityProject\Aliciza\Client\Packages\com.alicizax.unity.editor.extension\Editor\Toolbar\MainToolbarAutomaticExtender.cs +NAMESPACE Paps.UnityToolbarExtenderUIToolkit +L14 public static class MainToolbarAutomaticExtender +L33 public static event Action OnRefresh; +L34 public static event Action OnAddedCustomContainersToToolbar; + +###FILE G:\UnityProject\Aliciza\Client\Packages\com.alicizax.unity.editor.extension\Editor\Toolbar\MainToolbarCustomContainer.cs +NAMESPACE Paps.UnityToolbarExtenderUIToolkit +L20 public MainToolbarCustomContainer(string id, FlexDirection flexDirection) +L80 public void AddToContainer(VisualElement child) +L85 public void ClearContainer() +L90 public VisualElement[] GetContainerChilds() + +###FILE G:\UnityProject\Aliciza\Client\Packages\com.alicizax.unity.editor.extension\Editor\Toolbar\MainToolbarElement.cs +NAMESPACE Paps.UnityToolbarExtenderUIToolkit +L7 public string Id { get; } +L8 public VisualElement VisualElement { get; } +L9 public ToolbarAlign Alignment { get; } +L10 public int Order { get; } +L11 public bool UseRecommendedStyles { get; } +L13 public MainToolbarElement(string id, VisualElement visualElement, + +###FILE G:\UnityProject\Aliciza\Client\Packages\com.alicizax.unity.editor.extension\Editor\Toolbar\MainToolbarElementAttribute.cs +NAMESPACE Paps.UnityToolbarExtenderUIToolkit +L6 public class MainToolbarElementAttribute : Attribute +L8 public string Id { get; } +L9 public ToolbarAlign Alignment { get; } +L10 public int Order { get; } +L11 public bool UseRecommendedStyles { get; } +L21 public MainToolbarElementAttribute(string id, ToolbarAlign alignment = ToolbarAlign.Left, + +###FILE G:\UnityProject\Aliciza\Client\Packages\com.alicizax.unity.editor.extension\Editor\Toolbar\MainToolbarElementOverrideApplier.cs +NAMESPACE Paps.UnityToolbarExtenderUIToolkit +L12 public VisualElement RemovedVisualElement; +L13 public VisualElement Parent; +L14 public int Index; +L28 public MainToolbarElementOverrideApplier(IMainToolbarElementOverrideRepository mainToolbarElementOverrideRepository) +L33 public void SetNativeElements(NativeToolbarElement[] nativeElements) +L41 public void SetCustomElements(MainToolbarElement[] mainToolbarElements) +L46 public void ApplyOverrides() + +###FILE G:\UnityProject\Aliciza\Client\Packages\com.alicizax.unity.editor.extension\Editor\Toolbar\NativeToolbarElement.cs +NAMESPACE Paps.UnityToolbarExtenderUIToolkit +L7 public string Id { get; } +L8 public VisualElement VisualElement { get; } +L10 public NativeToolbarElement(string id, VisualElement visualElement) + +###FILE G:\UnityProject\Aliciza\Client\Packages\com.alicizax.unity.editor.extension\Editor\Toolbar\ToolbarAlign.cs +NAMESPACE Paps.UnityToolbarExtenderUIToolkit +L3 public enum ToolbarAlign + +###FILE G:\UnityProject\Aliciza\Client\Packages\com.alicizax.unity.editor.extension\Editor\ToolBarExtension\BuildSettingWindow.cs +NAMESPACE + +###FILE G:\UnityProject\Aliciza\Client\Packages\com.alicizax.unity.editor.extension\Editor\ToolBarExtension\EditorQuickToolBar.cs +NAMESPACE AlicizaX.Editor.Extension +L13 public class EditorQuickToolBar : IMGUIContainer +L17 public void InitializeElement() + +###FILE G:\UnityProject\Aliciza\Client\Packages\com.alicizax.unity.editor.extension\Editor\ToolBarExtension\LocalizationDropdownField.cs +NAMESPACE +L15 public class LocalizationDropdownField : IMGUIContainer +L21 public void InitializeElement() +L72 public static void InvokeOnValidateInScene() + +###FILE G:\UnityProject\Aliciza\Client\Packages\com.alicizax.unity.editor.extension\Editor\ToolBarExtension\ResourceModeDropdownField.cs +NAMESPACE AlicizaX.Editor.Extension +L13 public class ResourceModeDropdownField : IMGUIContainer +L25 public void InitializeElement() + +###FILE G:\UnityProject\Aliciza\Client\Packages\com.alicizax.unity.editor.extension\Editor\ToolBarExtension\SwitchSceneToolBar.cs +NAMESPACE +L10 public class SwitchSceneToolBar : IMGUIContainer +L14 public void InitializeElement() + +###FILE G:\UnityProject\Aliciza\Client\Packages\com.alicizax.unity.editor.extension\Editor\EditorIcons.cs +NAMESPACE +L10 public class EditorIcons : EditorWindow +L13 public static void EditorIconsOpen() +L395 public static string[] ico_list = + +###FILE G:\UnityProject\Aliciza\Client\Packages\com.alicizax.unity.editor.extension\Editor\EditorToolFunctionAttribute.cs +NAMESPACE AlicizaX.Editor.Extension +L11 public class EditorToolFunctionAttribute : Attribute +L13 public string ToolMenuPath { get; private set; } +L14 public int MenuOrder { get; private set; } +L15 public MethodInfo MethodInfo { get; private set; } +L17 public EditorToolFunctionAttribute(string menu, int menuOrder = 0) +L23 public void SetMethodInfo(MethodInfo methodInfo) +L31 public static List Attributes = new List(); + diff --git a/Client/.tmp_api_ui.txt b/Client/.tmp_api_ui.txt new file mode 100644 index 0000000..d482576 --- /dev/null +++ b/Client/.tmp_api_ui.txt @@ -0,0 +1,1156 @@ +###FILE G:\UnityProject\Aliciza\Client\Packages\com.alicizax.unity.ui.extension\Editor\Constant\UXGUIConfig.cs +NAMESPACE +L4 public static readonly string RootPath = "Packages/com.alicizax.unity.ui.extension/"; +L5 public static readonly string GUIPath = RootPath + "Runtime/Res/UX-GUI/"; +L6 public static readonly string UIDefaultMatPath = GUIPath + "UX_ImageDefault.mat"; + +###FILE G:\UnityProject\Aliciza\Client\Packages\com.alicizax.unity.ui.extension\Editor\Helper\ExtensionHelper.cs +NAMESPACE UnityEditor.Extensions +L10 public static void PreviewAudioClip(AudioClip clip) + +###FILE G:\UnityProject\Aliciza\Client\Packages\com.alicizax.unity.ui.extension\Editor\Helper\GUILayoutHelper.cs +NAMESPACE UnityEditor.DrawUtils +L9 public static void DrawProperty(SerializedProperty property, GUISkin skin, string content) +L19 public static void DrawProperty(SerializedProperty property, GUISkin skin, string content, +L41 public static void DrawProperty(SerializedProperty property, GUISkin skin, string content, +L63 public static void DrawProperty(SerializedProperty property, GUISkin skin, string content, string btnName, Action callback) +L77 public static void DrawPropertyPlain(SerializedProperty property, GUISkin skin, string content) +L87 public static void DrawPropertyCW(SerializedProperty property, GUISkin skin, string content, float width) +L97 public static void DrawPropertyPlainCW(SerializedProperty property, GUISkin skin, string content, float width) +L107 public static int DrawTabs(int tabIndex, GUIContent[] tabs, GUISkin skin) +L122 public static void DrawComponentHeader(GUISkin skin, string content) +L130 public static void DrawHeader(GUISkin skin, string content, int space) +L136 public static bool DrawToggle(bool value, GUISkin skin, string content) +L147 public static bool DrawTogglePlain(bool value, GUISkin skin, string content) + +###FILE G:\UnityProject\Aliciza\Client\Packages\com.alicizax.unity.ui.extension\Editor\Helper\SerializedClassDrawer.cs +NAMESPACE +L5 public static class SerializedClassDrawer +L7 public static void DrawSerializableProperty(SerializedProperty prop, string title = null) + +###FILE G:\UnityProject\Aliciza\Client\Packages\com.alicizax.unity.ui.extension\Editor\Helper\SerializedPropertyUtility.cs +NAMESPACE +L6 public static class SerializedPropertyUtility +L11 public static object GetPropertyValue(SerializedProperty property) +L64 public static T GetPropertyValue(SerializedProperty property, T defaultValue = default(T)) +L88 public static bool TryGetPropertyValue(SerializedProperty property, out T value, T defaultValue = default(T)) + +###FILE G:\UnityProject\Aliciza\Client\Packages\com.alicizax.unity.ui.extension\Editor\Inspector\TabbedInspector.cs +NAMESPACE UnityEditor.Utils +L11 public class TabbedInspector +L13 public class Tab +L15 public string title; +L16 public string iconName; +L17 public List callbacks = new List(); +L19 public Tab(string t, string icon) +L30 public TabbedInspector(string prefsKey = null) +L39 public void RegisterTab(string title, string iconName, Action drawCallback) +L58 public void AppendToTab(string title, Action drawCallback, bool last = true) +L75 public void RemoveCallbackFromTab(string title, Action drawCallback) +L83 public void UnregisterTab(string title) +L91 public void EnsureDefaultTab(string title, string iconName, Action defaultCallback) +L104 public void DrawTabs() + +###FILE G:\UnityProject\Aliciza\Client\Packages\com.alicizax.unity.ui.extension\Editor\Inspector\UIEffectWrapDrawer.cs +NAMESPACE UnityEngine.UI +L11 public static void InitInspectorString() +L19 public static void Draw(Rect position, GameObject target) + +###FILE G:\UnityProject\Aliciza\Client\Packages\com.alicizax.unity.ui.extension\Editor\Inspector\UXCreateHelper.cs +NAMESPACE +L13 public class UXCreateHelper : Editor +L32 public static void CreateUXImage(MenuCommand menuCommand) +L45 public static void CreateUXToggle(MenuCommand menuCommand) +L60 public static void CreateUXTextMeshPro(MenuCommand menuCommand) +L71 public static void CreateUXButton(MenuCommand menuCommand) +L82 public static void CreateUXInputField(MenuCommand menuCommand) +L104 public static void CreateUxRecyclerView() + +###FILE G:\UnityProject\Aliciza\Client\Packages\com.alicizax.unity.ui.extension\Editor\RecyclerView\RecyclerViewEditor.cs +NAMESPACE AlicizaX.UI.Editor +L13 public class RecyclerViewEditor : UnityEditor.Editor +L132 public override void OnInspectorGUI() + +###FILE G:\UnityProject\Aliciza\Client\Packages\com.alicizax.unity.ui.extension\Editor\UX\Button\UButtonEditor.cs +NAMESPACE UnityEditor.UI + +###FILE G:\UnityProject\Aliciza\Client\Packages\com.alicizax.unity.ui.extension\Editor\UX\Controller\UXBindingEditor.cs +NAMESPACE UnityEngine.UI +L9 public sealed class UXBindingEditor : UnityEditor.Editor +L22 public override void OnInspectorGUI() + +###FILE G:\UnityProject\Aliciza\Client\Packages\com.alicizax.unity.ui.extension\Editor\UX\Controller\UXControllerEditor.cs +NAMESPACE UnityEngine.UI +L9 public sealed class UXControllerEditor : UnityEditor.Editor +L21 public override void OnInspectorGUI() + +###FILE G:\UnityProject\Aliciza\Client\Packages\com.alicizax.unity.ui.extension\Editor\UX\Controller\UXControllerSceneOverlayWindow.cs +NAMESPACE UnityEngine.UI +L8 public sealed class UXControllerSceneOverlayWindow : EditorWindow +L21 public static void ShowWindow() + +###FILE G:\UnityProject\Aliciza\Client\Packages\com.alicizax.unity.ui.extension\Editor\UX\Controller\UXControllerUIScriptFileWriter.cs +NAMESPACE UnityEngine.UI +L9 public sealed class UXControllerUIScriptFileWriter : IUIScriptFileWriter +L11 public void Write(GameObject targetObject, string className, string scriptContent, UIScriptGenerateData scriptGenerateData) + +###FILE G:\UnityProject\Aliciza\Client\Packages\com.alicizax.unity.ui.extension\Editor\UX\Group\UXGroupEditor.cs +NAMESPACE UnityEditor.UI +L10 public class UXGroupInspector : Editor +L35 public override void OnInspectorGUI() + +###FILE G:\UnityProject\Aliciza\Client\Packages\com.alicizax.unity.ui.extension\Editor\UX\Hotkey\HotkeyComponentEditor.cs +NAMESPACE UnityEditor.UI +L9 public class HotkeyComponentEditor : UnityEditor.Editor +L22 public override void OnInspectorGUI() + +###FILE G:\UnityProject\Aliciza\Client\Packages\com.alicizax.unity.ui.extension\Editor\UX\Image\UXImageEditor.cs +NAMESPACE UnityEngine.UI +L79 public static GUIContent text = EditorGUIUtility.TrTextContent("Fill Origin"); +L81 public static GUIContent[] OriginHorizontalStyle = +L87 public static GUIContent[] OriginVerticalStyle = +L93 public static GUIContent[] Origin90Style = +L101 public static GUIContent[] Origin180Style = +L109 public static GUIContent[] Origin360Style = +L205 public override void OnInspectorGUI() +L822 public int EnumPopupLayoutEx(string label, Type type, int enumValueIndex, string[] labels) + +###FILE G:\UnityProject\Aliciza\Client\Packages\com.alicizax.unity.ui.extension\Editor\UX\Selectable\UXSelectableEditor.cs +NAMESPACE UnityEditor.UI +L22 public TabbedInspector _tabs; +L84 public override void OnInspectorGUI() + +###FILE G:\UnityProject\Aliciza\Client\Packages\com.alicizax.unity.ui.extension\Editor\UX\Toggle\UXToggleEditor.cs +NAMESPACE UnityEditor.UI + +###FILE G:\UnityProject\Aliciza\Client\Packages\com.alicizax.unity.ui.extension\Editor\UX\UXTextMeshPro\UXTextMeshProEditor.cs +NAMESPACE UnityEngine.UI +L17 public readonly int Id; +L18 public readonly string CombineKey; // SectionName/Key 鈫?鑿滃崟灞傜骇 +L19 public readonly string CombineValue; // SectionName.Key 鈫?瀛樺偍鐢? +L21 public TableSelectionData(int id, string combineKey, string combineValue) +L42 public override VisualElement CreateInspectorGUI() +L137 public override void OnInspectorGUI() +L201 public LocalizationSearchProvider Init(List options, System.Action onSelect) +L208 public List CreateSearchTree(SearchWindowContext context) +L251 public bool OnSelectEntry(SearchTreeEntry searchTreeEntry, SearchWindowContext context) + +###FILE G:\UnityProject\Aliciza\Client\Packages\com.alicizax.unity.ui.extension\Editor\UX\UXTextMeshPro\UXTextMeshProLocalization.cs +NAMESPACE UnityEngine.UI + +###FILE G:\UnityProject\Aliciza\Client\Packages\com.alicizax.unity.ui.extension\Runtime\InputGlyph\Core\GlyphService.cs +NAMESPACE +L6 public static class GlyphService +L31 public static string GetBindingControlPath( +L41 public static string GetBindingControlPath( +L49 public static bool TryGetTMPTagForActionPath( +L61 public static bool TryGetTMPTagForActionPath( +L72 public static bool TryGetUISpriteForActionPath( +L83 public static bool TryGetUISpriteForActionPath( +L93 public static bool TryGetTMPTagForActionPath( +L112 public static bool TryGetUISpriteForActionPath( +L123 public static string GetDisplayNameFromInputAction( +L137 public static string GetDisplayNameFromControlPath(string controlPath) +L162 public static bool TryGetBindingControl( + +###FILE G:\UnityProject\Aliciza\Client\Packages\com.alicizax.unity.ui.extension\Runtime\InputGlyph\Core\InputActionReader.cs +NAMESPACE +L10 public static class InputActionReader +L18 public readonly string ActionName; +L19 public readonly int OwnerId; +L20 public readonly string OwnerKey; +L25 public InputReadKey(string actionName, int ownerId) +L35 public InputReadKey(string actionName, string ownerKey) +L42 public bool Equals(InputReadKey other) +L49 public override bool Equals(object obj) +L54 public override int GetHashCode() +L75 public static T ReadValue(string actionName) where T : struct +L83 public static object ReadValue(string actionName) +L91 public static bool TryReadValue(string actionName, out T value) where T : struct +L107 public static bool TryReadValue(string actionName, out object value) +L124 public static bool TryReadValueOnce(UnityEngine.Object owner, string actionName, out T value) where T : struct +L139 public static bool ReadButton(string actionName) +L153 public static bool ReadButtonOnce(UnityEngine.Object owner, string actionName) +L161 public static bool ReadButtonOnce(int instanceID, string actionName) +L169 public static bool ReadButtonOnce(string key, string actionName) +L178 public static bool ReadButtonToggle(UnityEngine.Object owner, string actionName) +L186 public static bool ReadButtonToggle(int instanceID, string actionName) +L194 public static bool ReadButtonToggle(string key, string actionName) +L202 public static void ResetToggledButton(string key, string actionName) +L210 public static void ResetToggledButton(string actionName) +L231 public static void ResetToggledButtons() + +###FILE G:\UnityProject\Aliciza\Client\Packages\com.alicizax.unity.ui.extension\Runtime\InputGlyph\Core\InputBindingManager.cs +NAMESPACE +L9 public sealed class InputBindingManager : MonoServiceBehaviour +L20 public InputActionAsset actions; +L23 public bool debugMode = false; +L35 public event Action> OnApply; +L36 public event Action OnRebindPrepare; +L37 public event Action OnRebindStart; +L38 public event Action OnRebindEnd; +L39 public event Action OnRebindConflict; +L40 public static event Action BindingsChanged; +L43 public IReadOnlyDictionary ActionMaps => actionMap; +L44 public IReadOnlyCollection PreparedRebinds => preparedRebinds; +L188 public sealed class ActionMap +L190 public string name; +L191 public Dictionary actions; +L193 public ActionMap(InputActionMap map) +L204 public sealed class Action +L206 public InputAction action; +L207 public Dictionary bindings; +L209 public Action(InputAction action) +L245 public readonly struct Binding +L247 public readonly string name; +L248 public readonly string parentAction; +L249 public readonly string compositePart; +L250 public readonly int bindingIndex; +L251 public readonly BindingPath bindingPath; +L252 public readonly InputBinding inputBinding; +L254 public Binding(string name, string parentAction, string compositePart, int bindingIndex, +L268 public sealed class BindingPath +L270 public string bindingPath; +L271 public string overridePath; +L274 public BindingPath(string bindingPath, string overridePath) +L280 public string EffectivePath +L290 public void SubscribeToEffectivePathChanged(Action callback) +L295 public void UnsubscribeFromEffectivePathChanged(Action callback) +L300 public void Dispose() +L306 public sealed class RebindContext +L308 public InputAction action; +L309 public int bindingIndex; +L310 public string overridePath; +L313 public RebindContext(InputAction action, int bindingIndex, string overridePath) +L320 public override bool Equals(object obj) +L327 public override int GetHashCode() +L338 public override string ToString() +L533 public int FindBestBindingIndexForKeyboard(InputAction action, string compositePartName = null) +L576 public static InputAction Action(string actionName) +L594 public bool TryGetAction(string actionName, out InputAction action) +L617 public void StartRebind(string actionName, string compositePartName = null) +L642 public void CancelRebind() => rebindOperation?.Cancel(); +L649 public async Task ConfirmApply(bool clearConflicts = true) +L704 public void DiscardPrepared() +L725 public async Task ResetToDefaultAsync() +L767 public BindingPath GetBindingPath(string actionName, int bindingIndex = 0) +L778 public BindingPath GetBindingPath(InputAction action, int bindingIndex = 0) + +###FILE G:\UnityProject\Aliciza\Client\Packages\com.alicizax.unity.ui.extension\Runtime\InputGlyph\Core\InputDeviceWatcher.cs +NAMESPACE +L10 public static class InputDeviceWatcher +L12 public enum InputDeviceCategory +L20 public readonly struct DeviceContext : IEquatable +L22 public readonly InputDeviceCategory Category; +L23 public readonly int DeviceId; +L24 public readonly int VendorId; +L25 public readonly int ProductId; +L26 public readonly string DeviceName; +L27 public readonly string Layout; +L29 public DeviceContext( +L45 public bool Equals(DeviceContext other) +L55 public override bool Equals(object obj) +L60 public override int GetHashCode() +L78 public int vendorId; +L79 public int productId; +L87 public static InputDeviceCategory CurrentCategory { get; private set; } = InputDeviceCategory.Keyboard; +L88 public static string CurrentDeviceName { get; private set; } = DefaultKeyboardDeviceName; +L89 public static int CurrentDeviceId { get; private set; } = -1; +L90 public static int CurrentVendorId { get; private set; } +L91 public static int CurrentProductId { get; private set; } +L92 public static DeviceContext CurrentContext { get; private set; } = CreateDefaultContext(); +L100 public static event Action OnDeviceChanged; +L101 public static event Action OnDeviceContextChanged; +L104 public static void Initialize() +L145 public static void Dispose() + +###FILE G:\UnityProject\Aliciza\Client\Packages\com.alicizax.unity.ui.extension\Runtime\InputGlyph\Data\InputGlyphDatabase.cs +NAMESPACE +L7 public sealed class GlyphEntry +L9 public Sprite Sprite; +L10 public InputAction action; +L14 public sealed class DeviceGlyphTable +L16 public string deviceName; +L17 public Texture2D spriteSheetTexture; +L18 public Sprite platformIcons; +L19 public List entries = new List(); +L23 public sealed class InputGlyphDatabase : ScriptableObject +L50 public List tables = new List(); +L51 public Sprite placeholderSprite; +L68 public DeviceGlyphTable GetTable(string deviceName) +L80 public DeviceGlyphTable GetTable(InputDeviceWatcher.InputDeviceCategory device) +L95 public Sprite GetPlatformIcon(InputDeviceWatcher.InputDeviceCategory device) +L101 public bool TryGetSprite(string controlPath, InputDeviceWatcher.InputDeviceCategory device, out Sprite sprite) +L125 public Sprite FindSprite(string controlPath, InputDeviceWatcher.InputDeviceCategory device) +L130 public GlyphEntry FindEntryByControlPath(string controlPath, InputDeviceWatcher.InputDeviceCategory device) +L200 public void EditorRefreshCache() +L205 public static string EditorNormalizeControlPath(string controlPath) + +###FILE G:\UnityProject\Aliciza\Client\Packages\com.alicizax.unity.ui.extension\Runtime\InputGlyph\Editor\InputGlyphDatabaseEditor.cs +NAMESPACE +L8 public sealed class InputGlyphDatabaseEditor : Editor +L29 public Sprite PendingSprite; +L30 public bool ShowValidation = true; +L31 public string EntrySearch = string.Empty; +L32 public int CurrentPage; +L33 public int EntriesPerPage = DefaultEntriesPerPage; +L34 public readonly List FilteredEntryIndices = new(); +L35 public string CachedSearch = string.Empty; +L36 public int CachedEntryCount = -1; +L63 public override void OnInspectorGUI() + +###FILE G:\UnityProject\Aliciza\Client\Packages\com.alicizax.unity.ui.extension\Runtime\InputGlyph\Editor\InputGlyphEditor.cs +NAMESPACE +L10 public sealed class InputGlyphEditor : Editor +L40 public override void OnInspectorGUI() + +###FILE G:\UnityProject\Aliciza\Client\Packages\com.alicizax.unity.ui.extension\Runtime\InputGlyph\InputGlyph.cs +NAMESPACE +L11 public sealed class InputGlyph : InputGlyphBehaviourBase +L13 public enum ActionSourceMode +L20 public enum OutputMode +L27 public sealed class DeviceCategoryEvent +L29 public InputDeviceWatcher.InputDeviceCategory category; +L30 public UnityEvent onMatched; +L31 public UnityEvent onNotMatched; + +###FILE G:\UnityProject\Aliciza\Client\Packages\com.alicizax.unity.ui.extension\Runtime\InputGlyph\InputGlyphBehaviourBase.cs +NAMESPACE +L3 public abstract class InputGlyphBehaviourBase : MonoBehaviour + +###FILE G:\UnityProject\Aliciza\Client\Packages\com.alicizax.unity.ui.extension\Runtime\InputGlyph\TestRebindScript.cs +NAMESPACE + +###FILE G:\UnityProject\Aliciza\Client\Packages\com.alicizax.unity.ui.extension\Runtime\RecyclerView\Adapter\Adapter.cs +NAMESPACE AlicizaX.UI +L11 public class Adapter : IAdapter, IItemRenderCacheOwner where T : ISimpleViewData +L15 public ItemRenderEntry(string viewName, IItemRender itemRender) +L21 public string ViewName { get; } +L23 public IItemRender ItemRender { get; } +L34 public int ChoiceIndex +L40 public Adapter(RecyclerView recyclerView) : this(recyclerView, new List()) +L44 public Adapter(RecyclerView recyclerView, List list) +L50 public virtual int GetItemCount() +L55 public virtual int GetRealCount() +L60 public virtual string GetViewName(int index) +L65 public virtual void OnBindViewHolder(ViewHolder viewHolder, int index) +L93 public virtual void OnRecycleViewHolder(ViewHolder viewHolder) +L104 public virtual void NotifyDataChanged() +L111 public virtual void SetList(List list) +L118 public virtual void NotifyItemChanged(int index, bool relayout = false) +L136 public virtual void NotifyItemRangeChanged(int index, int count, bool relayout = false) +L154 public virtual void NotifyItemInserted(int index) +L161 public virtual void NotifyItemRangeInserted(int index, int count) +L173 public virtual void NotifyItemRemoved(int index) +L180 public virtual void NotifyItemRangeRemoved(int index, int count) +L192 public void RegisterItemRender(string viewName = "") where TItemRender : ItemRenderBase +L197 public void RegisterItemRender(Type itemRenderType, string viewName = "") +L212 public bool UnregisterItemRender(string viewName = "") +L235 public void ClearItemRenderRegistrations() +L242 public T GetData(int index) +L249 public void Add(T item) +L260 public void AddRange(IEnumerable collection) +L278 public void Insert(int index, T item) +L284 public void InsertRange(int index, IEnumerable collection) +L301 public void Remove(T item) +L307 public void RemoveAt(int index) +L315 public void RemoveRange(int index, int count) +L321 public void RemoveAll(Predicate match) +L327 public void Clear() +L339 public void Reverse(int index, int count) +L345 public void Reverse() +L351 public void Sort(Comparison comparison) + +###FILE G:\UnityProject\Aliciza\Client\Packages\com.alicizax.unity.ui.extension\Runtime\RecyclerView\Adapter\GroupAdapter.cs +NAMESPACE AlicizaX.UI +L6 public class GroupAdapter : Adapter where TData : IGroupViewData, new() +L11 public GroupAdapter(RecyclerView recyclerView, string groupViewName) : base(recyclerView) +L16 public GroupAdapter(RecyclerView recyclerView, List list) : base(recyclerView, list) +L20 public GroupAdapter(RecyclerView recyclerView) : base(recyclerView) +L24 public override int GetItemCount() +L29 public override string GetViewName(int index) +L36 public override void NotifyDataChanged() +L75 public override void SetList(List list) +L95 public void Expand(int index) +L113 public void Collapse(int index) +L150 public void Activate(int index) + +###FILE G:\UnityProject\Aliciza\Client\Packages\com.alicizax.unity.ui.extension\Runtime\RecyclerView\Adapter\IAdapter.cs +NAMESPACE AlicizaX.UI +L3 public interface IAdapter + +###FILE G:\UnityProject\Aliciza\Client\Packages\com.alicizax.unity.ui.extension\Runtime\RecyclerView\Adapter\ItemRender.cs +NAMESPACE AlicizaX.UI +L43 public abstract class ItemRenderBase : IItemRender +L101 public abstract class ItemRender : ItemRenderBase, IItemInteractionHost, ITypedItemRender +L154 public virtual ItemInteractionFlags InteractionFlags => ItemInteractionFlags.None; +L581 public ItemRenderDefinition(Type itemRenderType, Type holderType, Func createInstance) +L590 public Type ItemRenderType { get; } +L594 public Type HolderType { get; } +L607 public IItemRender Create(ViewHolder viewHolder, RecyclerView recyclerView, IAdapter adapter, Action selectionHandler) +L639 public static ItemRenderDefinition GetOrCreate(Type itemRenderType) + +###FILE G:\UnityProject\Aliciza\Client\Packages\com.alicizax.unity.ui.extension\Runtime\RecyclerView\Adapter\LoopAdapter.cs +NAMESPACE AlicizaX.UI +L5 public class LoopAdapter : Adapter where T : ISimpleViewData +L7 public LoopAdapter(RecyclerView recyclerView) : base(recyclerView) +L11 public LoopAdapter(RecyclerView recyclerView, List list) : base(recyclerView, list) +L15 public override int GetItemCount() +L20 public override int GetRealCount() +L25 public override void OnBindViewHolder(ViewHolder viewHolder, int index) + +###FILE G:\UnityProject\Aliciza\Client\Packages\com.alicizax.unity.ui.extension\Runtime\RecyclerView\Adapter\MixedAdapter.cs +NAMESPACE AlicizaX.UI +L5 public class MixedAdapter : Adapter where TData : IMixedViewData +L7 public MixedAdapter(RecyclerView recyclerView) : base(recyclerView) +L11 public MixedAdapter(RecyclerView recyclerView, List list) : base(recyclerView, list) +L15 public override string GetViewName(int index) + +###FILE G:\UnityProject\Aliciza\Client\Packages\com.alicizax.unity.ui.extension\Runtime\RecyclerView\Data\ISimpleViewData.cs +NAMESPACE AlicizaX.UI +L3 public interface ISimpleViewData +L7 public interface IMixedViewData : ISimpleViewData +L12 public interface IGroupViewData : IMixedViewData + +###FILE G:\UnityProject\Aliciza\Client\Packages\com.alicizax.unity.ui.extension\Runtime\RecyclerView\Interaction\IItemInteractionHost.cs +NAMESPACE AlicizaX.UI + +###FILE G:\UnityProject\Aliciza\Client\Packages\com.alicizax.unity.ui.extension\Runtime\RecyclerView\Interaction\ItemInteractionFlags.cs +NAMESPACE AlicizaX.UI +L6 public enum ItemInteractionFlags + +###FILE G:\UnityProject\Aliciza\Client\Packages\com.alicizax.unity.ui.extension\Runtime\RecyclerView\Interaction\ItemInteractionProxy.cs +NAMESPACE AlicizaX.UI +L8 public sealed class ItemInteractionProxy : MonoBehaviour, +L44 public void Clear() +L59 public Selectable GetSelectable() +L65 public void OnPointerClick(PointerEventData eventData) +L73 public void OnPointerEnter(PointerEventData eventData) +L81 public void OnPointerExit(PointerEventData eventData) +L89 public void OnSelect(BaseEventData eventData) +L97 public void OnDeselect(BaseEventData eventData) +L105 public void OnMove(AxisEventData eventData) +L113 public void OnBeginDrag(PointerEventData eventData) +L124 public void OnDrag(PointerEventData eventData) +L135 public void OnEndDrag(PointerEventData eventData) +L146 public void OnSubmit(BaseEventData eventData) +L154 public void OnCancel(BaseEventData eventData) + +###FILE G:\UnityProject\Aliciza\Client\Packages\com.alicizax.unity.ui.extension\Runtime\RecyclerView\Interaction\RecyclerItemSelectable.cs +NAMESPACE AlicizaX.UI +L5 public sealed class RecyclerItemSelectable : Selectable + +###FILE G:\UnityProject\Aliciza\Client\Packages\com.alicizax.unity.ui.extension\Runtime\RecyclerView\Layout\CircleLayoutManager.cs +NAMESPACE AlicizaX.UI +L6 public class CircleLayoutManager : LayoutManager +L16 public CircleLayoutManager() +L21 public override Vector2 CalculateContentSize() +L38 public override Vector2 CalculateContentOffset() +L43 public override Vector2 CalculateViewportOffset() +L48 public override void Layout(ViewHolder viewHolder, int index) +L53 public override Vector2 CalculatePosition(int index) +L65 public override int GetStartIndex() +L70 public override int GetEndIndex() +L75 public override bool IsFullVisibleStart(int index) => false; +L77 public override bool IsFullInvisibleStart(int index) => false; +L79 public override bool IsFullVisibleEnd(int index) => false; +L81 public override bool IsFullInvisibleEnd(int index) => false; +L83 public override bool IsVisible(int index) => true; +L85 public override float IndexToPosition(int index) +L97 public override int PositionToIndex(float position) +L108 public override void DoItemAnimation() +L126 public enum CircleDirection + +###FILE G:\UnityProject\Aliciza\Client\Packages\com.alicizax.unity.ui.extension\Runtime\RecyclerView\Layout\GridLayoutManager.cs +NAMESPACE AlicizaX.UI +L7 public class GridLayoutManager : LayoutManager +L14 public GridLayoutManager() +L19 public override Vector2 CalculateContentSize() +L44 public override Vector2 CalculatePosition(int index) +L63 public override Vector2 CalculateContentOffset() +L84 public override Vector2 CalculateViewportOffset() +L105 public override int GetStartIndex() +L122 public override int GetEndIndex() +L140 public override float IndexToPosition(int index) +L165 public override int PositionToIndex(float position) + +###FILE G:\UnityProject\Aliciza\Client\Packages\com.alicizax.unity.ui.extension\Runtime\RecyclerView\Layout\ILayoutManager.cs +NAMESPACE AlicizaX.UI +L5 public interface ILayoutManager + +###FILE G:\UnityProject\Aliciza\Client\Packages\com.alicizax.unity.ui.extension\Runtime\RecyclerView\Layout\LayoutManager.cs +NAMESPACE AlicizaX.UI +L6 public abstract class LayoutManager : ILayoutManager +L9 public Vector2 ViewportSize +L16 public Vector2 ContentSize +L23 public Vector2 ContentOffset +L30 public Vector2 ViewportOffset +L37 public IAdapter Adapter +L44 public ViewProvider ViewProvider +L51 public virtual RecyclerView RecyclerView +L58 public Direction Direction +L65 public Alignment Alignment +L72 public Vector2 Spacing +L79 public Vector2 Padding +L86 public int Unit +L93 public float ScrollPosition => recyclerView.GetScrollPosition(); +L95 public LayoutManager() { } +L97 public void SetContentSize() +L105 public void UpdateLayout() +L113 public virtual void Layout(ViewHolder viewHolder, int index) +L122 public abstract Vector2 CalculateContentSize(); +L124 public abstract Vector2 CalculatePosition(int index); +L126 public abstract Vector2 CalculateContentOffset(); +L128 public abstract Vector2 CalculateViewportOffset(); +L130 public abstract int GetStartIndex(); +L132 public abstract int GetEndIndex(); +L134 public abstract float IndexToPosition(int index); +L136 public abstract int PositionToIndex(float position); +L138 public virtual void DoItemAnimation() { } +L140 public virtual bool IsFullVisibleStart(int index) +L147 public virtual bool IsFullInvisibleStart(int index) +L154 public virtual bool IsFullVisibleEnd(int index) +L162 public virtual bool IsFullInvisibleEnd(int index) +L170 public virtual bool IsVisible(int index) +L212 public enum Direction +L219 public enum Alignment + +###FILE G:\UnityProject\Aliciza\Client\Packages\com.alicizax.unity.ui.extension\Runtime\RecyclerView\Layout\LinearLayoutManager.cs +NAMESPACE AlicizaX.UI +L7 public class LinearLayoutManager : LayoutManager +L11 public LinearLayoutManager() { } +L13 public override Vector2 CalculateContentSize() +L37 public override Vector2 CalculatePosition(int index) +L49 public override Vector2 CalculateContentOffset() +L64 public override Vector2 CalculateViewportOffset() +L78 public override int GetStartIndex() +L95 public override int GetEndIndex() +L113 public override float IndexToPosition(int index) +L134 public override int PositionToIndex(float position) + +###FILE G:\UnityProject\Aliciza\Client\Packages\com.alicizax.unity.ui.extension\Runtime\RecyclerView\Layout\MixedLayoutManager.cs +NAMESPACE AlicizaX.UI +L7 public class MixedLayoutManager : LayoutManager +L15 public MixedLayoutManager() { } +L17 public override Vector2 CalculateContentSize() +L32 public override Vector2 CalculatePosition(int index) +L42 public override Vector2 CalculateContentOffset() +L59 public override Vector2 CalculateViewportOffset() +L75 public override int GetStartIndex() +L87 public override int GetEndIndex() +L100 public override float IndexToPosition(int index) +L117 public override int PositionToIndex(float position) + +###FILE G:\UnityProject\Aliciza\Client\Packages\com.alicizax.unity.ui.extension\Runtime\RecyclerView\Layout\PageLayoutManager.cs +NAMESPACE AlicizaX.UI +L7 public class PageLayoutManager : LinearLayoutManager +L11 public PageLayoutManager() +L15 public override Vector2 CalculateContentSize() +L42 public override Vector2 CalculatePosition(int index) +L55 public override Vector2 CalculateContentOffset() +L60 public override Vector2 CalculateViewportOffset() +L76 public override int PositionToIndex(float position) +L95 public override void DoItemAnimation() + +###FILE G:\UnityProject\Aliciza\Client\Packages\com.alicizax.unity.ui.extension\Runtime\RecyclerView\Navigation\RecyclerNavigationBridge.cs +NAMESPACE AlicizaX.UI +L8 public sealed class RecyclerNavigationBridge : Selectable, ISelectHandler, IMoveHandler, ISubmitHandler +L24 public override void OnSelect(BaseEventData eventData) +L30 public override void OnMove(AxisEventData eventData) +L38 public void OnSubmit(BaseEventData eventData) + +###FILE G:\UnityProject\Aliciza\Client\Packages\com.alicizax.unity.ui.extension\Runtime\RecyclerView\Navigation\RecyclerNavigationController.cs +NAMESPACE AlicizaX.UI +L6 public sealed class RecyclerNavigationController +L10 public RecyclerNavigationController(RecyclerView recyclerView) +L15 public bool TryMove(ViewHolder currentHolder, MoveDirection direction, RecyclerNavigationOptions options) + +###FILE G:\UnityProject\Aliciza\Client\Packages\com.alicizax.unity.ui.extension\Runtime\RecyclerView\Navigation\RecyclerNavigationOptions.cs +NAMESPACE AlicizaX.UI +L3 public readonly struct RecyclerNavigationOptions +L5 public static readonly RecyclerNavigationOptions Clamped = new(false, false, ScrollAlignment.Center); +L6 public static readonly RecyclerNavigationOptions Circular = new(true, false, ScrollAlignment.Center); +L8 public RecyclerNavigationOptions(bool wrap, bool smoothScroll, ScrollAlignment alignment) +L15 public bool Wrap { get; } +L17 public bool SmoothScroll { get; } +L19 public ScrollAlignment Alignment { get; } + +###FILE G:\UnityProject\Aliciza\Client\Packages\com.alicizax.unity.ui.extension\Runtime\RecyclerView\ObjectPool\IMixedObjectFactory.cs +NAMESPACE AlicizaX.UI +L3 public interface IMixedObjectFactory where T : class + +###FILE G:\UnityProject\Aliciza\Client\Packages\com.alicizax.unity.ui.extension\Runtime\RecyclerView\ObjectPool\IMixedObjectPool.cs +NAMESPACE AlicizaX.UI +L5 public interface IMixedObjectPool : IDisposable where T : class + +###FILE G:\UnityProject\Aliciza\Client\Packages\com.alicizax.unity.ui.extension\Runtime\RecyclerView\ObjectPool\IObjectFactory.cs +NAMESPACE AlicizaX.UI +L3 public interface IObjectFactory where T : class + +###FILE G:\UnityProject\Aliciza\Client\Packages\com.alicizax.unity.ui.extension\Runtime\RecyclerView\ObjectPool\IObjectPool.cs +NAMESPACE AlicizaX.UI +L5 public interface IObjectPool : IDisposable +L12 public interface IObjectPool : IObjectPool, IDisposable where T : class + +###FILE G:\UnityProject\Aliciza\Client\Packages\com.alicizax.unity.ui.extension\Runtime\RecyclerView\ObjectPool\MixedObjectPool.cs +NAMESPACE AlicizaX.UI +L6 public class MixedObjectPool : IMixedObjectPool where T : class +L21 public MixedObjectPool(IMixedObjectFactory factory) : this(factory, DEFAULT_MAX_SIZE_PER_TYPE) +L25 public MixedObjectPool(IMixedObjectFactory factory, int defaultMaxSizePerType) +L41 public T Allocate(string typeName) +L57 public void Free(string typeName, T obj) +L85 public int GetMaxSize(string typeName) +L95 public void SetMaxSize(string typeName, int value) +L100 public void EnsureCapacity(string typeName, int value) +L114 public void Warm(string typeName, int count) +L134 public int GetActiveCount(string typeName) +L139 public int GetPeakActiveCount(string typeName) +L144 public int HitCount => hitCount; +L146 public int MissCount => missCount; +L148 public int DestroyCount => destroyCount; +L172 public void Dispose() + +###FILE G:\UnityProject\Aliciza\Client\Packages\com.alicizax.unity.ui.extension\Runtime\RecyclerView\ObjectPool\ObjectPool.cs +NAMESPACE AlicizaX.UI +L6 public class ObjectPool : IObjectPool where T : class +L19 public ObjectPool(IObjectFactory factory) : this(factory, Environment.ProcessorCount * 2) +L23 public ObjectPool(IObjectFactory factory, int maxSize) : this(factory, 0, maxSize) +L27 public ObjectPool(IObjectFactory factory, int initialSize, int maxSize) +L42 public int MaxSize => maxSize; +L44 public int InitialSize => initialSize; +L46 public int InactiveCount => entries.Count; +L48 public int ActiveCount => activeCount; +L50 public int TotalCount => totalCount; +L52 public int PeakActive => peakActive; +L54 public int HitCount => hitCount; +L56 public int MissCount => missCount; +L58 public int DestroyCount => destroyCount; +L60 public virtual T Allocate() +L90 public virtual void Free(T obj) +L137 public void Free(object obj) +L157 public void Dispose() +L163 public void EnsureCapacity(int value) +L176 public void Warm(int count) + +###FILE G:\UnityProject\Aliciza\Client\Packages\com.alicizax.unity.ui.extension\Runtime\RecyclerView\ObjectPool\UnityComponentFactory.cs +NAMESPACE AlicizaX.UI +L5 public class UnityComponentFactory : IObjectFactory where T : Component +L10 public UnityComponentFactory(T template, Transform parent) +L16 public T Create() +L22 public void Destroy(T obj) +L27 public void Reset(T obj) +L32 public bool Validate(T obj) + +###FILE G:\UnityProject\Aliciza\Client\Packages\com.alicizax.unity.ui.extension\Runtime\RecyclerView\ObjectPool\UnityGameObjectFactory.cs +NAMESPACE AlicizaX.UI +L5 public class UnityGameObjectFactory : IObjectFactory +L10 public UnityGameObjectFactory(GameObject template, Transform parent) +L16 public virtual GameObject Create() +L21 public virtual void Reset(GameObject obj) +L26 public virtual void Destroy(GameObject obj) +L31 public virtual bool Validate(GameObject obj) + +###FILE G:\UnityProject\Aliciza\Client\Packages\com.alicizax.unity.ui.extension\Runtime\RecyclerView\ObjectPool\UnityMixedComponentFactory.cs +NAMESPACE AlicizaX.UI +L6 public class UnityMixedComponentFactory : IMixedObjectFactory where T : Component +L14 public UnityMixedComponentFactory(T template, Transform parent) +L20 public UnityMixedComponentFactory(List list, Transform parent) +L31 public UnityMixedComponentFactory(Dictionary dict, Transform parent) +L37 public T Create(string typeName) +L56 public void Destroy(string typeName, T obj) +L61 public void Reset(string typeName, T obj) +L66 public bool Validate(string typeName, T obj) + +###FILE G:\UnityProject\Aliciza\Client\Packages\com.alicizax.unity.ui.extension\Runtime\RecyclerView\ObjectPool\UnityMixedGameObjectFactory.cs +NAMESPACE AlicizaX.UI +L5 public class UnityMixedGameObjectFactory : IMixedObjectFactory +L10 public UnityMixedGameObjectFactory(GameObject template, Transform parent) +L16 public GameObject Create(string typeName) +L26 public void Destroy(string typeName, GameObject obj) +L31 public void Reset(string typeName, GameObject obj) +L36 public bool Validate(string typeName, GameObject obj) + +###FILE G:\UnityProject\Aliciza\Client\Packages\com.alicizax.unity.ui.extension\Runtime\RecyclerView\Scroller\CircleScroller.cs +NAMESPACE AlicizaX.UI +L6 public class CircleScroller : Scroller + +###FILE G:\UnityProject\Aliciza\Client\Packages\com.alicizax.unity.ui.extension\Runtime\RecyclerView\Scroller\IScroller.cs +NAMESPACE AlicizaX.UI +L5 public interface IScroller +L12 public class ScrollerEvent : UnityEvent { } +L14 public class MoveStopEvent : UnityEvent { } +L16 public class DraggingEvent : UnityEvent { } + +###FILE G:\UnityProject\Aliciza\Client\Packages\com.alicizax.unity.ui.extension\Runtime\RecyclerView\Scroller\ScrollbarEx.cs +NAMESPACE AlicizaX.UI +L8 public class ScrollbarEx : MonoBehaviour, IBeginDragHandler, IEndDragHandler, IPointerEnterHandler, IPointerExitHandler +L13 public Action OnDragEnd; +L24 public void OnBeginDrag(PointerEventData eventData) +L29 public void OnEndDrag(PointerEventData eventData) +L56 public void OnPointerEnter(PointerEventData eventData) +L79 public void OnPointerExit(PointerEventData eventData) + +###FILE G:\UnityProject\Aliciza\Client\Packages\com.alicizax.unity.ui.extension\Runtime\RecyclerView\Scroller\Scroller.cs +NAMESPACE AlicizaX.UI +L7 public class Scroller : MonoBehaviour, IScroller, IBeginDragHandler, IEndDragHandler, IDragHandler, IScrollHandler +L13 public float Position { get => position; set => position = value; } +L16 public float Velocity => velocity; +L19 public Direction Direction +L26 public Vector2 ContentSize +L33 public Vector2 ViewSize +L40 public float ScrollSpeed +L47 public float WheelSpeed +L54 public bool Snap +L64 public float MaxPosition => direction == Direction.Vertical ? +L68 public float ViewLength => direction == Direction.Vertical ? viewSize.y : viewSize.x; +L70 public ScrollerEvent OnValueChanged { get => scrollerEvent; set => scrollerEvent = value; } +L72 public MoveStopEvent OnMoveStoped { get => moveStopEvent; set => moveStopEvent = value; } +L74 public DraggingEvent OnDragging { get => draggingEvent; set => draggingEvent = value; } +L76 public float dragStopTime = 0f; +L79 public virtual void ScrollTo(float position, bool smooth = false) +L96 public virtual void ScrollToDuration(float position, float duration) +L114 public virtual void ScrollToRatio(float ratio) +L119 public void OnBeginDrag(PointerEventData eventData) +L125 public void OnEndDrag(PointerEventData eventData) +L132 public void OnDrag(PointerEventData eventData) +L142 public void OnScroll(PointerEventData eventData) + +###FILE G:\UnityProject\Aliciza\Client\Packages\com.alicizax.unity.ui.extension\Runtime\RecyclerView\ViewHolder\ViewHolder.cs +NAMESPACE AlicizaX.UI +L6 public abstract class ViewHolder : MonoBehaviour +L12 public RectTransform RectTransform +L25 public string Name { get; internal set; } +L27 public int Index { get; internal set; } +L29 public int DataIndex { get; internal set; } = -1; +L31 public RecyclerView RecyclerView { get; internal set; } +L33 public uint BindingVersion { get; private set; } +L35 public Vector2 SizeDelta => RectTransform.sizeDelta; + +###FILE G:\UnityProject\Aliciza\Client\Packages\com.alicizax.unity.ui.extension\Runtime\RecyclerView\ViewProvider\MixedViewProvider.cs +NAMESPACE AlicizaX.UI +L6 public class MixedViewProvider : ViewProvider +L11 public override string PoolStats => +L14 public MixedViewProvider(RecyclerView recyclerView, ViewHolder[] templates) : base(recyclerView, templates) +L31 public override ViewHolder GetTemplate(string viewName) +L46 public override ViewHolder[] GetTemplates() +L58 public override ViewHolder Allocate(string viewName) +L65 public override void Free(string viewName, ViewHolder viewHolder) +L70 public override void Reset() +L77 public override void PreparePool() + +###FILE G:\UnityProject\Aliciza\Client\Packages\com.alicizax.unity.ui.extension\Runtime\RecyclerView\ViewProvider\SimpleViewProvider.cs +NAMESPACE AlicizaX.UI +L5 public sealed class SimpleViewProvider : ViewProvider +L9 public override string PoolStats => +L12 public SimpleViewProvider(RecyclerView recyclerView, ViewHolder[] templates) : base(recyclerView, templates) +L18 public override ViewHolder GetTemplate(string viewName = "") +L27 public override ViewHolder[] GetTemplates() +L36 public override ViewHolder Allocate(string viewName) +L43 public override void Free(string viewName, ViewHolder viewHolder) +L48 public override void Reset() +L55 public override void PreparePool() + +###FILE G:\UnityProject\Aliciza\Client\Packages\com.alicizax.unity.ui.extension\Runtime\RecyclerView\ViewProvider\ViewProvider.cs +NAMESPACE AlicizaX.UI +L7 public abstract class ViewProvider +L14 public IAdapter Adapter { get; set; } +L16 public LayoutManager LayoutManager { get; set; } +L18 public IReadOnlyList ViewHolders => viewHolders; +L20 public abstract string PoolStats { get; } +L25 public ViewProvider(RecyclerView recyclerView, ViewHolder[] templates) +L31 public abstract ViewHolder GetTemplate(string viewName); +L33 public abstract ViewHolder[] GetTemplates(); +L35 public abstract ViewHolder Allocate(string viewName); +L37 public abstract void Free(string viewName, ViewHolder viewHolder); +L39 public abstract void Reset(); +L41 public abstract void PreparePool(); +L43 public void CreateViewHolder(int index) +L63 public void RemoveViewHolder(int index) +L85 public ViewHolder GetViewHolder(int index) +L92 public ViewHolder GetViewHolderByDataIndex(int dataIndex) +L100 public bool TryGetViewHoldersByDataIndex(int dataIndex, out IReadOnlyList holders) +L112 public int GetViewHolderIndex(int index) +L119 public void Clear() +L137 public Vector2 CalculateViewSize(int index) +L143 public int GetItemCount() + +###FILE G:\UnityProject\Aliciza\Client\Packages\com.alicizax.unity.ui.extension\Runtime\RecyclerView\EaseUtil.cs +NAMESPACE +L3 public class EaseUtil +L5 public static double EaseInSine(float x) +L10 public static double EaseOutSine(float x) +L15 public static double EaseInOutSine(float x) +L20 public static double EaseInQuad(float x) +L25 public static double EaseOutQuad(float x) +L30 public static double EaseInOutQuad(float x) +L35 public static double EaseInQuart(float x) +L40 public static double EaseOutQuart(float x) +L45 public static double EaseInOutQuart(float x) +L50 public static double EaseInCubic(float x) +L55 public static double EaseOutCubic(float x) +L60 public static double EaseInOutCubic(float x) +L65 public static double EaseInQuint(float x) +L70 public static double EaseOutQuint(float x) +L75 public static double EaseInOutQuint(float x) +L80 public static double EaseInExpo(float x) +L85 public static double EaseOutExpo(float x) +L90 public static double EaseInOutExpo(float x) +L98 public static double EaseInCirc(float x) +L103 public static double EaseOutCirc(float x) +L108 public static double EaseInOutCirc(float x) +L113 public static double EaseInBack(float x) +L121 public static double EaseOutBack(float x) +L129 public static double EaseInOutBack(float x) +L139 public static double EaseInElastic(float x) +L145 public static double EaseOutElastic(float x) +L151 public static double EaseInOutElastic(float x) +L160 public static double EaseInBounce(float x) +L165 public static double EaseOutBounce(float x) +L188 public static double EaseInOutBounce(float x) + +###FILE G:\UnityProject\Aliciza\Client\Packages\com.alicizax.unity.ui.extension\Runtime\RecyclerView\RecyclerView.cs +NAMESPACE AlicizaX.UI +L14 public class RecyclerView : MonoBehaviour +L183 public Direction Direction +L192 public Alignment Alignment +L201 public Vector2 Spacing +L210 public Vector2 Padding +L223 public bool Scroll +L250 public bool Snap +L271 public float ScrollSpeed +L289 public float WheelSpeed +L307 public bool ShowScrollBarOnlyWhenScrollable +L326 public ViewHolder[] Templates +L335 public RectTransform Content +L360 public Scrollbar Scrollbar => scrollbar; +L365 public Scroller Scroller => scroller; +L370 public ViewProvider ViewProvider +L393 public string PoolStats => viewProvider?.PoolStats ?? string.Empty; +L398 public LayoutManager LayoutManager => layoutManager; +L403 public RecyclerNavigationController NavigationController => navigationController ??= new RecyclerNavigationController(this); +L412 public IAdapter RecyclerViewAdapter { get; set; } +L417 public int CurrentIndex +L430 public Action OnIndexChanged; +L435 public Action OnScrollValueChanged; +L567 public void SetAdapter(IAdapter adapter) +L616 public bool TryGetVisibleViewHolder(int index, out ViewHolder viewHolder) +L629 public bool TryFocusIndex(int index, bool smooth = false, ScrollAlignment alignment = ScrollAlignment.Center) +L680 public bool TryFocusEntry(MoveDirection entryDirection) +L829 public void Reset() +L861 public void Refresh() +L894 public void RequestLayout() +L935 public float GetScrollPosition() +L945 public void ScrollTo(int index, bool smooth = false) +L967 public void ScrollToWithAlignment(int index, ScrollAlignment alignment, float offset = 0f, bool smooth = false, float duration = 0.3f) + +###FILE G:\UnityProject\Aliciza\Client\Packages\com.alicizax.unity.ui.extension\Runtime\RecyclerView\ScrollAlignment.cs +NAMESPACE AlicizaX.UI +L3 public enum ScrollAlignment + +###FILE G:\UnityProject\Aliciza\Client\Packages\com.alicizax.unity.ui.extension\Runtime\RecyclerView\UGList.cs +NAMESPACE AlicizaX.UI +L10 public abstract class UGListBase where TAdapter : Adapter where TData : ISimpleViewData +L22 public RecyclerView RecyclerView => _recyclerView; +L28 public UGListBase(RecyclerView recyclerView, TAdapter adapter) +L41 public TAdapter Adapter => _adapter; +L47 public void RegisterItemRender(string viewName = "") where TItemRender : ItemRenderBase +L56 public void RegisterItemRender(Type itemRenderType, string viewName = "") +L65 public bool UnregisterItemRender(string viewName = "") +L72 public void ClearItemRenderRegistrations() +L85 public List Data +L99 public class UGList : UGListBase> where TData : ISimpleViewData +L104 public UGList(RecyclerView recyclerView) +L113 public class UGGroupList : UGListBase> where TData : class, IGroupViewData, new() +L119 public UGGroupList(RecyclerView recyclerView, string groupViewName) +L128 public class UGLoopList : UGListBase> where TData : ISimpleViewData, new() +L133 public UGLoopList(RecyclerView recyclerView) +L142 public class UGMixedList : UGListBase> where TData : IMixedViewData +L147 public UGMixedList(RecyclerView recyclerView) +L155 public static class UGListCreateHelper +L162 public static UGList Create(RecyclerView recyclerView) where TData : ISimpleViewData +L171 public static UGGroupList CreateGroup(RecyclerView recyclerView, string groupViewName) where TData : class, IGroupViewData, new() +L179 public static UGLoopList CreateLoop(RecyclerView recyclerView) where TData : ISimpleViewData, new() +L187 public static UGMixedList CreateMixed(RecyclerView recyclerView) where TData : IMixedViewData + +###FILE G:\UnityProject\Aliciza\Client\Packages\com.alicizax.unity.ui.extension\Runtime\RecyclerView\UGListExtensions.cs +NAMESPACE AlicizaX.UI +L9 public static class UGListExtensions +L14 public static bool DebugScrollTo { get; set; } = false; +L27 public static void ScrollTo( +L61 public static void ScrollToStart( +L83 public static void ScrollToCenter( +L105 public static void ScrollToEnd( + +###FILE G:\UnityProject\Aliciza\Client\Packages\com.alicizax.unity.ui.extension\Runtime\UXComponent\Button\IButton.cs +NAMESPACE AlicizaX.UI.Extension +L5 public interface IButton + +###FILE G:\UnityProject\Aliciza\Client\Packages\com.alicizax.unity.ui.extension\Runtime\UXComponent\Button\UXButton.cs +NAMESPACE UnityEngine.UI +L11 public class UXButton : UXSelectable, IPointerClickHandler, ISubmitHandler +L22 public override void OnPointerEnter(PointerEventData eventData) +L28 public virtual void OnPointerClick(PointerEventData eventData) +L38 public virtual void OnSubmit(BaseEventData eventData) +L73 public Button.ButtonClickedEvent onClick + +###FILE G:\UnityProject\Aliciza\Client\Packages\com.alicizax.unity.ui.extension\Runtime\UXComponent\Controller\UXBinding.cs +NAMESPACE UnityEngine.UI +L7 public enum UXBindingFallbackMode +L14 public enum UXBindingValueKind +L25 public enum UXBindingProperty +L42 public sealed class UXBindingValue +L52 public bool BoolValue +L58 public float FloatValue +L64 public string StringValue +L70 public Color ColorValue +L76 public Vector2 Vector2Value +L82 public Vector3 Vector3Value +L88 public UnityEngine.Object ObjectValue +L94 public void CopyFrom(UXBindingValue other) +L113 public sealed class UXBinding : MonoBehaviour +L116 public sealed class BindingEntry +L128 public string ControllerId +L134 public int ControllerIndex +L140 public UXBindingProperty Property +L146 public UXBindingValue Value => _value; +L148 public UXBindingFallbackMode FallbackMode +L154 public UXBindingValue FallbackValue => _fallbackValue; +L155 public bool HasCapturedDefault => _hasCapturedDefault; +L263 public UXController Controller => _controller; +L264 public List Entries => _entries; +L266 public void Initialize() +L280 public void SetController(UXController controller) +L296 public void CaptureDefaults() +L307 public void ResetToDefaults() +L318 public void PreviewEntry(int entryIndex) +L334 public void CaptureEntryValue(int entryIndex) +L344 public void CaptureEntryFallbackValue(int entryIndex) + +###FILE G:\UnityProject\Aliciza\Client\Packages\com.alicizax.unity.ui.extension\Runtime\UXComponent\Controller\UXBindingPropertyUtility.cs +NAMESPACE UnityEngine.UI +L7 public sealed class UXBindingPropertyMetadata +L9 public UXBindingPropertyMetadata( +L21 public UXBindingProperty Property { get; } +L22 public string DisplayName { get; } +L23 public UXBindingValueKind ValueKind { get; } +L24 public Type ObjectReferenceType { get; } +L27 public static class UXBindingPropertyUtility +L45 public static IReadOnlyList AllMetadata => Metadata; +L47 public static UXBindingPropertyMetadata GetMetadata(UXBindingProperty property) +L60 public static bool IsSupported(GameObject target, UXBindingProperty property) +L92 public static void GetSupportedProperties(GameObject target, List output) +L110 public static void CaptureValue(GameObject target, UXBindingProperty property, UXBindingValue destination) +L172 public static void ApplyValue(GameObject target, UXBindingProperty property, UXBindingValue value) + +###FILE G:\UnityProject\Aliciza\Client\Packages\com.alicizax.unity.ui.extension\Runtime\UXComponent\Controller\UXController.cs +NAMESPACE UnityEngine.UI +L12 public sealed class UXController : MonoBehaviour +L15 public sealed class ControllerDefinition +L24 public string Id => _id; +L26 public string Name +L32 public int Length +L38 public string Description +L44 public int SelectedIndex +L84 public IReadOnlyList Controllers +L93 public IReadOnlyList Bindings => _bindings; +L94 public int ControllerCount => _controllers.Count; +L96 public bool TryGetControllerById(string controllerId, out ControllerDefinition controller) +L114 public bool TryGetControllerByName(string controllerName, out ControllerDefinition controller) +L132 public ControllerDefinition GetControllerByName(string controllerName) +L148 public ControllerDefinition GetControllerAt(int index) +L160 public int GetControllerIndex(string controllerId) +L167 public bool SetControllerIndex(string controllerId, int selectedIndex) +L177 public bool SetControllerIndexByName(string controllerName, int selectedIndex) +L187 public void ResetAllControllers() + +###FILE G:\UnityProject\Aliciza\Client\Packages\com.alicizax.unity.ui.extension\Runtime\UXComponent\Drag\UXDraggable.cs +NAMESPACE UnityEngine.UI +L7 public class UXDraggable:MonoBehaviour, IDragHandler, IBeginDragHandler, IEndDragHandler +L9 public UnityEvent onDrag; +L10 public UnityEvent onBeginDrag; +L11 public UnityEvent onEndDrag; + +###FILE G:\UnityProject\Aliciza\Client\Packages\com.alicizax.unity.ui.extension\Runtime\UXComponent\Group\UXGroup.cs +NAMESPACE UnityEngine.UI +L11 public class UXGroup : UIBehaviour +L15 public bool allowSwitchOff +L27 public UXToggle defaultToggle +L55 public void NotifyToggleOn(UXToggle toggle, bool sendCallback = true) +L70 public void UnregisterToggle(UXToggle toggle) +L84 public void RegisterToggle(UXToggle toggle) +L122 public bool ContainsToggle(UXToggle toggle) +L127 public void EnsureValidState() +L183 public bool AnyTogglesOn() +L188 public IEnumerable ActiveToggles() +L193 public UXToggle GetFirstActiveToggle() +L202 public void SetAllTogglesOff(bool sendCallback = true) +L223 public void Next() +L228 public void Preview() + +###FILE G:\UnityProject\Aliciza\Client\Packages\com.alicizax.unity.ui.extension\Runtime\UXComponent\Group\UXToggle.cs +NAMESPACE UnityEngine.UI +L9 public class UXToggle : UXSelectable, IPointerClickHandler, ISubmitHandler, ICanvasElement +L12 public class ToggleEvent : UnityEvent +L16 public Toggle.ToggleTransition toggleTransition = Toggle.ToggleTransition.Fade; +L17 public Graphic graphic; +L21 public UXGroup group +L31 public ToggleEvent onValueChanged = new ToggleEvent(); +L50 public virtual void Rebuild(CanvasUpdate executing) +L58 public virtual void LayoutComplete() +L62 public virtual void GraphicUpdateComplete() +L133 public bool isOn +L139 public void SetIsOnWithoutNotify(bool value) +L215 public override void OnPointerEnter(PointerEventData eventData) +L221 public virtual void OnPointerClick(PointerEventData eventData) +L230 public virtual void OnSubmit(BaseEventData eventData) + +###FILE G:\UnityProject\Aliciza\Client\Packages\com.alicizax.unity.ui.extension\Runtime\UXComponent\Hotkey\HotkeyComponent.cs +NAMESPACE UnityEngine.UI +L8 public sealed class HotkeyComponent : MonoBehaviour, IHotkeyTrigger +L14 public InputActionReference HotkeyAction + +###FILE G:\UnityProject\Aliciza\Client\Packages\com.alicizax.unity.ui.extension\Runtime\UXComponent\Hotkey\IHotkeyTrigger.cs +NAMESPACE UnityEngine.UI +L6 public interface IHotkeyTrigger +L8 public InputActionReference HotkeyAction { get; } + +###FILE G:\UnityProject\Aliciza\Client\Packages\com.alicizax.unity.ui.extension\Runtime\UXComponent\Hotkey\UXHotkeyRegisterManager.cs +NAMESPACE UnityEngine.UI +L19 public readonly IHotkeyTrigger Trigger; +L20 public readonly EHotkeyPressType PressType; +L23 public HotkeyRegistration(IHotkeyTrigger trigger, EHotkeyPressType pressType) +L32 public HotkeyScope(UIHolderObjectBase holder) +L39 public readonly UIHolderObjectBase Holder; +L40 public readonly int HierarchyDepth; +L41 public readonly bool BlocksLowerScopes; +L42 public readonly Dictionary> RegistrationsByAction = new(StringComparer.Ordinal); +L44 public bool LifecycleActive; +L45 public ulong ActivationSerial; +L47 public Action OnBeforeShow; +L48 public Action OnBeforeClosed; +L49 public Action OnDestroy; +L53 public Canvas Canvas +L104 public InputActionReference ActionReference; +L105 public Action StartedHandler; +L106 public Action PerformedHandler; +L107 public int StartedCount; +L108 public int PerformedCount; +L110 public int TotalCount => StartedCount + PerformedCount; +L115 public readonly string ActionId; +L116 public readonly UIHolderObjectBase Holder; +L117 public readonly EHotkeyPressType PressType; +L119 public TriggerRegistration(string actionId, UIHolderObjectBase holder, EHotkeyPressType pressType) +L587 public static string GetDebugInfo() +L597 public static class UXHotkeyHotkeyExtension +L600 public static void BindHotKey(this IHotkeyTrigger trigger) +L623 public static void UnBindHotKey(this IHotkeyTrigger trigger) +L632 public static void BindHotKeyBatch(this IHotkeyTrigger[] triggers) +L646 public static void UnBindHotKeyBatch(this IHotkeyTrigger[] triggers) + +###FILE G:\UnityProject\Aliciza\Client\Packages\com.alicizax.unity.ui.extension\Runtime\UXComponent\Image\UXImage.cs +NAMESPACE UnityEngine.UI +L8 public class UXImage : Image +L10 public enum ColorType +L31 public Gradient gradient +L36 public enum GradientDirection +L42 public GradientDirection Direction +L49 public enum FlipPart +L57 public enum FlipDirection +L68 public enum FlipMode +L76 public enum FlipEdge +L87 public enum FlipEdgeHorizontal +L95 public enum FlipEdgeVertical +L102 public enum FlipFillCenter +L111 public FlipMode m_OriginFlipMode = FlipMode.None; +L113 public FlipMode m_FlipMode = FlipMode.None; +L114 public FlipMode flipMode { get { return m_FlipMode; } set { SetVerticesDirty(); } } +L116 public bool m_FlipWithCopy = true; +L117 public bool flipWithCopy { get { return m_FlipWithCopy; } set { SetVerticesDirty(); } } +L119 public FlipEdge flipEdge +L137 public FlipEdgeHorizontal m_FlipEdgeHorizontal = FlipEdgeHorizontal.Right; +L138 public FlipEdgeHorizontal flipEdgeHorizontal { get { return m_FlipEdgeHorizontal; } set { SetVerticesDirty(); } } +L140 public FlipEdgeVertical m_FlipEdgeVertical = FlipEdgeVertical.Down; +L141 public FlipEdgeVertical flipEdgeVertical { get { return m_FlipEdgeVertical; } set { SetVerticesDirty(); } } +L144 public FlipFillCenter m_FlipFillCenter = FlipFillCenter.LeftBottom; +L145 public FlipFillCenter flipFillCenter { get { return m_FlipFillCenter; } set { SetVerticesDirty(); } } +L149 public FlipDirection m_FlipDirection = FlipDirection.FourCorner; +L150 public FlipDirection flipDirection { get { return m_FlipDirection; } set { SetVerticesDirty(); } } +L393 public void RemapVertex(ref UIVertex vertex, FlipMode flipMode, float Min1, float Max1, float Min2, float Max2) +L614 public class Vert2D //鎻忚堪alphakey鍜宑olorkey鐨勭被 +L616 public Vector3 position = default; +L617 public Color color = Color.white; +L618 public Vector2 uv0 = default; +L619 public int type;//1=alphakey,2=colorkey, 0=涓ょ +L620 public Vert2D(int type) +L624 public Vert2D(UIVertex vertex) +L628 public int CompareToVertically(Vert2D vert) +L643 public int CompareToHorizontally(Vert2D vert) +L663 public void SetColor(Color col) +L669 public void SetAlpha(float a) +L673 public void SetUV(float u, float v) +L678 public void SetPosition(Vector3 pos) +L685 public class Comparer : IEqualityComparer +L687 public bool Equals(Vert2D x, Vert2D y) +L693 public int GetHashCode(Vert2D obj) + +###FILE G:\UnityProject\Aliciza\Client\Packages\com.alicizax.unity.ui.extension\Runtime\UXComponent\Navigation\UXInputMode.cs +NAMESPACE UnityEngine.UI +L4 public enum UXInputMode : byte + +###FILE G:\UnityProject\Aliciza\Client\Packages\com.alicizax.unity.ui.extension\Runtime\UXComponent\Navigation\UXInputModeService.cs +NAMESPACE UnityEngine.UI +L15 public static UXInputMode CurrentMode { get; private set; } = UXInputMode.Pointer; +L17 public static event Action OnModeChanged; + +###FILE G:\UnityProject\Aliciza\Client\Packages\com.alicizax.unity.ui.extension\Runtime\UXComponent\Navigation\UXNavigationLayerWatcher.cs +NAMESPACE UnityEngine.UI + +###FILE G:\UnityProject\Aliciza\Client\Packages\com.alicizax.unity.ui.extension\Runtime\UXComponent\Navigation\UXNavigationRuntime.cs +NAMESPACE UnityEngine.UI + +###FILE G:\UnityProject\Aliciza\Client\Packages\com.alicizax.unity.ui.extension\Runtime\UXComponent\Navigation\UXNavigationScope.cs +NAMESPACE UnityEngine.UI +L9 public sealed class UXNavigationScope : MonoBehaviour +L36 public Selectable DefaultSelectable +L42 public bool RememberLastSelection => _rememberLastSelection; +L43 public bool RequireSelectionWhenGamepad => _requireSelectionWhenGamepad; +L44 public bool BlockLowerScopes => _blockLowerScopes; +L294 public void InvalidateSelectableCache() + +###FILE G:\UnityProject\Aliciza\Client\Packages\com.alicizax.unity.ui.extension\Runtime\UXComponent\Navigation\UXNavigationSkip.cs +NAMESPACE UnityEngine.UI +L6 public sealed class UXNavigationSkip : MonoBehaviour + +###FILE G:\UnityProject\Aliciza\Client\Packages\com.alicizax.unity.ui.extension\Runtime\UXComponent\Selectable\UXSelectable.cs +NAMESPACE UnityEngine.UI +L8 public class TransitionData +L10 public Graphic targetGraphic; +L11 public Selectable.Transition transition = Selectable.Transition.ColorTint; +L12 public ColorBlock colors = ColorBlock.defaultColorBlock; +L13 public SpriteState spriteState; +L16 public class UXSelectable : Selectable +L117 public override Selectable FindSelectableOnLeft() +L132 public override Selectable FindSelectableOnRight() +L147 public override Selectable FindSelectableOnUp() +L162 public override Selectable FindSelectableOnDown() + +###FILE G:\UnityProject\Aliciza\Client\Packages\com.alicizax.unity.ui.extension\Runtime\UXComponent\Text\LocalizationRefreshHelper.cs +NAMESPACE UnityEngine.UI + +###FILE G:\UnityProject\Aliciza\Client\Packages\com.alicizax.unity.ui.extension\Runtime\UXComponent\Text\UXTextMeshPro.cs +NAMESPACE UnityEngine.UI +L6 public class UXTextMeshPro : TextMeshProUGUI +L40 public void SetLocalization(string localizationID) + +###FILE G:\UnityProject\Aliciza\Client\Packages\com.alicizax.unity.ui.extension\Runtime\UXComponent\UXHelper.cs +NAMESPACE +L3 public static class UXComponentExtensionsHelper +L8 public static void SetLocalizationHelper(IUXLocalizationHelper helper) +L13 public static void SetAudioHelper(IUXAudioHelper helper) +L19 public interface IUXLocalizationHelper +L21 public string GetString(string key); +L25 public interface IUXAudioHelper + +###FILE G:\UnityProject\Aliciza\Client\Packages\com.alicizax.unity.ui.extension\Runtime\_InternalVisibleTo.cs +NAMESPACE + diff --git a/Client/Assets/Books.meta b/Client/Assets/Books.meta new file mode 100644 index 0000000..65748bf --- /dev/null +++ b/Client/Assets/Books.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 00a54e10cd11cbd419de04cbd506b6d3 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Client/Assets/Books/EditorExtension.meta b/Client/Assets/Books/EditorExtension.meta new file mode 100644 index 0000000..1fd5f93 --- /dev/null +++ b/Client/Assets/Books/EditorExtension.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 55079f02326c270468f1eca1808de28c +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Client/Assets/Books/EditorExtension/AtlasPostprocessor.md b/Client/Assets/Books/EditorExtension/AtlasPostprocessor.md new file mode 100644 index 0000000..b4fb7ef --- /dev/null +++ b/Client/Assets/Books/EditorExtension/AtlasPostprocessor.md @@ -0,0 +1,83 @@ +# EditorExtension Atlas 自动图集模块手册 + +## 模块概述 +该模块位于 `Editor/Postprocessor/Atlas`,用于维护图集配置、响应 Sprite 导入/删除、标记脏图集并执行全量生成。 + +## 适用场景 +- 以目录为单位自动生成 UI 图集。 +- 不同平台使用不同压缩格式。 +- 导入资源时自动更新图集状态。 + +## 快速上手 +### 打开配置窗口 +- 菜单:`Tools/Extension/图集工具/Configuration Panel` + +### 全量生成 +```csharp +EditorSpriteSaveInfo.ForceGenerateAll(); +``` + +## 模块组成 +- `AtlasConfiguration`:图集配置单例。 +- `AtlasConfigWindow`:配置窗口。 +- `EditorSpriteSaveInfo`:缓存、脏标记、全量生成。 +- `SpritePostprocessor`:接入 Unity 导入流程。 + +## 可调用 API +### 类型:`AtlasConfiguration` +源码:`Packages/com.alicizax.unity.editor.extension/Editor/Postprocessor/Atlas/AtlasConfiguration.cs` + +#### 公开配置字段 +- `outputAtlasDir` +- `sourceAtlasRoot` +- `androidFormat` +- `iosFormat` +- `webglFormat` +- `padding` +- `enableRotation` +- `blockOffset` +- `tightPacking` +- `compressionQuality` +- `autoGenerate` +- `enableLogging` +- `enableV2` +- `excludeKeywords` +- `excludeFolders` + +示例: +```csharp +AtlasConfiguration.Instance.autoGenerate = true; +AtlasConfiguration.Save(true); +``` + +### 类型:`AtlasConfigWindow` +#### `public static void ShowWindow()` +- 作用:打开图集配置窗口。 + +### 类型:`EditorSpriteSaveInfo` +源码:`Packages/com.alicizax.unity.editor.extension/Editor/Postprocessor/Atlas/EditorSpriteSaveInfo.cs` + +#### `public static void ConvertToSprite(string assetPath)` +- 作用:把资源路径转换或校正为 Sprite 导入设置。 + +#### `public static void OnImportSprite(string assetPath)` +- 作用:处理 Sprite 导入。 + +#### `public static void OnDeleteSprite(string assetPath)` +- 作用:处理 Sprite 删除。 + +#### `public static void ForceGenerateAll()` +- 作用:全量生成图集。 + +#### `public static void ClearCache()` +- 作用:清理模块缓存。 + +#### `public static void MarkParentAtlasesDirty(string assetPath)` +- 作用:标记资源所在父图集为脏。 + +### 类型:`SpritePostprocessor` +- 说明:继承 `AssetPostprocessor`,作为导入事件入口,本身无额外公开调用接口。 + +## 注意事项 +- 建议原图目录与输出目录严格分离。 +- 开启自动生成时,排除目录和关键字要统一维护。 diff --git a/Client/Assets/Books/EditorExtension/AtlasPostprocessor.md.meta b/Client/Assets/Books/EditorExtension/AtlasPostprocessor.md.meta new file mode 100644 index 0000000..412c7b7 --- /dev/null +++ b/Client/Assets/Books/EditorExtension/AtlasPostprocessor.md.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: edc716e06a048f240a9f9bda8ff156ce +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Client/Assets/Books/EditorExtension/BaseTools.md b/Client/Assets/Books/EditorExtension/BaseTools.md new file mode 100644 index 0000000..9ada900 --- /dev/null +++ b/Client/Assets/Books/EditorExtension/BaseTools.md @@ -0,0 +1,87 @@ +# EditorExtension 基础工具模块手册 + +## 模块概述 +基础工具模块由 `EditorIcons` 和 `EditorToolFunctionAttribute` 两部分组成: +- `EditorIcons`:浏览、检索、预览并导出 Unity 内置编辑器图标。 +- `EditorToolFunctionAttribute`:把静态方法声明为“编辑器快捷工具”,供 `EditorQuickToolBar` 自动收集。 + +## 适用场景 +- 快速查找 Unity 内置图标。 +- 将团队常用编辑器方法集中到工具栏菜单。 +- 避免为每个工具重复创建顶级菜单。 + +## 快速上手 +### 打开图标浏览器 +- 菜单:`Tools/Extension/Editor Icons` + +### 声明工具函数 +```csharp +using AlicizaX.Editor.Extension; +using UnityEngine; + +public static class DemoEditorTools +{ + [EditorToolFunction("Demo/打印当前场景", 10)] + public static void PrintScene() + { + Debug.Log(UnityEditor.SceneManagement.EditorSceneManager.GetActiveScene().path); + } +} +``` + +## 详细使用说明 +### `EditorIcons` +- 支持关键字搜索。 +- 支持大小图标切换。 +- 支持单个导出 PNG。 +- 支持批量导出全部图标到目录。 + +### `EditorToolFunctionAttribute` +- 仅扫描静态方法。 +- 扫描时机为编辑器加载阶段。 +- 扫描结果按 `MenuOrder` 升序排列。 +- 建议被标记的方法不要带参数。 + +## 可调用 API +### 类型:`EditorIcons` +源码:`Packages/com.alicizax.unity.editor.extension/Editor/EditorIcons.cs` + +#### `public static void EditorIconsOpen()` +- 作用:打开图标浏览窗口。 +- 参数:无。 +- 返回值:`void`。 + +#### `public static string[] ico_list` +- 作用:内置图标名称列表。 +- 示例: +```csharp +foreach (var iconName in EditorIcons.ico_list) +{ + Debug.Log(iconName); +} +``` + +### 类型:`AlicizaX.Editor.Extension.EditorToolFunctionAttribute` +源码:`Packages/com.alicizax.unity.editor.extension/Editor/EditorToolFunctionAttribute.cs` + +#### `public EditorToolFunctionAttribute(string menu, int menuOrder = 0)` +- 作用:声明工具菜单路径与排序值。 +- 参数: + - `menu`:菜单路径。 + - `menuOrder`:排序值。 + +#### `public string ToolMenuPath { get; }` +- 作用:获取菜单路径。 + +#### `public int MenuOrder { get; }` +- 作用:获取排序值。 + +#### `public MethodInfo MethodInfo { get; }` +- 作用:获取实际绑定的方法信息。 + +#### `public void SetMethodInfo(MethodInfo methodInfo)` +- 作用:由收集器在扫描时写入目标方法。 + +## 集成建议 +- 推荐把所有项目编辑器命令统一声明为 `EditorToolFunctionAttribute`。 +- 图标挑选可先通过 `EditorIcons` 查名,再用于按钮和菜单项。 diff --git a/Client/Assets/Books/EditorExtension/BaseTools.md.meta b/Client/Assets/Books/EditorExtension/BaseTools.md.meta new file mode 100644 index 0000000..4cbc99d --- /dev/null +++ b/Client/Assets/Books/EditorExtension/BaseTools.md.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 3cd21cd1bc02c44449bf7b999ae7bdd4 +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Client/Assets/Books/EditorExtension/CustomToolbarItems.md b/Client/Assets/Books/EditorExtension/CustomToolbarItems.md new file mode 100644 index 0000000..5d1fe83 --- /dev/null +++ b/Client/Assets/Books/EditorExtension/CustomToolbarItems.md @@ -0,0 +1,55 @@ +# EditorExtension 自定义工具栏项目模块手册 + +## 模块概述 +`ToolBarExtension` 目录在 `Toolbar` 框架之上实现了一组项目级工具栏元素: +- `EditorQuickToolBar` +- `LocalizationDropdownField` +- `ResourceModeDropdownField` +- `SwitchSceneToolBar` +- `BuildSettingWindow`(当前源码为注释模板) + +## 适用场景 +- 快速切换语言、资源模式与场景。 +- 把项目工具统一收口到主工具栏。 +- 自动调度 `EditorToolFunctionAttribute` 标记的方法。 + +## 快速上手 +这些类型均通过 `MainToolbarElementAttribute` 自动挂载,无需手动注册。 + +## 可调用 API +### 类型:`AlicizaX.Editor.Extension.EditorQuickToolBar` +源码:`Packages/com.alicizax.unity.editor.extension/Editor/ToolBarExtension/EditorQuickToolBar.cs` +- `InitializeElement()`:初始化工具下拉按钮。 + +### 类型:`LocalizationDropdownField` +源码:`Packages/com.alicizax.unity.editor.extension/Editor/ToolBarExtension/LocalizationDropdownField.cs` +- `InitializeElement()`:初始化语言切换下拉框。 +- `InvokeOnValidateInScene()`:强制刷新场景中所有 `UXTextMeshPro` 的编辑器预览。 + +### 类型:`AlicizaX.Editor.Extension.ResourceModeDropdownField` +源码:`Packages/com.alicizax.unity.editor.extension/Editor/ToolBarExtension/ResourceModeDropdownField.cs` +- `InitializeElement()`:初始化资源模式下拉框。 + +### 类型:`SwitchSceneToolBar` +源码:`Packages/com.alicizax.unity.editor.extension/Editor/ToolBarExtension/SwitchSceneToolBar.cs` +- `InitializeElement()`:初始化场景切换按钮并监听场景切换。 + +### 类型:`BuildSettingWindow` +- 说明:当前源码整体注释,无生效公开 API,可作为未来扩展模板。 + +## 详细使用说明 +### `EditorQuickToolBar` +- 从 `EditorToolFunctionAttributeCollector.Attributes` 读取菜单项。 +- 点击菜单后通过反射执行静态方法。 + +### `LocalizationDropdownField` +- 读取语言列表并写入 `EditorPrefs`。 +- 切换后会刷新场景里的 `UXTextMeshPro` 文本预览。 + +### `ResourceModeDropdownField` +- 提供 `Editor / Offline / Host / Webgl` 四种模式。 +- 模式结果通过 `EditorPrefs` 持久化。 + +### `SwitchSceneToolBar` +- 默认扫描 `Assets/Bundles/` 与 `Assets/Scenes/` 下的场景。 +- 切换前会检查当前场景未保存状态。 diff --git a/Client/Assets/Books/EditorExtension/CustomToolbarItems.md.meta b/Client/Assets/Books/EditorExtension/CustomToolbarItems.md.meta new file mode 100644 index 0000000..c278d9e --- /dev/null +++ b/Client/Assets/Books/EditorExtension/CustomToolbarItems.md.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 19d768fd857ab3d42887f3cddd38bf37 +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Client/Assets/Books/EditorExtension/HybridCLR.md b/Client/Assets/Books/EditorExtension/HybridCLR.md new file mode 100644 index 0000000..2813b36 --- /dev/null +++ b/Client/Assets/Books/EditorExtension/HybridCLR.md @@ -0,0 +1,74 @@ +# EditorExtension HybridCLR 模块手册 + +## 模块概述 +`HybridCLR` 模块封装热更新 DLL 构建、AOT 补充 DLL 复制、热更 DLL 复制与宏开关。核心入口为 `BuildDLLCommand`。 + +## 适用场景 +- 一键编译并拷贝热更新 DLL。 +- 启用/关闭 `ENABLE_HYBRIDCLR`。 +- 输出 `.dll.bytes` 供运行时加载。 + +## 快速上手 +### 菜单 +- `HybridCLR/Tools/Define Symbols/Disable HybridCLR` +- `HybridCLR/Tools/Define Symbols/Enable HybridCLR` +- `HybridCLR/Tools/BuildAssets And CopyTo AssemblyTextAssetPath` + +### 代码 +```csharp +using UnityEditor; + +BuildDLLCommand.Enable(); +BuildDLLCommand.BuildAndCopyDlls(EditorUserBuildSettings.activeBuildTarget); +``` + +## 工作流程 +1. 通过 `Enable()` / `Disable()` 控制宏与 HybridCLR 设置。 +2. `BuildAndCopyDlls()` 编译热更程序集。 +3. `CopyAOTHotUpdateDlls()` 复制 AOT 与 HotUpdate DLL。 +4. `AssetDatabase.Refresh()` 刷新资源库。 + +## 可调用 API +源码:`Packages/com.alicizax.unity.editor.extension/Editor/HybridCLR/BuildDLLCommand.cs` + +### 类型:`BuildDLLCommand` +#### `public static string AssemblyTextAssetPath` +- 作用:目标输出目录。 + +#### `public static void Disable()` +- 作用:关闭 HybridCLR 宏与设置。 + +#### `public static void Enable()` +- 作用:启用 HybridCLR 宏与设置。 + +#### `public static void BuildAndCopyDlls()` +- 作用:按当前激活平台编译并复制 DLL。 + +#### `public static void GenerateAll()` +- 作用:执行 `PrebuildCommand.GenerateAll()`。 + +#### `public static void BuildAndCopyDlls(BuildTarget target)` +- 作用:按指定平台编译并复制 DLL。 + +#### `public static void CopyAOTHotUpdateDlls(BuildTarget target)` +- 作用:统一复制 AOT 与 HotUpdate DLL。 + +#### `public static void CopyAOTAssembliesToAssetPath()` +- 作用:复制 AOT 补充程序集到输出目录。 + +#### `public static void CopyHotUpdateAssembliesToAssetPath()` +- 作用:复制 HotUpdate 程序集到输出目录。 + +## 示例 +```csharp +[UnityEditor.MenuItem("Tools/HybridCLR/全部生成")] +public static void BuildAll() +{ + BuildDLLCommand.GenerateAll(); + BuildDLLCommand.BuildAndCopyDlls(UnityEditor.EditorUserBuildSettings.activeBuildTarget); +} +``` + +## 注意事项 +- 依赖 HybridCLR 包和相关生成脚本完整可用。 +- AOT 源目录通常需要先执行生成或构建流程后才存在。 diff --git a/Client/Assets/Books/EditorExtension/HybridCLR.md.meta b/Client/Assets/Books/EditorExtension/HybridCLR.md.meta new file mode 100644 index 0000000..5296656 --- /dev/null +++ b/Client/Assets/Books/EditorExtension/HybridCLR.md.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: efaf24517e127234183a8852e2de38db +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Client/Assets/Books/EditorExtension/ReferenceFinder.md b/Client/Assets/Books/EditorExtension/ReferenceFinder.md new file mode 100644 index 0000000..d1bcca2 --- /dev/null +++ b/Client/Assets/Books/EditorExtension/ReferenceFinder.md @@ -0,0 +1,103 @@ +# EditorExtension ReferenceFinder 引用分析模块手册 + +## 模块概述 +`ReferenceFinder` 用于分析项目资源的依赖与反向引用关系,支持缓存、树状展示和排序。 + +## 适用场景 +- 查询资源被谁引用。 +- 清理无用资源前做依赖确认。 +- 排查深层依赖链和循环引用风险。 + +## 快速上手 +```csharp +using TEngine.Editor; + +ResourceReferenceInfo.FindRef(); +``` + +## 模块结构 +- `ResourceReferenceInfo`:入口与界面层。 +- `ReferenceFinderData`:采集、缓存、状态管理。 +- `AssetTreeView`:树状显示。 +- `SortHelper`:排序逻辑。 + +## 可调用 API +### 类型:`TEngine.Editor.ReferenceFinderData` +源码:`Packages/com.alicizax.unity.editor.extension/Editor/ReferenceFinder/ReferenceFinderData.cs` + +#### 公开成员 +- `AssetState` +- `MinThreadCount` +- `allAssets` +- `assetDict` +- `CollectDependenciesInfo()` +- `ReadAssetInfo()` +- `GetAsset(string dataPath, string assetPath)` +- `ReadFromCache()` +- `UpdateAssetState(string guid)` +- `GetInfoByState(AssetState state)` +- `ClearCache()` +- `GetRefCount(AssetDescription desc, AssetDescription parentDesc)` + +#### `AssetDescription` 字段 +- `assetDependencyHashString` +- `dependencies` +- `name` +- `path` +- `references` +- `state` + +### 类型:`TEngine.Editor.ResourceReferenceInfo` +源码:`Packages/com.alicizax.unity.editor.extension/Editor/ReferenceFinder/ResourceReferenceInfo.cs` + +#### 公开成员 +- `Data` +- `needUpdateAssetTree` +- `needUpdateState` +- `selectedAssetGuid` +- `mAssetTreeView` +- `FindRef()` +- `DrawOptionBar()` + +### 类型:`AssetTreeView` +- `AssetTreeView(TreeViewState state, MultiColumnHeader multicolumnHeader)` +- `SortExpandItem()` +- `CreateDefaultMultiColumnHeaderState(float treeViewWidth, bool isDepend)` + +### 类型:`ClickColumn` +- `SortInColumn` +- `SortWithIndex` +- `ClickColumn(MultiColumnHeaderState state)` +- `SortByName()` +- `SortByPath()` + +### 类型:`DragAreaGetObject` +- `GetObjects(string meg = null)` + +### 类型:`SortHelper` +- `Init()` +- `ChangeSortType(...)` +- `SortByName()` +- `SortByPath()` +- `SortChild(...)` +- `NormalSort(...)` +- `FastSort(...)` +- `CompareWithName(...)` +- `CompareWithNameDesc(...)` +- `CompareWithPath(...)` +- `CompareWithPathDesc(...)` + +### 其他公开类型 +- `ListInfo` +- `SortType` +- `SortConfig` + +## 示例 +```csharp +var data = new TEngine.Editor.ReferenceFinderData(); +data.CollectDependenciesInfo(); +``` + +## 注意事项 +- 大项目建议优先使用缓存。 +- 资源大规模变更后建议先清缓存再重新采集。 diff --git a/Client/Assets/Books/EditorExtension/ReferenceFinder.md.meta b/Client/Assets/Books/EditorExtension/ReferenceFinder.md.meta new file mode 100644 index 0000000..2948202 --- /dev/null +++ b/Client/Assets/Books/EditorExtension/ReferenceFinder.md.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 6e2acda8e9c7959409e0c16d83cc852f +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Client/Assets/Books/EditorExtension/TexturePacker.md b/Client/Assets/Books/EditorExtension/TexturePacker.md new file mode 100644 index 0000000..308b132 --- /dev/null +++ b/Client/Assets/Books/EditorExtension/TexturePacker.md @@ -0,0 +1,42 @@ +# EditorExtension TexturePacker 模块手册 + +## 模块概述 +`TexturePacker` 提供一个轻量级编辑器窗口 `UnityTexturePackerEditor`,用于把多张纹理打包为一张大图并记录子图布局信息。 + +## 适用场景 +- 临时合并散图。 +- 验证贴图布局。 +- 做非正式生产链路的编辑器打包。 + +## 快速上手 +```csharp +UnityTexturePackerEditor.ShowWindow(); +``` + +## 可调用 API +源码:`Packages/com.alicizax.unity.editor.extension/Editor/TexturePacker/UnityTexturePackEditor.cs` + +### 类型:`UnityTexturePackerEditor` +#### `public static void ShowWindow()` +- 作用:打开 TexturePacker 窗口。 + +### 公开布局信息字段 +- `name` +- `sourcePath` +- `x` +- `y` +- `w` +- `h` +- `sourceW` +- `sourceH` + +### 公开导入备份字段 +- `path` +- `isReadable` +- `type` +- `compression` +- `maxSize` + +## 使用建议 +- 如果项目已有正式 SpriteAtlas 流程,该工具更适合临时处理和验证。 +- 执行前后应注意纹理导入配置恢复。 diff --git a/Client/Assets/Books/EditorExtension/TexturePacker.md.meta b/Client/Assets/Books/EditorExtension/TexturePacker.md.meta new file mode 100644 index 0000000..00070fd --- /dev/null +++ b/Client/Assets/Books/EditorExtension/TexturePacker.md.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: fd5b62c9141e9654ba29b499d0888dde +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Client/Assets/Books/EditorExtension/ToolbarExtender.md b/Client/Assets/Books/EditorExtension/ToolbarExtender.md new file mode 100644 index 0000000..b9dad4b --- /dev/null +++ b/Client/Assets/Books/EditorExtension/ToolbarExtender.md @@ -0,0 +1,98 @@ +# EditorExtension Toolbar 扩展框架手册 + +## 模块概述 +`Toolbar` 模块用于向 Unity 主工具栏动态注入自定义 `VisualElement` / `IMGUIContainer`,支持自动发现、排序、对齐、推荐样式、分组与用户覆盖配置。 + +## 适用场景 +- 给 Unity 顶部工具栏挂项目级入口。 +- 统一管理开发、调试、资源、场景、构建工具。 +- 让工具栏元素支持分组、显隐与个性化排序。 + +## 快速上手 +```csharp +using Paps.UnityToolbarExtenderUIToolkit; +using UnityEngine.UIElements; + +[MainToolbarElement("DemoToolbar", ToolbarAlign.Right, 100)] +public class DemoToolbarElement : IMGUIContainer +{ + public void InitializeElement() + { + onGUIHandler = () => UnityEngine.GUILayout.Label("Demo"); + } +} +``` + +## 工作机制 +1. `MainToolbar` 定位 Unity 原生 Toolbar 容器。 +2. `MainToolbarAutomaticExtender` 负责刷新与注入。 +3. 特性仓储扫描带 `MainToolbarElementAttribute` 的类型。 +4. 元素实例化后包装为 `MainToolbarElement`。 +5. 控制面板和覆盖配置负责用户态管理。 + +## 可调用 API +### 类型:`MainToolbarElementAttribute` +源码:`Packages/com.alicizax.unity.editor.extension/Editor/Toolbar/MainToolbarElementAttribute.cs` + +- `MainToolbarElementAttribute(string id, ToolbarAlign alignment = ToolbarAlign.Left, int order = 0, bool useRecommendedStyles = true)` +- `Id` +- `Alignment` +- `Order` +- `UseRecommendedStyles` + +### 类型:`MainToolbarElement` +源码:`Packages/com.alicizax.unity.editor.extension/Editor/Toolbar/MainToolbarElement.cs` + +- `Id` +- `VisualElement` +- `Alignment` +- `Order` +- `UseRecommendedStyles` +- 构造函数 `MainToolbarElement(...)` + +### 类型:`MainToolbar` +源码:`Packages/com.alicizax.unity.editor.extension/Editor/Toolbar/MainToolbar.cs` + +- `OnInitialized` +- `OnRefresh` +- `UnityToolbarRoot` +- `LeftContainer` +- `CenterContainer` +- `RightContainer` +- `PlayModeButtonsContainer` +- `IsAvailable` + +### 类型:`MainToolbarAutomaticExtender` +源码:`Packages/com.alicizax.unity.editor.extension/Editor/Toolbar/MainToolbarAutomaticExtender.cs` + +- `OnRefresh` +- `OnAddedCustomContainersToToolbar` + +### 类型:`MainToolbarControlPanelWindow` +源码:`Packages/com.alicizax.unity.editor.extension/Editor/Toolbar/ControlPanelWindow/MainToolbarControlPanelWindow.cs` + +- `OpenWindow()` + +### 类型:`MainToolbarElementController` +- `Id` +- `ControlledVisualElement` +- `HoldsAGroup` +- `HoldsANativeElement` + +### 其他重要公开类型 +- `ToolbarAlign` +- `GroupDefinition` +- `GroupElement` +- `ScriptableGroupDefinition` +- `MainToolbarElementDropdownAttribute` +- `MainToolbarElementOverride` +- `RecommendedStyle` 及派生类 +- `Variable` / `SerializableVariable` 等变量序列化类型 + +## 打开控制面板 +- 菜单:`Tools/Extension/Toolbar/Main Toolbar Control Panel` +- 代码:`MainToolbarControlPanelWindow.OpenWindow();` + +## 注意事项 +- `Id` 应稳定且全局唯一。 +- 自定义元素建议在 `InitializeElement()` 中做依赖判空。 diff --git a/Client/Assets/Books/EditorExtension/ToolbarExtender.md.meta b/Client/Assets/Books/EditorExtension/ToolbarExtender.md.meta new file mode 100644 index 0000000..fe32e3e --- /dev/null +++ b/Client/Assets/Books/EditorExtension/ToolbarExtender.md.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 7f5ebb19e8045f24c8f0de3a2218fabb +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Client/Assets/Books/Framework.meta b/Client/Assets/Books/Framework.meta new file mode 100644 index 0000000..62b6000 --- /dev/null +++ b/Client/Assets/Books/Framework.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 8e2de7df9f6693246b6ce82a1753c975 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Client/Assets/Books/Framework/Core.meta b/Client/Assets/Books/Framework/Core.meta new file mode 100644 index 0000000..5b5f789 --- /dev/null +++ b/Client/Assets/Books/Framework/Core.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: cbb4b30bf931b134094b7d4d196ce68f +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Client/Assets/Books/Framework/Core/Event.md b/Client/Assets/Books/Framework/Core/Event.md new file mode 100644 index 0000000..a40da97 --- /dev/null +++ b/Client/Assets/Books/Framework/Core/Event.md @@ -0,0 +1,182 @@ +# Event + +## 模块概述 + +Event 模块提供轻量级、低分配的结构体事件系统,适合模块解耦、状态广播、UI/玩法/流程间通知,以及编辑器内订阅行为监控。 + +设计重点: + +- 使用 `struct` 事件降低 GC 压力 +- 通过泛型容器按事件类型隔离订阅列表 +- 使用调试注册表记录订阅、发布、扩容等行为 + +## 快速开始 + +1. 定义 `struct` 事件并实现 `IEventArgs` +2. 调用 `EventBus.Subscribe()` 订阅 +3. 调用 `EventBus.Publish(in evt)` 发布 +4. 在对象失活或销毁时释放订阅句柄 + +```csharp +using AlicizaX; + +public struct PlayerLevelUpEvent : IEventArgs +{ + public int NewLevel; +} + +public sealed class EventQuickStart +{ + private EventRuntimeHandle _handle; + + public void Initialize() + { + _handle = EventBus.Subscribe(OnLevelUp); + EventBus.Publish(new PlayerLevelUpEvent { NewLevel = 10 }); + } + + private void OnLevelUp(PlayerLevelUpEvent evt) + { + Log.Info($"Level => {evt.NewLevel}"); + } +} +``` + +## 架构说明 + +```text +业务代码 + ├─ EventBus.Subscribe() + ├─ EventBus.Publish() + └─ EventBus.Clear() + ↓ + EventContainer + ↓ + EventRuntimeHandle / 订阅槽 + ↓ + EventDebugRegistry +``` + +## 核心类型说明 + +### `IEventArgs` + +- 所有事件载体的标记接口 +- 事件类型必须是 `struct` + +### `EventBus` + +公开能力: + +- `Subscribe(Action handler)` +- `Publish(in T evt)` +- `GetSubscriberCount()` +- `EnsureCapacity(int capacity)` +- `Clear()` + +### `EventRuntimeHandle` + +- 表示一次已建立的订阅 +- 用于解绑或自动管理订阅生命周期 + +### `EventInitialSize` + +- 用于为某类事件声明初始订阅容量 +- 高频事件建议预热容量 + +## API 参考 + +### `EventBus.Subscribe(Action handler) where T : struct, IEventArgs` + +- 必填参数:`handler` +- 返回值:`EventRuntimeHandle` +- 泛型约束:`T : struct, IEventArgs` +- 说明:订阅某种事件 +- 注意:建议保存返回句柄,便于后续解绑 + +### `EventBus.Publish(in T evt) where T : struct, IEventArgs` + +- 必填参数:`evt` +- 返回值:无 +- 说明:同步发布事件 +- 注意:回调会立即执行,发布链路中不要写过重逻辑 + +### `EventBus.GetSubscriberCount()` + +- 返回值:`int` +- 说明:返回当前事件订阅数量 + +### `EventBus.EnsureCapacity(int capacity)` + +- 必填参数:`capacity` +- 返回值:无 +- 说明:预热内部容器容量 + +### `EventBus.Clear()` + +- 返回值:无 +- 说明:清空某类事件订阅 + +## 完整使用示例 + +```csharp +using AlicizaX; +using UnityEngine; + +public struct CoinChangedEvent : IEventArgs +{ + public int CurrentValue; + public int Delta; +} + +public sealed class EventExample : MonoBehaviour +{ + private EventRuntimeHandle _handle; + private int _coin; + + private void OnEnable() + { + EventBus.EnsureCapacity(8); + _handle = EventBus.Subscribe(OnCoinChanged); + } + + private void Update() + { + if (Input.GetKeyDown(KeyCode.C)) + { + _coin += 100; + EventBus.Publish(new CoinChangedEvent + { + CurrentValue = _coin, + Delta = 100 + }); + } + } + + private void OnDisable() + { + _handle.Dispose(); + } + + private void OnCoinChanged(CoinChangedEvent evt) + { + Debug.Log($"Coin changed: {evt.CurrentValue} (+{evt.Delta})"); + } +} +``` + +## 最佳实践 + +- 事件体尽量小,避免塞入大对象 +- 高频事件预热容量 +- 订阅与释放时机成对出现 + +## 常见错误 + +- 事件类型定义成 `class` +- 只订阅不解绑 + +## 性能注意事项 + +- `Publish` 为同步执行,避免重逻辑 +- 结合编辑器事件监视器检查扩容次数和订阅抖动 diff --git a/Client/Assets/Books/Framework/Core/Event.md.meta b/Client/Assets/Books/Framework/Core/Event.md.meta new file mode 100644 index 0000000..306742f --- /dev/null +++ b/Client/Assets/Books/Framework/Core/Event.md.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: c7741236daa2b2949b67f42a855ae3e3 +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Client/Assets/Books/Framework/Core/Foundation.md b/Client/Assets/Books/Framework/Core/Foundation.md new file mode 100644 index 0000000..5f92fe1 --- /dev/null +++ b/Client/Assets/Books/Framework/Core/Foundation.md @@ -0,0 +1,186 @@ +# Foundation + +## 模块概述 + +Foundation 是整个框架的运行时基础层,负责: + +- 应用根节点初始化 +- 服务容器与作用域管理 +- 公共异常、内存池、数据结构 +- 全局访问入口与运行时基础配置 +- 模块动态绑定与启动时配置注入 + +如果把整个包看成一棵树,Foundation 就是树根;其他模块都建立在它提供的服务世界之上。 + +## 快速开始 + +### 最少步骤 + +1. 在启动场景挂载 `RootModule` +2. 让各功能模块组件与 `RootModule` 位于同一生命周期体系内 +3. 使用 `GameApp` 或 `AppServices` 获取模块服务 + +### 最小示例 + +```csharp +using AlicizaX; +using UnityEngine; + +public sealed class FoundationQuickStart : MonoBehaviour +{ + private void Start() + { + RootModule root = GameApp.Base; + root.FrameRate = 120; + root.GameSpeed = 1f; + + Debug.Log($"Has world: {AppServices.HasWorld}"); + } +} +``` + +## 架构说明 + +```text +RootModule + └─ AppServiceRoot + └─ ServiceWorld + ├─ App Scope + ├─ Scene Scope + └─ Gameplay Scope +``` + +### 各部分职责 + +- `RootModule`:全局根节点,负责运行参数和全局清理 +- `AppServiceRoot`:创建 `ServiceWorld` 并驱动 Tick +- `ServiceWorld`:持有多个 `ServiceScope` +- `ServiceScope`:某个作用域下的服务容器 +- `AppServices`:统一静态访问门面 +- `GameApp`:高频模块快捷入口 + +## 核心类型说明 + +### `RootModule` + +关键属性: + +- `FrameRate`:目标帧率 +- `GameSpeed`:时间缩放 +- `IsGamePaused`:是否暂停 +- `RunInBackground`:后台运行 +- `NeverSleep`:禁止休眠 + +关键方法: + +- `PauseGame()` +- `ResumeGame()` +- `ResetNormalGameSpeed()` + +### `AppServices` + +常用成员: + +- `HasWorld` / `HasScene` / `HasGameplay` +- `App` / `Scene` / `Gameplay` +- `EnsureWorld()` / `EnsureScene()` / `EnsureGameplay()` +- `Require()` +- `TryGet(out T service)` +- `Shutdown()` + +### `GameApp` + +公开入口: + +- `Base` +- `Audio` +- `Localization` +- `ObjectPool` +- `Procedure` +- `Resource` +- `Scene` +- `Timer` +- `UI` + +### `ModuleDynamicBind` + +作用: + +- 在非 Editor 环境中读取 `ModuleDynamicBindInfo` +- 对 `ResourceComponent`、`DebuggerComponent`、`LocalizationComponent` 注入启动配置 + +## API 参考 + +### `AppServices.Require() where T : class, IService` + +- 参数:无 +- 返回值:`T` +- 泛型约束:`class, IService` +- 说明:强制获取已注册服务 +- 异常:服务世界未创建或服务未注册时失败 + +### `AppServices.TryGet(out T service) where T : class, IService` + +- 输出参数:`service` +- 返回值:`bool` +- 说明:安全查询服务 + +### `AppServices.EnsureWorld(int appScopeOrder = ServiceDomainOrder.App)` + +- 可选参数:`appScopeOrder` +- 返回值:`ServiceWorld` + +### `RootModule.PauseGame()` + +- 返回值:无 +- 说明:保存当前速度并暂停 + +### `RootModule.ResumeGame()` + +- 返回值:无 +- 说明:恢复暂停前速度 + +## 完整使用示例 + +```csharp +using AlicizaX; +using UnityEngine; + +public sealed class FoundationExample : MonoBehaviour +{ + private void Start() + { + RootModule root = GameApp.Base; + root.FrameRate = 120; + root.RunInBackground = true; + root.NeverSleep = true; + } + + private void Update() + { + if (Input.GetKeyDown(KeyCode.P)) + { + if (GameApp.Base.IsGamePaused) + GameApp.Base.ResumeGame(); + else + GameApp.Base.PauseGame(); + } + } +} +``` + +## 最佳实践 + +- 只保留一个 `RootModule` +- 首场景统一挂载框架组件 +- 常用服务走 `GameApp`,扩展服务走 `AppServices` + +## 常见错误 + +- 服务未注册就访问 +- 多个 `RootModule` 并存 + +## 性能注意事项 + +- 不要频繁重建服务世界 +- 高频访问模块时优先使用 `GameApp` 的缓存入口 diff --git a/Client/Assets/Books/Framework/Core/Foundation.md.meta b/Client/Assets/Books/Framework/Core/Foundation.md.meta new file mode 100644 index 0000000..3b4c434 --- /dev/null +++ b/Client/Assets/Books/Framework/Core/Foundation.md.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 91578f5be03b1de4dbac802cab59cb24 +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Client/Assets/Books/Framework/Core/GameObjectPool.md b/Client/Assets/Books/Framework/Core/GameObjectPool.md new file mode 100644 index 0000000..df9cacf --- /dev/null +++ b/Client/Assets/Books/Framework/Core/GameObjectPool.md @@ -0,0 +1,98 @@ +# GameObjectPool + +## 模块概述 + +GameObjectPool 面向场景对象与预制体实例复用。它基于 `PoolConfig` 决定某个资源是否入池、如何匹配、如何预热,并可通过 `Resources` 或 AssetBundle/YooAsset 进行加载。 + +## 快速开始 + +1. 场景挂载 `GameObjectPoolManager` +2. 准备 `PoolConfigScriptableObject` +3. 确保 `ResourceComponent` 已注册 +4. 使用 `PreloadAsync` / `GetGameObjectAsync` / `Release` + +## 架构说明 + +```text +GameObjectPoolManager + ├─ PoolConfigScriptableObject + ├─ PoolConfig + ├─ RuntimePrefabPool + └─ IResourceLoader +``` + +## 核心类与接口 + +### `GameObjectPoolManager` + +主要能力: + +- `GetGameObject(...)` +- `GetGameObjectAsync(...)` +- `Preload(...)` +- `PreloadAsync(...)` +- `Release(GameObject gameObject)` +- `ClearAllPools()` +- `ForceCleanup()` + +### `PoolConfig` + +决定: + +- 资源匹配方式 +- 预热数量 +- 清理与过期策略 +- 使用哪种资源加载器 + +## API 参考 + +### `GetGameObject(string assetPath, Transform parent = null)` + +- 必填参数:`assetPath` +- 可选参数:`parent` +- 返回值:`GameObject` +- 异常:初始化未完成时会抛异常 + +### `GetGameObjectAsync(string assetPath, Transform parent = null, CancellationToken cancellationToken = default)` + +- 必填参数:`assetPath` +- 可选参数:`parent` +- 可选参数:`cancellationToken` +- 返回值:`UniTask` + +### `Release(GameObject gameObject)` + +- 必填参数:`gameObject` +- 返回值:无 + +## 完整使用示例 + +```csharp +using AlicizaX; +using Cysharp.Threading.Tasks; +using UnityEngine; + +public sealed class GameObjectPoolExample : MonoBehaviour +{ + [SerializeField] private GameObjectPoolManager poolManager; + + private async UniTaskVoid Start() + { + await poolManager.PreloadAsync("Assets/Bundles/Effects/Fx_Hit.prefab", 3); + + GameObject fx = await poolManager.GetGameObjectAsync("Assets/Bundles/Effects/Fx_Hit.prefab", transform); + await UniTask.Delay(1000); + poolManager.Release(fx); + } +} +``` + +## 最佳实践 + +- 特效、子弹、临时场景装饰物优先走该模块 +- 常见资源在初始化阶段预热 + +## 常见错误 + +- 在初始化完成前调用同步接口 +- 对池对象手动 `Destroy` diff --git a/Client/Assets/Books/Framework/Core/GameObjectPool.md.meta b/Client/Assets/Books/Framework/Core/GameObjectPool.md.meta new file mode 100644 index 0000000..76a70f4 --- /dev/null +++ b/Client/Assets/Books/Framework/Core/GameObjectPool.md.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: fb63f330ed43037448a8c927045727c5 +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Client/Assets/Books/Framework/Core/ObjectPool.md b/Client/Assets/Books/Framework/Core/ObjectPool.md new file mode 100644 index 0000000..71121e8 --- /dev/null +++ b/Client/Assets/Books/Framework/Core/ObjectPool.md @@ -0,0 +1,204 @@ +# ObjectPool + +## 模块概述 + +ObjectPool 是面向纯 C# 对象的通用对象池模块,适合缓存临时数据对象、资源包装对象、UI/资源扩展辅助对象和调试器内部缓存对象。 + +它与 `MemoryPool` 的区别: + +- `MemoryPool` 更偏向单个对象快速申请/释放 +- `ObjectPool` 更偏向带策略的池管理,支持容量、过期、优先级、锁定等行为 + +## 快速开始 + +1. 场景挂载 `ObjectPoolComponent` +2. 定义继承 `ObjectBase` 的对象类型 +3. 使用 `GameApp.ObjectPool.CreatePool()` +4. `Register` / `Spawn` / `Unspawn` + +```csharp +using AlicizaX; +using AlicizaX.ObjectPool; + +public sealed class CacheItem : ObjectBase +{ + public string Key; + + public override void Clear() + { + Key = null; + } +} + +public sealed class ObjectPoolQuickStart +{ + public void Run() + { + var pool = GameApp.ObjectPool.CreatePool(ObjectPoolCreateOptions.Single("Cache")); + CacheItem item = MemoryPool.Acquire(); + item.Key = "A"; + pool.Register(item, false); + + CacheItem spawned = pool.Spawn(); + pool.Unspawn(spawned); + } +} +``` + +## 架构说明 + +```text +ObjectPoolComponent + └─ ObjectPoolService + ├─ IObjectPool + ├─ ObjectPoolBase + └─ ObjectBase +``` + +## 核心类型说明 + +### `IObjectPoolService` + +负责: + +- 创建对象池 +- 查询对象池 +- 销毁对象池 +- 统一释放未使用对象 + +### `IObjectPool` + +常用公开方法: + +- `Register` +- `Spawn` +- `Unspawn` +- `SetLocked` +- `SetPriority` +- `Release` +- `ReleaseAllUnused` + +### `ObjectBase` + +- 池内对象基类 +- 需要正确实现 `Clear()` + +### `ObjectPoolCreateOptions` + +主要参数: + +- `name` +- `allowMultiSpawn` +- `autoReleaseInterval` +- `capacity` +- `expireTime` +- `priority` + +## API 参考 + +### `IObjectPoolService.CreatePool(ObjectPoolCreateOptions options = default) where T : ObjectBase` + +- 可选参数:`options` +- 返回值:`IObjectPool` +- 泛型约束:`T : ObjectBase` + +### `IObjectPool.Register(T obj, bool spawned)` + +- 必填参数:`obj` +- 必填参数:`spawned` +- 返回值:无 + +### `IObjectPool.Spawn()` + +- 返回值:`T` +- 说明:取出一个可用对象;无可用对象时通常返回 `null` + +### `IObjectPool.Unspawn(T obj)` + +- 必填参数:`obj` +- 返回值:无 + +### `IObjectPool.ReleaseAllUnused()` + +- 返回值:无 +- 说明:释放当前池中未使用对象 + +## 完整使用示例 + +```csharp +using AlicizaX; +using AlicizaX.ObjectPool; +using UnityEngine; + +public sealed class DamageTextData : ObjectBase +{ + public int Value; + public Color Color; + + public void Init(int value, Color color) + { + Value = value; + Color = color; + } + + public override void Clear() + { + Value = 0; + Color = default; + } +} + +public sealed class ObjectPoolExample : MonoBehaviour +{ + private IObjectPool _pool; + + private void Start() + { + _pool = GameApp.ObjectPool.CreatePool( + new ObjectPoolCreateOptions( + name: "DamageText", + allowMultiSpawn: false, + autoReleaseInterval: 30f, + capacity: 64, + expireTime: 120f, + priority: 0)); + + for (int i = 0; i < 10; i++) + { + DamageTextData data = MemoryPool.Acquire(); + data.Init(i, Color.red); + _pool.Register(data, false); + } + } + + public void ShowDamage(int damage) + { + DamageTextData data = _pool.Spawn(); + if (data == null) + { + data = MemoryPool.Acquire(); + } + + data.Init(damage, Color.yellow); + Debug.Log($"Damage => {data.Value}"); + _pool.Unspawn(data); + } +} +``` + +## 最佳实践 + +- 对象应保持“可完全重置” +- 通过 `ObjectPoolCreateOptions` 明确池策略 +- 大量短生命周期对象优先考虑对象池 + +## 常见错误 + +- 把 `GameObject` 放入该池 +- `Clear()` 未重置字段 +- 未区分 `Register(..., true/false)` + +## 性能注意事项 + +- 大对象池请配置 `capacity` 和 `expireTime` +- 过多小池会增加管理成本 diff --git a/Client/Assets/Books/Framework/Core/ObjectPool.md.meta b/Client/Assets/Books/Framework/Core/ObjectPool.md.meta new file mode 100644 index 0000000..884321d --- /dev/null +++ b/Client/Assets/Books/Framework/Core/ObjectPool.md.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: eaf9c0f1f6ac7e241b6b95a9c1a5b6a4 +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Client/Assets/Books/Framework/Editor.meta b/Client/Assets/Books/Framework/Editor.meta new file mode 100644 index 0000000..75ab518 --- /dev/null +++ b/Client/Assets/Books/Framework/Editor.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: dabe76bff0e82ac49acbce538ac31bc2 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Client/Assets/Books/Framework/Editor/EditorTools.md b/Client/Assets/Books/Framework/Editor/EditorTools.md new file mode 100644 index 0000000..5b34841 --- /dev/null +++ b/Client/Assets/Books/Framework/Editor/EditorTools.md @@ -0,0 +1,157 @@ +# EditorTools + +## 模块概述 + +EditorTools 是框架的编辑器侧配套,覆盖 Inspector、自定义窗口、配置工具与代码生成流程,目的是降低模块接入与排查成本。 + +与 UI 最相关的部分是: + +- `UISettingEditorWindow` +- `UIGenerateQuick` +- `UIScriptGeneratorHelper` +- `UIGenerateConfiguration` + +它们共同组成了 **UIHolder 自动生成工作流**。 + +## 快速开始 + +### UI 绑定工具的基本使用流程 + +1. 打开 UI 生成配置窗口,配置 UI 规则 +2. 把 UI 预制体放在配置的 `UIPrefabRootPath` 目录下 +3. 按命名规范布置需要绑定的节点 +4. 选中 UI 预制体根节点 +5. 执行菜单 `GameObject/UI生成绑定` +6. 等待脚本编译完成 +7. 生成器自动把 `XXXHolder` 挂回预制体,并回填控件字段 + +## UIHolder 自动生成说明 + +### 1. `UIHolder` 的定位 + +在本框架中: + +- `UIHolder` 指的是继承自 `UIHolderObjectBase` 的绑定类 +- 它主要用于保存控件引用和资源标签 +- 它不是主要的业务逻辑承载类 + +推荐方式: + +- **手写 Window / Widget 逻辑** +- **自动生成 Holder 绑定类** + +### 2. 使用哪个工具生成 + +核心入口: + +- 菜单:`GameObject/UI生成绑定` + +对应代码入口位于: + +- `Packages/com.alicizax.unity.framework/Editor/UI/GenerateWindow/UIGenerateQuick.cs:1` + +当你执行该菜单时,生成器会: + +- 检查当前选中的对象是否是 prefab +- 读取 `UIGenerateConfiguration` +- 校验 prefab 是否位于允许的 UI 根路径下 +- 分析节点名并推断组件类型 +- 输出 `UIHolder` 脚本 +- 在脚本编译后自动挂到 prefab 上 +- 自动把字段与节点/组件引用绑定完成 + +### 3. 命名规则如何影响生成 + +生成器会结合: + +- `UIElementRegexConfigs` +- `ComCheckSplitName` +- `ComCheckEndName` +- `ArrayComSplitName` + +来识别节点绑定信息。 + +例如默认配置会把: + +- `Btn#Close@` 识别为按钮字段 +- `Text#Title@` 识别为文本字段 +- `Img#Icon@` 识别为图片字段 + +### 4. 生成结果是什么 + +生成出的 Holder 类通常: + +- 继承 `UIHolderObjectBase` +- 自动带有 `UIResAttribute` +- 自动写入资源路径常量 +- 自动生成字段与公开访问器 + +这使得 UI 逻辑层可以直接通过泛型 Holder 访问控件,而无需再手动查找。 + +## 相关工具与职责 + +### `UISettingEditorWindow` + +作用: + +- 维护 UI 生成配置 +- 配置输出目录、命名空间、Prefab 根路径、资源加载方式 + +### `UIGenerateQuick` + +作用: + +- 提供快速菜单入口 +- 校验当前对象是否可生成 + +### `UIScriptGeneratorHelper` + +作用: + +- 扫描节点 +- 推断字段名和组件类型 +- 生成代码 +- 在脚本重载后自动挂载脚本并绑定字段 + +### `UIGenerateConfiguration` + +作用: + +- 保存生成规则与路径设置 +- 支持不同项目或不同目录使用不同配置 + +## 使用示例 + +```csharp +// 推荐流程: +// 1. 设计 InventoryItem.prefab +// 2. 选中 prefab 根节点 +// 3. 执行 “GameObject/UI生成绑定” +// 4. 得到 InventoryItemHolder : UIHolderObjectBase +// 5. 在业务代码中编写: +// public sealed class InventoryItemWidget : UIWidget { ... } +``` + +## 最佳实践 + +- 把 UI 生成规则纳入项目模板,避免每个模块自行定义一套命名方式 +- Holder 文件视为“生成产物”,不要手工长期维护 +- 如果修改了 prefab 绑定节点,重新执行一次生成工具 +- 逻辑类和生成类分离,减少合并冲突 + +## 常见错误 + +### 预制体不在配置的 UI 根路径下 + +- 现象:执行生成工具无结果 +- 原因:`UIGenerateQuick.CheckCanGenerate` 校验失败 + +### 修改了预制体但没重新生成 Holder + +- 现象:字段缺失或控件引用错误 +- 处理:重新执行 `GameObject/UI生成绑定` + +### 手动改了生成类名称或命名空间 + +- 现象:逻辑类泛型类型失配 +- 处理:尽量不要手改生成文件,必要时同步更新生成配置与逻辑类引用 diff --git a/Client/Assets/Books/Framework/Editor/EditorTools.md.meta b/Client/Assets/Books/Framework/Editor/EditorTools.md.meta new file mode 100644 index 0000000..80251a8 --- /dev/null +++ b/Client/Assets/Books/Framework/Editor/EditorTools.md.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 0c080bcc065201840afe739351071504 +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Client/Assets/Books/Framework/Plugins.meta b/Client/Assets/Books/Framework/Plugins.meta new file mode 100644 index 0000000..6e19e92 --- /dev/null +++ b/Client/Assets/Books/Framework/Plugins.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 4faf052fe9676814a83fd3638d12e801 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Client/Assets/Books/Framework/Plugins/Plugins.md b/Client/Assets/Books/Framework/Plugins/Plugins.md new file mode 100644 index 0000000..ecc35af --- /dev/null +++ b/Client/Assets/Books/Framework/Plugins/Plugins.md @@ -0,0 +1,54 @@ +# Plugins + +## 模块概述 + +`Plugins` 目录保存框架依赖的第三方程序集与源码生成插件,它们补充了框架运行时能力、兼容层与自动注册逻辑。 + +## 快速开始 + +通常不需要业务层主动初始化本模块。开发者需要做的是: + +1. 确认 Unity 正常导入 DLL +2. 不随意删除或移动 `Plugins` 目录文件 +3. 在 UI / Event 自动注册异常时优先检查生成器 DLL 是否生效 + +## 架构说明 + +```text +Plugins + ├─ XLog.dll + ├─ SharpZipLib + ├─ System.Memory / Buffers / Unsafe + ├─ EventSourceGenerator.dll + └─ UISourceGenerator.dll +``` + +## 主要内容 + +- `XLog.dll`:日志能力支持 +- `ICSharpCode.SharpZipLib.dll`:压缩/解压相关能力 +- `System.Buffers.dll`、`System.Memory.dll`、`System.Runtime.CompilerServices.Unsafe.dll`:底层兼容依赖 +- `EventSourceGenerator.dll`:事件源码生成插件 +- `UISourceGenerator.dll`:UI 源码生成插件 + +## API / 作用说明 + +### `EventSourceGenerator.dll` + +- 作用:辅助事件相关代码生成 + +### `UISourceGenerator.dll` + +- 作用:辅助 UI 元数据与资源绑定注册 + +## 完整使用示例 + +```csharp +// 本模块通常不直接由业务代码调用。 +// 若出现 UI/Event 自动注册异常,请先检查对应 DLL 在 Unity 中是否正常导入。 +``` + +## 最佳实践 + +- 锁定插件版本 +- 升级 Unity 后优先验证生成器与 IL2CPP 打包链路 diff --git a/Client/Assets/Books/Framework/Plugins/Plugins.md.meta b/Client/Assets/Books/Framework/Plugins/Plugins.md.meta new file mode 100644 index 0000000..711c776 --- /dev/null +++ b/Client/Assets/Books/Framework/Plugins/Plugins.md.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 2052075fd72f78349bc745789185c70d +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Client/Assets/Books/Framework/QuickStart.md b/Client/Assets/Books/Framework/QuickStart.md new file mode 100644 index 0000000..06f378b --- /dev/null +++ b/Client/Assets/Books/Framework/QuickStart.md @@ -0,0 +1,134 @@ +# Aliciza X Framework 快速入口 + +## 1. 框架简介 + +`com.alicizax.unity.framework` 是一套面向 Unity 项目的模块化基础框架,围绕 **根节点统一启动、服务容器统一获取、运行时模块按需组合** 的设计思想构建。 +框架通过 `RootModule` 建立全局运行时入口,通过 `AppServices` / `GameApp` 暴露常用服务访问点,再由 UI、资源、音频、本地化、场景、流程、池化、定时器等模块分别承载具体业务能力。 + +### 核心设计理念 + +- **组合优先**:各模块以组件或服务形式注册,按项目需要启用。 +- **职责分层**:区分 `Core / Runtime / Editor / Plugins`,运行时与编辑器逻辑边界清晰。 +- **统一入口**:通过 `GameApp`、服务定位与根模块生命周期减少业务侧样板代码。 +- **面向工程化**:覆盖资源加载、UI 绑定、流程切换、调试监视、池化复用等常见项目基础设施。 + +## 2. 推荐阅读顺序 + +1. [Foundation](./Core/Foundation.md) +2. [Resource](./Runtime/Resource.md) +3. [Timer](./Runtime/Timer.md) +4. [UI](./Runtime/UI.md) +5. 其余业务模块 + +## 3. 模块目录 + +### Core + +- [Foundation](./Core/Foundation.md):根模块、服务容器、通用基础设施与核心抽象。 +- [Event](./Core/Event.md):结构体事件总线、事件订阅与调试监视。 +- [ObjectPool](./Core/ObjectPool.md):通用对象池与可复用实例管理。 +- [GameObjectPool](./Core/GameObjectPool.md):面向预制体/场景对象的 GameObject 级池化系统。 + +### Runtime + +- [Audio](./Runtime/Audio.md):音频播放、分组音量、播放句柄与运行时控制。 +- [Debugger](./Runtime/Debugger.md):运行时调试面板、监控入口与调试扩展。 +- [Localization](./Runtime/Localization.md):本地化表、语言切换与文本刷新机制。 +- [Procedure](./Runtime/Procedure.md):流程/状态节点切换与启动流程组织。 +- [Resource](./Runtime/Resource.md):资源系统封装、包初始化、资源加载与实例化。 +- [Scene](./Runtime/Scene.md):场景加载、场景域切换与场景生命周期协作。 +- [Timer](./Runtime/Timer.md):统一定时器服务、延时/循环/取消与时间驱动逻辑。 +- [UI](./Runtime/UI.md):UI 窗口、Widget、Holder 绑定、层级管理与动画过渡。 + +### Editor + +- [EditorTools](./Editor/EditorTools.md):Inspector、事件监视器、UI 绑定生成器与本地化编辑工具。 + +### Plugins + +- [Plugins](./Plugins/Plugins.md):第三方程序集、代码生成器与插件职责说明。 + +## 4. 整体协作关系 + +```text +RootModule +└── AppServiceRoot + └── AppServices / GameApp + ├── Resource + ├── Timer + ├── UI + ├── Audio + ├── Localization + ├── Procedure + ├── ObjectPool / GameObjectPool + └── Scene +``` + +### 常见依赖关系 + +- `UI` 通常依赖 `Resource` 加载界面资源,并依赖 `Timer` 驱动过渡或延迟行为。 +- `Scene` 常与 `Resource` 协作处理场景包加载,与 `Procedure` 协作组织关卡切换。 +- `Localization` 常与 `UI` 或 `UX` 文本组件协作,负责语言刷新。 +- `GameObjectPool` 常与 `Resource` 协作完成预制体异步加载与回收复用。 + +## 5. 最小接入步骤 + +### 场景基础组件 + +推荐在启动场景中至少挂载: + +- `RootModule` +- `ResourceComponent` +- `TimerComponent` +- `UIComponent` + +### 启动顺序建议 + +```text +RootModule -> ResourceComponent -> TimerComponent -> UIComponent -> 其他业务模块 +``` + +### 最小示例 + +```csharp +using AlicizaX; +using AlicizaX.Resource.Runtime; +using Cysharp.Threading.Tasks; +using UnityEngine; + +public sealed class BootstrapExample : MonoBehaviour +{ + private async void Start() + { + IResourceService resourceService = GameApp.Resource; + await resourceService.InitPackageAsync(); + await GameApp.UI.ShowUI(); + } +} +``` + +## 6. 常见问题 + +### `GameApp.xxx` 为空 + +- 对应模块组件尚未挂载或未完成初始化。 +- `RootModule` 尚未建立根服务容器。 +- 获取时机早于模块注册阶段。 + +### UI 无法打开 + +- `UIComponent` 未配置 `UIRoot`。 +- UI 资源路径或 `UIResAttribute` 配置错误。 +- UI 绑定代码未生成,或 `UIWidget` 泛型未引用正确的 Holder 类型。 + +### 资源加载失败 + +- 资源包尚未初始化。 +- 资源路径与包内地址不一致。 +- 运行环境缺少对应包或依赖资源。 + +## 7. 下一步 + +- 从 [Foundation](./Core/Foundation.md) 理解根模块与服务注册方式。 +- 从 [Resource](./Runtime/Resource.md) 理解资源系统初始化与加载链路。 +- 从 [UI](./Runtime/UI.md) 理解 UIHolder 自动生成、`UIWidget` 绑定与窗口生命周期。 diff --git a/Client/Assets/Books/Framework/QuickStart.md.meta b/Client/Assets/Books/Framework/QuickStart.md.meta new file mode 100644 index 0000000..1de7699 --- /dev/null +++ b/Client/Assets/Books/Framework/QuickStart.md.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: edb42bd58f70a29419fef9babc8b6152 +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Client/Assets/Books/Framework/Runtime.meta b/Client/Assets/Books/Framework/Runtime.meta new file mode 100644 index 0000000..95b786c --- /dev/null +++ b/Client/Assets/Books/Framework/Runtime.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 6296361d31a52a045948fe1d9a074f6e +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Client/Assets/Books/Framework/Runtime/Audio.md b/Client/Assets/Books/Framework/Runtime/Audio.md new file mode 100644 index 0000000..f063340 --- /dev/null +++ b/Client/Assets/Books/Framework/Runtime/Audio.md @@ -0,0 +1,94 @@ +# Audio + +## 模块概述 + +Audio 模块负责统一音频分类、音量控制、音频池与播放行为。它把音频拆分为音乐、音效、UI 音效、语音等类别,并支持使用 `AudioMixer` 做混音控制。 + +## 快速开始 + +1. 场景挂载 `AudioComponent` +2. 配置 `AudioMixer`、实例根节点与 `AudioGroupConfig` +3. 通过 `GameApp.Audio` 播放音频 + +```csharp +using AlicizaX; +using AlicizaX.Audio.Runtime; + +public sealed class AudioQuickStart +{ + public void PlayClick() + { + GameApp.Audio.Play(AudioType.UISound, "Audio/UI/Click"); + } +} +``` + +## 架构说明 + +```text +AudioComponent + └─ AudioService + ├─ AudioCategory + ├─ AudioAgent + ├─ AudioGroupConfig + └─ AudioClipPool +``` + +## 核心类与接口 + +### `IAudioService` + +主要属性: + +- `Volume` +- `Enable` +- `MusicVolume` / `SoundVolume` / `UISoundVolume` / `VoiceVolume` +- `MusicEnable` / `SoundEnable` / `UISoundEnable` / `VoiceEnable` +- `AudioMixer` +- `InstanceRoot` + +主要方法: + +- `Initialize(...)` +- `Play(...)` +- `Stop(...)` +- `StopAll(...)` +- `PutInAudioPool(...)` +- `CleanSoundPool()` + +## API 参考 + +### `Initialize(AudioGroupConfig[] audioGroupConfigs, Transform instanceRoot = null, AudioMixer audioMixer = null)` + +- 必填参数:`audioGroupConfigs` +- 可选参数:`instanceRoot` +- 可选参数:`audioMixer` +- 返回值:无 + +### `Play(AudioType type, string path, bool bLoop = false, float volume = 1.0f, bool bAsync = false, bool bInPool = false)` + +- 必填参数:`type` +- 必填参数:`path` +- 其余均为可选参数 +- 返回值:`AudioAgent` + +## 完整使用示例 + +```csharp +using AlicizaX; +using AlicizaX.Audio.Runtime; + +public sealed class AudioExample +{ + public void PlayBgm() + { + GameApp.Audio.MusicVolume = 0.8f; + GameApp.Audio.Play(AudioType.Music, "Audio/Bgm/MainTheme", true, 1f, true, true); + } +} +``` + +## 最佳实践 + +- 常用短音效建议预热 +- 背景音乐和 UI 音效分混音组管理 diff --git a/Client/Assets/Books/Framework/Runtime/Audio.md.meta b/Client/Assets/Books/Framework/Runtime/Audio.md.meta new file mode 100644 index 0000000..7d37de8 --- /dev/null +++ b/Client/Assets/Books/Framework/Runtime/Audio.md.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: bb1c0a7757fb6764392d1e5256e350ed +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Client/Assets/Books/Framework/Runtime/Debugger.md b/Client/Assets/Books/Framework/Runtime/Debugger.md new file mode 100644 index 0000000..6c19b27 --- /dev/null +++ b/Client/Assets/Books/Framework/Runtime/Debugger.md @@ -0,0 +1,68 @@ +# Debugger + +## 模块概述 + +Debugger 模块提供运行时调试面板,可查看日志、FPS、系统信息、场景信息、对象池、运行时内存等。调试窗口采用树状注册方式,支持扩展自定义页面。 + +## 快速开始 + +1. 场景挂载 `DebuggerComponent` +2. 选择激活策略 `DebuggerActiveWindowType` +3. 通过 `RegisterDebuggerWindow` 扩展自定义页面 + +## 架构说明 + +```text +DebuggerComponent + └─ DebuggerService + ├─ IDebuggerWindowGroup + └─ IDebuggerWindow +``` + +## 核心类与接口 + +### `IDebuggerService` + +公开能力: + +- `ActiveWindow` +- `DebuggerWindowRoot` +- `RegisterDebuggerWindow(...)` +- `UnregisterDebuggerWindow(...)` +- `GetDebuggerWindow(...)` +- `SelectDebuggerWindow(...)` + +## API 参考 + +### `RegisterDebuggerWindow(string path, IDebuggerWindow debuggerWindow, params object[] args)` + +- 必填参数:`path` +- 必填参数:`debuggerWindow` +- 可选参数:`args` +- 返回值:无 + +### `UnregisterDebuggerWindow(string path)` + +- 返回值:`bool` + +## 完整使用示例 + +```csharp +using AlicizaX.Debugger.Runtime; +using UnityEngine; + +public sealed class SimpleDebuggerWindow : IDebuggerWindow +{ + public void Initialize(params object[] args) { } + public void Shutdown() { } + public void OnEnter() { } + public void OnLeave() { } + public void OnUpdate(float elapseSeconds, float realElapseSeconds) { } + public void OnDraw() => GUILayout.Label("Custom debug page"); +} +``` + +## 最佳实践 + +- 自定义窗口只放诊断信息 +- 发布环境建议仅开发版开启 diff --git a/Client/Assets/Books/Framework/Runtime/Debugger.md.meta b/Client/Assets/Books/Framework/Runtime/Debugger.md.meta new file mode 100644 index 0000000..c7707df --- /dev/null +++ b/Client/Assets/Books/Framework/Runtime/Debugger.md.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 622e9f4d6de5d6f4d90bec501dcc2592 +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Client/Assets/Books/Framework/Runtime/Localization.md b/Client/Assets/Books/Framework/Runtime/Localization.md new file mode 100644 index 0000000..233d707 --- /dev/null +++ b/Client/Assets/Books/Framework/Runtime/Localization.md @@ -0,0 +1,97 @@ +# Localization + +## 模块概述 + +Localization 模块负责语言管理、本地化表装载、键值查询与格式化输出。它支持启动时加载内置表与资源表,也支持运行时异步切换语言。 + +## 快速开始 + +1. 挂载 `LocalizationComponent` +2. 设置默认语言 +3. 配置启动表或资源路径 +4. 使用 `GameApp.Localization.GetString()` + +```csharp +using AlicizaX; + +public sealed class LocalizationQuickStart +{ + public string GetTitle() + { + return GameApp.Localization.GetString("UI_Title"); + } +} +``` + +## 架构说明 + +```text +LocalizationComponent + └─ LocalizationService + ├─ GameLocaizationTable + ├─ LocalizationLanguage + └─ LocalizationChangeEvent +``` + +## 核心类与接口 + +### `ILocalizationService` + +公开能力: + +- `Language` +- `Initialize(string language)` +- `ChangedLanguage(string language)` +- `SwitchLanguageAsync(string language, CancellationToken cancellationToken = default)` +- `GetString(...)` +- `TryGetRawString(...)` +- `CoverAddLocalizationConfig(...)` +- `IncreAddLocalizationConfig(...)` +- `ReloadLocalizationConfig(...)` + +## API 参考 + +### `Initialize(string language)` + +- 必填参数:`language` +- 返回值:无 + +### `SwitchLanguageAsync(string language, CancellationToken cancellationToken = default)` + +- 必填参数:`language` +- 可选参数:`cancellationToken` +- 返回值:`UniTask` + +### `GetString(string key, params object[] args)` + +- 必填参数:`key` +- 可选参数:`args` +- 返回值:`string` + +## 完整使用示例 + +```csharp +using AlicizaX; +using Cysharp.Threading.Tasks; +using UnityEngine; + +public sealed class LocalizationExample : MonoBehaviour +{ + private async void Start() + { + Debug.Log(GameApp.Localization.GetString("UI_Start")); + await GameApp.Localization.SwitchLanguageAsync("English"); + Debug.Log(GameApp.Localization.GetString("UI_Start")); + } +} +``` + +## 最佳实践 + +- 统一 Key 命名规则 +- 语言切换事件中只做刷新,不做重资源初始化 + +## 常见错误 + +- Key 不存在却未做兜底 +- 资源服务未注册时依赖资源路径表 diff --git a/Client/Assets/Books/Framework/Runtime/Localization.md.meta b/Client/Assets/Books/Framework/Runtime/Localization.md.meta new file mode 100644 index 0000000..680e024 --- /dev/null +++ b/Client/Assets/Books/Framework/Runtime/Localization.md.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 5e7853f21507379449bb237975fb20ae +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Client/Assets/Books/Framework/Runtime/Procedure.md b/Client/Assets/Books/Framework/Runtime/Procedure.md new file mode 100644 index 0000000..96dce62 --- /dev/null +++ b/Client/Assets/Books/Framework/Runtime/Procedure.md @@ -0,0 +1,103 @@ +# Procedure + +## 模块概述 + +Procedure 模块用于组织游戏主流程,如启动、登录、主城、战斗、结算等状态切换。它以 `ProcedureBase` 为模板方法基类,把生命周期拆成初始化、进入、离开、更新、销毁。 + +## 快速开始 + +1. 挂载 `ProcedureComponent` +2. 定义多个继承 `ProcedureBase` 的流程 +3. 调用 `InitializeProcedure` +4. 通过 `SwitchProcedure()` 切换 + +## 架构说明 + +```text +ProcedureComponent + └─ ProcedureService + ├─ IProcedure + └─ ProcedureBase +``` + +## 核心类与接口 + +### `IProcedureService` + +公开能力: + +- `CurrentProcedureType` +- `InitializeProcedure(...)` +- `ClearAllProcedures()` +- `ContainsProcedure(Type procedureType)` +- `TrySwitchProcedure(Type procedureType)` + +### `ProcedureBase` + +生命周期模板: + +- `OnInit()` +- `OnEnter()` +- `OnLeave()` +- `OnUpdate()` +- `OnDestroy()` + +## API 参考 + +### `InitializeProcedure(IEnumerable availableProcedures, Type defaultProcedureType)` + +- 必填参数:`availableProcedures` +- 必填参数:`defaultProcedureType` +- 返回值:无 + +### `TrySwitchProcedure(Type procedureType)` + +- 必填参数:`procedureType` +- 返回值:`bool` + +### `CurrentProcedureType` + +- 返回值:`Type` + +## 完整使用示例 + +```csharp +using System; +using AlicizaX; + +public sealed class BootProcedure : ProcedureBase +{ + protected override void OnEnter() + { + SwitchProcedure(); + } +} + +public sealed class LoginProcedure : ProcedureBase +{ + protected override void OnEnter() + { + Log.Info("Enter LoginProcedure"); + } +} + +public sealed class ProcedureExample +{ + public void Initialize() + { + GameApp.Procedure.InitializeProcedure( + new IProcedure[] { new BootProcedure(), new LoginProcedure() }, + typeof(BootProcedure)); + } +} +``` + +## 最佳实践 + +- 一个流程只负责一个清晰状态 +- 初始化逻辑放 `OnInit`,进入/退出逻辑放 `OnEnter` / `OnLeave` + +## 常见错误 + +- 未初始化就切换流程 +- 把长时间并行任务塞入流程本身 diff --git a/Client/Assets/Books/Framework/Runtime/Procedure.md.meta b/Client/Assets/Books/Framework/Runtime/Procedure.md.meta new file mode 100644 index 0000000..1a1377b --- /dev/null +++ b/Client/Assets/Books/Framework/Runtime/Procedure.md.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 8e26efa856cdeb8458b10c02c655dad6 +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Client/Assets/Books/Framework/Runtime/Resource.md b/Client/Assets/Books/Framework/Runtime/Resource.md new file mode 100644 index 0000000..9744dba --- /dev/null +++ b/Client/Assets/Books/Framework/Runtime/Resource.md @@ -0,0 +1,673 @@ +# Resource + +## 模块概述 + +Resource 模块基于 YooAsset 封装了资源包初始化、版本管理、资源查询、同步/异步加载、实例化、缓存回收与低内存处理能力。 + +它是多个模块的底层依赖: + +- `UI`:加载窗口和 Widget 预制体 +- `Audio`:加载音频资源 +- `Localization`:加载语言表 +- `GameObjectPool`:加载池对象原始 prefab +- `Scene`:主场景与附加场景的资源加载链路 + +从当前实现来看,`ResourceService` 还额外提供了: + +- 多包管理 +- 同一路径并发加载合并 +- 资源对象池缓存 +- 低内存时统一回收入口 +- 下载、版本、清单更新能力 + +## 快速开始 + +### 最少步骤 + +1. 在场景中挂载 `ResourceComponent` +2. 配置 `PlayMode`、默认包名和解密服务名 +3. 游戏启动时调用 `InitPackageAsync` +4. 通过 `GameApp.Resource` 或 `AppServices.Require()` 加载资源 + +### 最小示例 + +```csharp +using AlicizaX; +using UnityEngine; + +public sealed class ResourceQuickStart : MonoBehaviour +{ + private async void Start() + { + await GameApp.Resource.InitPackageAsync(); + + Texture2D icon = await GameApp.Resource.LoadAssetAsync("UI/Common/Icon"); + Debug.Log(icon != null); + } +} +``` + +## 架构说明 + +```text +ResourceComponent + └─ ResourceService + ├─ YooAsset PackageMap + ├─ AssetInfo Cache + ├─ AssetObject Pool + ├─ Loading Operation Map + ├─ Download / Version / Manifest + └─ ResourceExtComponent / AssetsReference +``` + +### 关键协作关系 + +- `ResourceComponent`:负责注册服务、配置运行模式并接管低内存回收节奏 +- `ResourceService`:负责包初始化、加载、缓存和释放 +- `AssetsReference`:负责把实例对象与源资源句柄关联起来,在对象销毁时自动释放源资源引用 +- `ResourceExtComponent`:负责追踪资源绑定目标并分帧回收无引用资源 + +### 运行模式 + +当前实现支持以下 `PlayMode`: + +- `EditorSimulateMode` +- `OfflinePlayMode` +- `HostPlayMode` +- `WebPlayMode` + +不同模式决定初始化参数: + +- **EditorSimulateMode**:编辑器模拟构建目录 +- **OfflinePlayMode**:本地内置资源 +- **HostPlayMode**:内置资源 + 缓存 + 远端下载 +- **WebPlayMode**:Web 环境资源拉取方式 + +### 并发加载合并机制 + +`ResourceService` 内部使用 `_assetLoadingOperations` 合并同一路径的并发加载请求。 + +这意味着: + +- 多个系统同时加载同一个资源时,不会重复发起多次底层加载 +- 后来的请求会等待第一条加载链路完成,再复用结果 + +这对 UI 和公共图集资源特别有价值。 + +## 核心类与接口 + +### `IResourceService` + +该接口同时覆盖: + +- 包初始化 +- 包版本与清单管理 +- 资源存在性检查 +- 同步/异步加载 +- 场景对象实例化 +- 缓存清理 +- 低内存回收 + +### `ResourceComponent` + +它是 Resource 模块在场景中的入口组件,主要职责: + +- 注册 `ResourceService` +- 配置默认包名 `PackageName` +- 设置运行模式 `PlayMode` +- 设置解密服务名 `decryptionServices` +- 设置资源对象池参数 +- 接管 `Application.lowMemory` 事件 + +### `ResourceService` + +从当前实现看,`ResourceService` 维护以下关键状态: + +- `DefaultPackageName` +- `PlayMode` +- `PackageMap` +- `_assetInfoMap` +- `_assetLoadingOperations` +- `_assetPool` + +### `AssetsReference` + +这是实例对象与源资源句柄的桥接组件。 + +作用: + +- 当你通过 `LoadGameObject` / `LoadGameObjectAsync` 实例化资源时 +- 框架会在实例对象上自动挂一个 `AssetsReference` +- 当该实例对象销毁时,对应源资源引用会自动 `UnloadAsset` + +这就是为什么: + +- `LoadGameObject` 返回的实例对象通常不需要你手动 `UnloadAsset` + +### `ResourceExtComponent` + +主要用于: + +- 追踪已绑定给 UI、Sprite 或其他目标对象的资源 +- 自动清理已经失去引用的资源包装对象 +- 以分帧方式做回收,降低一次性遍历成本 + +## API 参考 + +以下内容按使用频率分组说明。 + +### 一、初始化与运行配置 + +#### `void Initialize()` + +- 参数:无 +- 返回值:无 +- 说明:初始化 YooAsset、默认包与资产池 +- 备注:通常由 `ResourceComponent.Start()` 自动调用 + +#### `UniTask InitPackageAsync(string packageName = "", string hostServerURL = "", string fallbackHostServerURL = "")` + +- 可选参数:`packageName` +- 可选参数:`hostServerURL` +- 可选参数:`fallbackHostServerURL` +- 返回值:`UniTask` + +说明: + +- 初始化指定资源包 +- 如果 `packageName` 为空,则使用 `DefaultPackageName` +- 在 Host/Web 模式下通常需要传入远端地址 + +异常与边界: + +- 如果同一个包正在初始化或已成功初始化,会记录错误并直接返回失败结果 +- 初始化失败时任务会抛出异常 + +#### `string DefaultPackageName { get; set; }` + +- 说明:默认资源包名 +- 推荐:整个项目统一默认包名约定 + +#### `EPlayMode PlayMode { get; set; }` + +- 说明:资源系统运行模式 +- 注意:Editor 与真机配置往往不同 + +#### `string DecryptionServices { get; set; }` + +- 说明:解密服务类型名 +- 注意:该值应是可被反射创建的类型全名 + +#### `bool AutoUnloadBundleWhenUnused { get; set; }` + +- 说明:初始化参数中的自动卸载选项 + +### 二、版本与下载管理 + +#### `string GetPackageVersion(string customPackageName = "")` + +- 可选参数:`customPackageName` +- 返回值:包版本字符串 + +#### `RequestPackageVersionOperation RequestPackageVersionAsync(bool appendTimeTicks = false, int timeout = 60, string customPackageName = "")` + +- 可选参数:`appendTimeTicks` +- 可选参数:`timeout` +- 可选参数:`customPackageName` +- 返回值:`RequestPackageVersionOperation` + +#### `UpdatePackageManifestOperation UpdatePackageManifestAsync(string packageVersion, int timeout = 60, string customPackageName = "")` + +- 必填参数:`packageVersion` +- 可选参数:`timeout` +- 可选参数:`customPackageName` +- 返回值:`UpdatePackageManifestOperation` + +#### `ResourceDownloaderOperation CreateResourceDownloader(string customPackageName = "")` + +- 可选参数:`customPackageName` +- 返回值:`ResourceDownloaderOperation` + +说明: + +- 会使用当前 `DownloadingMaxNum` 和 `FailedTryAgain` + +### 三、资源查询 + +#### `HasAssetResult HasAsset(string location, string packageName = "")` + +- 必填参数:`location` +- 可选参数:`packageName` +- 返回值:`HasAssetResult` + +说明: + +- 用于判断资源是否存在、是否在本地、是否需要远端下载 + +注意: + +- 如果 `location` 为空会抛异常 + +#### `bool CheckLocationValid(string location, string packageName = "")` + +- 必填参数:`location` +- 可选参数:`packageName` +- 返回值:`bool` + +#### `AssetInfo GetAssetInfo(string location, string packageName = "")` + +- 必填参数:`location` +- 可选参数:`packageName` +- 返回值:`AssetInfo` + +说明: + +- 内部带有 `_assetInfoMap` 缓存 +- 同一路径重复获取成本较低 + +### 四、同步加载 + +#### `T LoadAsset(string location, string packageName = "") where T : UnityEngine.Object` + +- 必填参数:`location` +- 可选参数:`packageName` +- 返回值:`T` +- 泛型约束:`T : UnityEngine.Object` + +说明: + +- 同步加载资源 +- 首次加载会进入资源池缓存 +- 重复获取相同资源时会优先从 `_assetPool` 中复用 + +#### `GameObject LoadGameObject(string location, Transform parent = null, string packageName = "")` + +- 必填参数:`location` +- 可选参数:`parent` +- 可选参数:`packageName` +- 返回值:`GameObject` + +说明: + +- 同步加载 prefab 并实例化 +- 实例对象会自动挂 `AssetsReference` +- 销毁实例时会自动释放源资源引用 + +### 五、异步加载 + +#### `UniTask LoadAssetAsync(string location, CancellationToken cancellationToken = default, string packageName = "") where T : UnityEngine.Object` + +- 必填参数:`location` +- 可选参数:`cancellationToken` +- 可选参数:`packageName` +- 返回值:`UniTask` + +#### `UniTask LoadGameObjectAsync(string location, Transform parent = null, CancellationToken cancellationToken = default, string packageName = "")` + +- 必填参数:`location` +- 可选参数:`parent` +- 可选参数:`cancellationToken` +- 可选参数:`packageName` +- 返回值:`UniTask` + +### 六、回调式加载 + +#### `UniTask LoadAssetAsync(string location, int priority, LoadAssetCallbacks loadAssetCallbacks, object userData, string packageName = "")` + +#### `UniTask LoadAssetAsync(string location, Type assetType, int priority, LoadAssetCallbacks loadAssetCallbacks, object userData, string packageName = "")` + +`LoadAssetCallbacks` 中的回调签名: + +- 成功:`(string assetName, object asset, float duration, object userData)` +- 失败:`(string assetName, LoadResourceStatus status, string errorMessage, object userData)` +- 进度:`(string assetName, float progress, object userData)` + +适合: + +- 旧式回调链 +- 想同时观察进度与错误 +- 不方便直接 `await` 的场景 + +### 七、句柄接口 + +#### `AssetHandle LoadAssetSyncHandle(string location, string packageName = "") where T : UnityEngine.Object` + +#### `AssetHandle LoadAssetAsyncHandle(string location, string packageName = "") where T : UnityEngine.Object` + +适合: + +- 需要直接管理底层句柄 +- 需要和 YooAsset 句柄 API 配合 + +### 八、释放与回收 + +#### `void UnloadAsset(object asset)` + +- 必填参数:`asset` +- 返回值:无 + +说明: + +- 本质上是把资源对象从 `_assetPool` 中 `Unspawn` + +#### `void UnloadUnusedAssets()` + +- 返回值:无 + +说明: + +- 释放资源对象池中未使用的对象 +- 并对所有已成功初始化的包执行 `UnloadUnusedAssetsAsync` + +#### `void ForceUnloadAllAssets()` + +- 返回值:无 + +说明: + +- 强制卸载所有资源 +- WebGL 下会直接警告并退出 + +#### `void ForceUnloadUnusedAssets(bool performGCCollect)` + +- 必填参数:`performGCCollect` +- 返回值:无 + +说明: + +- 委托给 `ResourceComponent` 控制实际的系统资源回收时机 + +## 常见用法 + +### 1. 初始化默认包 + +```csharp +using AlicizaX; +using AlicizaX.Resource.Runtime; +using UnityEngine; + +public sealed class ResourceInitExample : MonoBehaviour +{ + private async void Start() + { + bool ok = await GameApp.Resource.InitPackageAsync(); + Debug.Log($"Init default package success: {ok}"); + } +} +``` + +### 2. 初始化远端包 + +```csharp +using AlicizaX; +using UnityEngine; + +public sealed class HostModeInitExample : MonoBehaviour +{ + private async void Start() + { + await GameApp.Resource.InitPackageAsync( + packageName: "DefaultPackage", + hostServerURL: "https://cdn.example.com/game", + fallbackHostServerURL: "https://backup.example.com/game"); + } +} +``` + +### 3. 同步加载配置资源 + +```csharp +using AlicizaX; +using UnityEngine; + +public sealed class SyncLoadExample : MonoBehaviour +{ + private void Start() + { + TextAsset config = GameApp.Resource.LoadAsset("Config/GameBalance"); + Debug.Log(config != null ? config.text : "Config missing"); + } +} +``` + +### 4. 异步加载图片资源 + +```csharp +using AlicizaX; +using UnityEngine; + +public sealed class AsyncLoadExample : MonoBehaviour +{ + private async void Start() + { + Sprite sprite = await GameApp.Resource.LoadAssetAsync("UI/Common/Atlas/Icon_Star"); + Debug.Log(sprite != null); + } +} +``` + +### 5. 加载并实例化 GameObject + +```csharp +using AlicizaX; +using UnityEngine; + +public sealed class InstantiateExample : MonoBehaviour +{ + private GameObject _hero; + + private async void Start() + { + _hero = await GameApp.Resource.LoadGameObjectAsync("Character/Hero.prefab", transform); + } + + private void OnDestroy() + { + if (_hero != null) + { + Destroy(_hero); + } + } +} +``` + +说明: + +- 这里不需要对 `_hero` 再调用 `UnloadAsset` +- 销毁实例时 `AssetsReference` 会自动释放源资源引用 + +### 6. 使用回调式加载并监听进度 + +```csharp +using AlicizaX; +using AlicizaX.Resource.Runtime; +using UnityEngine; + +public sealed class CallbackLoadExample : MonoBehaviour +{ + private async void Start() + { + var callbacks = new LoadAssetCallbacks( + (assetName, asset, duration, userData) => + { + Debug.Log($"Load success: {assetName}, duration: {duration:F3}s"); + }, + (assetName, status, errorMessage, userData) => + { + Debug.LogError($"Load failed: {assetName}, {status}, {errorMessage}"); + }, + (assetName, progress, userData) => + { + Debug.Log($"Progress {assetName}: {progress:P0}"); + }); + + await GameApp.Resource.LoadAssetAsync( + "UI/Common/Icon", + priority: 0, + loadAssetCallbacks: callbacks, + userData: null); + } +} +``` + +### 7. 下载资源版本并更新清单 + +```csharp +using AlicizaX; +using UnityEngine; + +public sealed class UpdateManifestExample : MonoBehaviour +{ + private async void Start() + { + var versionOp = GameApp.Resource.RequestPackageVersionAsync(); + await versionOp.ToUniTask(); + + if (versionOp.Status == YooAsset.EOperationStatus.Succeed) + { + string version = versionOp.PackageVersion; + var manifestOp = GameApp.Resource.UpdatePackageManifestAsync(version); + await manifestOp.ToUniTask(); + Debug.Log($"Manifest updated to: {version}"); + } + } +} +``` + +### 8. 创建下载器 + +```csharp +using AlicizaX; +using UnityEngine; + +public sealed class DownloaderExample : MonoBehaviour +{ + private async void Start() + { + var downloader = GameApp.Resource.CreateResourceDownloader(); + downloader.BeginDownload(); + await downloader.ToUniTask(); + Debug.Log($"Download status: {downloader.Status}"); + } +} +``` + +## 运行行为细节 + +### 1. 重复加载同一路径会优先复用缓存 + +当前实现通过 `_assetPool` 保存 `AssetObject`,同一路径的资源会复用池内对象。 + +好处: + +- 减少重复加载 +- 降低频繁生成/销毁底层句柄的开销 + +### 2. 并发同路径加载会合并请求 + +如果同一路径资源正在加载中: + +- 后续请求不会重复发起底层加载 +- 会等待首个请求完成后再复用结果 + +### 3. `LoadGameObject` 与 `LoadAsset` 的释放语义不同 + +#### `LoadAsset` + +- 返回的是资源对象本身 +- 如有需要可以显式 `UnloadAsset(asset)` + +#### `LoadGameObject` + +- 返回的是实例化后的场景对象 +- 源 prefab 资源句柄通过 `AssetsReference` 绑定到实例对象 +- 通常直接 `Destroy(instance)` 即可 + +### 4. 低内存行为 + +触发 `OnLowMemory()` 时: + +- 会调用 `_forceUnloadUnusedAssetsAction` +- 实际清理由 `ResourceComponent` 驱动 + +### 5. `AssetInfo` 有内部缓存 + +多次 `GetAssetInfo` 会命中 `_assetInfoMap` + +适合: + +- 频繁查询资源合法性 +- UI 打开前做路径校验 + +### 6. 失败行为 + +以下情况通常会导致异常或错误日志: + +- 资源路径为空 +- 包不存在 +- 资源定位无效 +- 初始化失败 + +## 最佳实践 + +- 统一资源路径规范,避免大小写和目录命名混乱 +- 默认优先异步加载 +- 把“包初始化 / 版本更新 / 资源下载 / 资源使用”分成不同阶段处理 +- UI、音频、图集等高复用资源尽量走缓存复用 +- 对需要长期驻留的资源,业务层要明确生命周期,不要频繁反复加载 + +## 常见错误 + +### 1. 包未初始化就加载资源 + +现象: + +- 资源查找失败 +- 返回空对象或直接异常 + +规避: + +- 确保启动流程中先执行 `InitPackageAsync` + +### 2. 默认包名与实际包名不一致 + +现象: + +- 路径正确但查不到资源 + +规避: + +- 统一 `DefaultPackageName` +- 多包场景显式传入 `packageName` + +### 3. 手动 `UnloadAsset` 已实例化场景对象 + +现象: + +- 语义不清晰 +- 可能导致资源生命周期混乱 + +规避: + +- `LoadGameObject` 返回的实例优先 `Destroy` + +### 4. 解密服务类型名错误 + +现象: + +- 初始化时反射失败 + +规避: + +- `DecryptionServices` 必须是可反射创建的完整类型名 + +## 性能注意事项 + +- 避免在主线程大量同步加载 +- 常用资源优先缓存复用 +- 合理设置: + - `AssetAutoReleaseInterval` + - `AssetCapacity` + - `AssetExpireTime` + - `AssetPriority` +- 高并发异步加载时,优先复用路径和包配置,发挥内部合并机制优势 diff --git a/Client/Assets/Books/Framework/Runtime/Resource.md.meta b/Client/Assets/Books/Framework/Runtime/Resource.md.meta new file mode 100644 index 0000000..80a1d7e --- /dev/null +++ b/Client/Assets/Books/Framework/Runtime/Resource.md.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: df8519e09d84ea34b8ffd2c4b93c21f9 +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Client/Assets/Books/Framework/Runtime/Scene.md b/Client/Assets/Books/Framework/Runtime/Scene.md new file mode 100644 index 0000000..f486e1c --- /dev/null +++ b/Client/Assets/Books/Framework/Runtime/Scene.md @@ -0,0 +1,477 @@ +# Scene + +## 模块概述 + +Scene 模块负责: + +- 主场景加载 +- Additive 子场景加载 +- 延迟激活与解除挂起 +- 子场景卸载 +- 主场景状态追踪 +- 场景作用域(Scene Scope)建立与重置 + +它不是 Unity 默认 `SceneManager` 的简单封装,而是把场景切换与框架服务作用域绑定在一起。 + +这意味着: + +- **加载主场景时,会重置 Scene Scope** +- **加载 Additive 子场景时,不会重置主场景状态** +- **主场景和子场景在框架内是两套不同语义** + +## 快速开始 + +### 最少步骤 + +1. 在场景中挂载 `SceneComponent` +2. 确保 `ResourceComponent` 已可用 +3. 通过 `GameApp.Scene.LoadSceneAsync(...)` 加载场景 +4. 如需卸载 Additive 场景,调用 `UnloadAsync(...)` + +### 最小示例 + +```csharp +using AlicizaX; +using UnityEngine.SceneManagement; + +public sealed class SceneQuickStart +{ + public async void Load() + { + await GameApp.Scene.LoadSceneAsync("Scene/Battle", LoadSceneMode.Single); + } +} +``` + +## 架构说明 + +```text +SceneComponent + └─ SceneService + └─ SceneDomainStateService + ├─ CurrentMainSceneName + ├─ CurrentMainSceneHandle + ├─ SubScene Map + └─ Handling Scene Set +``` + +### 关键协作关系 + +- `SceneComponent`:注册 `SceneService` 并确保 Scene Scope 存在 +- `SceneService`:处理加载、激活、卸载与状态切换 +- `SceneDomainStateService`:记录当前主场景、子场景和处理中场景 +- `IResourceService`:主场景切换完成后触发资源回收 + +### 主场景与子场景的区别 + +#### 主场景(Main Scene) + +- 通过 `LoadSceneMode.Single` 加载 +- 加载前会重置 Scene Scope +- 加载完成后更新 `CurrentMainSceneName` +- 切换完成后会触发一次资源回收 + +#### 子场景(Sub Scene / Additive) + +- 通过 `LoadSceneMode.Additive` 加载 +- 记录在 `_subScenes` 字典中 +- 可通过 `UnloadAsync(location)` 卸载 + +## 核心类与接口 + +### `ISceneService` + +公开能力: + +- `CurrentMainSceneName` +- `LoadSceneAsync(...)` +- `LoadScene(...)` +- `ActivateScene(string location)` +- `UnSuspend(string location)` +- `IsMainScene(string location)` +- `UnloadAsync(string location, Action progressCallBack = null)` +- `Unload(string location, Action callBack = null, Action progressCallBack = null)` +- `IsContainScene(string location)` + +### `ISceneStateService` + +偏状态查询接口,主要用于: + +- 查询当前主场景 +- 查询某个场景是否已记录在当前 Scene Scope 中 + +### `SceneDomainStateService` + +当前实现中负责维护: + +- `CurrentMainSceneName` +- `CurrentMainSceneHandle` +- `_subScenes` +- `_handlingScenes` + +用途: + +- 避免同一场景重复并发加载/卸载 +- 为 `IsContainScene` / `IsMainScene` 提供判断基础 + +## API 参考 + +### 一、场景加载 + +#### `UniTask LoadSceneAsync(string location, LoadSceneMode sceneMode = LoadSceneMode.Single, bool suspendLoad = false, uint priority = 100, bool gcCollect = true, Action progressCallBack = null)` + +- 必填参数:`location` +- 可选参数:`sceneMode` +- 可选参数:`suspendLoad` +- 可选参数:`priority` +- 可选参数:`gcCollect` +- 可选参数:`progressCallBack` +- 返回值:`UniTask` + +参数说明: + +- `location`:场景资源定位地址 +- `sceneMode`:`Single` 或 `Additive` +- `suspendLoad`:是否挂起加载后的激活 +- `priority`:加载优先级 +- `gcCollect`:主场景切换后是否执行资源回收 +- `progressCallBack`:加载进度回调 + +行为说明: + +- `Single`:会重置当前 Scene Scope +- `Additive`:会作为子场景注册 +- 如果同一场景正在处理,当前实现会记录错误并返回默认值 + +#### `void LoadScene(string location, LoadSceneMode sceneMode = LoadSceneMode.Single, bool suspendLoad = false, uint priority = 100, Action callBack = null, bool gcCollect = true, Action progressCallBack = null)` + +- 注意:这个方法**不是同步加载** +- 本质上仍是异步加载,只是使用回调而不是 `await` + +推荐: + +- 新代码优先使用 `LoadSceneAsync` + +### 二、场景激活与挂起 + +#### `bool ActivateScene(string location)` + +- 必填参数:`location` +- 返回值:`bool` + +说明: + +- 对应场景已被挂起时,尝试激活它 +- 可用于 `suspendLoad = true` 的场景 + +#### `bool UnSuspend(string location)` + +- 必填参数:`location` +- 返回值:`bool` + +说明: + +- 解除场景挂起 +- 语义接近 `ActivateScene` + +### 三、场景查询 + +#### `bool IsMainScene(string location)` + +- 必填参数:`location` +- 返回值:`bool` + +说明: + +- 判断给定场景是否为当前主场景 +- 内部结合 `SceneDomainStateService` 与 `SceneManager.GetActiveScene()` 做判断 + +#### `bool IsContainScene(string location)` + +- 必填参数:`location` +- 返回值:`bool` + +说明: + +- 判断当前主场景或子场景列表中是否包含该场景 + +#### `string CurrentMainSceneName` + +- 返回值:主场景名 + +### 四、场景卸载 + +#### `UniTask UnloadAsync(string location, Action progressCallBack = null)` + +- 必填参数:`location` +- 可选参数:`progressCallBack` +- 返回值:`UniTask` + +说明: + +- 用于卸载 Additive 子场景 +- 当前实现**不用于直接卸载主场景** + +#### `void Unload(string location, Action callBack = null, Action progressCallBack = null)` + +- 必填参数:`location` +- 可选参数:`callBack` +- 可选参数:`progressCallBack` +- 返回值:无 + +说明: + +- 回调式异步卸载 + +## 常见用法 + +### 1. 加载主场景 + +```csharp +using AlicizaX; +using UnityEngine.SceneManagement; + +public sealed class LoadMainSceneExample +{ + public async void GoBattle() + { + await GameApp.Scene.LoadSceneAsync("Scene/Battle", LoadSceneMode.Single); + } +} +``` + +### 2. Additive 加载子场景 + +```csharp +using AlicizaX; +using UnityEngine.SceneManagement; + +public sealed class AdditiveSceneExample +{ + public async void OpenSubScene() + { + await GameApp.Scene.LoadSceneAsync("Scene/PhotoRoom", LoadSceneMode.Additive); + } +} +``` + +### 3. 卸载 Additive 子场景 + +```csharp +using AlicizaX; + +public sealed class UnloadSubSceneExample +{ + public async void CloseSubScene() + { + if (GameApp.Scene.IsContainScene("Scene/PhotoRoom")) + { + bool ok = await GameApp.Scene.UnloadAsync("Scene/PhotoRoom"); + UnityEngine.Debug.Log($"Unload result: {ok}"); + } + } +} +``` + +### 4. 带进度的场景加载 + +```csharp +using AlicizaX; +using UnityEngine; +using UnityEngine.SceneManagement; + +public sealed class SceneProgressExample +{ + public async void LoadWithProgress() + { + await GameApp.Scene.LoadSceneAsync( + "Scene/Battle", + LoadSceneMode.Single, + suspendLoad: false, + priority: 100, + gcCollect: true, + progressCallBack: progress => + { + Debug.Log($"Scene progress: {progress:P0}"); + }); + } +} +``` + +### 5. 挂起加载后手动激活 + +```csharp +using AlicizaX; +using UnityEngine.SceneManagement; + +public sealed class SuspendLoadExample +{ + public async void LoadThenActivate() + { + await GameApp.Scene.LoadSceneAsync( + "Scene/Battle", + LoadSceneMode.Single, + suspendLoad: true); + + GameApp.Scene.ActivateScene("Scene/Battle"); + } +} +``` + +### 6. 使用回调式加载 + +```csharp +using AlicizaX; +using UnityEngine; +using UnityEngine.SceneManagement; + +public sealed class SceneCallbackExample +{ + public void LoadLobby() + { + GameApp.Scene.LoadScene( + "Scene/Lobby", + LoadSceneMode.Single, + suspendLoad: false, + priority: 100, + callBack: scene => + { + Debug.Log($"Loaded scene: {scene.name}"); + }, + gcCollect: true, + progressCallBack: progress => + { + Debug.Log($"Loading: {progress:P0}"); + }); + } +} +``` + +### 7. 查询当前主场景 + +```csharp +using AlicizaX; +using UnityEngine; + +public sealed class SceneStateExample : MonoBehaviour +{ + private void Update() + { + Debug.Log($"Main Scene: {GameApp.Scene.CurrentMainSceneName}"); + } +} +``` + +## 运行行为细节 + +### 1. 主场景加载会重置 Scene Scope + +这是本模块最重要的设计点之一。 + +当调用: + +```csharp +GameApp.Scene.LoadSceneAsync("Scene/Battle", LoadSceneMode.Single) +``` + +内部会: + +1. `Context.ResetScene()` +2. 重新注册 `SceneDomainStateService` +3. 标记新主场景进入加载中 + +这意味着: + +- 旧的 Scene Scope 服务会被重建 +- 与旧主场景强绑定的场景级服务也应重新初始化 + +### 2. Additive 场景不会重置主场景作用域 + +使用 `LoadSceneMode.Additive` 时: + +- 场景会被加入 `_subScenes` +- 主场景状态保留 +- 适合加载摄影间、剧情副场景、临时房间等 + +### 3. `UnloadAsync` 只对 Additive 子场景有效 + +当前实现中: + +- 只有 `_subScenes` 中登记的场景才会走卸载逻辑 +- 主场景切换依赖新的 `LoadScene(Single)`,而不是单独 `Unload` 主场景 + +### 4. 同一场景并发处理会被拦截 + +`SceneDomainStateService` 会使用 `_handlingScenes` 记录“正在加载/卸载”的场景。 + +效果: + +- 避免同一路径重复加载或重复卸载 +- 减少状态错乱 + +### 5. 主场景加载完成后会触发资源回收 + +当主场景切换完成后,会调用: + +```csharp +Context.Require().ForceUnloadUnusedAssets(gcCollect); +``` + +因此: + +- 场景切换是资源回收的重要时间点 +- `gcCollect` 参数会影响切场景后的回收强度 + +### 6. `LoadScene` 方法名容易误导 + +虽然名字像“同步加载”,但当前实现中: + +- `LoadScene(...)` 仍然是异步加载 +- 区别只是它通过回调返回结果,而不是 `await` + +## 最佳实践 + +- 主场景切换统一交给流程层管理 +- Additive 场景只用于临时叠加内容,不要滥用为主流程状态切换 +- 若需要加载转场动画,可用 `suspendLoad = true` + 手动激活 +- 场景切换后如有场景级服务初始化,放在新的 Scene Scope 生命周期里完成 + +## 常见错误 + +### 1. 试图用 `UnloadAsync` 卸载主场景 + +现象: + +- 返回 `false` 或警告 + +正确方式: + +- 通过加载新的 `Single` 主场景来替换 + +### 2. 把 `LoadScene(...)` 当同步函数使用 + +现象: + +- 加载还没完成就执行后续依赖逻辑 + +规避: + +- 优先使用 `LoadSceneAsync(...)` +- 或把后续逻辑写入回调中 + +### 3. 重复 Additive 加载同一场景 + +现象: + +- 异步版可能直接抛异常 +- 回调版会记录警告 + +规避: + +- 在加载前先用 `IsContainScene(location)` 做检查 + +## 性能注意事项 + +- 场景切换本身是重量级操作,不要把短生命周期面板式内容做成 Additive 场景 +- 进度回调每帧执行,UI 刷新时应尽量轻量 +- 主场景切换后伴随资源回收,切场景阶段要预估回收开销和 GC 波动 diff --git a/Client/Assets/Books/Framework/Runtime/Scene.md.meta b/Client/Assets/Books/Framework/Runtime/Scene.md.meta new file mode 100644 index 0000000..5d3f83c --- /dev/null +++ b/Client/Assets/Books/Framework/Runtime/Scene.md.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 13866105ba2b78a4688dbb55ad947045 +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Client/Assets/Books/Framework/Runtime/Timer.md b/Client/Assets/Books/Framework/Runtime/Timer.md new file mode 100644 index 0000000..2fe83b5 --- /dev/null +++ b/Client/Assets/Books/Framework/Runtime/Timer.md @@ -0,0 +1,672 @@ +# Timer + +## 模块概述 + +Timer 模块提供统一定时器服务,适合处理: + +- 延时执行 +- 循环轮询 +- UI 倒计时 +- 技能延时结算 +- 超时控制 +- 轻量轮询逻辑 + +它由 `TimerComponent` 注册 `TimerService`,并通过 `GameApp.Timer` 或 `AppServices.Require()` 暴露给业务层。 + +从实现上看,`TimerService` 使用时间轮来管理定时器,重点优化的是: + +- 大量定时器的调度效率 +- 低 GC 的回调执行 +- 支持缩放时间和非缩放时间两套时间基准 + +## 快速开始 + +### 最少步骤 + +1. 在场景中挂载 `TimerComponent` +2. 调用 `GameApp.Timer.AddTimer(...)` +3. 保存返回的 `timerId` +4. 在对象销毁或逻辑结束时调用 `RemoveTimer(timerId)` + +### 最小示例 + +```csharp +using AlicizaX; +using UnityEngine; + +public sealed class TimerQuickStart : MonoBehaviour +{ + private int _timerId; + + private void Start() + { + _timerId = GameApp.Timer.AddTimer(OnTick, 1f, true); + } + + private void OnDestroy() + { + GameApp.Timer.RemoveTimer(_timerId); + } + + private void OnTick() + { + Debug.Log("Tick"); + } +} +``` + +## 架构说明 + +```text +TimerComponent + └─ TimerService + ├─ TimerHandler + ├─ TimerHandlerNoArgs + ├─ TimerGenericInvokerCache + └─ HierarchicalTimeWheel +``` + +### 关键协作关系 + +- `TimerComponent`:负责在场景中注册 `TimerService` +- `TimerService`:真正执行定时调度 +- `ITimerService`:业务层使用的统一接口 +- `GameApp.Timer`:高频调用入口 + +### 时间基准 + +Timer 模块支持两种时间基准: + +- **缩放时间**:使用 `Time.time` +- **非缩放时间**:使用 `Time.unscaledTime` + +这由 `isUnscaled` 参数控制: + +- `false`:受 `Time.timeScale` 影响 +- `true`:不受 `Time.timeScale` 影响 + +## 核心类与接口 + +### `ITimerService` + +公开能力: + +- `AddTimer(TimerHandler callback, float time, bool isLoop = false, bool isUnscaled = false, params object[] args)` +- `AddTimer(TimerHandlerNoArgs callback, float time, bool isLoop = false, bool isUnscaled = false)` +- `AddTimer(Action callback, T arg, float time, bool isLoop = false, bool isUnscaled = false)` +- `Stop(int timerId)` +- `Resume(int timerId)` +- `IsRunning(int timerId)` +- `GetLeftTime(int timerId)` +- `Restart(int timerId)` +- `RemoveTimer(int timerId)` +- `RemoveAllTimer()` + +### `TimerHandler` + +定义: + +```csharp +public delegate void TimerHandler(params object[] args); +``` + +适合: + +- 参数数量不固定 +- 通用回调 +- 快速搭建原型 + +### `TimerHandlerNoArgs` + +定义: + +```csharp +public delegate void TimerHandlerNoArgs(); +``` + +适合: + +- 无参延时执行 +- 最常见的循环 tick + +### 泛型 `AddTimer` + +适合: + +- 单参数且类型明确的回调 +- 希望避免 `object[]` 拆装箱和手动转换 + +## API 参考 + +### `int AddTimer(TimerHandler callback, float time, bool isLoop = false, bool isUnscaled = false, params object[] args)` + +- 必填参数:`callback` +- 必填参数:`time` +- 可选参数:`isLoop` +- 可选参数:`isUnscaled` +- 可选参数:`args` +- 返回值:`timerId` + +说明: + +- 注册一个支持参数数组的定时器 +- `time` 为延迟秒数 +- `isLoop = true` 时会按相同间隔循环触发 + +适合: + +- 参数个数可变 +- 临时逻辑 +- 不想额外声明泛型回调的场景 + +### `int AddTimer(TimerHandlerNoArgs callback, float time, bool isLoop = false, bool isUnscaled = false)` + +- 必填参数:`callback` +- 必填参数:`time` +- 可选参数:`isLoop` +- 可选参数:`isUnscaled` +- 返回值:`timerId` + +说明: + +- 注册无参定时器 +- 是最常用、最简洁的用法 + +### `int AddTimer(Action callback, T arg, float time, bool isLoop = false, bool isUnscaled = false)` + +- 必填参数:`callback` +- 必填参数:`arg` +- 必填参数:`time` +- 可选参数:`isLoop` +- 可选参数:`isUnscaled` +- 返回值:`timerId` +- 泛型约束:无额外约束 + +说明: + +- 注册单参数强类型定时器 +- 比 `object[] args` 更清晰、更安全 + +### `void Stop(int timerId)` + +- 必填参数:`timerId` +- 返回值:无 + +说明: + +- 把指定定时器标记为停止运行 +- 对无效 `timerId` 为安全无操作 + +### `void Resume(int timerId)` + +- 必填参数:`timerId` +- 返回值:无 + +说明: + +- 恢复一个已停止的定时器 +- 对无效 `timerId` 为安全无操作 + +### `bool IsRunning(int timerId)` + +- 必填参数:`timerId` +- 返回值:`bool` + +说明: + +- 返回该定时器当前是否处于运行状态 +- 对无效 `timerId` 返回 `false` + +### `float GetLeftTime(int timerId)` + +- 必填参数:`timerId` +- 返回值:剩余秒数 + +说明: + +- 返回定时器剩余触发时间 +- 对无效 `timerId` 返回 `0` + +### `void Restart(int timerId)` + +- 必填参数:`timerId` +- 返回值:无 + +说明: + +- 重新调度该定时器 +- 对无效 `timerId` 为安全无操作 + +### `void RemoveTimer(int timerId)` + +- 必填参数:`timerId` +- 返回值:无 + +说明: + +- 从系统中移除指定定时器 +- 是最推荐的结束方式 + +### `void RemoveAllTimer()` + +- 返回值:无 + +说明: + +- 清空当前全部定时器 +- 通常只建议在服务销毁、场景彻底重置或特殊测试环境下使用 + +## 常见用法 + +### 1. 一次性延时执行 + +```csharp +using AlicizaX; +using UnityEngine; + +public sealed class DelayExample : MonoBehaviour +{ + private int _delayTimer; + + private void Start() + { + _delayTimer = GameApp.Timer.AddTimer(OnDelayFinish, 2f); + } + + private void OnDestroy() + { + GameApp.Timer.RemoveTimer(_delayTimer); + } + + private void OnDelayFinish() + { + Debug.Log("2 seconds later"); + } +} +``` + +### 2. 循环计时器 + +```csharp +using AlicizaX; +using UnityEngine; + +public sealed class LoopTimerExample : MonoBehaviour +{ + private int _loopTimer; + private int _counter; + + private void Start() + { + _loopTimer = GameApp.Timer.AddTimer(OnLoop, 0.5f, true); + } + + private void OnDestroy() + { + GameApp.Timer.RemoveTimer(_loopTimer); + } + + private void OnLoop() + { + _counter++; + Debug.Log($"Loop count: {_counter}"); + + if (_counter >= 5) + { + GameApp.Timer.RemoveTimer(_loopTimer); + } + } +} +``` + +### 3. 带参数的定时器 + +```csharp +using AlicizaX; +using UnityEngine; + +public sealed class TimerArgsExample : MonoBehaviour +{ + private int _timerId; + + private void Start() + { + _timerId = GameApp.Timer.AddTimer(OnRewardDelay, 3f, false, false, "Gold", 100); + } + + private void OnDestroy() + { + GameApp.Timer.RemoveTimer(_timerId); + } + + private void OnRewardDelay(params object[] args) + { + string rewardType = (string)args[0]; + int amount = (int)args[1]; + Debug.Log($"Reward => {rewardType}, amount => {amount}"); + } +} +``` + +### 4. 泛型参数定时器 + +```csharp +using AlicizaX; +using UnityEngine; + +public sealed class GenericTimerExample : MonoBehaviour +{ + private int _timerId; + + private void Start() + { + _timerId = GameApp.Timer.AddTimer(OnDamageDelay, 200, 1.5f); + } + + private void OnDestroy() + { + GameApp.Timer.RemoveTimer(_timerId); + } + + private void OnDamageDelay(int damage) + { + Debug.Log($"Delayed damage: {damage}"); + } +} +``` + +### 5. 不受暂停影响的 UI 倒计时 + +```csharp +using AlicizaX; +using UnityEngine; + +public sealed class UnscaledCountdownExample : MonoBehaviour +{ + private int _timerId; + private float _left = 5f; + + private void Start() + { + _timerId = GameApp.Timer.AddTimer(OnTick, 1f, true, true); + } + + private void OnDestroy() + { + GameApp.Timer.RemoveTimer(_timerId); + } + + private void OnTick() + { + _left -= 1f; + Debug.Log($"Countdown: {_left}"); + + if (_left <= 0f) + { + GameApp.Timer.RemoveTimer(_timerId); + } + } +} +``` + +### 6. 查询剩余时间 + +```csharp +using AlicizaX; +using UnityEngine; + +public sealed class LeftTimeExample : MonoBehaviour +{ + private int _timerId; + + private void Start() + { + _timerId = GameApp.Timer.AddTimer(OnFinish, 10f); + } + + private void Update() + { + float left = GameApp.Timer.GetLeftTime(_timerId); + Debug.Log($"Left: {left:F2}s"); + } + + private void OnDestroy() + { + GameApp.Timer.RemoveTimer(_timerId); + } + + private void OnFinish() + { + Debug.Log("Finished"); + } +} +``` + +### 7. 暂停、恢复与重启 + +```csharp +using AlicizaX; +using UnityEngine; + +public sealed class PauseResumeTimerExample : MonoBehaviour +{ + private int _timerId; + + private void Start() + { + _timerId = GameApp.Timer.AddTimer(OnTick, 1f, true); + } + + private void Update() + { + if (Input.GetKeyDown(KeyCode.S)) + { + GameApp.Timer.Stop(_timerId); + } + + if (Input.GetKeyDown(KeyCode.R)) + { + GameApp.Timer.Resume(_timerId); + } + + if (Input.GetKeyDown(KeyCode.T)) + { + GameApp.Timer.Restart(_timerId); + } + } + + private void OnDestroy() + { + GameApp.Timer.RemoveTimer(_timerId); + } + + private void OnTick() + { + Debug.Log("Running timer"); + } +} +``` + +### 8. 组件生命周期绑定 + +```csharp +using AlicizaX; +using UnityEngine; + +public sealed class SafeTimerOwner : MonoBehaviour +{ + private int _timerId = -1; + + private void OnEnable() + { + _timerId = GameApp.Timer.AddTimer(OnHeartbeat, 2f, true); + } + + private void OnDisable() + { + if (_timerId > 0) + { + GameApp.Timer.RemoveTimer(_timerId); + _timerId = -1; + } + } + + private void OnHeartbeat() + { + Debug.Log("Heartbeat"); + } +} +``` + +## 运行行为细节 + +这一部分基于当前 `TimerService` 实现整理,适合开发时理解边界行为。 + +### 1. 无效 `timerId` 的行为 + +以下方法对无效 `timerId` 都是安全的: + +- `Stop` +- `Resume` +- `Restart` +- `RemoveTimer` + +对应返回值行为: + +- `IsRunning` 返回 `false` +- `GetLeftTime` 返回 `0` + +### 2. 非循环定时器会在触发后自动移除 + +一次性定时器执行回调后,不需要手动调用 `RemoveTimer` + +但如果组件生命周期不确定,仍建议在 `OnDestroy` / `OnDisable` 中做防守式移除。 + +### 3. 回调异常会被捕获 + +`TimerService` 内部会捕获回调异常并记录日志,不会因为单个定时器异常直接打断整个调度链。 + +### 4. `Stop` / `Resume` 的语义更像“运行标记” + +源码层面: + +- `Stop(timerId)` 只是把定时器标记为 `IsRunning = false` +- `Resume(timerId)` 只是把它重新标记为 `true` + +注意点: + +- 如果定时器已经到达触发时刻,但当时处于 `Stop` 状态,那么该次调度不会执行 +- 对循环定时器来说,如果它在应触发的那一刻是停止状态,也不会自动重新挂回时间轮 + +因此更稳妥的经验是: + +- **短暂停顿并在触发前恢复**:可以用 `Stop` / `Resume` +- **需要明确重新开始计时**:优先用 `Restart` + +### 5. `Restart` 对循环定时器更直观 + +当前实现里: + +- 循环定时器的 `Interval = time` +- 非循环定时器的 `Interval = 0` + +这意味着: + +- 对循环定时器调用 `Restart`,会从当前时刻重新按原间隔开始计时 +- 对非循环定时器调用 `Restart`,会因为内部间隔是 `0`,变成“下一次 Tick 几乎立刻触发” + +所以建议: + +- **循环定时器**:可以使用 `Restart` +- **一次性定时器**:如果要重新延时,直接重新创建一个新的 timer 更清晰 + +## 最佳实践 + +### 推荐做法 + +- 把 `timerId` 与对象生命周期绑定 +- UI 倒计时优先用 `isUnscaled = true` +- 对单参数回调优先使用泛型重载 +- 对复杂业务优先在回调中触发业务方法,而不是把整段逻辑都堆进匿名函数 + +### 推荐封装方式 + +如果你的项目里大量使用定时器,建议封装一层: + +- `StartOnce(float delay, Action action)` +- `StartLoop(float interval, Action action)` +- `StopAndClear(ref int timerId)` + +这样可以减少重复样板代码和漏删问题。 + +## 常见错误 + +### 1. 循环定时器不移除 + +现象: + +- 组件销毁后仍继续运行 + +规避: + +- 在 `OnDisable` 或 `OnDestroy` 中移除 + +### 2. 暂停界面仍使用缩放时间 + +现象: + +- 游戏暂停后倒计时也停住 + +规避: + +- UI 倒计时使用 `isUnscaled = true` + +### 3. 在非循环定时器上依赖 `Restart` + +现象: + +- 行为不像“重新开始原延时”,而是几乎立即触发 + +规避: + +- 直接重新创建一次性定时器 + +### 4. `object[] args` 中频繁装箱拆箱 + +现象: + +- 代码可读性差 +- 更容易写错类型转换 + +规避: + +- 单参数时优先用 `AddTimer` + +## 性能注意事项 + +- 少量长生命周期定时器成本很低 +- 大量高频定时器建议业务上合并 +- 能用泛型单参数回调时,优先别用 `object[]` +- 大量短周期循环定时器应谨慎使用,优先考虑合并成统一更新器 + +## 适用场景建议 + +### 适合使用 Timer 模块 + +- 秒级倒计时 +- UI 展示延迟 +- 技能或状态延后执行 +- 轻量循环任务 + +### 不适合使用 Timer 模块 + +- 每帧复杂逻辑 +- 高频实时物理计算 +- 长链路异步流程编排 + +这些场景更适合: + +- `Update` +- `Coroutine` +- `UniTask` +- 专门的状态机/调度器 diff --git a/Client/Assets/Books/Framework/Runtime/Timer.md.meta b/Client/Assets/Books/Framework/Runtime/Timer.md.meta new file mode 100644 index 0000000..8d0f3b5 --- /dev/null +++ b/Client/Assets/Books/Framework/Runtime/Timer.md.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 6a5da693652014446867c2397a69fd27 +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Client/Assets/Books/Framework/Runtime/UI.md b/Client/Assets/Books/Framework/Runtime/UI.md new file mode 100644 index 0000000..2721b5b --- /dev/null +++ b/Client/Assets/Books/Framework/Runtime/UI.md @@ -0,0 +1,552 @@ +# UI + +## 模块概述 + +UI 模块负责窗口创建、资源定位、层级管理、显示/关闭、缓存、过渡动画与界面绑定代码协作。 + +本模块里最容易误解的一点是: + +- `UIHolderObjectBase`(通常简称 **UIHolder**)不是推荐手写的业务类 +- 项目中的大多数 `UIHolder` 都应通过 **UI 绑定工具自动生成** +- 业务代码通常只编写 `UIWindow`、`UIWidget`、`UITabWindow` 这类逻辑类 + +换句话说: + +- **UIHolder 负责“控件引用和预制体桥接”** +- **UI 逻辑类负责“界面行为和生命周期”** + +## 快速开始 + +1. 在场景中挂载 `UIComponent` +2. 为 `UIComponent.uiRoot` 指定 UI 根预制体 +3. 在 `UISettingEditorWindow` 中配置 UI 生成规则 +4. 选中 UI 预制体根节点,执行 `GameObject/UI生成绑定` +5. 让生成器自动生成 `UIHolder` 类并挂回预制体 +6. 在逻辑类中使用 `UIWindow` 或 `UIWidget`,其中 `T` 就是生成出来的 `UIHolder` 类型 + +## 架构说明 + +```text +UIComponent + └─ UIService + ├─ UIBase + ├─ UIWindow + ├─ UIWidget + ├─ UITabWindow + ├─ UIHolderObjectBase + ├─ UIMetaRegistry + ├─ UIResRegistry + └─ UIHolderFactory +``` + +### 角色分工 + +- `UIBase`:UI 逻辑生命周期基类 +- `UIWindow`:顶层窗口逻辑 +- `UIWidget`:挂在窗口或其他 Widget 下的子部件逻辑 +- `UITabWindow`:支持 Tab 页懒加载与切换的窗口基类 +- `UIHolderObjectBase`:预制体绑定脚本基类,负责暴露控件引用、`RectTransform`、转场播放器 +- `UIHolderFactory`:根据注册信息加载预制体并创建对应 Holder + +### `UIBase`、`UIHolderObjectBase`、`UIWidget` 的关系 + +```text +UI 预制体 + └─ 挂载生成的 XXXHolder : UIHolderObjectBase + ↑ +UIWidget / UIWindow + ↑ + 通过泛型参数 T 访问 baseui +``` + +说明: + +- `UIHolderObjectBase` 挂在预制体上,持有控件引用 +- `UIWindow` / `UIWidget` 的泛型参数 `T` 指向该 Holder 类型 +- 在逻辑类内部可以通过 `baseui` 访问生成好的控件字段 + +## UIHolder 的作用 + +`UIHolderObjectBase` 的职责不是写业务逻辑,而是充当: + +- **控件引用容器**:保存按钮、文本、图片、节点等引用 +- **预制体桥接层**:让 UI 逻辑层不直接依赖层级查找 +- **生命周期事件承载层**:暴露 `OnWindowInitEvent`、`OnWindowAfterShowEvent` 等事件 +- **转场入口**:自动寻找并驱动 `IUITransitionPlayer` + +因此推荐做法是: + +- 业务不手写具体 `XXXHolder` +- 由 UI 绑定工具从预制体结构自动生成 + +## UIHolder 自动生成工作流 + +### 1. 配置生成规则 + +先在编辑器中配置 `UIGenerateConfiguration`,核心配置包括: + +- `UIPrefabRootPath`:UI 预制体根目录 +- `GenerateHolderCodePath`:生成代码输出目录 +- `NameSpace`:生成类所在命名空间 +- `LoadType`:`Resources` 或 `AssetBundle` + +这些配置通常通过 `UISettingEditorWindow` 维护。 + +### 2. 按命名规则搭建 UI 预制体 + +生成器会扫描 UI 节点名和组件类型。例如默认规则会识别: + +- `Btn` → 按钮组件 +- `Text` → `TextMeshProUGUI` +- `Img` → `Image` +- `Tf` → `Transform` +- `Rect` → `RectTransform` + +例如: + +- `Btn#Close@` +- `Text#Title@` +- `Img#Icon@` + +生成器会根据这些节点名推断字段名和字段类型。 + +### 3. 选中 UI 预制体根节点 + +支持两种常见操作方式: + +- 在 Project 中选中 prefab 资源 +- 或在 Prefab Mode 中编辑当前预制体 + +### 4. 执行绑定工具 + +菜单入口: + +- `GameObject/UI生成绑定` + +执行后生成器会: + +1. 读取当前 UI 生成配置 +2. 校验预制体路径是否位于配置的 UI 根目录 +3. 扫描可绑定节点 +4. 生成 `XXXHolder` 代码文件 +5. 脚本编译后自动把生成的 `XXXHolder` 挂到目标预制体根节点 +6. 自动回填对应字段引用 + +这意味着: + +- **正常情况下不需要手动创建 Holder 脚本** +- **也不需要手工把字段拖到 Inspector** + +### 5. 生成代码的结果 + +生成的 `UIHolder` 类本质上: + +- 继承自 `UIHolderObjectBase` +- 带有 `UIResAttribute` +- 包含自动生成的控件字段 + +形态类似: + +```csharp +using AlicizaX.UI.Runtime; + +[UIRes(InventoryItemHolder.ResTag, EUIResLoadType.AssetBundle)] +public class InventoryItemHolder : UIHolderObjectBase +{ + public const string ResTag = "UI/Inventory/InventoryItem.prefab"; + + [UnityEngine.SerializeField] private UnityEngine.UI.Button uiBtnClose; + [UnityEngine.SerializeField] private TMPro.TextMeshProUGUI uiTextTitle; + + public UnityEngine.UI.Button BtnClose => uiBtnClose; + public TMPro.TextMeshProUGUI TextTitle => uiTextTitle; +} +``` + +> 上面是示意结构;实际字段名由生成规则决定。 + +## 在 `UIWidget` 中如何引用生成的 UIHolder + +关键点: + +- `T` 就是生成工具输出的 Holder 类型 +- 逻辑类不需要自己声明控件字段 +- 通过 `baseui` 访问自动生成的 Holder 成员 + +例如: + +```csharp +using AlicizaX.UI.Runtime; +using UnityEngine; + +public sealed class InventoryItemWidget : UIWidget +{ + protected override void OnInitialize() + { + baseui.BtnClose.onClick.AddListener(OnClickClose); + } + + protected override void OnOpen() + { + baseui.TextTitle.text = "Potion"; + } + + private void OnClickClose() + { + Close(); + Destroy(); + } +} +``` + +这里的含义是: + +- `InventoryItemHolder` 由 UI 绑定工具生成 +- `InventoryItemWidget` 是手写业务逻辑 +- `UIWidget` 把逻辑和绑定类关联起来 + +## 核心类与接口 + +### `IUIService` + +负责: + +- 初始化 UI 根节点 +- 打开/关闭窗口 +- 查询已打开窗口 +- 获取层级根节点 +- 注入 `ITimerService` + +### `UIBase` + +关键生命周期: + +- `OnInitialize()` +- `OnOpen()` +- `OnClose()` +- `OnDestroy()` +- `OnUpdate()` + +以及对应异步版本: + +- `OnInitializeAsync()` +- `OnOpenAsync()` +- `OnCloseAsync()` + +并提供: + +- `CreateWidgetAsync()` +- `CreateWidgetSync()` +- `RemoveWidget(UIBase widget)` + +### `UIWindow` + +适合顶层窗口,通常用于: + +- 主界面 +- 设置页 +- 背包页 +- 弹窗 + +常用能力: + +- `CloseSelf()` +- 强制关闭 +- 打开后顶层排序与层级遮挡处理 + +### `UIWidget` + +适合子部件,通常用于: + +- 列表项 +- 面板块 +- 详情条目 +- 页签子页面 + +公开方法: + +- `Open(params object[] userDatas)` +- `Close()` +- `Destroy()` + +### `UITabWindow` + +用于页签式窗口,支持: + +- 预注册 Tab +- 按需懒加载 +- `SwitchTab(int index, params object[] userDatas)` + +### `UIHolderObjectBase` + +核心成员: + +- `Target` +- `RectTransform` +- `Visible` +- `OnWindowInitEvent` +- `OnWindowBeforeShowEvent` +- `OnWindowAfterShowEvent` +- `OnWindowBeforeClosedEvent` +- `OnWindowAfterClosedEvent` +- `OnWindowDestroyEvent` + +### `UIHolderFactory` + +`UIHolderFactory` 是 UI 资源实例化与 Holder 绑定的桥梁,作用是: + +- 根据 `UIResRegistry` 中登记的资源信息定位 UI 预制体 +- 调用 `IResourceService` 或 `Resources` 加载 UI 资源 +- 实例化预制体并获取对应的 `UIHolderObjectBase` +- 把生成的 Holder 绑定到 `UIWindow` / `UIWidget` 对应的逻辑实例上 + +你通常**不会在业务层频繁直接调用它**,因为: + +- 打开窗口时,`UIService` 会在内部调用 `UIHolderFactory` +- 创建 Widget 时,`UIBase.CreateWidgetAsync()` / `CreateWidgetSync()` 也会在内部调用它 + +可以把它理解为: + +```text +UI 逻辑类 + -> UIService / UIBase + -> UIHolderFactory + -> 加载预制体 + -> 找到生成的 XXXHolder + -> 绑定到 UIWindow / UIWidget +``` + +#### 典型作用场景 + +1. `ShowUI()` + - `UIService` 找到 `InventoryWindow` 对应的元数据 + - `UIHolderFactory` 根据 `InventoryWindowHolder` 的 `UIResAttribute` 加载预制体 + - 创建并返回 `InventoryWindowHolder` + - 把 Holder 绑定给 `InventoryWindow` + +2. `CreateWidgetAsync(parent)` + - `UIBase` 创建 `InventoryItemWidget` 的元数据 + - `UIHolderFactory` 加载 `InventoryItemHolder` 对应的 Widget 预制体 + - 把 Holder 绑定给 `InventoryItemWidget` + +#### 直接调用示例 + +虽然业务层通常不需要直接调用,但在工具代码、调试代码或特殊预加载场景下,可以这样使用: + +```csharp +using AlicizaX.UI.Runtime; +using Cysharp.Threading.Tasks; +using UnityEngine; + +public sealed class UIHolderFactoryExample : MonoBehaviour +{ + [SerializeField] private Transform previewRoot; + + private async UniTaskVoid Start() + { + InventoryItemHolder holder = await UIHolderFactory.CreateUIHolderAsync(previewRoot); + if (holder != null) + { + holder.TextName.text = "Preview Item"; + holder.TextCount.text = "99"; + } + } +} +``` + +同步版本示例: + +```csharp +using AlicizaX.UI.Runtime; +using UnityEngine; + +public sealed class UIHolderFactorySyncExample : MonoBehaviour +{ + [SerializeField] private Transform previewRoot; + + private void Start() + { + InventoryItemHolder holder = UIHolderFactory.CreateUIHolderSync(previewRoot); + if (holder != null) + { + holder.TextName.text = "Sync Preview"; + holder.TextCount.text = "1"; + } + } +} +``` + +#### 注意事项 + +- `T` 必须是正确的生成型 `UIHolder`,且继承自 `UIHolderObjectBase` +- 对应 Holder 需要已具备 `UIResAttribute`,通常由绑定工具自动生成 +- 如果资源路径错误、预制体未挂对应 Holder,`UIHolderFactory` 绑定会失败 +- 正常业务打开窗口和创建 Widget 时,优先走 `GameApp.UI.ShowUI()`、`CreateWidgetAsync()`,不建议绕过框架直接大量使用工厂 + +## API 参考 + +### `IUIService.Initialize(Transform root, bool isOrthographic)` + +- 必填参数:`root` +- 必填参数:`isOrthographic` +- 返回值:无 +- 说明:初始化 UI 根、Canvas、Camera 与各层级节点 + +### `UniTask ShowUI(params object[] userDatas) where T : UIBase` + +- 可选参数:`userDatas` +- 返回值:`UniTask` +- 泛型约束:`T : UIBase` +- 说明:异步打开窗口 +- 推荐:默认优先使用该方法 + +### `T ShowUISync(params object[] userDatas) where T : UIBase` + +- 返回值:`T` +- 说明:同步打开窗口 +- 注意:仅在资源已就绪时使用 + +### `void CloseUI(bool force = false) where T : UIBase` + +- 可选参数:`force` +- 返回值:无 +- 说明:关闭指定窗口 + +### `CreateWidgetAsync(Transform parent, bool visible = true) where T : UIBase` + +- 必填参数:`parent` +- 可选参数:`visible` +- 返回值:`UniTask` +- 泛型约束:`T : UIBase` +- 说明:从父 UI 创建 Widget + +### `RemoveWidget(UIBase widget)` + +- 必填参数:`widget` +- 返回值:`UniTask` +- 说明:从父 UI 中移除并销毁 Widget + +## 完整使用示例 + +### 示例 1:窗口逻辑 + 自动生成 Holder + +```csharp +using AlicizaX.UI.Runtime; +using UnityEngine; + +[Window(UILayer.UI, fullScreen: true, cacheTime: 10)] +public sealed class InventoryWindow : UIWindow +{ + protected override async Cysharp.Threading.Tasks.UniTask OnInitializeAsync() + { + baseui.BtnClose.onClick.AddListener(CloseSelf); + + InventoryItemWidget item = await CreateWidgetAsync(baseui.TfContent, false); + item.Open("Potion", 10); + } + + protected override void OnOpen() + { + baseui.TextTitle.text = "Inventory"; + } +} +``` + +说明: + +- `InventoryWindowHolder` 推荐由 UI 绑定工具生成 +- `InventoryWindow` 由业务手写 + +### 示例 2:Widget 使用生成的 Holder + +```csharp +using AlicizaX.UI.Runtime; +using UnityEngine; + +public sealed class InventoryItemWidget : UIWidget +{ + private string _itemName; + private int _count; + + protected override void OnInitialize() + { + baseui.BtnUse.onClick.AddListener(OnClickUse); + } + + protected override void OnOpen() + { + _itemName = (string)UserDatas[0]; + _count = (int)UserDatas[1]; + + baseui.TextName.text = _itemName; + baseui.TextCount.text = _count.ToString(); + } + + protected override void OnClose() + { + baseui.TextName.text = string.Empty; + baseui.TextCount.text = string.Empty; + } + + private void OnClickUse() + { + Debug.Log($"Use item: {_itemName}"); + Close(); + Destroy(); + } +} +``` + +### 示例 3:TabWindow + +```csharp +using AlicizaX.UI.Runtime; + +[Window(UILayer.UI, fullScreen: true)] +public sealed class SettingWindow : UITabWindow +{ + protected override void OnInitialize() + { + InitTabVirtuallyView(baseui.TfTabRoot); + InitTabVirtuallyView(baseui.TfTabRoot); + + baseui.BtnGraphics.onClick.AddListener(() => SwitchTab(0)); + baseui.BtnAudio.onClick.AddListener(() => SwitchTab(1)); + } + + protected override void OnOpen() + { + SwitchTab(0); + } +} +``` + +## 最佳实践 + +- **不要手写大多数 UIHolder**,优先使用自动生成 +- 窗口逻辑类只处理状态和行为,控件引用统一放进 Holder +- `OnInitialize` 做一次性事件绑定,`OnOpen` 做参数刷新 +- 默认使用异步打开,避免首帧阻塞 +- 列表项和重复块优先拆成 `UIWidget` + +## 常见错误 + +### 手工编写 UIHolder 导致与生成器冲突 + +- 现象:字段名、命名空间或资源路径不一致 +- 规避:把 Holder 视为生成产物,由工具维护 + +### 在 `OnOpen` 中重复注册按钮事件 + +- 风险:窗口每次打开都会重复绑定 +- 正确做法:放到 `OnInitialize` + +### 把 `UIWidget` 当顶层窗口直接 `ShowUI` + +- `UIWidget` 应由父 `UIBase` 通过 `CreateWidgetAsync()` 或 `CreateWidgetSync()` 创建 + +## 性能注意事项 + +- 首次打开大窗口优先预热资源或异步显示 +- 使用自动生成 Holder 可以避免大量运行时查找和手工拖引用错误 +- 高频销毁/重建的块状内容优先用 `UIWidget` diff --git a/Client/Assets/Books/Framework/Runtime/UI.md.meta b/Client/Assets/Books/Framework/Runtime/UI.md.meta new file mode 100644 index 0000000..d20010e --- /dev/null +++ b/Client/Assets/Books/Framework/Runtime/UI.md.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 1fe4c4405e0dfd54cbe0f51ac2fbfe4c +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Client/Assets/Books/UIExtension.meta b/Client/Assets/Books/UIExtension.meta new file mode 100644 index 0000000..dcaca12 --- /dev/null +++ b/Client/Assets/Books/UIExtension.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 6877cc0b0a167be4aacecb525d56cc60 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Client/Assets/Books/UIExtension/InputGlyph.md b/Client/Assets/Books/UIExtension/InputGlyph.md new file mode 100644 index 0000000..cbe016c --- /dev/null +++ b/Client/Assets/Books/UIExtension/InputGlyph.md @@ -0,0 +1,94 @@ +# UIExtension InputGlyph 输入图标模块手册 + +## 模块概述 +`InputGlyph` 提供输入设备检测、绑定解析、图标数据库查询、UI Image/TMP 输出和重绑定后自动刷新能力。 + +## 适用场景 +- 显示键盘、手柄按键图标。 +- 文本中嵌入按键提示。 +- 输入重绑定后自动刷新 UI 提示。 + +## 快速上手 +1. 创建 `InputGlyphDatabase` 并放到 `Resources/InputGlyphDatabase.asset`。 +2. 给目标对象挂 `InputGlyph`。 +3. 选择动作来源与输出模式。 + +## 详细使用说明 +- `InputGlyph` 会监听 `InputDeviceWatcher` 与 `InputBindingManager.BindingsChanged`。 +- 输出为 `Image` 时设置 `targetImage.sprite`。 +- 输出为 `Text` 时写入 TMP `` 标签或回退显示文本。 + +## 可调用 API +### 类型:`InputGlyph` +源码:`Packages/com.alicizax.unity.ui.extension/Runtime/InputGlyph/InputGlyph.cs` +- 枚举:`ActionSourceMode`、`OutputMode` +- 类型:`DeviceCategoryEvent` + - `category` + - `onMatched` + - `onNotMatched` + +### 类型:`InputGlyphBehaviourBase` +源码:`Packages/com.alicizax.unity.ui.extension/Runtime/InputGlyph/InputGlyphBehaviourBase.cs` +- 说明:抽象基类,负责设备变化与绑定变化监听。 + +### 类型:`GlyphService` +源码:`Packages/com.alicizax.unity.ui.extension/Runtime/InputGlyph/Core/GlyphService.cs` +- `GetBindingControlPath(InputAction action, string compositePartName = null, InputDeviceWatcher.InputDeviceCategory? deviceOverride = null)` +- `GetBindingControlPath(InputActionReference actionReference, string compositePartName = null, InputDeviceWatcher.InputDeviceCategory? deviceOverride = null)` +- `TryGetTMPTagForActionPath(InputAction action, string compositePartName, InputDeviceWatcher.InputDeviceCategory device, out string tag, out string displayFallback, InputGlyphDatabase db = null)` +- `TryGetTMPTagForActionPath(InputActionReference actionReference, string compositePartName, InputDeviceWatcher.InputDeviceCategory device, out string tag, out string displayFallback, InputGlyphDatabase db = null)` +- `TryGetUISpriteForActionPath(InputAction action, string compositePartName, InputDeviceWatcher.InputDeviceCategory device, out Sprite sprite, InputGlyphDatabase db = null)` +- `TryGetUISpriteForActionPath(InputActionReference actionReference, string compositePartName, InputDeviceWatcher.InputDeviceCategory device, out Sprite sprite, InputGlyphDatabase db = null)` +- `TryGetTMPTagForActionPath(string controlPath, InputDeviceWatcher.InputDeviceCategory device, out string tag, out string displayFallback, InputGlyphDatabase db = null)` +- `TryGetUISpriteForActionPath(string controlPath, InputDeviceWatcher.InputDeviceCategory device, out Sprite sprite, InputGlyphDatabase db = null)` +- `GetDisplayNameFromInputAction(InputAction action, string compositePartName = null, InputDeviceWatcher.InputDeviceCategory? deviceOverride = null)` +- `GetDisplayNameFromControlPath(string controlPath)` +- `TryGetBindingControl(InputAction action, string compositePartName, InputDeviceWatcher.InputDeviceCategory? deviceOverride, out InputBinding binding)` + +### 类型:`InputActionReader` +源码:`Packages/com.alicizax.unity.ui.extension/Runtime/InputGlyph/Core/InputActionReader.cs` +- `ReadValue(string actionName)` +- `ReadValue(string actionName)` +- `TryReadValue(string actionName, out T value)` +- `TryReadValue(string actionName, out object value)` +- `TryReadValueOnce(UnityEngine.Object owner, string actionName, out T value)` +- `ReadButton(string actionName)` +- `ReadButtonOnce(UnityEngine.Object owner, string actionName)` +- `ReadButtonOnce(int instanceID, string actionName)` +- `ReadButtonOnce(string key, string actionName)` +- `ReadButtonToggle(UnityEngine.Object owner, string actionName)` +- `ReadButtonToggle(int instanceID, string actionName)` +- `ReadButtonToggle(string key, string actionName)` +- `ResetToggledButton(string key, string actionName)` +- `ResetToggledButton(string actionName)` +- `ResetToggledButtons()` + +### 类型:`InputBindingManager` +源码:`Packages/com.alicizax.unity.ui.extension/Runtime/InputGlyph/Core/InputBindingManager.cs` +- 公开字段:`actions`、`debugMode` +- 事件:`OnApply`、`OnRebindPrepare`、`OnRebindStart`、`OnRebindEnd`、`OnRebindConflict`、`BindingsChanged` +- 属性:`ActionMaps`、`PreparedRebinds` +- 方法:`FindBestBindingIndexForKeyboard(...)`、`Action(string actionName)`、`TryGetAction(...)`、`StartRebind(...)`、`CancelRebind()`、`ConfirmApply(...)`、`DiscardPrepared()`、`ResetToDefaultAsync()`、`GetBindingPath(...)` +- 公开嵌套类型:`ActionMap`、`Action`、`Binding`、`BindingPath`、`RebindContext` + +### 类型:`InputDeviceWatcher` +源码:`Packages/com.alicizax.unity.ui.extension/Runtime/InputGlyph/Core/InputDeviceWatcher.cs` +- 枚举:`InputDeviceCategory` +- 结构:`DeviceContext` +- 属性:`CurrentCategory`、`CurrentDeviceName`、`CurrentDeviceId`、`CurrentVendorId`、`CurrentProductId`、`CurrentContext` +- 事件:`OnDeviceChanged`、`OnDeviceContextChanged` +- 方法:`Initialize()`、`Dispose()` + +### 类型:`InputGlyphDatabase` +源码:`Packages/com.alicizax.unity.ui.extension/Runtime/InputGlyph/Data/InputGlyphDatabase.cs` +- 字段:`tables`、`placeholderSprite` +- 方法:`GetTable(string deviceName)`、`GetTable(InputDeviceWatcher.InputDeviceCategory device)`、`GetPlatformIcon(...)`、`TryGetSprite(...)`、`FindSprite(...)`、`FindEntryByControlPath(...)`、`EditorRefreshCache()`、`EditorNormalizeControlPath(...)` +- 类型:`GlyphEntry`、`DeviceGlyphTable` + +## 示例 +```csharp +if (GlyphService.TryGetUISpriteForActionPath(actionRef, null, InputDeviceWatcher.CurrentCategory, out var sprite)) +{ + image.sprite = sprite; +} +``` diff --git a/Client/Assets/Books/UIExtension/InputGlyph.md.meta b/Client/Assets/Books/UIExtension/InputGlyph.md.meta new file mode 100644 index 0000000..44c28ee --- /dev/null +++ b/Client/Assets/Books/UIExtension/InputGlyph.md.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: d9b74be0f15fe854fadca69b14d98883 +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Client/Assets/Books/UIExtension/RecyclerView.md b/Client/Assets/Books/UIExtension/RecyclerView.md new file mode 100644 index 0000000..5334ac6 --- /dev/null +++ b/Client/Assets/Books/UIExtension/RecyclerView.md @@ -0,0 +1,2402 @@ +# RecyclerView 专业手册 + +> 📌 本文档基于 `Packages/com.alicizax.unity.ui.extension/Runtime/RecyclerView` 的运行时 API 编写,重点说明业务侧可直接调用的能力;文中提到的 `private` / `internal` 成员仅用于解释内部机制,不属于业务层 API。 +> +> ⚠️ `SetAdapter()`、`Reset()`、`Refresh()`、`RequestLayout()` 已收敛为框架内部流程;业务层请通过 `UGList`、`UGListCreateHelper`、`UGList.Data`、`Adapter.SetList()`、`Notify*()` 等正式入口驱动列表更新,不要手动调用这些底层方法。所有 UI 相关调用仍应统一放在 Unity 主线程。 +> +> 💡 `RecyclerView` 的 `LayoutManager` 与 `Scroller` 通过 Inspector 序列化引用配置,业务代码通常只“读取与使用”,不建议在运行中直接替换类型。 + +## 目录 + +- [模块概览](#overview) +- [基础概念与共享示例类型](#basics) +- [容器(RecyclerView)](#container) + - [API 说明](#container-api) + - [初始化与刷新](#container-init) + - [定位与焦点控制](#container-focus) +- [适配器(Adapter / GroupAdapter / LoopAdapter / MixedAdapter)](#adapter) + - [API 说明](#adapter-api) + - [基础数据更新](#adapter-data) + - [多模板、分组与循环](#adapter-advanced) +- [布局(LayoutManager 系列)](#layout) + - [API 说明](#layout-api) + - [线性与网格布局](#layout-basic) + - [分页、圆环与异构长度布局](#layout-advanced) +- [滚动器(Scroller / CircleScroller)](#scroller) + - [API 说明](#scroller-api) + - [程序化滚动](#scroller-programmatic) + - [惯性、吸附与自动播放](#scroller-advanced) +- [视图池(ViewProvider / ObjectPool)](#pool) + - [API 说明](#pool-api) + - [预热与复用](#pool-basic) + - [多模板对象池配置](#pool-mixed) +- [导航(RecyclerNavigation)](#navigation) + - [API 说明](#navigation-api) + - [列表内导航](#navigation-inner) + - [列表入口与焦点恢复](#navigation-entry) +- [业务包装层(UGList / UGListExtensions)](#uglist) + - [API 说明](#uglist-api) + - [快速创建与滚动扩展](#uglist-basic) + - [业务封装示例](#uglist-advanced) +- [场景专题](#scenarios) + - [大量列表项复用(含对象池配置)](#scenario-massive) + - [多模板混排](#scenario-mixed) + - [分页加载(加载态、空态、错误态)](#scenario-paging) + - [轮播与循环滚动(自动播放、手动切换)](#scenario-carousel) + - [手柄/键盘导航结合列表滚动](#scenario-nav-scroll) +- [FAQ](#faq) +- [Anti-patterns](#anti-patterns) +- [性能优化建议](#performance) +- [交付前检查清单](#checklist) + + +## 模块概览 + +`RecyclerView` 是一套面向 Unity UGUI 的高性能列表组件,职责拆分如下: + +- 容器:`RecyclerView` 负责模板、可见区、滚动同步、滚动条、焦点恢复。 +- 适配器:`Adapter` 家族负责数据量、模板选择、视图绑定、局部刷新。 +- 布局:`LayoutManager` 家族负责位置计算、可见区间与索引转换。 +- 滚动器:`Scroller` 家族负责拖拽、滚轮、惯性、吸附与平滑滚动。 +- 视图池:`ViewProvider` + `ObjectPool` 负责创建、回收、预热与统计。 +- 导航:`RecyclerNavigationController` 家族负责手柄/键盘在列表内的可达性。 +- 业务包装层:`UGList` 家族负责降低泛型与注册样板代码。 + +> 💡 一般调用顺序是:准备模板与布局 → 创建业务包装层/适配器包装 → 注册 `ItemRender` → 赋值数据;后续布局计算、池预热与首屏刷新由框架自动完成。 + +> ⚠️ 如果 `Templates` 为空、`LayoutManager` 未配置或 `Content` 解析失败,`RecyclerView` 会在首次运行时抛出错误或记录错误日志。 + + +## 基础概念与共享示例类型 + +本节给出后续示例默认共用的最小类型。除非某个示例额外声明了自己的 `Data` / `Holder` / `Render`,否则都可以直接与以下代码一起编译。 + +### 示例基础类型 + +#### 共享类型代码 +```csharp +using AlicizaX.UI; +using UnityEngine; +using UnityEngine.EventSystems; +using UnityEngine.UI; + +namespace RecyclerViewBookSamples +{ + [System.Serializable] + public sealed class DemoSimpleData : ISimpleViewData + { + public string Title; + public string Subtitle; + public Sprite Icon; + } + + [System.Serializable] + public sealed class DemoMixedData : IMixedViewData + { + public string TemplateName { get; set; } // 必须与模板名完全一致 + public string Title; + public string Subtitle; + public Sprite Icon; + } + + [System.Serializable] + public sealed class DemoGroupData : IGroupViewData + { + public string TemplateName { get; set; } // 组头与普通项共用此字段 + public bool Expanded { get; set; } // 组头展开状态 + public int Type { get; set; } // 分组键 + public string Title; + } + + public sealed class DemoItemHolder : ViewHolder + { + public Text title; + public Text subtitle; + public Image icon; + public Button actionButton; + public GameObject selectedMarker; + } + + public sealed class DemoStateHolder : ViewHolder + { + public Text message; + public Button retryButton; + } + + public sealed class DemoSimpleRender : ItemRender + { + protected override void OnBind(DemoSimpleData data, int index) + { + Holder.title.text = $"{index + 1}. {data.Title}"; // 绑定标题 + Holder.subtitle.text = data.Subtitle; // 绑定副标题 + Holder.icon.sprite = data.Icon; // 绑定图标 + } + + protected override void OnSelectionChanged(bool selected) + { + if (Holder.selectedMarker != null) + { + Holder.selectedMarker.SetActive(selected); // 显示选中态 + } + } + } + + public sealed class DemoMixedRender : ItemRender + { + protected override void OnBind(DemoMixedData data, int index) + { + Holder.title.text = $"{index + 1}. {data.Title}"; // 绑定标题 + Holder.subtitle.text = data.Subtitle; // 绑定副标题 + Holder.icon.sprite = data.Icon; // 绑定图标 + } + } +} +``` + +> 📌 所有示例都默认你已经在 Inspector 中把模板预制体上的 `Text`、`Image`、`Button` 等引用拖好。 + + +## 容器(RecyclerView) + +`RecyclerView` 是系统的中心节点:它管理模板、内容区域、当前适配器、布局、滚动器、滚动条以及焦点导航。 + + +### API 说明 + +#### 类型:`RecyclerView` + +源码位置:`Packages/com.alicizax.unity.ui.extension/Runtime/RecyclerView/RecyclerView.cs` + +**属性** + +| 成员名 | 类型 | 默认值 | 说明 | 使用限制 | +| --- | --- | --- | --- | --- | +| `Direction` | `Direction` | `Direction.Vertical`(未序列化时) | 主滚动方向。 | 与 `LayoutManager`、`Scroller` 的方向必须一致。 | +| `Alignment` | `Alignment` | `Alignment.Left`(未序列化时) | 交叉轴对齐方式。 | `Alignment.Center` 会影响内容偏移计算。 | +| `Spacing` | `Vector2` | `Vector2.zero` | Item 间距。 | 竖向列表通常使用 `y`;横向列表通常使用 `x`。 | +| `Padding` | `Vector2` | `Vector2.zero` | 内容内边距。 | 只影响布局计算,不会自动改模板尺寸。 | +| `Scroll` | `bool` | `false` | 是否启用滚动。 | 关闭后 `ScrollTo*()` 直接失效。 | +| `Snap` | `bool` | `false` | 是否在停下后吸附到最近项。 | 只有 `Scroll == true` 时才会真正生效。 | +| `ScrollSpeed` | `float` | `7f` | 平滑滚动速度。 | 建议保持在 `0.5f ~ 50f`。 | +| `WheelSpeed` | `float` | `30f` | 滚轮速度。 | 建议保持在 `1f ~ 50f`。 | +| `ShowScrollBarOnlyWhenScrollable` | `bool` | `false` | 是否仅在内容溢出时显示滚动条。 | 仅对横向/纵向方向生效。 | +| `Templates` | `ViewHolder[]` | `null` | 视图模板集合。 | 不能为空;多模板时模板名必须唯一。 | +| `Content` | `RectTransform` | `null`,首次访问时尝试取第一个子节点 | 内容容器。 | 第一个子节点必须有 `RectTransform`。 | +| `Scrollbar` | `Scrollbar` | `null` | 关联滚动条。 | 仅在启用滚动条时使用。 | +| `Scroller` | `Scroller` | `null` | 当前滚动器实例。 | 通常由 Inspector 指定为 `Scroller` 或 `CircleScroller`。 | +| `ViewProvider` | `ViewProvider` | 懒加载 | 模板创建/回收提供器。 | 依赖 `Templates` 正常初始化。 | +| `PoolStats` | `string` | `string.Empty` | 当前视图池统计文本。 | 仅用于诊断,格式不保证长期稳定。 | +| `LayoutManager` | `LayoutManager` | `null` | 当前布局实例。 | 必须先配置,否则列表初始化时无法完成绑定。 | +| `NavigationController` | `RecyclerNavigationController` | 懒加载 | 列表导航控制器。 | 仅在导航场景下用到。 | + +**事件** + +| 事件名 | 类型 | 默认值 | 说明 | 使用限制 | +| --- | --- | --- | --- | --- | +| `OnIndexChanged` | `Action` | `null` | 当前逻辑索引变化时触发。 | 对循环列表返回的是“真实索引”。 | +| `OnScrollValueChanged` | `Action` | `null` | 滚动位置变化时触发。 | 高频事件,不要在回调里做重活。 | + +> 💡 业务层如需感知当前焦点项,请监听 `OnIndexChanged`;如需读写业务选中态,请使用 `Adapter.ChoiceIndex`。 + +**方法** + +| 方法 | 返回值 | 默认参数 | 说明 | 使用限制 | +| --- | --- | --- | --- | --- | +| `TryFocusIndex(int index, bool smooth = false, ScrollAlignment alignment = ScrollAlignment.Center)` | `bool` | `smooth=false`, `alignment=Center` | 滚动并聚焦目标项。 | 目标索引必须在适配器合法范围内。 | +| `TryFocusEntry(MoveDirection entryDirection)` | `bool` | 无 | 从外部把焦点“送入”列表。 | 空列表返回 `false`。 | +| `GetScrollPosition()` | `float` | 无 | 获取当前滚动偏移。 | 没有滚动器时返回 `0`。 | +| `ScrollTo(int index, bool smooth = false)` | `void` | `smooth=false` | 滚到指定项。 | 依赖 `Scroll == true` 且 `Scroller != null`。 | +| `ScrollToWithAlignment(int index, ScrollAlignment alignment, float offset = 0f, bool smooth = false, float duration = 0.3f)` | `void` | `offset=0f`, `smooth=false`, `duration=0.3f` | 以指定对齐方式滚动到目标项。 | `duration` 仅在 `smooth=true` 时有意义。 | + +#### 参数:`TryFocusIndex(int index, bool smooth = false, ScrollAlignment alignment = ScrollAlignment.Center)` + +| 参数名 | 类型 | 默认值 | 说明 | +| --- | --- | --- | --- | +| `index` | `int` | 无 | 目标数据索引。 | +| `smooth` | `bool` | `false` | 是否先平滑滚动到可见区再聚焦。 | +| `alignment` | `ScrollAlignment` | `ScrollAlignment.Center` | 聚焦时目标项的对齐方式。 | + +- 返回值:`bool`,成功定位并设置焦点时为 `true`。 +- 使用限制:空列表、越界索引或没有可聚焦对象都会返回 `false`。 + +#### 参数:`ScrollToWithAlignment(...)` + +| 参数名 | 类型 | 默认值 | 说明 | +| --- | --- | --- | --- | +| `index` | `int` | 无 | 目标索引。 | +| `alignment` | `ScrollAlignment` | 无 | 目标项停靠位置。 | +| `offset` | `float` | `0f` | 在对齐结果基础上的附加偏移。 | +| `smooth` | `bool` | `false` | 是否平滑滚动。 | +| `duration` | `float` | `0.3f` | 平滑滚动时长。 | + +- 返回值:无。 +- 使用限制:`Scroll == false`、`Scroller == null` 或目标索引非法时无效果。 + +> ⚠️ `ScrollTo()` / `TryFocusIndex()` 依赖列表已通过 `UGList.Data`、`Adapter.SetList()` 或 `Notify*()` 完成一次正式更新;布局与刷新会随这些入口自动完成,业务层不要额外手动补 `RequestLayout()` / `Refresh()`。 + + +### 初始化与刷新 + +#### 示例 1:基础纵向列表初始化 +```csharp +using System.Collections.Generic; +using AlicizaX.UI; +using RecyclerViewBookSamples; +using UnityEngine; + +public sealed class BasicRecyclerViewDemo : MonoBehaviour +{ + [SerializeField] private RecyclerView recyclerView; // Inspector: 绑定 LinearLayoutManager + Scroller + + private UGList list; + + private void Awake() + { + list = UGListCreateHelper.Create(recyclerView); + list.RegisterItemRender(); // 注册默认模板渲染器 + list.Data = new List + { + new DemoSimpleData { Title = "邮件", Subtitle = "今天 09:00" }, + new DemoSimpleData { Title = "任务", Subtitle = "今天 10:30" }, + new DemoSimpleData { Title = "公告", Subtitle = "今天 14:00" }, + }; + } +} +``` + +#### 示例 2:异步拉取后再刷新容器 +```csharp +using System.Collections; +using System.Collections.Generic; +using AlicizaX.UI; +using RecyclerViewBookSamples; +using UnityEngine; + +public sealed class AsyncRecyclerViewDemo : MonoBehaviour +{ + [SerializeField] private RecyclerView recyclerView; // Inspector: 模板与 LayoutManager 已配置 + + private UGList list; + private Adapter adapter; + + private IEnumerator Start() + { + list = UGListCreateHelper.Create(recyclerView); + list.RegisterItemRender(); + adapter = list.Adapter; + + yield return new WaitForSeconds(0.5f); // 模拟网络请求 + + list.Data = new List + { + new DemoSimpleData { Title = "远程数据 A", Subtitle = "加载完成" }, + new DemoSimpleData { Title = "远程数据 B", Subtitle = "加载完成" }, + }; // 赋值后框架自动布局并刷新 + } +} +``` + +#### 示例 3:空数据与安全刷新 +```csharp +using System.Collections.Generic; +using AlicizaX.UI; +using RecyclerViewBookSamples; +using UnityEngine; + +public sealed class EmptyStateRecyclerViewDemo : MonoBehaviour +{ + [SerializeField] private RecyclerView recyclerView; + + private UGList list; + + private void OnEnable() + { + list ??= UGListCreateHelper.Create(recyclerView); + list.RegisterItemRender(); + list.Data = new List(); // 空列表也是合法输入 + + bool focused = recyclerView.TryFocusIndex(0); // 空列表会返回 false + Debug.Log($"Focus result: {focused}"); + } +} +``` + + +### 定位与焦点控制 + +#### 示例 1:按钮驱动滚动到指定项 +```csharp +using AlicizaX.UI; +using UnityEngine; +using UnityEngine.UI; + +public sealed class ScrollToIndexDemo : MonoBehaviour +{ + [SerializeField] private RecyclerView recyclerView; + [SerializeField] private Button jumpButton; + + private void Awake() + { + jumpButton.onClick.AddListener(() => + { + recyclerView.ScrollToWithAlignment( + index: 10, + alignment: ScrollAlignment.Center, + offset: 0f, + smooth: true, + duration: 0.25f); // 以 0.25 秒平滑滚到中间 + }); + } +} +``` + +#### 示例 2:打开面板时恢复焦点 +```csharp +using AlicizaX.UI; +using UnityEngine; + +public sealed class FocusRecoveryDemo : MonoBehaviour +{ + [SerializeField] private RecyclerView recyclerView; + [SerializeField] private int lastSelectedIndex = 4; + + private void OnEnable() + { + recyclerView.TryFocusIndex(lastSelectedIndex, true, ScrollAlignment.Center); + } +} +``` + +#### 示例 3:监听焦点索引变化 +```csharp +using AlicizaX.UI; +using UnityEngine; + +public sealed class FocusTraceDemo : MonoBehaviour +{ + [SerializeField] private RecyclerView recyclerView; + + private void Awake() + { + recyclerView.OnIndexChanged += index => + Debug.Log($"Focused data index: {index}"); + } +} +``` + + +## 适配器(Adapter / GroupAdapter / LoopAdapter / MixedAdapter) + +适配器负责把数据对象映射为模板名、绑定流程和局部刷新操作,是业务层最常直接扩展的模块。 + + +### API 说明 + +#### 类型:`Adapter`(`where T : ISimpleViewData`) + +源码位置:`Packages/com.alicizax.unity.ui.extension/Runtime/RecyclerView/Adapter/Adapter.cs` + +**构造与状态** + +| 成员名 | 类型 | 默认值 | 说明 | 使用限制 | +| --- | --- | --- | --- | --- | +| `Adapter(RecyclerView recyclerView)` | 构造函数 | `list = new List()` | 创建空适配器。 | `recyclerView` 不能为空。 | +| `Adapter(RecyclerView recyclerView, List list)` | 构造函数 | `list ?? new List()` | 创建带初始数据的适配器。 | 传入 `null` 会被替换为空列表。 | +| `ChoiceIndex` | `int` | `-1` | 当前选择索引。 | 超出范围会被自动收敛。 | + +**查询与绑定** + +| 方法 | 返回值 | 默认参数 | 说明 | 使用限制 | +| --- | --- | --- | --- | --- | +| `GetItemCount()` | `int` | 无 | 当前布局项数量。 | 普通列表等于数据量;循环列表可能不是。 | +| `GetRealCount()` | `int` | 无 | 真实数据量。 | 循环列表场景优先用它。 | +| `GetViewName(int index)` | `string` | 无 | 返回模板名。 | 单模板默认返回空字符串。 | +| `OnBindViewHolder(ViewHolder viewHolder, int index)` | `void` | 无 | 绑定 Holder。 | 必须先注册与模板匹配的 `ItemRender`。 | +| `OnRecycleViewHolder(ViewHolder viewHolder)` | `void` | 无 | 回收前清理绑定。 | 业务层通常无需手动调用。 | +| `GetData(int index)` | `T` | 无 | 返回数据项。 | 越界返回 `default`。 | + +**刷新通知** + +| 方法 | 返回值 | 默认参数 | 说明 | 使用限制 | +| --- | --- | --- | --- | --- | +| `NotifyDataChanged()` | `void` | 无 | 全量刷新。 | 代价最高。 | +| `SetList(List list)` | `void` | 无 | 替换整个数据源。 | 会触发 `RecyclerView.Reset()`。 | +| `NotifyItemChanged(int index, bool relayout = false)` | `void` | `relayout=false` | 刷新单项。 | 仅当项尺寸不变时推荐 `relayout=false`。 | +| `NotifyItemRangeChanged(int index, int count, bool relayout = false)` | `void` | `relayout=false` | 刷新区间。 | `count <= 0` 直接返回。 | +| `NotifyItemInserted(int index)` | `void` | 无 | 通知插入。 | 当前实现会重新请求布局并刷新。 | +| `NotifyItemRangeInserted(int index, int count)` | `void` | 无 | 通知批量插入。 | `count <= 0` 直接返回。 | +| `NotifyItemRemoved(int index)` | `void` | 无 | 通知删除。 | 当前实现会重新请求布局并刷新。 | +| `NotifyItemRangeRemoved(int index, int count)` | `void` | 无 | 通知批量删除。 | `count <= 0` 直接返回。 | + +**渲染器注册** + +| 方法 | 返回值 | 默认参数 | 说明 | 使用限制 | +| --- | --- | --- | --- | --- | +| `RegisterItemRender(string viewName = "")` | `void` | `viewName=""` | 以泛型注册渲染器。 | 空字符串表示默认模板。 | +| `RegisterItemRender(Type itemRenderType, string viewName = "")` | `void` | `viewName=""` | 以运行时类型注册渲染器。 | 类型必须继承 `ItemRenderBase`。 | +| `UnregisterItemRender(string viewName = "")` | `bool` | `viewName=""` | 注销渲染器。 | 注销后相关缓存会被释放。 | +| `ClearItemRenderRegistrations()` | `void` | 无 | 清空所有注册。 | 适合切换整套模板时使用。 | + +**集合操作** + +| 方法 | 返回值 | 默认参数 | 说明 | 使用限制 | +| --- | --- | --- | --- | --- | +| `Add(T item)` | `void` | 无 | 末尾追加。 | 自动触发插入通知。 | +| `AddRange(IEnumerable collection)` | `void` | 无 | 批量追加。 | `collection == null` 时忽略。 | +| `Insert(int index, T item)` | `void` | 无 | 指定位置插入。 | 越界由 `List` 自己抛错。 | +| `InsertRange(int index, IEnumerable collection)` | `void` | 无 | 批量插入。 | `collection == null` 时忽略。 | +| `Remove(T item)` | `void` | 无 | 删除首个匹配项。 | 找不到时等价于 `RemoveAt(-1)`,最终无效果。 | +| `RemoveAt(int index)` | `void` | 无 | 删除指定索引。 | 越界直接返回。 | +| `RemoveRange(int index, int count)` | `void` | 无 | 删除区间。 | 参数必须满足 `List.RemoveRange` 要求。 | +| `RemoveAll(Predicate match)` | `void` | 无 | 条件删除。 | 始终触发全量刷新。 | +| `Clear()` | `void` | 无 | 清空列表。 | 空列表时直接返回。 | +| `Reverse()` / `Reverse(int index, int count)` | `void` | 无 | 反转顺序。 | 最终触发全量刷新。 | +| `Sort(Comparison comparison)` | `void` | 无 | 排序。 | 最终触发全量刷新。 | + +#### 类型:`GroupAdapter`(`where TData : IGroupViewData, new()`) + +| 方法/成员 | 返回值 | 默认值 | 说明 | 使用限制 | +| --- | --- | --- | --- | --- | +| `GroupAdapter(RecyclerView recyclerView, string groupViewName)` | 构造函数 | 无 | 指定组头模板名。 | `NotifyDataChanged()` 前必须传非空模板名。 | +| `Expand(int index)` | `void` | 无 | 展开组头后的子项。 | `index` 必须指向组头。 | +| `Collapse(int index)` | `void` | 无 | 收起某组。 | 不会改原始 `list`,只改展示列表。 | +| `Activate(int index)` | `void` | 无 | 组头时切换展开;普通项时切换选中。 | 常用于点击事件。 | + +#### 类型:`LoopAdapter` + +| 方法/成员 | 返回值 | 默认值 | 说明 | 使用限制 | +| --- | --- | --- | --- | --- | +| `GetItemCount()` | `int` | 无 | 有数据时返回 `int.MaxValue`。 | 只适合循环/轮播场景。 | +| `GetRealCount()` | `int` | 无 | 返回真实数据量。 | 业务逻辑应优先使用此值。 | +| `OnBindViewHolder(...)` | `void` | 无 | 绑定时对索引做取模。 | 数据为空时不绑定。 | + +#### 类型:`MixedAdapter`(`where TData : IMixedViewData`) + +| 方法/成员 | 返回值 | 默认值 | 说明 | 使用限制 | +| --- | --- | --- | --- | --- | +| `GetViewName(int index)` | `string` | 无 | 返回 `TemplateName`。 | 模板名必须与 `Templates[].name` 一致。 | + +#### 参数:`RegisterItemRender(Type itemRenderType, string viewName = "")` + +| 参数名 | 类型 | 默认值 | 说明 | +| --- | --- | --- | --- | +| `itemRenderType` | `Type` | 无 | 渲染器运行时类型。 | +| `viewName` | `string` | `""` | 关联模板名;空字符串表示默认模板。 | + +- 返回值:无。 +- 使用限制:类型必须可实例化,且继承自 `ItemRenderBase`。 + +#### 参数:`NotifyItemChanged(int index, bool relayout = false)` + +| 参数名 | 类型 | 默认值 | 说明 | +| --- | --- | --- | --- | +| `index` | `int` | 无 | 目标数据索引。 | +| `relayout` | `bool` | `false` | 是否重新计算布局。 | + +- 返回值:无。 +- 使用限制:如果文本高度、图片尺寸、模板类型发生变化,应该传 `relayout=true`。 + + +### 基础数据更新 + +#### 示例 1:通过 `UGList.Adapter` 进行增删改 +```csharp +using System.Collections.Generic; +using AlicizaX.UI; +using RecyclerViewBookSamples; +using UnityEngine; + +public sealed class AdapterCrudDemo : MonoBehaviour +{ + [SerializeField] private RecyclerView recyclerView; + + private UGList list; + private Adapter adapter; + + private void Start() + { + list = UGListCreateHelper.Create(recyclerView); + list.RegisterItemRender(); + list.Data = new List(); + adapter = list.Adapter; + + adapter.Add(new DemoSimpleData { Title = "第一条", Subtitle = "Add()" }); // 自动触发插入 + adapter.Add(new DemoSimpleData { Title = "第二条", Subtitle = "Add()" }); + adapter.Insert(1, new DemoSimpleData { Title = "插入项", Subtitle = "Insert()" }); + } +} +``` + +#### 示例 2:差量刷新单项与区间 +```csharp +using System.Collections.Generic; +using AlicizaX.UI; +using RecyclerViewBookSamples; +using UnityEngine; + +public sealed class AdapterPartialRefreshDemo : MonoBehaviour +{ + [SerializeField] private RecyclerView recyclerView; + + private UGList list; + private Adapter adapter; + private readonly List data = new(); + + private void Start() + { + data.Add(new DemoSimpleData { Title = "HP", Subtitle = "100" }); + data.Add(new DemoSimpleData { Title = "MP", Subtitle = "60" }); + data.Add(new DemoSimpleData { Title = "SP", Subtitle = "30" }); + + list = UGListCreateHelper.Create(recyclerView); + list.RegisterItemRender(); + list.Data = data; + adapter = list.Adapter; + } + + public void UpdateHud() + { + data[0].Subtitle = "95"; + adapter.NotifyItemChanged(0); // 尺寸不变,只重绑可见项 + + data[1].Subtitle = "58"; + data[2].Subtitle = "28"; + adapter.NotifyItemRangeChanged(1, 2); // 批量局部刷新 + } +} +``` + +#### 示例 3:尺寸变化时强制重布局 +```csharp +using System.Collections.Generic; +using AlicizaX.UI; +using RecyclerViewBookSamples; +using UnityEngine; + +public sealed class AdapterRelayoutDemo : MonoBehaviour +{ + [SerializeField] private RecyclerView recyclerView; + + private UGList list; + private Adapter adapter; + private readonly List data = new(); + + private void Start() + { + data.Add(new DemoSimpleData { Title = "短文本", Subtitle = "1 行" }); + list = UGListCreateHelper.Create(recyclerView); + list.RegisterItemRender(); + list.Data = data; + adapter = list.Adapter; + } + + public void ExpandText() + { + data[0].Subtitle = "这一条文本被拉长后可能导致高度变化,因此需要 relayout=true。"; + adapter.NotifyItemChanged(0, relayout: true); // 尺寸变化时必须重布局 + } +} +``` + + +### 多模板、分组与循环 + +#### 示例 1:多模板注册与绑定 +```csharp +using System.Collections.Generic; +using AlicizaX.UI; +using RecyclerViewBookSamples; +using UnityEngine; + +public sealed class DemoLargeHolder : ViewHolder +{ + public UnityEngine.UI.Text title; + public UnityEngine.UI.Text subtitle; +} + +public sealed class DemoLargeRender : ItemRender +{ + protected override void OnBind(DemoMixedData data, int index) + { + Holder.title.text = $"[L] {data.Title}"; // 大卡片模板 + Holder.subtitle.text = data.Subtitle; + } +} + +public sealed class MixedAdapterDemo : MonoBehaviour +{ + [SerializeField] private RecyclerView recyclerView; // Inspector: Templates 含 DemoItemHolder 与 DemoLargeHolder + + private UGMixedList list; + + private void Start() + { + list = UGListCreateHelper.CreateMixed(recyclerView); + list.RegisterItemRender("SmallItem"); // 绑定小模板 + list.RegisterItemRender("LargeItem"); // 绑定大模板 + + list.Data = new List + { + new DemoMixedData { TemplateName = "LargeItem", Title = "头图", Subtitle = "大卡片" }, + new DemoMixedData { TemplateName = "SmallItem", Title = "正文 A", Subtitle = "小卡片" }, + new DemoMixedData { TemplateName = "SmallItem", Title = "正文 B", Subtitle = "小卡片" }, + }; + } +} +``` + +#### 示例 2:分组展开/收起 +```csharp +using System.Collections.Generic; +using AlicizaX.UI; +using RecyclerViewBookSamples; +using UnityEngine; + +public sealed class GroupHeaderRender : ItemRender +{ + protected override void OnBind(DemoGroupData data, int index) + { + Holder.title.text = data.Expanded ? $"▼ {data.Title}" : $"▶ {data.Title}"; + Holder.subtitle.text = $"Type = {data.Type}"; + } +} + +public sealed class GroupItemRender : ItemRender +{ + protected override void OnBind(DemoGroupData data, int index) + { + Holder.title.text = data.Title; + Holder.subtitle.text = $"Group {data.Type}"; + } +} + +public sealed class GroupAdapterDemo : MonoBehaviour +{ + [SerializeField] private RecyclerView recyclerView; + + private UGGroupList list; + + private void Start() + { + list = UGListCreateHelper.CreateGroup(recyclerView, "GroupHeader"); + list.RegisterItemRender("GroupHeader"); + list.RegisterItemRender("GroupItem"); + + list.Data = new List + { + new DemoGroupData { TemplateName = "GroupItem", Type = 1, Title = "剑" }, + new DemoGroupData { TemplateName = "GroupItem", Type = 1, Title = "盾" }, + new DemoGroupData { TemplateName = "GroupItem", Type = 2, Title = "药水" }, + }; + } + + public void ToggleFirstGroup() + { + list.Adapter.Activate(0); // 组头位置会切换 Expanded + } +} +``` + +#### 示例 3:循环适配器的虚拟索引 +```csharp +using System.Collections.Generic; +using AlicizaX.UI; +using RecyclerViewBookSamples; +using UnityEngine; + +public sealed class LoopAdapterDemo : MonoBehaviour +{ + [SerializeField] private RecyclerView recyclerView; + + private UGLoopList list; + private int virtualIndex; + + private void Start() + { + list = UGListCreateHelper.CreateLoop(recyclerView); + list.RegisterItemRender(); + list.Data = new List + { + new DemoSimpleData { Title = "Banner A" }, + new DemoSimpleData { Title = "Banner B" }, + new DemoSimpleData { Title = "Banner C" }, + }; + + virtualIndex = list.Adapter.GetRealCount() * 100; // 从中间起步,便于双向循环 + recyclerView.ScrollToWithAlignment(virtualIndex, ScrollAlignment.Center); + } + + public void MoveNext() + { + virtualIndex += 1; // 维护虚拟索引,而不是只用真实索引 + recyclerView.ScrollToWithAlignment(virtualIndex, ScrollAlignment.Center, 0f, true, 0.2f); + } +} +``` + + +## 布局(LayoutManager 系列) + +布局器只负责“算位置”,不负责数据绑定。它决定内容尺寸、可见区间、索引到坐标的映射以及吸附基准。 + + +### API 说明 + +#### 类型:`LayoutManager` + +源码位置:`Packages/com.alicizax.unity.ui.extension/Runtime/RecyclerView/Layout/LayoutManager.cs` + +**核心属性** + +| 成员名 | 类型 | 默认值 | 说明 | 使用限制 | +| --- | --- | --- | --- | --- | +| `ViewportSize` | `Vector2` | 运行时计算 | 视口尺寸。 | 只有执行过 `SetContentSize()` 后才可靠。 | +| `ContentSize` | `Vector2` | 运行时计算 | 内容总尺寸。 | 滚动条与滚动范围都依赖此值。 | +| `ContentOffset` | `Vector2` | 运行时计算 | 内容对齐偏移。 | 与 `Alignment` 有关。 | +| `ViewportOffset` | `Vector2` | 运行时计算 | 视口偏移。 | 分页/居中场景常用。 | +| `Adapter` | `IAdapter` | `null` | 当前适配器。 | 由容器内部绑定流程注入。 | +| `ViewProvider` | `ViewProvider` | `null` | 当前视图提供器。 | 由容器内部绑定流程注入。 | +| `RecyclerView` | `RecyclerView` | `null` | 所属列表。 | 由容器内部绑定流程注入。 | +| `Direction` | `Direction` | `Direction.Vertical` | 主轴方向。 | 应与容器一致。 | +| `Alignment` | `Alignment` | `Alignment.Left` | 对齐方式。 | `Center` 会改变偏移计算。 | +| `Spacing` | `Vector2` | `Vector2.zero` | 间距。 | 按方向使用对应轴。 | +| `Padding` | `Vector2` | `Vector2.zero` | 内边距。 | 直接参与内容尺寸计算。 | +| `Unit` | `int` | `1` | 每次创建/回收的步进量。 | 网格通常等于一行或一列的元素数。 | +| `ScrollPosition` | `float` | 运行时读取 | 当前滚动位置。 | 只读,来自 `RecyclerView.GetScrollPosition()`。 | + +**通用方法** + +| 方法 | 返回值 | 默认参数 | 说明 | 使用限制 | +| --- | --- | --- | --- | --- | +| `SetContentSize()` | `void` | 无 | 计算视口、内容、偏移。 | 由容器内部布局流程调用。 | +| `UpdateLayout()` | `void` | 无 | 重新布局当前可见 Holder。 | 仅操作已创建视图。 | +| `Layout(ViewHolder viewHolder, int index)` | `void` | 无 | 将指定 Holder 摆放到目标位置。 | 默认按 `CalculatePosition()` 放置。 | +| `CalculateContentSize()` | `Vector2` | 无 | 计算内容总尺寸。 | 由具体布局实现。 | +| `CalculatePosition(int index)` | `Vector2` | 无 | 计算指定索引位置。 | 由具体布局实现。 | +| `CalculateContentOffset()` | `Vector2` | 无 | 计算内容偏移。 | 与对齐方式强相关。 | +| `CalculateViewportOffset()` | `Vector2` | 无 | 计算视口偏移。 | 分页与居中布局更明显。 | +| `GetStartIndex()` / `GetEndIndex()` | `int` | 无 | 当前滚动位置的可见区间。 | `end < start` 时表示当前无需创建项。 | +| `IndexToPosition(int index)` | `float` | 无 | 索引转滚动位置。 | 吸附与 `ScrollTo` 都依赖它。 | +| `PositionToIndex(float position)` | `int` | 无 | 滚动位置转最近索引。 | 吸附时用来找最近项。 | +| `DoItemAnimation()` | `void` | 无 | 执行布局相关动画。 | `PageLayoutManager`、`CircleLayoutManager` 会覆写。 | + +#### 类型:布局派生类 + +| 类型 | 主要特性 | 默认值 | 使用限制 | +| --- | --- | --- | --- | +| `LinearLayoutManager` | 等尺寸线性布局。 | 无额外字段 | 适合单列/单行列表。 | +| `GridLayoutManager` | 网格布局。 | `cellCount = 1` | `cellCount` 通过 Inspector 配置。 | +| `PageLayoutManager` | 分页式线性布局,带缩放动画。 | `minScale = 0.9f` | 常与 `Snap` 联用。 | +| `CircleLayoutManager` | 圆环布局。 | `circleDirection = Positive` | `intervalAngle` 在运行时按项数重算。 | +| `MixedLayoutManager` | 支持不同模板尺寸的异构布局。 | 内部缓存为空 | 最适合多模板或变高度列表。 | + +#### 参数:`IndexToPosition(int index)` + +| 参数名 | 类型 | 默认值 | 说明 | +| --- | --- | --- | --- | +| `index` | `int` | 无 | 目标布局索引。 | + +- 返回值:`float`,滚动器目标位置。 +- 使用限制:越界时通常被实现类收敛到合法范围,但业务层仍应传递有效索引。 + + +### 线性与网格布局 + +#### 示例 1:线性列表 +```csharp +using System.Collections.Generic; +using AlicizaX.UI; +using RecyclerViewBookSamples; +using UnityEngine; + +public sealed class LinearLayoutDemo : MonoBehaviour +{ + [SerializeField] private RecyclerView recyclerView; // Inspector: LayoutManager = LinearLayoutManager + + private UGList list; + + private void Start() + { + list = UGListCreateHelper.Create(recyclerView); + list.RegisterItemRender(); + list.Data = new List + { + new DemoSimpleData { Title = "线性列表 1" }, + new DemoSimpleData { Title = "线性列表 2" }, + new DemoSimpleData { Title = "线性列表 3" }, + }; + } +} +``` + +#### 示例 2:网格背包 +```csharp +using System.Collections.Generic; +using AlicizaX.UI; +using RecyclerViewBookSamples; +using UnityEngine; + +public sealed class GridInventoryDemo : MonoBehaviour +{ + [SerializeField] private RecyclerView recyclerView; // Inspector: LayoutManager = GridLayoutManager, cellCount = 4 + + private UGList list; + + private void Start() + { + list = UGListCreateHelper.Create(recyclerView); + list.RegisterItemRender(); + + var items = new List(); + for (int i = 0; i < 20; i++) + { + items.Add(new DemoSimpleData { Title = $"格子 {i + 1}", Subtitle = $"Index={i}" }); + } + + list.Data = items; + } +} +``` + +#### 示例 3:空网格的边界结果 +```csharp +using System.Collections.Generic; +using AlicizaX.UI; +using RecyclerViewBookSamples; +using UnityEngine; + +public sealed class EmptyGridDemo : MonoBehaviour +{ + [SerializeField] private RecyclerView recyclerView; + + private void Start() + { + var list = UGListCreateHelper.Create(recyclerView); + list.RegisterItemRender(); + list.Data = new List(); // 空网格 + } +} +``` + + +### 分页、圆环与异构长度布局 + +#### 示例 1:分页卡片 +```csharp +using System.Collections.Generic; +using AlicizaX.UI; +using RecyclerViewBookSamples; +using UnityEngine; + +public sealed class PageLayoutDemo : MonoBehaviour +{ + [SerializeField] private RecyclerView recyclerView; // Inspector: LayoutManager = PageLayoutManager, Snap = true + + private UGList list; + + private void Start() + { + list = UGListCreateHelper.Create(recyclerView); + list.RegisterItemRender(); + list.Data = new List + { + new DemoSimpleData { Title = "Page 1" }, + new DemoSimpleData { Title = "Page 2" }, + new DemoSimpleData { Title = "Page 3" }, + }; + } +} +``` + +#### 示例 2:圆环菜单 +```csharp +using System.Collections.Generic; +using AlicizaX.UI; +using RecyclerViewBookSamples; +using UnityEngine; + +public sealed class CircleMenuDemo : MonoBehaviour +{ + [SerializeField] private RecyclerView recyclerView; // Inspector: LayoutManager = CircleLayoutManager, Scroller = CircleScroller + + private UGList list; + + private void Start() + { + list = UGListCreateHelper.Create(recyclerView); + list.RegisterItemRender(); + list.Data = new List + { + new DemoSimpleData { Title = "装备" }, + new DemoSimpleData { Title = "技能" }, + new DemoSimpleData { Title = "任务" }, + new DemoSimpleData { Title = "地图" }, + }; + } +} +``` + +#### 示例 3:异构长度列表 +```csharp +using System.Collections.Generic; +using AlicizaX.UI; +using RecyclerViewBookSamples; +using UnityEngine; + +public sealed class MixedLayoutDemo : MonoBehaviour +{ + [SerializeField] private RecyclerView recyclerView; // Inspector: LayoutManager = MixedLayoutManager + + private UGMixedList list; + + private void Start() + { + list = UGListCreateHelper.CreateMixed(recyclerView); + list.RegisterItemRender("SmallItem"); + list.RegisterItemRender("LargeItem"); // 两个模板尺寸不同 + + list.Data = new List + { + new DemoMixedData { TemplateName = "LargeItem", Title = "长卡片" }, + new DemoMixedData { TemplateName = "SmallItem", Title = "短卡片 A" }, + new DemoMixedData { TemplateName = "SmallItem", Title = "短卡片 B" }, + }; + } +} +``` + + +## 滚动器(Scroller / CircleScroller) + +滚动器负责手势输入与位移变化;布局负责“每个位移意味着什么”。二者解耦后,滚动和显示方式可以灵活组合。 + + +### API 说明 + +#### 类型:`Scroller` + +源码位置:`Packages/com.alicizax.unity.ui.extension/Runtime/RecyclerView/Scroller/Scroller.cs` + +**属性** + +| 成员名 | 类型 | 默认值 | 说明 | 使用限制 | +| --- | --- | --- | --- | --- | +| `Position` | `float` | `0f` | 当前滚动位置。 | 直接赋值不会自动刷新 UI,通常由 `RecyclerView` 驱动。 | +| `Velocity` | `float` | 只读 | 当前速度。 | 仅用于诊断或自定义效果。 | +| `Direction` | `Direction` | `Direction.Vertical` | 滚动方向。 | 应与容器方向一致。 | +| `ContentSize` | `Vector2` | `Vector2.zero` | 内容尺寸。 | 由容器内部布局流程同步更新。 | +| `ViewSize` | `Vector2` | `Vector2.zero` | 视口尺寸。 | 由容器内部布局流程同步更新。 | +| `ScrollSpeed` | `float` | `1f` | `MoveTo` 速度。 | 外部通常通过 `RecyclerView.ScrollSpeed` 配置。 | +| `WheelSpeed` | `float` | `30f` | 滚轮速度。 | 过大容易导致过冲。 | +| `Snap` | `bool` | `false` | 是否启用吸附模式。 | 吸附由容器在停止时完成。 | +| `MaxPosition` | `float` | 只读 | 最大合法滚动位置。 | 由内容和视口尺寸计算。 | +| `ViewLength` | `float` | 只读 | 视口主轴长度。 | 用于边缘阻尼。 | +| `OnValueChanged` | `ScrollerEvent` | 新实例 | 位移变化事件。 | 高频事件。 | +| `OnMoveStoped` | `MoveStopEvent` | 新实例 | 惯性/动画停止事件。 | 拼写保留源码 `Stoped`。 | +| `OnDragging` | `DraggingEvent` | 新实例 | 拖拽开始/结束事件。 | 参数为是否正在拖拽。 | +| `dragStopTime` | `float` | `0f` | 最近一次拖拽时间戳。 | 通常仅供调试。 | + +**方法** + +| 方法 | 返回值 | 默认参数 | 说明 | 使用限制 | +| --- | --- | --- | --- | --- | +| `ScrollTo(float position, bool smooth = false)` | `void` | `smooth=false` | 滚到目标位置。 | 不主动夹取范围,推荐经由 `RecyclerView` 调用。 | +| `ScrollToDuration(float position, float duration)` | `void` | 无 | 按固定时长滚动。 | `duration <= 0` 时立即到位。 | +| `ScrollToRatio(float ratio)` | `void` | 无 | 以归一化滚动条值滚动。 | 通常由滚动条驱动。 | +| `OnBeginDrag(...)` / `OnDrag(...)` / `OnEndDrag(...)` | `void` | 无 | 处理拖拽输入。 | 通常由 Unity 事件系统自动调用。 | +| `OnScroll(PointerEventData eventData)` | `void` | 无 | 处理鼠标滚轮。 | 横向列表读取 `scrollDelta.x`。 | + +#### 类型:`CircleScroller` + +| 方法/成员 | 返回值 | 默认值 | 说明 | 使用限制 | +| --- | --- | --- | --- | --- | +| `GetDelta(PointerEventData eventData)` | `float` | 无 | 根据环形中心计算拖拽方向。 | 仅适合圆环布局。 | +| `Elastic()` | `void` | 无 | 被覆写为空实现。 | 圆环一般不需要边界回弹。 | + +#### 参数:`ScrollTo(float position, bool smooth = false)` + +| 参数名 | 类型 | 默认值 | 说明 | +| --- | --- | --- | --- | +| `position` | `float` | 无 | 目标滚动位置。 | +| `smooth` | `bool` | `false` | 是否使用平滑补间。 | + +- 返回值:无。 +- 使用限制:该方法不会主动把 `position` 夹到 `0 ~ MaxPosition`,需要上层自行保证。 + + +### 程序化滚动 + +#### 示例 1:立即滚动与平滑滚动 +```csharp +using AlicizaX.UI; +using UnityEngine; + +public sealed class ProgrammaticScrollDemo : MonoBehaviour +{ + [SerializeField] private RecyclerView recyclerView; + + public void JumpToTop() + { + recyclerView.ScrollTo(0, smooth: false); // 立即跳到顶部 + } + + public void SmoothToMiddle() + { + recyclerView.ScrollToWithAlignment(15, ScrollAlignment.Center, 0f, true, 0.35f); + } +} +``` + +#### 示例 2:按固定时长滚动 +```csharp +using AlicizaX.UI; +using UnityEngine; + +public sealed class TimedScrollDemo : MonoBehaviour +{ + [SerializeField] private RecyclerView recyclerView; + + public void ScrollByDuration() + { + if (recyclerView.Scroller == null) + { + return; + } + + float target = Mathf.Min(recyclerView.Scroller.MaxPosition, 320f); + recyclerView.Scroller.ScrollToDuration(target, 0.5f); // 0.5 秒到位 + } +} +``` + +#### 示例 3:无滚动能力时的防御式调用 +```csharp +using AlicizaX.UI; +using UnityEngine; + +public sealed class ScrollGuardDemo : MonoBehaviour +{ + [SerializeField] private RecyclerView recyclerView; + + public void TryScroll() + { + if (!recyclerView.Scroll || recyclerView.Scroller == null) + { + Debug.Log("Scroll disabled, ignore request."); // 避免空滚动器导致误判 + return; + } + + recyclerView.ScrollToWithAlignment(2, ScrollAlignment.Start); + } +} +``` + + +### 惯性、吸附与自动播放 + +#### 示例 1:分页列表的吸附体验 +```csharp +using AlicizaX.UI; +using UnityEngine; + +public sealed class SnapPageDemo : MonoBehaviour +{ + [SerializeField] private RecyclerView recyclerView; // Inspector: Snap = true, LayoutManager = PageLayoutManager + + private void Awake() + { + recyclerView.Scroll = true; // 启用滚动 + recyclerView.Snap = true; // 停止时自动吸附最近项 + recyclerView.ScrollSpeed = 10f; // 提高分页吸附速度 + } +} +``` + +#### 示例 2:自动播放轮播 +```csharp +using System.Collections; +using AlicizaX.UI; +using UnityEngine; + +public sealed class AutoPlayDemo : MonoBehaviour +{ + [SerializeField] private RecyclerView recyclerView; + + private int nextIndex; + private Coroutine autoPlayCoroutine; + + private void OnEnable() + { + autoPlayCoroutine = StartCoroutine(AutoPlay()); + } + + private void OnDisable() + { + if (autoPlayCoroutine != null) + { + StopCoroutine(autoPlayCoroutine); // 面板关闭时停止自动滚动 + } + } + + private IEnumerator AutoPlay() + { + while (true) + { + yield return new WaitForSeconds(3f); + recyclerView.ScrollToWithAlignment(nextIndex, ScrollAlignment.Center, 0f, true, 0.25f); + nextIndex += 1; // 循环列表可持续递增 + } + } +} +``` + +#### 示例 3:手动上一页 / 下一页 +```csharp +using AlicizaX.UI; +using UnityEngine; +using UnityEngine.UI; + +public sealed class PagerButtonsDemo : MonoBehaviour +{ + [SerializeField] private RecyclerView recyclerView; + [SerializeField] private Button prevButton; + [SerializeField] private Button nextButton; + private int currentIndex; + + private void Awake() + { + recyclerView.OnIndexChanged += index => currentIndex = Mathf.Max(index, 0); + + prevButton.onClick.AddListener(() => + { + recyclerView.ScrollToWithAlignment(Mathf.Max(currentIndex - 1, 0), ScrollAlignment.Center, 0f, true, 0.2f); + }); + + nextButton.onClick.AddListener(() => + { + recyclerView.ScrollToWithAlignment(currentIndex + 1, ScrollAlignment.Center, 0f, true, 0.2f); + }); + } +} +``` + + +## 视图池(ViewProvider / ObjectPool) + +视图池是 `RecyclerView` 性能表现的核心:它让“大列表”只保留可见区对象,其他对象进入可复用池而不是销毁重建。 + + +### API 说明 + +#### 类型:`ViewProvider` + +源码位置:`Packages/com.alicizax.unity.ui.extension/Runtime/RecyclerView/ViewProvider/ViewProvider.cs` + +| 方法/成员 | 返回值/类型 | 默认值 | 说明 | 使用限制 | +| --- | --- | --- | --- | --- | +| `Adapter` | `IAdapter` | `null` | 当前适配器。 | 由容器注入。 | +| `LayoutManager` | `LayoutManager` | `null` | 当前布局器。 | 由容器注入。 | +| `ViewHolders` | `IReadOnlyList` | 空集合 | 当前可见 Holder 集。 | 只读视图。 | +| `PoolStats` | `string` | 抽象成员 | 对象池统计。 | 用于诊断。 | +| `GetTemplate(string viewName)` | `ViewHolder` | 无 | 获取模板。 | 多模板时名字必须命中。 | +| `GetTemplates()` | `ViewHolder[]` | 无 | 获取所有模板。 | 模板不能为空。 | +| `Allocate(string viewName)` | `ViewHolder` | 无 | 从池中取出一个 Holder。 | 通常不手动调用。 | +| `Free(string viewName, ViewHolder viewHolder)` | `void` | 无 | 归还 Holder。 | 通常不手动调用。 | +| `Reset()` | `void` | 无 | 清空可见项并销毁池。 | 常用于彻底重建。 | +| `PreparePool()` | `void` | 无 | 依据可见区预热池。 | 由容器内部布局流程触发。 | +| `CreateViewHolder(int index)` | `void` | 无 | 创建一个 `Unit` 批次的 Holder。 | `index` 为布局起点。 | +| `RemoveViewHolder(int index)` | `void` | 无 | 回收一个 `Unit` 批次的 Holder。 | `index` 为布局起点。 | +| `GetViewHolder(int index)` | `ViewHolder` | 无 | 根据布局索引查找 Holder。 | 不可见时返回 `null`。 | +| `GetViewHolderByDataIndex(int dataIndex)` | `ViewHolder` | 无 | 根据数据索引查找 Holder。 | 循环列表可能存在多个映射。 | +| `TryGetViewHoldersByDataIndex(...)` | `bool` | 无 | 获取某数据索引对应的所有 Holder。 | 适合循环列表。 | +| `CalculateViewSize(int index)` | `Vector2` | 无 | 按模板估算尺寸。 | 依赖 `GetViewName(index)` 正确。 | + +#### 类型:`SimpleViewProvider` + +| 成员 | 类型 | 默认值 | 说明 | 使用限制 | +| --- | --- | --- | --- | --- | +| 内部池 | `ObjectPool` | `initialSize = 0`, `maxSize = 1` | 单模板池。 | 实际容量会在 `PreparePool()` 中扩容。 | +| `PoolStats` | `string` | 实时统计 | 命中、未命中、销毁、活跃等信息。 | 文本格式仅用于日志。 | + +#### 类型:`MixedViewProvider` + +| 成员 | 类型 | 默认值 | 说明 | 使用限制 | +| --- | --- | --- | --- | --- | +| 内部池 | `MixedObjectPool` | 每类型默认上限 `10` | 多模板池。 | 模板名必须唯一。 | +| `PoolStats` | `string` | 实时统计 | 当前命中、未命中、销毁信息。 | 细粒度活跃数需看 `MixedObjectPool`。 | + +#### 类型:`ObjectPool` + +源码位置:`Packages/com.alicizax.unity.ui.extension/Runtime/RecyclerView/ObjectPool/ObjectPool.cs` + +| 方法/成员 | 返回值/类型 | 默认值 | 说明 | 使用限制 | +| --- | --- | --- | --- | --- | +| `ObjectPool(IObjectFactory factory)` | 构造函数 | `maxSize = Environment.ProcessorCount * 2` | 用 CPU 数量推导上限。 | 初始大小为 `0`。 | +| `ObjectPool(IObjectFactory factory, int maxSize)` | 构造函数 | `initialSize = 0` | 指定最大池容量。 | `maxSize` 需大于等于 `0`。 | +| `ObjectPool(IObjectFactory factory, int initialSize, int maxSize)` | 构造函数 | 无 | 指定预热数量与上限。 | `maxSize < initialSize` 会抛异常。 | +| `Allocate()` | `T` | 无 | 取对象。 | 空池时会创建新对象。 | +| `Free(T obj)` | `void` | 无 | 归还对象。 | 超过上限时会销毁。 | +| `EnsureCapacity(int value)` | `void` | 无 | 调大池上限。 | `value <= 0` 抛异常。 | +| `Warm(int count)` | `void` | 无 | 预热到目标数量。 | 实际不会超过 `maxSize`。 | +| `Dispose()` | `void` | 无 | 销毁所有空闲对象。 | 活跃对象不受影响。 | +| `MaxSize` | `int` | 构造决定 | 当前最大容量。 | 可被 `EnsureCapacity()` 提高。 | +| `ActiveCount` / `InactiveCount` | `int` | `0` | 活跃 / 闲置数量。 | 用于评估是否需要调容量。 | +| `HitCount` / `MissCount` / `DestroyCount` | `int` | `0` | 命中、未命中、销毁统计。 | 用于调优。 | +| `PeakActive` | `int` | `0` | 历史峰值活跃数。 | 可作为容量基线。 | + +#### 类型:`MixedObjectPool` + +| 方法/成员 | 返回值/类型 | 默认值 | 说明 | 使用限制 | +| --- | --- | --- | --- | --- | +| `MixedObjectPool(IMixedObjectFactory factory)` | 构造函数 | `defaultMaxSizePerType = 10` | 使用默认每类型容量。 | `factory` 不能为空。 | +| `MixedObjectPool(IMixedObjectFactory factory, int defaultMaxSizePerType)` | 构造函数 | 无 | 显式指定每类型默认容量。 | `defaultMaxSizePerType <= 0` 抛异常。 | +| `Allocate(string typeName)` | `T` | 无 | 取指定模板类型对象。 | `typeName` 必须存在。 | +| `Free(string typeName, T obj)` | `void` | 无 | 归还指定类型对象。 | 超出该类型上限时会销毁。 | +| `SetMaxSize(string typeName, int value)` | `void` | 无 | 设置单类型上限。 | 不会自动创建对象。 | +| `EnsureCapacity(string typeName, int value)` | `void` | 无 | 单类型扩容。 | `value <= 0` 抛异常。 | +| `Warm(string typeName, int count)` | `void` | 无 | 单类型预热。 | 实际不会超过该类型上限。 | +| `GetActiveCount(string typeName)` | `int` | `0` | 当前活跃数。 | 适合按模板调优。 | +| `GetPeakActiveCount(string typeName)` | `int` | `0` | 历史峰值活跃数。 | 可用于回写容量。 | + + +### 预热与复用 + +#### 示例 1:依赖内建 `PreparePool()` 自动预热 +```csharp +using System.Collections.Generic; +using AlicizaX.UI; +using RecyclerViewBookSamples; +using UnityEngine; + +public sealed class BuiltinPoolWarmDemo : MonoBehaviour +{ + [SerializeField] private RecyclerView recyclerView; + + private void Start() + { + var list = UGListCreateHelper.Create(recyclerView); + list.RegisterItemRender(); + + var data = new List(); + for (int i = 0; i < 200; i++) + { + data.Add(new DemoSimpleData { Title = $"行 {i + 1}" }); + } + + list.Data = data; + Debug.Log(recyclerView.PoolStats); // 输出当前池统计 + } +} +``` + +#### 示例 2:直接使用 `ObjectPool` 自定义容量 +```csharp +using AlicizaX.UI; +using UnityEngine; +using UnityEngine.UI; + +public sealed class DemoPoolHolder : ViewHolder +{ + public Text title; +} + +public sealed class DemoPoolFactory : IObjectFactory +{ + private readonly DemoPoolHolder template; + private readonly Transform parent; + + public DemoPoolFactory(DemoPoolHolder template, Transform parent) + { + this.template = template; + this.parent = parent; + } + + public DemoPoolHolder Create() + { + return Object.Instantiate(template, parent); // 池空时创建 + } + + public void Reset(DemoPoolHolder obj) + { + obj.gameObject.SetActive(false); // 归还时隐藏 + } + + public bool Validate(DemoPoolHolder obj) + { + return obj != null; // 被销毁对象不再回池 + } + + public void Destroy(DemoPoolHolder obj) + { + Object.Destroy(obj.gameObject); // 超上限时销毁 + } +} + +public sealed class ObjectPoolDemo : MonoBehaviour +{ + [SerializeField] private DemoPoolHolder template; + [SerializeField] private Transform parent; + + private ObjectPool pool; + + private void Awake() + { + pool = new ObjectPool(new DemoPoolFactory(template, parent), initialSize: 8, maxSize: 32); + pool.Warm(16); // 预热到 16 个 + pool.EnsureCapacity(48); // 动态扩容到 48 + } + + private void Start() + { + DemoPoolHolder holder = pool.Allocate(); + holder.title.text = "From pool"; + pool.Free(holder); // 归还后进入闲置栈 + + Debug.Log($"hit={pool.HitCount}, miss={pool.MissCount}, inactive={pool.InactiveCount}"); + } +} +``` + +#### 示例 3:校验失败时自动销毁 +```csharp +using AlicizaX.UI; +using UnityEngine; + +public sealed class PoolValidateDemo : MonoBehaviour +{ + [SerializeField] private DemoPoolHolder template; + [SerializeField] private Transform parent; + + private ObjectPool pool; + + private void Start() + { + pool = new ObjectPool(new DemoPoolFactory(template, parent), 1, 2); + + DemoPoolHolder holder = pool.Allocate(); + Destroy(holder.gameObject); // 模拟外部错误销毁 + pool.Free(holder); // Validate 失败后不会重新入池 + } +} +``` + + +### 多模板对象池配置 + +#### 示例 1:按模板类型分别预热 +```csharp +using System.Collections.Generic; +using AlicizaX.UI; +using UnityEngine; + +public sealed class MixedPoolDemo : MonoBehaviour +{ + [SerializeField] private DemoPoolHolder smallTemplate; + [SerializeField] private DemoPoolHolder largeTemplate; + [SerializeField] private Transform parent; + + private MixedObjectPool pool; + + private void Awake() + { + var templates = new Dictionary + { + ["SmallItem"] = smallTemplate, + ["LargeItem"] = largeTemplate, + }; + + pool = new MixedObjectPool( + new UnityMixedComponentFactory(templates, parent), + defaultMaxSizePerType: 6); + + pool.EnsureCapacity("SmallItem", 24); // 小卡片出现频率更高 + pool.EnsureCapacity("LargeItem", 8); // 大卡片数量更少 + pool.Warm("SmallItem", 16); + pool.Warm("LargeItem", 4); + } +} +``` + +#### 示例 2:根据峰值活跃数回写容量 +```csharp +using AlicizaX.UI; +using UnityEngine; + +public sealed class MixedPoolTuningDemo : MonoBehaviour +{ + public void PrintRecommendation(MixedObjectPool pool) + { + int peak = pool.GetPeakActiveCount("SmallItem"); + int recommended = peak + 2; // 峰值 + buffer + Debug.Log($"Recommended SmallItem capacity = {recommended}"); + } +} +``` + + +## 导航(RecyclerNavigation) + +导航模块负责把 `EventSystem` 的移动事件转成“列表内跳转 + 必要滚动 + 焦点恢复”。 + + +### API 说明 + +#### 类型:`RecyclerNavigationController` + +源码位置:`Packages/com.alicizax.unity.ui.extension/Runtime/RecyclerView/Navigation/RecyclerNavigationController.cs` + +| 方法/成员 | 返回值 | 默认值 | 说明 | 使用限制 | +| --- | --- | --- | --- | --- | +| `RecyclerNavigationController(RecyclerView recyclerView)` | 构造函数 | 无 | 创建导航控制器。 | 一般由 `RecyclerView.NavigationController` 懒加载。 | +| `TryMove(ViewHolder currentHolder, MoveDirection direction, RecyclerNavigationOptions options)` | `bool` | 无 | 尝试从当前项移动到下一个项。 | `currentHolder` 不能为空,空列表返回 `false`。 | + +#### 类型:`RecyclerNavigationBridge` + +| 成员/方法 | 类型/返回值 | 默认值 | 说明 | 使用限制 | +| --- | --- | --- | --- | --- | +| `defaultEntryDirection` | `MoveDirection` | `MoveDirection.Down` | 列表首次接收焦点时的进入方向。 | Inspector 配置。 | +| `OnSelect(...)` | `void` | 无 | 当桥节点被选中时尝试进入列表。 | 挂在 `RecyclerView` 同节点上。 | +| `OnMove(...)` | `void` | 无 | 当桥节点收到方向键时尝试进入列表。 | 失败后才回落到原 `Selectable` 行为。 | +| `OnSubmit(...)` | `void` | 无 | 提交键进入列表。 | 常用于确认键。 | + +#### 类型:`RecyclerNavigationOptions` + +| 成员 | 类型 | 默认值 | 说明 | 使用限制 | +| --- | --- | --- | --- | --- | +| `Wrap` | `bool` | 构造决定 | 是否允许首尾环绕。 | 对非循环列表通常应关闭。 | +| `SmoothScroll` | `bool` | 构造决定 | 导航时是否平滑滚动。 | 开启后焦点会等待滚动完成。 | +| `Alignment` | `ScrollAlignment` | 构造决定 | 导航触发滚动时的对齐方式。 | 常用 `Center`。 | +| `Clamped` | `RecyclerNavigationOptions` | `wrap=false, smooth=false, alignment=Center` | 不环绕方案。 | 静态只读。 | +| `Circular` | `RecyclerNavigationOptions` | `wrap=true, smooth=false, alignment=Center` | 允许环绕方案。 | 静态只读。 | + +#### 类型:`ItemInteractionProxy` / `RecyclerItemSelectable` + +| 成员/方法 | 类型/返回值 | 默认值 | 说明 | 使用限制 | +| --- | --- | --- | --- | --- | +| `GetSelectable()` | `Selectable` | 无 | 返回真正参与导航的焦点锚点。 | 若节点上没有 `Selectable` 会自动补 `RecyclerItemSelectable`。 | +| `Bind(IItemInteractionHost)` | `void` | 无 | 绑定交互宿主。 | 由 `ItemRender` 内部驱动。 | +| `Clear()` | `void` | 无 | 清理交互宿主与状态。 | 回收时自动调用。 | + + +### 列表内导航 + +#### 示例 1:ItemRender 启用点击、移动与提交 +```csharp +using AlicizaX.UI; +using RecyclerViewBookSamples; +using UnityEngine; +using UnityEngine.EventSystems; + +public sealed class NavigableItemRender : ItemRender +{ + public override ItemInteractionFlags InteractionFlags => + ItemInteractionFlags.PointerClick | + ItemInteractionFlags.Select | + ItemInteractionFlags.Move | + ItemInteractionFlags.Submit; + + protected override void OnBind(DemoSimpleData data, int index) + { + Holder.title.text = data.Title; + Holder.subtitle.text = $"可导航项 {index}"; + } + + protected override void OnPointerClick(PointerEventData eventData) + { + Debug.Log($"Clicked {CurrentIndex}"); // 点击时选择当前项 + } + + protected override void OnSubmit(BaseEventData eventData) + { + Debug.Log($"Submit {CurrentIndex}"); // 回车 / A 键确认 + } +} +``` + +#### 示例 2:主动驱动导航器移动 +```csharp +using AlicizaX.UI; +using UnityEngine; + +public sealed class ManualNavigationDemo : MonoBehaviour +{ + [SerializeField] private RecyclerView recyclerView; + private int currentIndex; + + private void Awake() + { + recyclerView.OnIndexChanged += index => currentIndex = Mathf.Max(index, 0); + } + + private void Update() + { + if (!Input.GetKeyDown(KeyCode.RightArrow)) + { + return; + } + + var current = EventSystem.current?.currentSelectedGameObject?.GetComponentInParent(); + if (current == null) + { + recyclerView.TryFocusIndex(currentIndex, false, ScrollAlignment.Center); + return; + } + + recyclerView.NavigationController.TryMove(current, MoveDirection.Right, RecyclerNavigationOptions.Clamped); + } +} +``` + +#### 示例 3:环绕导航与钳制导航 +```csharp +using AlicizaX.UI; +using RecyclerViewBookSamples; + +public sealed class CircularNavigableRender : ItemRender +{ + public override ItemInteractionFlags InteractionFlags => + ItemInteractionFlags.Select | ItemInteractionFlags.Move; + + protected override RecyclerNavigationOptions NavigationOptions => + RecyclerNavigationOptions.Circular; // 到尾部后回到头部 + + protected override void OnBind(DemoSimpleData data, int index) + { + Holder.title.text = data.Title; + } +} +``` + + +### 列表入口与焦点恢复 + +#### 示例 1:外部按钮把焦点送入列表 +```csharp +using AlicizaX.UI; +using UnityEngine; +using UnityEngine.EventSystems; +using UnityEngine.UI; + +public sealed class EntryBridgeDemo : MonoBehaviour +{ + [SerializeField] private Button entryButton; + [SerializeField] private RecyclerView recyclerView; + + private void Awake() + { + entryButton.onClick.AddListener(() => + { + EventSystem.current.SetSelectedGameObject(recyclerView.gameObject); // 选中桥节点 + recyclerView.TryFocusEntry(MoveDirection.Down); // 然后进入列表 + }); + } +} +``` + +#### 示例 2:重新打开面板时恢复上次项 +```csharp +using AlicizaX.UI; +using UnityEngine; + +public sealed class NavigationRestoreDemo : MonoBehaviour +{ + [SerializeField] private RecyclerView recyclerView; + + private int lastIndex; + + private void Awake() + { + recyclerView.OnIndexChanged += index => lastIndex = index; // 记录上次选中索引 + } + + private void OnEnable() + { + recyclerView.TryFocusIndex(lastIndex, smooth: true); // 平滑恢复焦点 + } +} +``` + +#### 示例 3:空列表时避免错误进入 +```csharp +using AlicizaX.UI; +using UnityEngine; + +public sealed class EmptyNavigationGuardDemo : MonoBehaviour +{ + [SerializeField] private RecyclerView recyclerView; + + public void TryEnter() + { + if (!recyclerView.TryFocusEntry(MoveDirection.Down)) + { + Debug.Log("List is empty or not ready."); // 空列表不强行进入 + } + } +} +``` + + +## 业务包装层(UGList / UGListExtensions) + +`UGList` 家族是官方提供的“业务友好包装层”,适合绝大多数游戏页面。它把适配器创建、内部绑定流程和常见滚动操作做了简化。 + + +### API 说明 + +#### 类型:`UGListBase` + +源码位置:`Packages/com.alicizax.unity.ui.extension/Runtime/RecyclerView/UGList.cs` + +| 成员/方法 | 类型/返回值 | 默认值 | 说明 | 使用限制 | +| --- | --- | --- | --- | --- | +| `RecyclerView` | `RecyclerView` | 构造注入 | 关联容器。 | 只读。 | +| `Adapter` | `TAdapter` | 构造注入 | 关联适配器。 | 只读。 | +| `Data` | `List` | `null` | 当前数据集合。 | 赋值时会调用 `_adapter.SetList()`。 | +| `RegisterItemRender(string viewName = "")` | `void` | `viewName=""` | 注册渲染器。 | 空字符串表示默认模板。 | +| `RegisterItemRender(Type itemRenderType, string viewName = "")` | `void` | `viewName=""` | 运行时注册渲染器。 | 类型需继承 `ItemRenderBase`。 | +| `UnregisterItemRender(string viewName = "")` | `bool` | `viewName=""` | 注销渲染器。 | 成功返回 `true`。 | +| `ClearItemRenderRegistrations()` | `void` | 无 | 清空注册。 | 适合切换整套模板。 | + +#### 类型:具体包装类 + +| 类型 | 适配器类型 | 说明 | 使用限制 | +| --- | --- | --- | --- | +| `UGList` | `Adapter` | 单模板普通列表。 | `TData : ISimpleViewData` | +| `UGGroupList` | `GroupAdapter` | 分组列表。 | `TData : class, IGroupViewData, new()` | +| `UGLoopList` | `LoopAdapter` | 循环列表。 | `TData : ISimpleViewData, new()` | +| `UGMixedList` | `MixedAdapter` | 多模板列表。 | `TData : IMixedViewData` | + +#### 类型:`UGListCreateHelper` + +| 方法 | 返回值 | 默认参数 | 说明 | 使用限制 | +| --- | --- | --- | --- | --- | +| `Create(RecyclerView recyclerView)` | `UGList` | 无 | 创建普通列表包装。 | `TData : ISimpleViewData` | +| `CreateGroup(RecyclerView recyclerView, string groupViewName)` | `UGGroupList` | 无 | 创建分组列表包装。 | 必须提供组头模板名。 | +| `CreateLoop(RecyclerView recyclerView)` | `UGLoopList` | 无 | 创建循环列表包装。 | `TData : ISimpleViewData, new()` | +| `CreateMixed(RecyclerView recyclerView)` | `UGMixedList` | 无 | 创建多模板列表包装。 | `TData : IMixedViewData` | + +#### 类型:`UGListExtensions` + +源码位置:`Packages/com.alicizax.unity.ui.extension/Runtime/RecyclerView/UGListExtensions.cs` + +| 成员/方法 | 返回值 | 默认值 | 说明 | 使用限制 | +| --- | --- | --- | --- | --- | +| `DebugScrollTo` | `bool` | `false` | 输出滚动定位日志。 | 仅用于调试。 | +| `ScrollTo(...)` | `void` | `alignment=Start, offset=0f, smooth=false, duration=0.3f` | 包装 `RecyclerView.ScrollToWithAlignment()`。 | `ugList.RecyclerView` 为空时打印警告。 | +| `ScrollToStart(...)` | `void` | `offset=0f, smooth=false, duration=0.3f` | 滚到起始端。 | 同上。 | +| `ScrollToCenter(...)` | `void` | `offset=0f, smooth=false, duration=0.3f` | 滚到中间。 | 同上。 | +| `ScrollToEnd(...)` | `void` | `offset=0f, smooth=false, duration=0.3f` | 滚到末端。 | 同上。 | + + +### 快速创建与滚动扩展 + +#### 示例 1:`UGList` 最简创建 +```csharp +using System.Collections.Generic; +using AlicizaX.UI; +using RecyclerViewBookSamples; +using UnityEngine; + +public sealed class QuickUgListDemo : MonoBehaviour +{ + [SerializeField] private RecyclerView recyclerView; + + private UGList list; + + private void Start() + { + list = UGListCreateHelper.Create(recyclerView); + list.RegisterItemRender(); + list.Data = new List + { + new DemoSimpleData { Title = "快速创建 1" }, + new DemoSimpleData { Title = "快速创建 2" }, + }; + } +} +``` + +#### 示例 2:使用扩展方法滚动 +```csharp +using AlicizaX.UI; +using RecyclerViewBookSamples; +using UnityEngine; + +public sealed class UgListExtensionsDemo : MonoBehaviour +{ + [SerializeField] private RecyclerView recyclerView; + + private UGList list; + + private void Start() + { + list = UGListCreateHelper.Create(recyclerView); + list.RegisterItemRender(); + } + + public void CenterItem(int index) + { + list.ScrollToCenter(index, offset: 0f, smooth: true, duration: 0.2f); // 使用扩展方法 + } +} +``` + +#### 示例 3:打开滚动定位调试日志 +```csharp +using AlicizaX.UI; +using UnityEngine; + +public sealed class UgListDebugDemo : MonoBehaviour +{ + private void Awake() + { + UGListExtensions.DebugScrollTo = true; // 打开定位日志 + } +} +``` + + +### 业务封装示例 + +#### 示例 1:业务侧封装统一入口 +```csharp +using System.Collections.Generic; +using AlicizaX.UI; +using RecyclerViewBookSamples; +using UnityEngine; + +public sealed class MailListFacade +{ + private readonly UGList list; + + public MailListFacade(RecyclerView recyclerView) + { + list = UGListCreateHelper.Create(recyclerView); + list.RegisterItemRender(); + } + + public void SetMails(List mails) + { + list.Data = mails; // 统一数据入口 + } + + public void FocusFirstUnread() + { + list.RecyclerView.TryFocusIndex(0, true, ScrollAlignment.Center); + } +} +``` + +#### 示例 2:封装“刷新 + 保留焦点” +```csharp +using System.Collections.Generic; +using AlicizaX.UI; +using RecyclerViewBookSamples; + +public sealed class FocusAwareFacade +{ + private readonly UGList list; + private int lastIndex; + + public FocusAwareFacade(RecyclerView recyclerView) + { + list = UGListCreateHelper.Create(recyclerView); + list.RegisterItemRender(); + recyclerView.OnIndexChanged += index => lastIndex = index; + } + + public void Replace(List data) + { + list.Data = data; + list.RecyclerView.TryFocusIndex(lastIndex, false); // 数据刷新后恢复焦点 + } +} +``` + + +## 场景专题 + +本节给出更贴近业务的完整场景脚本,重点覆盖性能与交互要求较高的页面。 + + +### 大量列表项复用(含对象池配置) + +#### 示例 1:一万条数据的大列表 +```csharp +using System.Collections.Generic; +using AlicizaX.UI; +using RecyclerViewBookSamples; +using UnityEngine; + +public sealed class MassiveListDemo : MonoBehaviour +{ + [SerializeField] private RecyclerView recyclerView; // Inspector: LinearLayoutManager + Scroller + + private UGList list; + + private void Start() + { + list = UGListCreateHelper.Create(recyclerView); + list.RegisterItemRender(); + + var rows = new List(10000); + for (int i = 0; i < 10000; i++) + { + rows.Add(new DemoSimpleData + { + Title = $"战利品 {i + 1}", + Subtitle = $"ID = {100000 + i}", + }); + } + + list.Data = rows; + Debug.Log(recyclerView.PoolStats); // 观察命中、未命中和活跃数 + } +} +``` + +#### 示例 2:手工调对象池容量 +```csharp +using AlicizaX.UI; +using UnityEngine; + +public sealed class MassiveListPoolConfigDemo : MonoBehaviour +{ + [SerializeField] private DemoPoolHolder rowTemplate; + [SerializeField] private Transform poolRoot; + + private ObjectPool pool; + + private void Awake() + { + pool = new ObjectPool( + new DemoPoolFactory(rowTemplate, poolRoot), + initialSize: 24, + maxSize: 96); // 明确给出大列表池容量 + + pool.Warm(48); // 提前创建一部分 + } + + private void Start() + { + Debug.Log($"active={pool.ActiveCount}, inactive={pool.InactiveCount}, peak={pool.PeakActive}"); + } +} +``` + +> 💡 大列表调优的首要目标不是“把所有项都放进池里”,而是让“可见项 + 一屏缓冲”命中率足够高。 + + +### 多模板混排 + +#### 示例 1:信息流混排 +```csharp +using System.Collections.Generic; +using AlicizaX.UI; +using RecyclerViewBookSamples; +using UnityEngine; + +public sealed class FeedCardHolder : ViewHolder +{ + public UnityEngine.UI.Text title; +} + +public sealed class FeedCardRender : ItemRender +{ + protected override void OnBind(DemoMixedData data, int index) + { + Holder.title.text = $"[Card] {data.Title}"; + } +} + +public sealed class MixedFeedDemo : MonoBehaviour +{ + [SerializeField] private RecyclerView recyclerView; // Inspector: MixedLayoutManager + 2 templates + + private UGMixedList list; + + private void Start() + { + list = UGListCreateHelper.CreateMixed(recyclerView); + list.RegisterItemRender("FeedText"); + list.RegisterItemRender("FeedCard"); + + list.Data = new List + { + new DemoMixedData { TemplateName = "FeedCard", Title = "头图 Banner" }, + new DemoMixedData { TemplateName = "FeedText", Title = "正文 1", Subtitle = "文本块" }, + new DemoMixedData { TemplateName = "FeedText", Title = "正文 2", Subtitle = "文本块" }, + new DemoMixedData { TemplateName = "FeedCard", Title = "推荐卡片" }, + }; + } +} +``` + +#### 示例 2:未知模板名的保护策略 +```csharp +using System.Collections.Generic; +using AlicizaX.UI; +using RecyclerViewBookSamples; +using UnityEngine; + +public sealed class MixedTemplateGuardDemo : MonoBehaviour +{ + [SerializeField] private RecyclerView recyclerView; + + public void BindSafely(List rows) + { + foreach (DemoMixedData row in rows) + { + if (string.IsNullOrEmpty(row.TemplateName)) + { + row.TemplateName = "FeedText"; // 回退到一个可用模板 + } + } + + var list = UGListCreateHelper.CreateMixed(recyclerView); + list.RegisterItemRender("FeedText"); + list.Data = rows; + } +} +``` + +> ⚠️ `MixedViewProvider.GetTemplate()` 找不到模板名时会抛 `KeyNotFoundException`;不要把兜底逻辑留给运行时异常。 + + +### 分页加载(加载态、空态、错误态) + +#### 示例 1:统一状态流 +```csharp +using System; +using System.Collections; +using System.Collections.Generic; +using AlicizaX.UI; +using RecyclerViewBookSamples; +using UnityEngine; + +public abstract class PagedEntryData : IMixedViewData +{ + public string TemplateName { get; set; } +} + +public sealed class PagedContentData : PagedEntryData +{ + public string Title; +} + +public sealed class PagedStateData : PagedEntryData +{ + public string Message; + public Action Retry; +} + +public sealed class PagedContentRender : ItemRender +{ + protected override void OnBind(PagedContentData data, int index) + { + Holder.title.text = data.Title; + Holder.subtitle.text = $"Item {index}"; + } +} + +public sealed class PagedStateRender : ItemRender +{ + private Action retry; + + protected override void OnHolderAttached() + { + base.OnHolderAttached(); + Holder.retryButton.onClick.AddListener(OnRetryClicked); // 只注册一次 + } + + protected override void OnHolderDetached() + { + Holder.retryButton.onClick.RemoveListener(OnRetryClicked); + base.OnHolderDetached(); + } + + protected override void OnBind(PagedStateData data, int index) + { + retry = data.Retry; + Holder.message.text = data.Message; + Holder.retryButton.gameObject.SetActive(data.Retry != null); // 错误态显示重试按钮 + } + + protected override void OnClear() + { + retry = null; // 回收时清掉业务引用 + } + + private void OnRetryClicked() + { + retry?.Invoke(); + } +} + +public sealed class PagedLoadingDemo : MonoBehaviour +{ + [SerializeField] private RecyclerView recyclerView; // Inspector: 需有 ContentItem / LoadingItem / EmptyItem / ErrorItem 模板 + + private UGMixedList list; + private int page = 1; + + private void Start() + { + list = UGListCreateHelper.CreateMixed(recyclerView); + list.RegisterItemRender("ContentItem"); + list.RegisterItemRender("LoadingItem"); + list.RegisterItemRender("EmptyItem"); + list.RegisterItemRender("ErrorItem"); + StartCoroutine(LoadPage()); + } + + private IEnumerator LoadPage() + { + ShowLoading(); + yield return new WaitForSeconds(0.5f); // 模拟网络 + + if (page == 1) + { + ShowContent(new List + { + new PagedContentData { TemplateName = "ContentItem", Title = "第一页-1" }, + new PagedContentData { TemplateName = "ContentItem", Title = "第一页-2" }, + }); + page++; + yield break; + } + + ShowEmpty(); // 第二次请求模拟空态 + } + + private void ShowLoading() + { + list.Data = new List + { + new PagedStateData { TemplateName = "LoadingItem", Message = "正在加载..." }, + }; + } + + private void ShowContent(List rows) + { + list.Data = rows; + } + + private void ShowEmpty() + { + list.Data = new List + { + new PagedStateData { TemplateName = "EmptyItem", Message = "暂无数据" }, + }; + } + + public void ShowError() + { + list.Data = new List + { + new PagedStateData + { + TemplateName = "ErrorItem", + Message = "加载失败,点击重试", + Retry = () => StartCoroutine(LoadPage()), // 错误态回调 + }, + }; + } +} +``` + +#### 示例 2:滚动到底部加载下一页 +```csharp +using AlicizaX.UI; +using UnityEngine; + +public sealed class PagedLoadMoreDemo : MonoBehaviour +{ + [SerializeField] private RecyclerView recyclerView; + + private bool isLoading; + + private void Awake() + { + recyclerView.OnScrollValueChanged += TryLoadMore; // 监听滚动变化 + } + + private void TryLoadMore() + { + if (isLoading || recyclerView.Scroller == null) + { + return; + } + + float remain = recyclerView.Scroller.MaxPosition - recyclerView.GetScrollPosition(); + if (remain > 120f) + { + return; // 距离底部还远,不加载 + } + + isLoading = true; + Debug.Log("Reach bottom, load next page."); + } +} +``` + + +### 轮播与循环滚动(自动播放、手动切换) + +#### 示例 1:循环 Banner +```csharp +using System.Collections; +using System.Collections.Generic; +using AlicizaX.UI; +using RecyclerViewBookSamples; +using UnityEngine; +using UnityEngine.UI; + +public sealed class BannerCarouselDemo : MonoBehaviour +{ + [SerializeField] private RecyclerView recyclerView; // Inspector: PageLayoutManager + Snap = true + [SerializeField] private Button prevButton; + [SerializeField] private Button nextButton; + + private UGLoopList list; + private int virtualIndex; + private Coroutine autoPlay; + + private void Start() + { + list = UGListCreateHelper.CreateLoop(recyclerView); + list.RegisterItemRender(); + list.Data = new List + { + new DemoSimpleData { Title = "Banner A" }, + new DemoSimpleData { Title = "Banner B" }, + new DemoSimpleData { Title = "Banner C" }, + }; + + virtualIndex = list.Adapter.GetRealCount() * 100; // 从中间起始,保证可双向滚 + recyclerView.ScrollToWithAlignment(virtualIndex, ScrollAlignment.Center); + + prevButton.onClick.AddListener(() => Move(-1)); // 手动上一张 + nextButton.onClick.AddListener(() => Move(1)); // 手动下一张 + autoPlay = StartCoroutine(AutoPlay()); + } + + private void OnDisable() + { + if (autoPlay != null) + { + StopCoroutine(autoPlay); + } + } + + private IEnumerator AutoPlay() + { + while (true) + { + yield return new WaitForSeconds(3f); + Move(1); // 自动播放 + } + } + + private void Move(int step) + { + virtualIndex += step; + recyclerView.ScrollToWithAlignment(virtualIndex, ScrollAlignment.Center, 0f, true, 0.25f); + } +} +``` + +#### 示例 2:非循环分页的边界保护 +```csharp +using AlicizaX.UI; +using UnityEngine; + +public sealed class FiniteCarouselDemo : MonoBehaviour +{ + [SerializeField] private RecyclerView recyclerView; + [SerializeField] private int itemCount = 5; + private int currentIndex; + + private void Awake() + { + recyclerView.OnIndexChanged += index => currentIndex = Mathf.Max(index, 0); + } + + public void Next() + { + int target = Mathf.Min(currentIndex + 1, itemCount - 1); + recyclerView.ScrollToWithAlignment(target, ScrollAlignment.Center, 0f, true, 0.2f); + } + + public void Prev() + { + int target = Mathf.Max(currentIndex - 1, 0); + recyclerView.ScrollToWithAlignment(target, ScrollAlignment.Center, 0f, true, 0.2f); + } +} +``` + + +### 手柄/键盘导航结合列表滚动 + +#### 示例 1:打开面板自动聚焦第一项 +```csharp +using System.Collections.Generic; +using AlicizaX.UI; +using RecyclerViewBookSamples; +using UnityEngine; + +public sealed class GamepadListDemo : MonoBehaviour +{ + [SerializeField] private RecyclerView recyclerView; + + private UGList list; + + private void Start() + { + list = UGListCreateHelper.Create(recyclerView); + list.RegisterItemRender(); + list.Data = new List + { + new DemoSimpleData { Title = "图像" }, + new DemoSimpleData { Title = "声音" }, + new DemoSimpleData { Title = "操作" }, + }; + recyclerView.TryFocusIndex(0, false, ScrollAlignment.Start); // 初次进入聚焦第一项 + } +} +``` + +#### 示例 2:导航时自动把目标项滚进可见区 +```csharp +using AlicizaX.UI; +using UnityEngine; + +public sealed class NavigationScrollSyncDemo : MonoBehaviour +{ + [SerializeField] private RecyclerView recyclerView; + private int currentIndex; + + private void Awake() + { + recyclerView.OnIndexChanged += index => currentIndex = Mathf.Max(index, 0); + } + + private void Update() + { + if (Input.GetKeyDown(KeyCode.DownArrow)) + { + recyclerView.TryFocusIndex(currentIndex + 1, smooth: true, alignment: ScrollAlignment.End); + } + + if (Input.GetKeyDown(KeyCode.UpArrow)) + { + recyclerView.TryFocusIndex(currentIndex - 1, smooth: true, alignment: ScrollAlignment.Start); + } + } +} +``` + +> 📌 `TryFocusIndex(..., smooth: true)` 的意义不只是“滚动”,还包括“滚动结束后恢复焦点”。 + + +## FAQ + +### 常见问题 + +#### Q1:为什么我已经赋值了数据,但列表里什么都没有? + +- 最常见原因是没有通过 `UGList.Data`、`Adapter.SetList()` 或 `Notify*()` 等正式入口触发列表更新。 +- 其次是 `Templates` 为空、`LayoutManager` 未配置或模板名与 `TemplateName` 不匹配。 +- 如果是多模板列表,还要确认已经为每个模板注册了 `ItemRender`。 + +#### Q2:为什么 `NotifyItemChanged()` 后界面没变? + +- 只会重绑“当前可见”的 Holder;不可见项会等滚动到可见区后再更新。 +- 如果该项高度发生变化,需要用 `NotifyItemChanged(index, true)`。 + +#### Q3:为什么循环列表里业务索引和 `Holder.Index` 不一样? + +- `OnIndexChanged` 回调参数与 `Adapter.ChoiceIndex` 代表的是业务层关注的“真实索引”。 +- `Holder.Index` 是布局层的“虚拟索引”。 +- `LoopAdapter` 绑定时会对虚拟索引取模。 + +#### Q4:为什么混排列表报模板找不到? + +- `MixedAdapter.GetViewName()` 直接返回 `TemplateName`。 +- `MixedViewProvider` 用模板对象的 `name` 做键。 +- 任何大小写、空格或重命名不一致都会导致失败。 + +#### Q5:为什么滚动条不显示? + +- `showScrollBar` 或 `Scroll` 可能未打开。 +- `ShowScrollBarOnlyWhenScrollable = true` 时,内容未超过视口就不会显示。 +- `Direction.Custom` 不支持当前实现的溢出检测。 + +#### Q6:为什么分页吸附不准确? + +- `PageLayoutManager` 最适合固定尺寸页面。 +- 项尺寸变化大时应换 `MixedLayoutManager`,并自己定义滚动定位策略。 + +#### Q7:为什么切换页面后焦点丢了? + +- 视图复用会销毁/回收之前的 `Selectable`。 +- 应该在 `OnEnable()` 或刷新完成后调用 `TryFocusIndex()`。 + +#### Q8:为什么对象池命中率低? + +- 预热量不足。 +- 列表每次都被 `Reset()`。 +- 使用了全量刷新替代差量刷新。 + + +## Anti-patterns + +### 常见反模式与替代方案 + +| 反模式 | 问题 | 正确做法 | +| --- | --- | --- | +| 每次数据变化都调用 `NotifyDataChanged()` | 频繁清空并重建可见项,浪费池命中率。 | 优先使用 `NotifyItemChanged()` / `NotifyItemRangeChanged()`。 | +| 多模板列表里把 `TemplateName` 写成业务文案 | 模板名一旦翻译或重命名就失效。 | 用固定常量,如 `const string FeedCard = "FeedCard";`。 | +| 在 `OnBind()` 里重复注册按钮事件 | Holder 被复用后会出现重复回调。 | 在 `OnHolderAttached()` 注册,在 `OnHolderDetached()` 解绑。 | +| 直接改 `Scroller.Position` 期待界面自动更新 | 位置变了但布局未刷新。 | 优先调用 `RecyclerView.ScrollTo*()`。 | +| 列表数据尚未通过正式入口完成更新就调用 `TryFocusIndex()` | 焦点目标尚未生成。 | 先写入 `Data` / `SetList()` / `Notify*()`,必要时延后到下一帧或 `OnEnable()`。 | +| 使用 `LoopAdapter` 却仍以真实索引做“下一页” | 会反复跳回前几项。 | 维护独立的虚拟索引。 | +| 每次打开页面都重新 new 包装层与数据容器 | 造成 GC 抖动和池统计失真。 | 页面级持有 `UGList` / `Adapter`,只替换数据。 | +| 把“加载中/空态/错误态”放在列表外部多套 UI | 逻辑分裂、切换难维护。 | 用 `MixedAdapter` 把状态也当作一种 Item。 | + + +## 性能优化建议 + +### 建议清单 + +- 视图池预热:首屏数据尽量一次性设置到位,内部布局流程会自动触发 `PreparePool()` 准备“可见项 + 缓冲项”。 +- 优先差量刷新:文本、数值、开关状态变化优先用 `NotifyItemChanged()` / `NotifyItemRangeChanged()`。 +- 避免全量刷新:`NotifyDataChanged()` 只用于排序、模板切换、尺寸大量变化等无法局部更新的情况。 +- 复用业务包装层:面板级缓存 `UGList` / `Adapter`,不要每次打开都重建。 +- 控制模板数量:多模板会降低池命中率;能合并的样式尽量合并。 +- 固定 Item 尺寸优先:能用 `LinearLayoutManager` / `GridLayoutManager` 时不要上来就用 `MixedLayoutManager`。 +- 减少 `OnBind()` 重活:不要在 `OnBind()` 里做同步大图加载、复杂字符串拼接或深层级查找。 +- 缓存引用:`Holder` 上的 `Text`、`Image`、`Button` 在模板初始化时就拖好,避免运行时 `GetComponent()`。 +- 记录峰值活跃数:观察 `PoolStats`、`PeakActive`、`GetPeakActiveCount()`,按峰值 + buffer 设置池容量。 +- 滚动事件节流:`OnScrollValueChanged` 是高频回调,尽量只做阈值判断,不做昂贵计算。 +- 大量分页时保留数据容器:分页追加优先 `AddRange()`,避免整页复制新 `List`。 +- 减少 `Reset()`:`SetList()` 会触发 `RecyclerView.Reset()`;如果只是局部追加,直接对适配器做增量操作更稳。 + +> 💡 一个常见经验值:池容量先按“单屏可见数 + 1 屏缓冲”估,再通过 `PeakActive` 微调,而不是拍脑袋设置超大容量。 + +> ⚠️ 如果列表项高度经常变化,却还持续使用 `NotifyItemChanged(index, false)`,最终会出现错位、重叠或滚动条比例异常。 + + +## 交付前检查清单 + +- `Templates` 已配置,且多模板名称与 `TemplateName` 完全一致。 +- `LayoutManager` 与 `Scroller` 已在 Inspector 上正确绑定。 +- 已为所有模板注册对应 `ItemRender`。 +- 数据赋值后已通过 `Data` / `SetList()` / `Notify*()` 触发内部布局与刷新流程。 +- 差量更新优先使用 `NotifyItemChanged()` / `NotifyItemRangeChanged()`。 +- 焦点型页面已验证 `TryFocusIndex()`、`TryFocusEntry()` 与关闭/重开流程。 +- 大列表页面已检查 `PoolStats` / `PeakActive` / `GetPeakActiveCount()`。 +- 加载态、空态、错误态已验证,且不会因模板缺失报错。 +- 循环列表已使用虚拟索引,不直接拿真实索引做连续滚动。 +- `OnBind()` 中没有重复注册事件,也没有未释放的临时引用。 diff --git a/Client/Assets/Books/UIExtension/RecyclerView.md.meta b/Client/Assets/Books/UIExtension/RecyclerView.md.meta new file mode 100644 index 0000000..665cf58 --- /dev/null +++ b/Client/Assets/Books/UIExtension/RecyclerView.md.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: a63300f4743578b4cbe32d788b056cd5 +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Client/Assets/Books/UIExtension/UXButton.md b/Client/Assets/Books/UIExtension/UXButton.md new file mode 100644 index 0000000..cd15452 --- /dev/null +++ b/Client/Assets/Books/UIExtension/UXButton.md @@ -0,0 +1,22 @@ +# UIExtension UXButton 按钮模块手册 + +## 模块概述 +`UXButton` 是 `Button` 的增强版本,继承自 `UXSelectable`,统一了鼠标、键盘和手柄提交行为。 + +## 可调用 API +源码:`Packages/com.alicizax.unity.ui.extension/Runtime/UXComponent/Button/UXButton.cs` + +### 类型:`UXButton` +- `onClick` +- `OnPointerEnter(PointerEventData eventData)` +- `OnPointerClick(PointerEventData eventData)` +- `OnSubmit(BaseEventData eventData)` + +### 接口:`AlicizaX.UI.Extension.IButton` +- 按钮能力抽象接口。 + +## 快速上手 +```csharp +var btn = gameObject.AddComponent(); +btn.onClick.AddListener(() => UnityEngine.Debug.Log("Clicked")); +``` diff --git a/Client/Assets/Books/UIExtension/UXButton.md.meta b/Client/Assets/Books/UIExtension/UXButton.md.meta new file mode 100644 index 0000000..069d9f6 --- /dev/null +++ b/Client/Assets/Books/UIExtension/UXButton.md.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: e56d6be154e2f23409e38811debf993c +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Client/Assets/Books/UIExtension/UXController.md b/Client/Assets/Books/UIExtension/UXController.md new file mode 100644 index 0000000..e9779a9 --- /dev/null +++ b/Client/Assets/Books/UIExtension/UXController.md @@ -0,0 +1,51 @@ +# UIExtension UXController 数据驱动绑定模块手册 + +## 模块概述 +`UXController` 与 `UXBinding` 组成 UI 状态驱动绑定系统,用于把控制器状态映射到 UI 属性值。 + +## 适用场景 +- 页签状态切换。 +- UI 主题或皮肤切换。 +- 可见性、文本、颜色、尺寸等属性联动。 + +## 可调用 API +### 类型:`UXController` +源码:`Packages/com.alicizax.unity.ui.extension/Runtime/UXComponent/Controller/UXController.cs` +- 属性:`Controllers`、`Bindings`、`ControllerCount` +- `ControllerDefinition` 属性:`Id`、`Name`、`Length`、`Description`、`SelectedIndex` +- 方法:`TryGetControllerById(...)`、`TryGetControllerByName(...)`、`GetControllerByName(...)`、`GetControllerAt(...)`、`GetControllerIndex(...)`、`SetControllerIndex(...)`、`SetControllerIndexByName(...)`、`ResetAllControllers()` + +### 类型:`UXBinding` +源码:`Packages/com.alicizax.unity.ui.extension/Runtime/UXComponent/Controller/UXBinding.cs` +- 属性:`Controller`、`Entries` +- 方法:`Initialize()`、`SetController(...)`、`CaptureDefaults()`、`ResetToDefaults()`、`PreviewEntry(...)`、`CaptureEntryValue(...)`、`CaptureEntryFallbackValue(...)` +- `BindingEntry` 属性:`ControllerId`、`ControllerIndex`、`Property`、`Value`、`FallbackMode`、`FallbackValue`、`HasCapturedDefault` + +### 类型:`UXBindingValue` +- `BoolValue` +- `FloatValue` +- `StringValue` +- `ColorValue` +- `Vector2Value` +- `Vector3Value` +- `ObjectValue` +- `CopyFrom(UXBindingValue other)` + +### 工具类:`UXBindingPropertyUtility` +源码:`Packages/com.alicizax.unity.ui.extension/Runtime/UXComponent/Controller/UXBindingPropertyUtility.cs` +- `AllMetadata` +- `GetMetadata(...)` +- `IsSupported(...)` +- `GetSupportedProperties(...)` +- `CaptureValue(...)` +- `ApplyValue(...)` + +### 枚举 +- `UXBindingFallbackMode` +- `UXBindingValueKind` +- `UXBindingProperty` + +## 快速上手 +```csharp +controller.SetControllerIndex("TabState", 1); +``` diff --git a/Client/Assets/Books/UIExtension/UXController.md.meta b/Client/Assets/Books/UIExtension/UXController.md.meta new file mode 100644 index 0000000..c614965 --- /dev/null +++ b/Client/Assets/Books/UIExtension/UXController.md.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 7c684c4c671e41847866139254703281 +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Client/Assets/Books/UIExtension/UXDraggable.md b/Client/Assets/Books/UIExtension/UXDraggable.md new file mode 100644 index 0000000..4c282fe --- /dev/null +++ b/Client/Assets/Books/UIExtension/UXDraggable.md @@ -0,0 +1,16 @@ +# UIExtension UXDraggable 拖拽模块手册 + +## 模块概述 +`UXDraggable` 把拖拽生命周期包装为 `UnityEvent`,便于直接在 Inspector 或代码中订阅。 + +## 可调用 API +源码:`Packages/com.alicizax.unity.ui.extension/Runtime/UXComponent/Drag/UXDraggable.cs` +- `onDrag` +- `onBeginDrag` +- `onEndDrag` + +## 快速上手 +```csharp +var draggable = gameObject.AddComponent(); +draggable.onBeginDrag.AddListener(_ => UnityEngine.Debug.Log("Begin")); +``` diff --git a/Client/Assets/Books/UIExtension/UXDraggable.md.meta b/Client/Assets/Books/UIExtension/UXDraggable.md.meta new file mode 100644 index 0000000..d13380a --- /dev/null +++ b/Client/Assets/Books/UIExtension/UXDraggable.md.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 5ab5ad69624ed404da25069210b5d4c2 +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Client/Assets/Books/UIExtension/UXGroupToggle.md b/Client/Assets/Books/UIExtension/UXGroupToggle.md new file mode 100644 index 0000000..ec3f51a --- /dev/null +++ b/Client/Assets/Books/UIExtension/UXGroupToggle.md @@ -0,0 +1,22 @@ +# UIExtension UXGroup 与 UXToggle 模块手册 + +## 模块概述 +`UXGroup` 与 `UXToggle` 提供增强的单选组与 Toggle 体系,兼容更好的导航和状态控制。 + +## 可调用 API +### 类型:`UXGroup` +源码:`Packages/com.alicizax.unity.ui.extension/Runtime/UXComponent/Group/UXGroup.cs` +- 字段:`allowSwitchOff`、`defaultToggle` +- 方法:`NotifyToggleOn(...)`、`UnregisterToggle(...)`、`RegisterToggle(...)`、`ContainsToggle(...)`、`EnsureValidState()`、`AnyTogglesOn()`、`ActiveToggles()`、`GetFirstActiveToggle()`、`SetAllTogglesOff(...)`、`Next()`、`Preview()` + +### 类型:`UXToggle` +源码:`Packages/com.alicizax.unity.ui.extension/Runtime/UXComponent/Group/UXToggle.cs` +- 字段/属性:`toggleTransition`、`graphic`、`group`、`onValueChanged`、`isOn` +- 方法:`Rebuild(...)`、`LayoutComplete()`、`GraphicUpdateComplete()`、`SetIsOnWithoutNotify(...)`、`OnPointerEnter(...)`、`OnPointerClick(...)`、`OnSubmit(...)` +- 类型:`ToggleEvent : UnityEvent` + +## 快速上手 +```csharp +toggle.group = group; +toggle.onValueChanged.AddListener(v => UnityEngine.Debug.Log(v)); +``` diff --git a/Client/Assets/Books/UIExtension/UXGroupToggle.md.meta b/Client/Assets/Books/UIExtension/UXGroupToggle.md.meta new file mode 100644 index 0000000..93b1d7a --- /dev/null +++ b/Client/Assets/Books/UIExtension/UXGroupToggle.md.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 74b5de19eb107b64d8e4ef6eb081d2c0 +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Client/Assets/Books/UIExtension/UXHelper.md b/Client/Assets/Books/UIExtension/UXHelper.md new file mode 100644 index 0000000..f5b4c5b --- /dev/null +++ b/Client/Assets/Books/UIExtension/UXHelper.md @@ -0,0 +1,31 @@ +# UIExtension UXHelper 辅助接口模块手册 + +## 模块概述 +`UXHelper` 用于把 UI 扩展层与项目本地化、音频等外部系统解耦。 + +## 适用场景 +- 接入项目本地化系统。 +- 为 UI 组件统一注入帮助器。 + +## 快速上手 +```csharp +public class DemoLocalizationHelper : IUXLocalizationHelper +{ + public string GetString(string key) => key; +} + +UXComponentExtensionsHelper.SetLocalizationHelper(new DemoLocalizationHelper()); +``` + +## 可调用 API +源码:`Packages/com.alicizax.unity.ui.extension/Runtime/UXComponent/UXHelper.cs` + +### 类型:`UXComponentExtensionsHelper` +- `SetLocalizationHelper(IUXLocalizationHelper helper)` +- `SetAudioHelper(IUXAudioHelper helper)` + +### 接口:`IUXLocalizationHelper` +- `GetString(string key)` + +### 接口:`IUXAudioHelper` +- 说明:当前未定义公开方法,作为项目扩展点保留。 diff --git a/Client/Assets/Books/UIExtension/UXHelper.md.meta b/Client/Assets/Books/UIExtension/UXHelper.md.meta new file mode 100644 index 0000000..018b981 --- /dev/null +++ b/Client/Assets/Books/UIExtension/UXHelper.md.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 5da96e90155e6a54697735f6428f6812 +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Client/Assets/Books/UIExtension/UXHotkey.md b/Client/Assets/Books/UIExtension/UXHotkey.md new file mode 100644 index 0000000..91723fa --- /dev/null +++ b/Client/Assets/Books/UIExtension/UXHotkey.md @@ -0,0 +1,24 @@ +# UIExtension UXHotkey 热键注册模块手册 + +## 模块概述 +`UXHotkey` 模块统一管理 UI 热键触发器,支持作用域、层级阻断、批量绑定与调试输出。 + +## 可调用 API +### 类型:`HotkeyComponent` +源码:`Packages/com.alicizax.unity.ui.extension/Runtime/UXComponent/Hotkey/HotkeyComponent.cs` +- `HotkeyAction` + +### 接口:`IHotkeyTrigger` +源码:`Packages/com.alicizax.unity.ui.extension/Runtime/UXComponent/Hotkey/IHotkeyTrigger.cs` +- `HotkeyAction { get; }` + +### 类型:`UXHotkeyRegisterManager` +源码:`Packages/com.alicizax.unity.ui.extension/Runtime/UXComponent/Hotkey/UXHotkeyRegisterManager.cs` +- `GetDebugInfo()` +- 扩展方法:`BindHotKey(...)`、`UnBindHotKey(...)`、`BindHotKeyBatch(...)`、`UnBindHotKeyBatch(...)` +- 公开辅助类型:`HotkeyRegistration`、`HotkeyScope`、`ActionRegistration`、`TriggerRegistration` + +## 快速上手 +```csharp +trigger.BindHotKey(); +``` diff --git a/Client/Assets/Books/UIExtension/UXHotkey.md.meta b/Client/Assets/Books/UIExtension/UXHotkey.md.meta new file mode 100644 index 0000000..d608c11 --- /dev/null +++ b/Client/Assets/Books/UIExtension/UXHotkey.md.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: d08c92b1da506154683918cc2a4766b4 +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Client/Assets/Books/UIExtension/UXImage.md b/Client/Assets/Books/UIExtension/UXImage.md new file mode 100644 index 0000000..762fa0c --- /dev/null +++ b/Client/Assets/Books/UIExtension/UXImage.md @@ -0,0 +1,41 @@ +# UIExtension UXImage 图像增强模块手册 + +## 模块概述 +`UXImage` 扩展了 Unity `Image`,支持渐变、镜像、翻转填充和顶点重映射。 + +## 可调用 API +源码:`Packages/com.alicizax.unity.ui.extension/Runtime/UXComponent/Image/UXImage.cs` + +### 类型:`UXImage` +- `gradient` +- `Direction` +- `m_OriginFlipMode` +- `m_FlipMode` +- `flipMode` +- `m_FlipWithCopy` +- `flipWithCopy` +- `flipEdge` +- `m_FlipEdgeHorizontal` +- `flipEdgeHorizontal` +- `m_FlipEdgeVertical` +- `flipEdgeVertical` +- `m_FlipFillCenter` +- `flipFillCenter` +- `m_FlipDirection` +- `flipDirection` +- `RemapVertex(ref UIVertex vertex, FlipMode flipMode, float Min1, float Max1, float Min2, float Max2)` + +### 枚举 +- `ColorType` +- `GradientDirection` +- `FlipPart` +- `FlipDirection` +- `FlipMode` +- `FlipEdge` +- `FlipEdgeHorizontal` +- `FlipEdgeVertical` +- `FlipFillCenter` + +### 辅助类型 +- `Vert2D` +- `Comparer` diff --git a/Client/Assets/Books/UIExtension/UXImage.md.meta b/Client/Assets/Books/UIExtension/UXImage.md.meta new file mode 100644 index 0000000..45c99a2 --- /dev/null +++ b/Client/Assets/Books/UIExtension/UXImage.md.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 6661d6e0bda019844b46c7b7e087a16a +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Client/Assets/Books/UIExtension/UXNavigation.md b/Client/Assets/Books/UIExtension/UXNavigation.md new file mode 100644 index 0000000..5590c33 --- /dev/null +++ b/Client/Assets/Books/UIExtension/UXNavigation.md @@ -0,0 +1,37 @@ +# UIExtension UXNavigation 导航模块手册 + +## 模块概述 +`UXNavigation` 在新输入系统下增强 UI 导航,支持输入模式识别、导航作用域、默认选中项、上次选中记忆和跳过导航对象。 + +## 适用场景 +- 手柄导航友好的菜单与弹窗。 +- 多层 UI 叠加时管理焦点归属。 +- 鼠标与手柄输入模式自动切换。 + +## 可调用 API +### 枚举:`UXInputMode` +源码:`Packages/com.alicizax.unity.ui.extension/Runtime/UXComponent/Navigation/UXInputMode.cs` +- 表示当前输入模式。 + +### 类型:`UXInputModeService` +源码:`Packages/com.alicizax.unity.ui.extension/Runtime/UXComponent/Navigation/UXInputModeService.cs` +- `CurrentMode` +- `OnModeChanged` + +### 类型:`UXNavigationScope` +源码:`Packages/com.alicizax.unity.ui.extension/Runtime/UXComponent/Navigation/UXNavigationScope.cs` +- 属性:`DefaultSelectable`、`RememberLastSelection`、`RequireSelectionWhenGamepad`、`BlockLowerScopes` +- 方法:`InvalidateSelectableCache()` + +### 类型:`UXNavigationSkip` +源码:`Packages/com.alicizax.unity.ui.extension/Runtime/UXComponent/Navigation/UXNavigationSkip.cs` +- 标记对象跳过导航体系。 + +### 其他核心类型 +- `UXNavigationRuntime` +- `UXNavigationLayerWatcher` + +## 快速上手 +```csharp +scope.DefaultSelectable = firstSelectable; +``` diff --git a/Client/Assets/Books/UIExtension/UXNavigation.md.meta b/Client/Assets/Books/UIExtension/UXNavigation.md.meta new file mode 100644 index 0000000..44742bb --- /dev/null +++ b/Client/Assets/Books/UIExtension/UXNavigation.md.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: f349249d5e2bb9d409be050f28f8562d +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Client/Assets/Books/UIExtension/UXSelectable.md b/Client/Assets/Books/UIExtension/UXSelectable.md new file mode 100644 index 0000000..fd5708c --- /dev/null +++ b/Client/Assets/Books/UIExtension/UXSelectable.md @@ -0,0 +1,22 @@ +# UIExtension UXSelectable 选择态增强模块手册 + +## 模块概述 +`UXSelectable` 是 `Selectable` 的增强基类,支持对子图形做额外颜色/精灵过渡,并重写方向查找逻辑。 + +## 可调用 API +源码:`Packages/com.alicizax.unity.ui.extension/Runtime/UXComponent/Selectable/UXSelectable.cs` + +### 类型:`TransitionData` +- `targetGraphic` +- `transition` +- `colors` +- `spriteState` + +### 类型:`UXSelectable` +- `FindSelectableOnLeft()` +- `FindSelectableOnRight()` +- `FindSelectableOnUp()` +- `FindSelectableOnDown()` + +## 使用建议 +- 若控件的多个子节点需要跟随选择态变化,可优先通过 `TransitionData` 配置。 diff --git a/Client/Assets/Books/UIExtension/UXSelectable.md.meta b/Client/Assets/Books/UIExtension/UXSelectable.md.meta new file mode 100644 index 0000000..2cf046b --- /dev/null +++ b/Client/Assets/Books/UIExtension/UXSelectable.md.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 28b1ee8e6cd0239429b1ff80625c8f6e +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Client/Assets/Books/UIExtension/UXText.md b/Client/Assets/Books/UIExtension/UXText.md new file mode 100644 index 0000000..c9cc829 --- /dev/null +++ b/Client/Assets/Books/UIExtension/UXText.md @@ -0,0 +1,16 @@ +# UIExtension UXTextMeshPro 文本模块手册 + +## 模块概述 +`UXTextMeshPro` 扩展了 `TextMeshProUGUI`,用于接入项目本地化系统并支持编辑器预览刷新。 + +## 可调用 API +源码:`Packages/com.alicizax.unity.ui.extension/Runtime/UXComponent/Text/UXTextMeshPro.cs` +- `SetLocalization(string localizationID)` + +## 快速上手 +```csharp +text.SetLocalization("UI_Common_Confirm"); +``` + +## 注意事项 +- 需先通过 `UXComponentExtensionsHelper.SetLocalizationHelper()` 注入本地化帮助器。 diff --git a/Client/Assets/Books/UIExtension/UXText.md.meta b/Client/Assets/Books/UIExtension/UXText.md.meta new file mode 100644 index 0000000..2ded17e --- /dev/null +++ b/Client/Assets/Books/UIExtension/UXText.md.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: cd107b4f367d78f4b903f7be1908cdd6 +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Client/Assets/Scripts/Hotfix/GameLogic/UI/TestScrollItemRender.cs b/Client/Assets/Scripts/Hotfix/GameLogic/UI/TestScrollItemRender.cs index e7af577..b9c371f 100644 --- a/Client/Assets/Scripts/Hotfix/GameLogic/UI/TestScrollItemRender.cs +++ b/Client/Assets/Scripts/Hotfix/GameLogic/UI/TestScrollItemRender.cs @@ -1,5 +1,6 @@ using AlicizaX; using AlicizaX.UI; +using UnityEngine; using UnityEngine.EventSystems; public sealed class TestScrollItemRender : ItemRender @@ -16,17 +17,17 @@ public sealed class TestScrollItemRender : ItemRender