更新框架

This commit is contained in:
陈思海 2026-04-24 20:50:35 +08:00
parent db745820e8
commit 435ccea285
102 changed files with 1198 additions and 7774 deletions

View File

@ -0,0 +1,15 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!612988286 &1
SpriteAtlasAsset:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_Name:
serializedVersion: 2
m_MasterAtlas: {fileID: 0}
m_ImporterData:
packables:
- {fileID: 21300000, guid: 5fe20064a619ceb4aacf49802ee4f70e, type: 3}
m_IsVariant: 0

View File

@ -0,0 +1,69 @@
fileFormatVersion: 2
guid: dc412ce8a0c03474dbc19475ebe3791a
SpriteAtlasImporter:
externalObjects: {}
textureSettings:
serializedVersion: 2
anisoLevel: 1
compressionQuality: 50
maxTextureSize: 2048
textureCompression: 0
filterMode: 1
generateMipMaps: 0
readable: 0
crunchedCompression: 0
sRGB: 1
platformSettings:
- serializedVersion: 3
buildTarget: Android
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: 50
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 1
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: iPhone
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: 49
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 1
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: WebGL
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: 50
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 1
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
packingSettings:
serializedVersion: 2
padding: 4
blockOffset: 1
allowAlphaSplitting: 0
enableRotation: 0
enableTightPacking: 1
enableAlphaDilation: 1
secondaryTextureSettings: {}
variantMultiplier: 1
bindAsDefault: 1
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,120 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &11400000
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 35a9ab9e2f42f744ca3801948d9b8c2e, type: 3}
m_Name: AudioGroupConfigs
m_EditorClassIdentifier:
m_GroupConfigs:
- m_Name: "\u97F3\u6548"
m_Mute: 0
m_Volume: 1
m_AgentHelperCount: 4
m_ExposedVolumeParameter: SoundVolume
m_SpatialBlend: 1
m_DopplerLevel: 1
m_Spread: 0
m_SourcePriority: 128
m_ReverbZoneMix: 1
m_OcclusionEnabled: 0
m_OcclusionMask:
serializedVersion: 2
m_Bits: 4294967295
m_OcclusionCheckInterval: 0.12
m_OcclusionLowPassCutoff: 1200
m_OcclusionVolumeMultiplier: 0.55
AudioType: 0
audioRolloffMode: 0
minDistance: 2
maxDistance: 80
- m_Name: "\u754C\u9762\u97F3\u6548"
m_Mute: 0
m_Volume: 1
m_AgentHelperCount: 12
m_ExposedVolumeParameter: UISoundVolume
m_SpatialBlend: 0
m_DopplerLevel: 1
m_Spread: 0
m_SourcePriority: 128
m_ReverbZoneMix: 1
m_OcclusionEnabled: 0
m_OcclusionMask:
serializedVersion: 2
m_Bits: 4294967295
m_OcclusionCheckInterval: 0.12
m_OcclusionLowPassCutoff: 1200
m_OcclusionVolumeMultiplier: 0.55
AudioType: 1
audioRolloffMode: 0
minDistance: 1
maxDistance: 25
- m_Name: "\u97F3\u4E50"
m_Mute: 0
m_Volume: 1
m_AgentHelperCount: 1
m_ExposedVolumeParameter: MusicVolume
m_SpatialBlend: 0
m_DopplerLevel: 1
m_Spread: 0
m_SourcePriority: 32
m_ReverbZoneMix: 1
m_OcclusionEnabled: 0
m_OcclusionMask:
serializedVersion: 2
m_Bits: 4294967295
m_OcclusionCheckInterval: 0.12
m_OcclusionLowPassCutoff: 1200
m_OcclusionVolumeMultiplier: 0.55
AudioType: 2
audioRolloffMode: 0
minDistance: 1
maxDistance: 25
- m_Name: "\u8BED\u97F3"
m_Mute: 0
m_Volume: 1
m_AgentHelperCount: 6
m_ExposedVolumeParameter: VoiceVolume
m_SpatialBlend: 1
m_DopplerLevel: 1
m_Spread: 0
m_SourcePriority: 128
m_ReverbZoneMix: 1
m_OcclusionEnabled: 1
m_OcclusionMask:
serializedVersion: 2
m_Bits: 4294967295
m_OcclusionCheckInterval: 0.12
m_OcclusionLowPassCutoff: 1200
m_OcclusionVolumeMultiplier: 0.55
AudioType: 3
audioRolloffMode: 0
minDistance: 2
maxDistance: 80
- m_Name: "\u73AF\u5883\u97F3"
m_Mute: 0
m_Volume: 1
m_AgentHelperCount: 6
m_ExposedVolumeParameter: AmbientVolume
m_SpatialBlend: 1
m_DopplerLevel: 1
m_Spread: 0
m_SourcePriority: 128
m_ReverbZoneMix: 1
m_OcclusionEnabled: 1
m_OcclusionMask:
serializedVersion: 2
m_Bits: 4294967295
m_OcclusionCheckInterval: 0.12
m_OcclusionLowPassCutoff: 1200
m_OcclusionVolumeMultiplier: 0.55
AudioType: 4
audioRolloffMode: 0
minDistance: 2
maxDistance: 80

View File

@ -1,7 +1,8 @@
fileFormatVersion: 2
guid: 3cd21cd1bc02c44449bf7b999ae7bdd4
TextScriptImporter:
guid: 7ae4699f0778d2441907aeccaf6c9b29
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,83 +0,0 @@
# 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`,作为导入事件入口,本身无额外公开调用接口。
## 注意事项
- 建议原图目录与输出目录严格分离。
- 开启自动生成时,排除目录和关键字要统一维护。

View File

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

View File

@ -1,87 +0,0 @@
# 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` 查名,再用于按钮和菜单项。

View File

@ -1,55 +0,0 @@
# 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/` 下的场景。
- 切换前会检查当前场景未保存状态。

View File

@ -1,7 +0,0 @@
fileFormatVersion: 2
guid: 19d768fd857ab3d42887f3cddd38bf37
TextScriptImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,74 +0,0 @@
# 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 源目录通常需要先执行生成或构建流程后才存在。

View File

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

View File

@ -1,103 +0,0 @@
# 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();
```
## 注意事项
- 大项目建议优先使用缓存。
- 资源大规模变更后建议先清缓存再重新采集。

View File

@ -1,7 +0,0 @@
fileFormatVersion: 2
guid: 6e2acda8e9c7959409e0c16d83cc852f
TextScriptImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,42 +0,0 @@
# 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 流程,该工具更适合临时处理和验证。
- 执行前后应注意纹理导入配置恢复。

View File

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

View File

@ -1,98 +0,0 @@
# 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()` 中做依赖判空。

View File

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

View File

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

View File

@ -1,182 +0,0 @@
# Event
## 模块概述
Event 模块提供轻量级、低分配的结构体事件系统适合模块解耦、状态广播、UI/玩法/流程间通知,以及编辑器内订阅行为监控。
设计重点:
- 使用 `struct` 事件降低 GC 压力
- 通过泛型容器按事件类型隔离订阅列表
- 使用调试注册表记录订阅、发布、扩容等行为
## 快速开始
1. 定义 `struct` 事件并实现 `IEventArgs`
2. 调用 `EventBus.Subscribe<T>()` 订阅
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<PlayerLevelUpEvent>(OnLevelUp);
EventBus.Publish(new PlayerLevelUpEvent { NewLevel = 10 });
}
private void OnLevelUp(PlayerLevelUpEvent evt)
{
Log.Info($"Level => {evt.NewLevel}");
}
}
```
## 架构说明
```text
业务代码
├─ EventBus.Subscribe<T>()
├─ EventBus.Publish<T>()
└─ EventBus.Clear<T>()
EventContainer<T>
EventRuntimeHandle / 订阅槽
EventDebugRegistry
```
## 核心类型说明
### `IEventArgs`
- 所有事件载体的标记接口
- 事件类型必须是 `struct`
### `EventBus`
公开能力:
- `Subscribe<T>(Action<T> handler)`
- `Publish<T>(in T evt)`
- `GetSubscriberCount<T>()`
- `EnsureCapacity<T>(int capacity)`
- `Clear<T>()`
### `EventRuntimeHandle`
- 表示一次已建立的订阅
- 用于解绑或自动管理订阅生命周期
### `EventInitialSize<T>`
- 用于为某类事件声明初始订阅容量
- 高频事件建议预热容量
## API 参考
### `EventBus.Subscribe<T>(Action<T> handler) where T : struct, IEventArgs`
- 必填参数:`handler`
- 返回值:`EventRuntimeHandle`
- 泛型约束:`T : struct, IEventArgs`
- 说明:订阅某种事件
- 注意:建议保存返回句柄,便于后续解绑
### `EventBus.Publish<T>(in T evt) where T : struct, IEventArgs`
- 必填参数:`evt`
- 返回值:无
- 说明:同步发布事件
- 注意:回调会立即执行,发布链路中不要写过重逻辑
### `EventBus.GetSubscriberCount<T>()`
- 返回值:`int`
- 说明:返回当前事件订阅数量
### `EventBus.EnsureCapacity<T>(int capacity)`
- 必填参数:`capacity`
- 返回值:无
- 说明:预热内部容器容量
### `EventBus.Clear<T>()`
- 返回值:无
- 说明:清空某类事件订阅
## 完整使用示例
```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<CoinChangedEvent>(8);
_handle = EventBus.Subscribe<CoinChangedEvent>(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` 为同步执行,避免重逻辑
- 结合编辑器事件监视器检查扩容次数和订阅抖动

View File

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

View File

@ -1,186 +0,0 @@
# 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<T>()`
- `TryGet<T>(out T service)`
- `Shutdown()`
### `GameApp`
公开入口:
- `Base`
- `Audio`
- `Localization`
- `ObjectPool`
- `Procedure`
- `Resource`
- `Scene`
- `Timer`
- `UI`
### `ModuleDynamicBind`
作用:
- 在非 Editor 环境中读取 `ModuleDynamicBindInfo`
- 对 `ResourceComponent`、`DebuggerComponent`、`LocalizationComponent` 注入启动配置
## API 参考
### `AppServices.Require<T>() where T : class, IService`
- 参数:无
- 返回值:`T`
- 泛型约束:`class, IService`
- 说明:强制获取已注册服务
- 异常:服务世界未创建或服务未注册时失败
### `AppServices.TryGet<T>(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` 的缓存入口

View File

@ -1,7 +0,0 @@
fileFormatVersion: 2
guid: 91578f5be03b1de4dbac802cab59cb24
TextScriptImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,98 +0,0 @@
# 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<GameObject>`
### `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`

View File

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

View File

@ -1,204 +0,0 @@
# ObjectPool
## 模块概述
ObjectPool 是面向纯 C# 对象的通用对象池模块适合缓存临时数据对象、资源包装对象、UI/资源扩展辅助对象和调试器内部缓存对象。
它与 `MemoryPool` 的区别:
- `MemoryPool` 更偏向单个对象快速申请/释放
- `ObjectPool` 更偏向带策略的池管理,支持容量、过期、优先级、锁定等行为
## 快速开始
1. 场景挂载 `ObjectPoolComponent`
2. 定义继承 `ObjectBase` 的对象类型
3. 使用 `GameApp.ObjectPool.CreatePool<T>()`
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<CacheItem>(ObjectPoolCreateOptions.Single("Cache"));
CacheItem item = MemoryPool.Acquire<CacheItem>();
item.Key = "A";
pool.Register(item, false);
CacheItem spawned = pool.Spawn();
pool.Unspawn(spawned);
}
}
```
## 架构说明
```text
ObjectPoolComponent
└─ ObjectPoolService
├─ IObjectPool<T>
├─ ObjectPoolBase
└─ ObjectBase
```
## 核心类型说明
### `IObjectPoolService`
负责:
- 创建对象池
- 查询对象池
- 销毁对象池
- 统一释放未使用对象
### `IObjectPool<T>`
常用公开方法:
- `Register`
- `Spawn`
- `Unspawn`
- `SetLocked`
- `SetPriority`
- `Release`
- `ReleaseAllUnused`
### `ObjectBase`
- 池内对象基类
- 需要正确实现 `Clear()`
### `ObjectPoolCreateOptions`
主要参数:
- `name`
- `allowMultiSpawn`
- `autoReleaseInterval`
- `capacity`
- `expireTime`
- `priority`
## API 参考
### `IObjectPoolService.CreatePool<T>(ObjectPoolCreateOptions options = default) where T : ObjectBase`
- 可选参数:`options`
- 返回值:`IObjectPool<T>`
- 泛型约束:`T : ObjectBase`
### `IObjectPool<T>.Register(T obj, bool spawned)`
- 必填参数:`obj`
- 必填参数:`spawned`
- 返回值:无
### `IObjectPool<T>.Spawn()`
- 返回值:`T`
- 说明:取出一个可用对象;无可用对象时通常返回 `null`
### `IObjectPool<T>.Unspawn(T obj)`
- 必填参数:`obj`
- 返回值:无
### `IObjectPool<T>.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<DamageTextData> _pool;
private void Start()
{
_pool = GameApp.ObjectPool.CreatePool<DamageTextData>(
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<DamageTextData>();
data.Init(i, Color.red);
_pool.Register(data, false);
}
}
public void ShowDamage(int damage)
{
DamageTextData data = _pool.Spawn();
if (data == null)
{
data = MemoryPool.Acquire<DamageTextData>();
}
data.Init(damage, Color.yellow);
Debug.Log($"Damage => {data.Value}");
_pool.Unspawn(data);
}
}
```
## 最佳实践
- 对象应保持“可完全重置”
- 通过 `ObjectPoolCreateOptions` 明确池策略
- 大量短生命周期对象优先考虑对象池
## 常见错误
- 把 `GameObject` 放入该池
- `Clear()` 未重置字段
- 未区分 `Register(..., true/false)`
## 性能注意事项
- 大对象池请配置 `capacity``expireTime`
- 过多小池会增加管理成本

View File

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

View File

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

View File

@ -1,157 +0,0 @@
# 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<InventoryItemHolder> { ... }
```
## 最佳实践
- 把 UI 生成规则纳入项目模板,避免每个模块自行定义一套命名方式
- Holder 文件视为“生成产物”,不要手工长期维护
- 如果修改了 prefab 绑定节点,重新执行一次生成工具
- 逻辑类和生成类分离,减少合并冲突
## 常见错误
### 预制体不在配置的 UI 根路径下
- 现象:执行生成工具无结果
- 原因:`UIGenerateQuick.CheckCanGenerate` 校验失败
### 修改了预制体但没重新生成 Holder
- 现象:字段缺失或控件引用错误
- 处理:重新执行 `GameObject/UI生成绑定`
### 手动改了生成类名称或命名空间
- 现象:逻辑类泛型类型失配
- 处理:尽量不要手改生成文件,必要时同步更新生成配置与逻辑类引用

View File

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

View File

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

View File

@ -1,54 +0,0 @@
# 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 打包链路

View File

@ -1,7 +0,0 @@
fileFormatVersion: 2
guid: 2052075fd72f78349bc745789185c70d
TextScriptImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,134 +0,0 @@
# 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<MainMenuWindow>();
}
}
```
## 6. 常见问题
### `GameApp.xxx` 为空
- 对应模块组件尚未挂载或未完成初始化。
- `RootModule` 尚未建立根服务容器。
- 获取时机早于模块注册阶段。
### UI 无法打开
- `UIComponent` 未配置 `UIRoot`
- UI 资源路径或 `UIResAttribute` 配置错误。
- UI 绑定代码未生成,或 `UIWidget<T>` 泛型未引用正确的 Holder 类型。
### 资源加载失败
- 资源包尚未初始化。
- 资源路径与包内地址不一致。
- 运行环境缺少对应包或依赖资源。
## 7. 下一步
- 从 [Foundation](./Core/Foundation.md) 理解根模块与服务注册方式。
- 从 [Resource](./Runtime/Resource.md) 理解资源系统初始化与加载链路。
- 从 [UI](./Runtime/UI.md) 理解 UIHolder 自动生成、`UIWidget<T>` 绑定与窗口生命周期。

View File

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

View File

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

View File

@ -1,94 +0,0 @@
# 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 音效分混音组管理

View File

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

View File

@ -1,68 +0,0 @@
# 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");
}
```
## 最佳实践
- 自定义窗口只放诊断信息
- 发布环境建议仅开发版开启

View File

@ -1,7 +0,0 @@
fileFormatVersion: 2
guid: 622e9f4d6de5d6f4d90bec501dcc2592
TextScriptImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,97 +0,0 @@
# 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 不存在却未做兜底
- 资源服务未注册时依赖资源路径表

View File

@ -1,7 +0,0 @@
fileFormatVersion: 2
guid: 5e7853f21507379449bb237975fb20ae
TextScriptImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,103 +0,0 @@
# Procedure
## 模块概述
Procedure 模块用于组织游戏主流程,如启动、登录、主城、战斗、结算等状态切换。它以 `ProcedureBase` 为模板方法基类,把生命周期拆成初始化、进入、离开、更新、销毁。
## 快速开始
1. 挂载 `ProcedureComponent`
2. 定义多个继承 `ProcedureBase` 的流程
3. 调用 `InitializeProcedure`
4. 通过 `SwitchProcedure<T>()` 切换
## 架构说明
```text
ProcedureComponent
└─ ProcedureService
├─ IProcedure
└─ ProcedureBase
```
## 核心类与接口
### `IProcedureService`
公开能力:
- `CurrentProcedureType`
- `InitializeProcedure(...)`
- `ClearAllProcedures()`
- `ContainsProcedure(Type procedureType)`
- `TrySwitchProcedure(Type procedureType)`
### `ProcedureBase`
生命周期模板:
- `OnInit()`
- `OnEnter()`
- `OnLeave()`
- `OnUpdate()`
- `OnDestroy()`
## API 参考
### `InitializeProcedure(IEnumerable<IProcedure> 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<LoginProcedure>();
}
}
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`
## 常见错误
- 未初始化就切换流程
- 把长时间并行任务塞入流程本身

View File

@ -1,7 +0,0 @@
fileFormatVersion: 2
guid: 8e26efa856cdeb8458b10c02c655dad6
TextScriptImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,673 +0,0 @@
# Resource
## 模块概述
Resource 模块基于 YooAsset 封装了资源包初始化、版本管理、资源查询、同步/异步加载、实例化、缓存回收与低内存处理能力。
它是多个模块的底层依赖:
- `UI`:加载窗口和 Widget 预制体
- `Audio`:加载音频资源
- `Localization`:加载语言表
- `GameObjectPool`:加载池对象原始 prefab
- `Scene`:主场景与附加场景的资源加载链路
从当前实现来看,`ResourceService` 还额外提供了:
- 多包管理
- 同一路径并发加载合并
- 资源对象池缓存
- 低内存时统一回收入口
- 下载、版本、清单更新能力
## 快速开始
### 最少步骤
1. 在场景中挂载 `ResourceComponent`
2. 配置 `PlayMode`、默认包名和解密服务名
3. 游戏启动时调用 `InitPackageAsync`
4. 通过 `GameApp.Resource``AppServices.Require<IResourceService>()` 加载资源
### 最小示例
```csharp
using AlicizaX;
using UnityEngine;
public sealed class ResourceQuickStart : MonoBehaviour
{
private async void Start()
{
await GameApp.Resource.InitPackageAsync();
Texture2D icon = await GameApp.Resource.LoadAssetAsync<Texture2D>("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<bool> InitPackageAsync(string packageName = "", string hostServerURL = "", string fallbackHostServerURL = "")`
- 可选参数:`packageName`
- 可选参数:`hostServerURL`
- 可选参数:`fallbackHostServerURL`
- 返回值:`UniTask<bool>`
说明:
- 初始化指定资源包
- 如果 `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<T>(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<T> LoadAssetAsync<T>(string location, CancellationToken cancellationToken = default, string packageName = "") where T : UnityEngine.Object`
- 必填参数:`location`
- 可选参数:`cancellationToken`
- 可选参数:`packageName`
- 返回值:`UniTask<T>`
#### `UniTask<GameObject> LoadGameObjectAsync(string location, Transform parent = null, CancellationToken cancellationToken = default, string packageName = "")`
- 必填参数:`location`
- 可选参数:`parent`
- 可选参数:`cancellationToken`
- 可选参数:`packageName`
- 返回值:`UniTask<GameObject>`
### 六、回调式加载
#### `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<T>(string location, string packageName = "") where T : UnityEngine.Object`
#### `AssetHandle LoadAssetAsyncHandle<T>(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<TextAsset>("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<Sprite>("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<T>` 的释放语义不同
#### `LoadAsset<T>`
- 返回的是资源对象本身
- 如有需要可以显式 `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`
- 高并发异步加载时,优先复用路径和包配置,发挥内部合并机制优势

View File

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

View File

@ -1,477 +0,0 @@
# 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<float> progressCallBack = null)`
- `Unload(string location, Action callBack = null, Action<float> progressCallBack = null)`
- `IsContainScene(string location)`
### `ISceneStateService`
偏状态查询接口,主要用于:
- 查询当前主场景
- 查询某个场景是否已记录在当前 Scene Scope 中
### `SceneDomainStateService`
当前实现中负责维护:
- `CurrentMainSceneName`
- `CurrentMainSceneHandle`
- `_subScenes`
- `_handlingScenes`
用途:
- 避免同一场景重复并发加载/卸载
- 为 `IsContainScene` / `IsMainScene` 提供判断基础
## API 参考
### 一、场景加载
#### `UniTask<Scene> LoadSceneAsync(string location, LoadSceneMode sceneMode = LoadSceneMode.Single, bool suspendLoad = false, uint priority = 100, bool gcCollect = true, Action<float> progressCallBack = null)`
- 必填参数:`location`
- 可选参数:`sceneMode`
- 可选参数:`suspendLoad`
- 可选参数:`priority`
- 可选参数:`gcCollect`
- 可选参数:`progressCallBack`
- 返回值:`UniTask<UnityEngine.SceneManagement.Scene>`
参数说明:
- `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<Scene> callBack = null, bool gcCollect = true, Action<float> 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<bool> UnloadAsync(string location, Action<float> progressCallBack = null)`
- 必填参数:`location`
- 可选参数:`progressCallBack`
- 返回值:`UniTask<bool>`
说明:
- 用于卸载 Additive 子场景
- 当前实现**不用于直接卸载主场景**
#### `void Unload(string location, Action callBack = null, Action<float> 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<IResourceService>().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 波动

View File

@ -1,7 +0,0 @@
fileFormatVersion: 2
guid: 13866105ba2b78a4688dbb55ad947045
TextScriptImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,672 +0,0 @@
# Timer
## 模块概述
Timer 模块提供统一定时器服务,适合处理:
- 延时执行
- 循环轮询
- UI 倒计时
- 技能延时结算
- 超时控制
- 轻量轮询逻辑
它由 `TimerComponent` 注册 `TimerService`,并通过 `GameApp.Timer``AppServices.Require<ITimerService>()` 暴露给业务层。
从实现上看,`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<T>
└─ 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<T>(Action<T> 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<T>`
适合:
- 单参数且类型明确的回调
- 希望避免 `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<T>(Action<T> 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<int>(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<T>`
## 性能注意事项
- 少量长生命周期定时器成本很低
- 大量高频定时器建议业务上合并
- 能用泛型单参数回调时,优先别用 `object[]`
- 大量短周期循环定时器应谨慎使用,优先考虑合并成统一更新器
## 适用场景建议
### 适合使用 Timer 模块
- 秒级倒计时
- UI 展示延迟
- 技能或状态延后执行
- 轻量循环任务
### 不适合使用 Timer 模块
- 每帧复杂逻辑
- 高频实时物理计算
- 长链路异步流程编排
这些场景更适合:
- `Update`
- `Coroutine`
- `UniTask`
- 专门的状态机/调度器

View File

@ -1,7 +0,0 @@
fileFormatVersion: 2
guid: 6a5da693652014446867c2397a69fd27
TextScriptImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,552 +0,0 @@
# UI
## 模块概述
UI 模块负责窗口创建、资源定位、层级管理、显示/关闭、缓存、过渡动画与界面绑定代码协作。
本模块里最容易误解的一点是:
- `UIHolderObjectBase`(通常简称 **UIHolder**)不是推荐手写的业务类
- 项目中的大多数 `UIHolder` 都应通过 **UI 绑定工具自动生成**
- 业务代码通常只编写 `UIWindow<T>`、`UIWidget<T>`、`UITabWindow<T>` 这类逻辑类
换句话说:
- **UIHolder 负责“控件引用和预制体桥接”**
- **UI 逻辑类负责“界面行为和生命周期”**
## 快速开始
1. 在场景中挂载 `UIComponent`
2. 为 `UIComponent.uiRoot` 指定 UI 根预制体
3. 在 `UISettingEditorWindow` 中配置 UI 生成规则
4. 选中 UI 预制体根节点,执行 `GameObject/UI生成绑定`
5. 让生成器自动生成 `UIHolder` 类并挂回预制体
6. 在逻辑类中使用 `UIWindow<T>``UIWidget<T>`,其中 `T` 就是生成出来的 `UIHolder` 类型
## 架构说明
```text
UIComponent
└─ UIService
├─ UIBase
├─ UIWindow<T>
├─ UIWidget<T>
├─ UITabWindow<T>
├─ UIHolderObjectBase
├─ UIMetaRegistry
├─ UIResRegistry
└─ UIHolderFactory
```
### 角色分工
- `UIBase`UI 逻辑生命周期基类
- `UIWindow<T>`:顶层窗口逻辑
- `UIWidget<T>`:挂在窗口或其他 Widget 下的子部件逻辑
- `UITabWindow<T>`:支持 Tab 页懒加载与切换的窗口基类
- `UIHolderObjectBase`:预制体绑定脚本基类,负责暴露控件引用、`RectTransform`、转场播放器
- `UIHolderFactory`:根据注册信息加载预制体并创建对应 Holder
### `UIBase`、`UIHolderObjectBase`、`UIWidget<T>` 的关系
```text
UI 预制体
└─ 挂载生成的 XXXHolder : UIHolderObjectBase
UIWidget<XXXHolder> / UIWindow<XXXHolder>
通过泛型参数 T 访问 baseui
```
说明:
- `UIHolderObjectBase` 挂在预制体上,持有控件引用
- `UIWindow<T>` / `UIWidget<T>` 的泛型参数 `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<T>` 中如何引用生成的 UIHolder
关键点:
- `T` 就是生成工具输出的 Holder 类型
- 逻辑类不需要自己声明控件字段
- 通过 `baseui` 访问自动生成的 Holder 成员
例如:
```csharp
using AlicizaX.UI.Runtime;
using UnityEngine;
public sealed class InventoryItemWidget : UIWidget<InventoryItemHolder>
{
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<InventoryItemHolder>` 把逻辑和绑定类关联起来
## 核心类与接口
### `IUIService`
负责:
- 初始化 UI 根节点
- 打开/关闭窗口
- 查询已打开窗口
- 获取层级根节点
- 注入 `ITimerService`
### `UIBase`
关键生命周期:
- `OnInitialize()`
- `OnOpen()`
- `OnClose()`
- `OnDestroy()`
- `OnUpdate()`
以及对应异步版本:
- `OnInitializeAsync()`
- `OnOpenAsync()`
- `OnCloseAsync()`
并提供:
- `CreateWidgetAsync<T>()`
- `CreateWidgetSync<T>()`
- `RemoveWidget(UIBase widget)`
### `UIWindow<T>`
适合顶层窗口,通常用于:
- 主界面
- 设置页
- 背包页
- 弹窗
常用能力:
- `CloseSelf()`
- 强制关闭
- 打开后顶层排序与层级遮挡处理
### `UIWidget<T>`
适合子部件,通常用于:
- 列表项
- 面板块
- 详情条目
- 页签子页面
公开方法:
- `Open(params object[] userDatas)`
- `Close()`
- `Destroy()`
### `UITabWindow<T>`
用于页签式窗口,支持:
- 预注册 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<T>` / `UIWidget<T>` 对应的逻辑实例上
你通常**不会在业务层频繁直接调用它**,因为:
- 打开窗口时,`UIService` 会在内部调用 `UIHolderFactory`
- 创建 Widget 时,`UIBase.CreateWidgetAsync<T>()` / `CreateWidgetSync<T>()` 也会在内部调用它
可以把它理解为:
```text
UI 逻辑类
-> UIService / UIBase
-> UIHolderFactory
-> 加载预制体
-> 找到生成的 XXXHolder
-> 绑定到 UIWindow<T> / UIWidget<T>
```
#### 典型作用场景
1. `ShowUI<InventoryWindow>()`
- `UIService` 找到 `InventoryWindow` 对应的元数据
- `UIHolderFactory` 根据 `InventoryWindowHolder``UIResAttribute` 加载预制体
- 创建并返回 `InventoryWindowHolder`
- 把 Holder 绑定给 `InventoryWindow`
2. `CreateWidgetAsync<InventoryItemWidget>(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<InventoryItemHolder>(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<InventoryItemHolder>(previewRoot);
if (holder != null)
{
holder.TextName.text = "Sync Preview";
holder.TextCount.text = "1";
}
}
}
```
#### 注意事项
- `T` 必须是正确的生成型 `UIHolder`,且继承自 `UIHolderObjectBase`
- 对应 Holder 需要已具备 `UIResAttribute`,通常由绑定工具自动生成
- 如果资源路径错误、预制体未挂对应 Holder`UIHolderFactory` 绑定会失败
- 正常业务打开窗口和创建 Widget 时,优先走 `GameApp.UI.ShowUI<T>()`、`CreateWidgetAsync<T>()`,不建议绕过框架直接大量使用工厂
## API 参考
### `IUIService.Initialize(Transform root, bool isOrthographic)`
- 必填参数:`root`
- 必填参数:`isOrthographic`
- 返回值:无
- 说明:初始化 UI 根、Canvas、Camera 与各层级节点
### `UniTask<T> ShowUI<T>(params object[] userDatas) where T : UIBase`
- 可选参数:`userDatas`
- 返回值:`UniTask<T>`
- 泛型约束:`T : UIBase`
- 说明:异步打开窗口
- 推荐:默认优先使用该方法
### `T ShowUISync<T>(params object[] userDatas) where T : UIBase`
- 返回值:`T`
- 说明:同步打开窗口
- 注意:仅在资源已就绪时使用
### `void CloseUI<T>(bool force = false) where T : UIBase`
- 可选参数:`force`
- 返回值:无
- 说明:关闭指定窗口
### `CreateWidgetAsync<T>(Transform parent, bool visible = true) where T : UIBase`
- 必填参数:`parent`
- 可选参数:`visible`
- 返回值:`UniTask<T>`
- 泛型约束:`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<InventoryWindowHolder>
{
protected override async Cysharp.Threading.Tasks.UniTask OnInitializeAsync()
{
baseui.BtnClose.onClick.AddListener(CloseSelf);
InventoryItemWidget item = await CreateWidgetAsync<InventoryItemWidget>(baseui.TfContent, false);
item.Open("Potion", 10);
}
protected override void OnOpen()
{
baseui.TextTitle.text = "Inventory";
}
}
```
说明:
- `InventoryWindowHolder` 推荐由 UI 绑定工具生成
- `InventoryWindow` 由业务手写
### 示例 2Widget 使用生成的 Holder
```csharp
using AlicizaX.UI.Runtime;
using UnityEngine;
public sealed class InventoryItemWidget : UIWidget<InventoryItemHolder>
{
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();
}
}
```
### 示例 3TabWindow
```csharp
using AlicizaX.UI.Runtime;
[Window(UILayer.UI, fullScreen: true)]
public sealed class SettingWindow : UITabWindow<SettingWindowHolder>
{
protected override void OnInitialize()
{
InitTabVirtuallyView<GraphicsTabWidget>(baseui.TfTabRoot);
InitTabVirtuallyView<AudioTabWidget>(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<T>`
## 常见错误
### 手工编写 UIHolder 导致与生成器冲突
- 现象:字段名、命名空间或资源路径不一致
- 规避:把 Holder 视为生成产物,由工具维护
### 在 `OnOpen` 中重复注册按钮事件
- 风险:窗口每次打开都会重复绑定
- 正确做法:放到 `OnInitialize`
### 把 `UIWidget<T>` 当顶层窗口直接 `ShowUI`
- `UIWidget<T>` 应由父 `UIBase` 通过 `CreateWidgetAsync<T>()``CreateWidgetSync<T>()` 创建
## 性能注意事项
- 首次打开大窗口优先预热资源或异步显示
- 使用自动生成 Holder 可以避免大量运行时查找和手工拖引用错误
- 高频销毁/重建的块状内容优先用 `UIWidget<T>`

View File

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

View File

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

View File

@ -1,94 +0,0 @@
# UIExtension InputGlyph 输入图标模块手册
## 模块概述
`InputGlyph` 提供输入设备检测、绑定解析、图标数据库查询、UI Image/TMP 输出和重绑定后自动刷新能力。
## 适用场景
- 显示键盘、手柄按键图标。
- 文本中嵌入按键提示。
- 输入重绑定后自动刷新 UI 提示。
## 快速上手
1. 创建 `InputGlyphDatabase` 并放到 `Resources/InputGlyphDatabase.asset`
2. 给目标对象挂 `InputGlyph`
3. 选择动作来源与输出模式。
## 详细使用说明
- `InputGlyph` 会监听 `InputDeviceWatcher``InputBindingManager.BindingsChanged`
- 输出为 `Image` 时设置 `targetImage.sprite`
- 输出为 `Text` 时写入 TMP `<sprite>` 标签或回退显示文本。
## 可调用 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<T>(string actionName)`
- `ReadValue(string actionName)`
- `TryReadValue<T>(string actionName, out T value)`
- `TryReadValue(string actionName, out object value)`
- `TryReadValueOnce<T>(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;
}
```

View File

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

File diff suppressed because it is too large Load Diff

View File

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

View File

@ -1,22 +0,0 @@
# 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<UnityEngine.UI.UXButton>();
btn.onClick.AddListener(() => UnityEngine.Debug.Log("Clicked"));
```

View File

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

View File

@ -1,51 +0,0 @@
# 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);
```

View File

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

View File

@ -1,16 +0,0 @@
# UIExtension UXDraggable 拖拽模块手册
## 模块概述
`UXDraggable` 把拖拽生命周期包装为 `UnityEvent<PointerEventData>`,便于直接在 Inspector 或代码中订阅。
## 可调用 API
源码:`Packages/com.alicizax.unity.ui.extension/Runtime/UXComponent/Drag/UXDraggable.cs`
- `onDrag`
- `onBeginDrag`
- `onEndDrag`
## 快速上手
```csharp
var draggable = gameObject.AddComponent<UnityEngine.UI.UXDraggable>();
draggable.onBeginDrag.AddListener(_ => UnityEngine.Debug.Log("Begin"));
```

View File

@ -1,7 +0,0 @@
fileFormatVersion: 2
guid: 5ab5ad69624ed404da25069210b5d4c2
TextScriptImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,22 +0,0 @@
# 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<bool>`
## 快速上手
```csharp
toggle.group = group;
toggle.onValueChanged.AddListener(v => UnityEngine.Debug.Log(v));
```

View File

@ -1,7 +0,0 @@
fileFormatVersion: 2
guid: 74b5de19eb107b64d8e4ef6eb081d2c0
TextScriptImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,31 +0,0 @@
# 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`
- 说明:当前未定义公开方法,作为项目扩展点保留。

View File

@ -1,7 +0,0 @@
fileFormatVersion: 2
guid: 5da96e90155e6a54697735f6428f6812
TextScriptImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,24 +0,0 @@
# 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();
```

View File

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

View File

@ -1,41 +0,0 @@
# 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`

View File

@ -1,7 +0,0 @@
fileFormatVersion: 2
guid: 6661d6e0bda019844b46c7b7e087a16a
TextScriptImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,37 +0,0 @@
# 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;
```

View File

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

View File

@ -1,22 +0,0 @@
# 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` 配置。

View File

@ -1,7 +0,0 @@
fileFormatVersion: 2
guid: 28b1ee8e6cd0239429b1ff80625c8f6e
TextScriptImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,16 +0,0 @@
# 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()` 注入本地化帮助器。

View File

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

View File

@ -12,4 +12,70 @@ MonoBehaviour:
m_Script: {fileID: 11500000, guid: 32739bac255eb5f428628746c6e427f4, type: 3}
m_Name: PoolConfig
m_EditorClassIdentifier:
configs: []
entries:
- entryName: PoolEntry1
group: Default
assetPath:
matchMode: 0
loaderType: 0
overflowPolicy: 0
trimPolicy: 1
activationMode: 0
resetMode: 1
minRetained: 0
softCapacity: 8
hardCapacity: 8
idleTrimDelay: 30
prefabUnloadDelay: 60
autoRecycleDelay: 0
trimBatchPerTick: 2
warmupBatchPerFrame: 2
warmupFrameBudgetMs: 1
allowRuntimeExpand: 0
keepPrefabResident: 0
aggressiveTrimOnLowMemory: 0
priority: 0
- entryName: PoolEntry2
group: Default
assetPath:
matchMode: 0
loaderType: 0
overflowPolicy: 0
trimPolicy: 1
activationMode: 0
resetMode: 1
minRetained: 0
softCapacity: 8
hardCapacity: 8
idleTrimDelay: 30
prefabUnloadDelay: 60
autoRecycleDelay: 0
trimBatchPerTick: 2
warmupBatchPerFrame: 2
warmupFrameBudgetMs: 1
allowRuntimeExpand: 0
keepPrefabResident: 0
aggressiveTrimOnLowMemory: 0
priority: 1
- entryName: PoolEntry3
group: Default
assetPath:
matchMode: 0
loaderType: 0
overflowPolicy: 0
trimPolicy: 1
activationMode: 0
resetMode: 1
minRetained: 0
softCapacity: 8
hardCapacity: 8
idleTrimDelay: 30
prefabUnloadDelay: 60
autoRecycleDelay: 0
trimBatchPerTick: 2
warmupBatchPerFrame: 2
warmupFrameBudgetMs: 1
allowRuntimeExpand: 0
keepPrefabResident: 0
aggressiveTrimOnLowMemory: 0
priority: 2

View File

@ -67,7 +67,7 @@ TextureImporter:
swizzle: 50462976
cookieLightType: 1
platformSettings:
- serializedVersion: 4
- serializedVersion: 3
buildTarget: DefaultTexturePlatform
maxTextureSize: 2048
resizeAlgorithm: 0
@ -80,7 +80,7 @@ TextureImporter:
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 4
- serializedVersion: 3
buildTarget: Standalone
maxTextureSize: 2048
resizeAlgorithm: 0
@ -93,11 +93,23 @@ TextureImporter:
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: WebGL
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
spriteSheet:
serializedVersion: 2
sprites: []
outline: []
customData:
physicsShape: []
bones: []
spriteID: 5e97eb03825dee720800000000000000
@ -107,8 +119,6 @@ TextureImporter:
edges: []
weights: []
secondaryTextures: []
spriteCustomMetadata:
entries: []
nameFileIdTable: {}
mipmapLimitGroupName:
pSDRemoveMatte: 0

View File

@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 00a54e10cd11cbd419de04cbd506b6d3
guid: bf510dab34b4e7847a7819b48a1cf122
folderAsset: yes
DefaultImporter:
externalObjects: {}

Binary file not shown.

After

Width:  |  Height:  |  Size: 806 B

View File

@ -0,0 +1,127 @@
fileFormatVersion: 2
guid: 5fe20064a619ceb4aacf49802ee4f70e
TextureImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 13
mipmaps:
mipMapMode: 0
enableMipMap: 0
sRGBTexture: 1
linearTexture: 0
fadeOut: 0
borderMipMap: 0
mipMapsPreserveCoverage: 0
alphaTestReferenceValue: 0.5
mipMapFadeDistanceStart: 1
mipMapFadeDistanceEnd: 3
bumpmap:
convertToNormalMap: 0
externalNormalMap: 0
heightScale: 0.25
normalMapFilter: 0
flipGreenChannel: 0
isReadable: 0
streamingMipmaps: 0
streamingMipmapsPriority: 0
vTOnly: 0
ignoreMipmapLimit: 0
grayScaleToAlpha: 0
generateCubemap: 6
cubemapConvolution: 0
seamlessCubemap: 0
textureFormat: 1
maxTextureSize: 2048
textureSettings:
serializedVersion: 2
filterMode: 1
aniso: 1
mipBias: 0
wrapU: 1
wrapV: 1
wrapW: 1
nPOTScale: 0
lightmap: 0
compressionQuality: 50
spriteMode: 1
spriteExtrude: 1
spriteMeshType: 1
alignment: 0
spritePivot: {x: 0.5, y: 0.5}
spritePixelsToUnits: 100
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
spriteGenerateFallbackPhysicsShape: 0
alphaUsage: 1
alphaIsTransparency: 1
spriteTessellationDetail: -1
textureType: 8
textureShape: 1
singleChannelComponent: 0
flipbookRows: 1
flipbookColumns: 1
maxTextureSizeSet: 0
compressionQualitySet: 0
textureFormatSet: 0
ignorePngGamma: 0
applyGammaDecoding: 0
swizzle: 50462976
cookieLightType: 0
platformSettings:
- serializedVersion: 3
buildTarget: DefaultTexturePlatform
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: Standalone
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: WebGL
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
spriteSheet:
serializedVersion: 2
sprites: []
outline: []
physicsShape: []
bones: []
spriteID: 5e97eb03825dee720800000000000000
internalID: 0
vertices: []
indices:
edges: []
weights: []
secondaryTextures: []
nameFileIdTable: {}
mipmapLimitGroupName:
pSDRemoveMatte: 0
userData:
assetBundleName:
assetBundleVariant:

View File

@ -41,6 +41,7 @@ namespace GameLogic
{
GameLocaizationTable table = await GameApp.Resource.LoadAssetAsync<GameLocaizationTable>("LocalizationTable");
GameApp.Localization.IncreAddLocalizationConfig(table);
GameApp.Resource.UnloadAsset(table);
Log.Info("加载多语言配置表完毕");
GameApp.UI.ShowUISync<UILoadUpdate>();
Log.Info("sdadasdas");

View File

@ -68,19 +68,18 @@ namespace Unity.Startup.Procedure
}
private static void SetProgressUpdate(AssetDownloadProgressUpdateEventArgs gameEventArgs)
private static void SetProgressUpdate(in AssetDownloadProgressUpdateEventArgs gameEventArgs)
{
var message = (AssetDownloadProgressUpdateEventArgs)gameEventArgs;
_currentDownloadBytes = message.CurrentDownloadSizeBytes;
float progress = message.CurrentDownloadSizeBytes / (message.TotalDownloadSizeBytes * 1f);
string currentSizeMb = Utility.File.GetBytesSize(message.CurrentDownloadSizeBytes);
string totalSizeMb = Utility.File.GetBytesSize(message.TotalDownloadSizeBytes);
_currentDownloadBytes = gameEventArgs.CurrentDownloadSizeBytes;
float progress = gameEventArgs.CurrentDownloadSizeBytes / (gameEventArgs.TotalDownloadSizeBytes * 1f);
string currentSizeMb = Utility.File.GetBytesSize(gameEventArgs.CurrentDownloadSizeBytes);
string totalSizeMb = Utility.File.GetBytesSize(gameEventArgs.TotalDownloadSizeBytes);
string speed = Utility.File.GetLengthString((int)CurrentSpeed);
string line1 = Utility.Text.Format("正在更新,已更新 {0}/{1} ({2:F2}%)", message.CurrentDownloadCount, message.TotalDownloadCount, progress);
string line1 = Utility.Text.Format("正在更新,已更新 {0}/{1} ({2:F2}%)", gameEventArgs.CurrentDownloadCount, gameEventArgs.TotalDownloadCount, progress);
string line2 = Utility.Text.Format("已更新大小 {0}MB/{1}MB", currentSizeMb, totalSizeMb);
string line3 = Utility.Text.Format("当前网速 {0}/s剩余时间 {1}", speed, GetRemainingTime(message.TotalDownloadSizeBytes, message.CurrentDownloadSizeBytes, CurrentSpeed));
string line3 = Utility.Text.Format("当前网速 {0}/s剩余时间 {1}", speed, GetRemainingTime(gameEventArgs.TotalDownloadSizeBytes, gameEventArgs.CurrentDownloadSizeBytes, CurrentSpeed));
Log.Info($"{line1} \n {line2}\n {line3}");

View File

@ -15,9 +15,8 @@ MonoBehaviour:
debugShaders:
debugReplacementPS: {fileID: 4800000, guid: cf852408f2e174538bcd9b7fda1c5ae7, type: 3}
hdrDebugViewPS: {fileID: 4800000, guid: 573620ae32aec764abd4d728906d2587, type: 3}
m_RendererFeatures:
- {fileID: 7833122117494664109}
m_RendererFeatureMap: ad6b866f10d7b46c
m_RendererFeatures: []
m_RendererFeatureMap:
m_UseNativeRenderPass: 1
postProcessData: {fileID: 11400000, guid: 41439944d30ece34e96484bdb6645b55, type: 2}
xrSystemData: {fileID: 11400000, guid: 60e1133243b97e347b653163a8c01b64, type: 2}
@ -56,38 +55,3 @@ MonoBehaviour:
m_CopyDepthMode: 0
m_AccurateGbufferNormals: 0
m_IntermediateTextureMode: 0
--- !u!114 &7833122117494664109
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: f62c9c65cf3354c93be831c8bc075510, type: 3}
m_Name: ScreenSpaceAmbientOcclusion
m_EditorClassIdentifier:
m_Active: 1
m_Settings:
AOMethod: 0
Downsample: 0
AfterOpaque: 0
Source: 1
NormalSamples: 1
Intensity: 0.4
DirectLightingStrength: 0.25
Radius: 0.3
Samples: 1
BlurQuality: 0
Falloff: 100
SampleCount: -1
m_BlueNoise256Textures:
- {fileID: 2800000, guid: 36f118343fc974119bee3d09e2111500, type: 3}
- {fileID: 2800000, guid: 4b7b083e6b6734e8bb2838b0b50a0bc8, type: 3}
- {fileID: 2800000, guid: c06cc21c692f94f5fb5206247191eeee, type: 3}
- {fileID: 2800000, guid: cb76dd40fa7654f9587f6a344f125c9a, type: 3}
- {fileID: 2800000, guid: e32226222ff144b24bf3a5a451de54bc, type: 3}
- {fileID: 2800000, guid: 3302065f671a8450b82c9ddf07426f3a, type: 3}
- {fileID: 2800000, guid: 56a77a3e8d64f47b6afe9e3c95cb57d5, type: 3}
m_Shader: {fileID: 4800000, guid: 0849e84e3d62649e8882e9d6f056a017, type: 3}

View File

@ -0,0 +1,53 @@
using AlicizaX;
using AlicizaX.Timer.Runtime;
using UnityEngine;
public sealed class TimerBugTest : MonoBehaviour
{
private ITimerService _timerService;
private void Start()
{
_timerService = AppServices.Require<ITimerService>();
Debug.Log("=== Timer Bug Test Started ===");
TestNormalOnceTimer();
TestStopInCallback();
TestRemoveInCallback();
}
private void TestNormalOnceTimer()
{
Debug.Log("[Test 1] Creating normal once timer (1 second)...");
_timerService.AddTimer(OnNormalOnceTimer, 1f, isLoop: false);
}
private void TestStopInCallback()
{
Debug.Log("[Test 2] Creating once timer that calls Stop() in callback (2 seconds)...");
int timerId = 0;
timerId = _timerService.AddTimer(() =>
{
Debug.Log("[Test 2] Timer callback executed, calling Stop()...");
_timerService.Stop(timerId);
}, 2f, isLoop: false);
}
private void TestRemoveInCallback()
{
Debug.Log("[Test 3] Creating once timer that calls RemoveTimer() in callback (3 seconds)...");
int timerId = 0;
timerId = _timerService.AddTimer(() =>
{
Debug.Log("[Test 3] Timer callback executed, calling RemoveTimer()...");
_timerService.RemoveTimer(timerId);
}, 3f, isLoop: false);
}
private static void OnNormalOnceTimer()
{
Debug.Log("[Test 1] Timer callback executed!");
}
}

View File

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

View File

@ -0,0 +1,122 @@
using AlicizaX;
using AlicizaX.Timer.Runtime;
using UnityEngine;
public sealed class TimerGenericTest : MonoBehaviour
{
private sealed class IntBox
{
public int Value;
}
private sealed class PlayerData
{
public int Id;
public string Name;
public int Score;
}
private sealed class Enemy
{
public string Name;
public int Health;
}
private ITimerService _timerService;
private void Start()
{
_timerService = AppServices.Require<ITimerService>();
Debug.Log("=== Timer Generic Test Started ===");
TestNoArgs();
TestIntArg();
TestStringArg();
TestPlayerDataArg();
TestClassArg();
TestLoopWithArg();
}
private void TestNoArgs()
{
Debug.Log("[Test 1] No args timer (1s)");
_timerService.AddTimer(OnNoArgsCallback, 1f);
}
private static void OnNoArgsCallback()
{
Debug.Log("[Test 1] Callback executed!");
}
private void TestIntArg()
{
Debug.Log("[Test 2] Boxed int reference timer (2s)");
_timerService.AddTimer(OnIntCallback, new IntBox { Value = 42 }, 2f);
}
private static void OnIntCallback(IntBox value)
{
Debug.Log(Utility.Text.Format("[Test 2] Int callback executed! Value: {0}", value.Value));
}
private void TestStringArg()
{
Debug.Log("[Test 3] String arg timer (3s)");
_timerService.AddTimer(OnStringCallback, "Hello Timer!", 3f);
}
private static void OnStringCallback(string message)
{
Debug.Log(Utility.Text.Format("[Test 3] String callback executed! Message: {0}", message));
}
private void TestPlayerDataArg()
{
Debug.Log("[Test 4] PlayerData class arg timer (4s)");
_timerService.AddTimer(OnPlayerDataCallback, new PlayerData
{
Id = 123,
Name = "Alice",
Score = 9999
}, 4f);
}
private static void OnPlayerDataCallback(PlayerData data)
{
Debug.Log(Utility.Text.Format("[Test 4] Player callback executed! Player: {0} (ID: {1}, Score: {2})", data.Name, data.Id, data.Score));
}
private void TestClassArg()
{
Debug.Log("[Test 5] Class arg timer (5s)");
_timerService.AddTimer(OnClassCallback, new Enemy
{
Name = "Goblin",
Health = 100
}, 5f);
}
private static void OnClassCallback(Enemy enemy)
{
Debug.Log(Utility.Text.Format("[Test 5] Class callback executed! Enemy: {0}, Health: {1}", enemy.Name, enemy.Health));
}
private void TestLoopWithArg()
{
Debug.Log("[Test 6] Loop timer with reference arg (0.5s interval)");
IntBox counter = new IntBox { Value = 1 };
int timerId = 0;
timerId = _timerService.AddTimer(count =>
{
Debug.Log(Utility.Text.Format("[Test 6] Loop callback #{0}", count.Value));
if (count.Value >= 3)
{
_timerService.RemoveTimer(timerId);
Debug.Log("[Test 6] Loop timer removed");
return;
}
count.Value++;
}, counter, 0.5f, isLoop: true);
}
}

View File

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

View File

@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 55079f02326c270468f1eca1808de28c
guid: 23fc170912d33cd499f7f04b662a6e9a
folderAsset: yes
DefaultImporter:
externalObjects: {}

View File

@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 8e2de7df9f6693246b6ce82a1753c975
guid: 9f2ea1142ef07324b8f6e23022cc34e5
folderAsset: yes
DefaultImporter:
externalObjects: {}

View File

@ -0,0 +1 @@
@import url("unity-theme://default");

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: e65490858afddc54a80013f2f6e36e21
ScriptedImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 2
userData:
assetBundleName:
assetBundleVariant:
script: {fileID: 12388, guid: 0000000000000000e000000000000000, type: 0}
disableValidation: 0

View File

@ -0,0 +1,185 @@
{
"FileVersion": "2025.9.30",
"EnableAddressable": true,
"SupportExtensionless": true,
"LocationToLower": false,
"IncludeAssetGUID": false,
"ReplaceAssetPathWithAddress": false,
"OutputNameStyle": 0,
"BuildBundleType": 1,
"BuildPipeline": "EditorSimulateBuildPipeline",
"PackageName": "DefaultPackage",
"PackageVersion": "Simulate",
"PackageNote": "2026/4/24 20:49:52",
"AssetList": [
{
"Address": "LocalizationTable",
"AssetPath": "Assets/Bundles/Configs/LocalizationTable.asset",
"AssetGUID": "",
"AssetTags": [
"WEBGL_PRELOAD",
"Configs"
],
"BundleID": 0,
"DependBundleIDs": []
},
{
"Address": "PoolConfig",
"AssetPath": "Assets/Bundles/Configs/PoolConfig.asset",
"AssetGUID": "",
"AssetTags": [
"WEBGL_PRELOAD",
"Configs"
],
"BundleID": 0,
"DependBundleIDs": []
},
{
"Address": "Map1000",
"AssetPath": "Assets/Bundles/Scenes/Map1000.unity",
"AssetGUID": "",
"AssetTags": [
"Scenes"
],
"BundleID": 1,
"DependBundleIDs": []
},
{
"Address": "MyShaderVariants",
"AssetPath": "Assets/Bundles/ShaderVariants/MyShaderVariants.shadervariants",
"AssetGUID": "",
"AssetTags": [
"ShaderVariants"
],
"BundleID": 6,
"DependBundleIDs": []
},
{
"Address": "UILoadUpdateWindow",
"AssetPath": "Assets/Bundles/UI/UILoadUpdateWindow.prefab",
"AssetGUID": "",
"AssetTags": [
"UI"
],
"BundleID": 2,
"DependBundleIDs": []
},
{
"Address": "UILogicTestAlert",
"AssetPath": "Assets/Bundles/UI/Window/UILogicTestAlert.prefab",
"AssetGUID": "",
"AssetTags": [
"UI"
],
"BundleID": 3,
"DependBundleIDs": []
},
{
"Address": "tst",
"AssetPath": "Assets/Bundles/UIRaw/Atlas/Common/bb/tst.png",
"AssetGUID": "",
"AssetTags": [
"UIRaw"
],
"BundleID": 4,
"DependBundleIDs": []
},
{
"Address": "Brush Impact 5",
"AssetPath": "Assets/Bundles/UIRaw/Atlas/Common/Grunge/Brush Impact 5.png",
"AssetGUID": "",
"AssetTags": [
"UIRaw"
],
"BundleID": 5,
"DependBundleIDs": []
}
],
"BundleList": [
{
"BundleName": "assets_bundles_configs.bundle",
"UnityCRC": 0,
"FileHash": "3a378d308429cd99b280d880a27af478",
"FileCRC": 0,
"FileSize": 6942,
"Encrypted": false,
"Tags": [
"WEBGL_PRELOAD",
"Configs"
],
"DependBundleIDs": []
},
{
"BundleName": "assets_bundles_scenes_map1000.bundle",
"UnityCRC": 0,
"FileHash": "4530edb970dd229fed5dd3259fee4ece",
"FileCRC": 0,
"FileSize": 12277,
"Encrypted": false,
"Tags": [
"Scenes"
],
"DependBundleIDs": []
},
{
"BundleName": "assets_bundles_ui_uiloadupdatewindow.bundle",
"UnityCRC": 0,
"FileHash": "9ff2ef11fc95cbce9b51368acd1d4383",
"FileCRC": 0,
"FileSize": 59076,
"Encrypted": false,
"Tags": [
"UI"
],
"DependBundleIDs": []
},
{
"BundleName": "assets_bundles_ui_window_uilogictestalert.bundle",
"UnityCRC": 0,
"FileHash": "cf84038a2a1620e6ff49815bcd8e6a73",
"FileCRC": 0,
"FileSize": 21519,
"Encrypted": false,
"Tags": [
"UI"
],
"DependBundleIDs": []
},
{
"BundleName": "assets_bundles_uiraw_atlas_common_bb.bundle",
"UnityCRC": 0,
"FileHash": "472a7fd3623bc56802c6e39c904d00a3",
"FileCRC": 0,
"FileSize": 806,
"Encrypted": false,
"Tags": [
"UIRaw"
],
"DependBundleIDs": []
},
{
"BundleName": "assets_bundles_uiraw_atlas_common_grunge.bundle",
"UnityCRC": 0,
"FileHash": "3dcf5b07fab616778c379807810653ea",
"FileCRC": 0,
"FileSize": 36701,
"Encrypted": false,
"Tags": [
"UIRaw"
],
"DependBundleIDs": []
},
{
"BundleName": "unityshaders.bundle",
"UnityCRC": 0,
"FileHash": "c9f21eee9f3febcafa65a26802cf31f0",
"FileCRC": 0,
"FileSize": 3518,
"Encrypted": false,
"Tags": [
"ShaderVariants"
],
"DependBundleIDs": []
}
]
}

@ -1 +1 @@
Subproject commit 8d9b4a32ce2274020796f54a9f0465725d0bd84f
Subproject commit 28edc2dfa710217fa3446e7e500b0030d9020263

@ -1 +1 @@
Subproject commit 9d755479987795111e7b8721713b70abf98846e5
Subproject commit f4f0ea1754a33b3e224d04cbcccedacfd41fe278

@ -1 +1 @@
Subproject commit ae16bdcf37f77929aec1c6178321c333ad9d4d92
Subproject commit 8c3c13634d25ce3a1808253962e483c9bbbd9f66

View File

@ -30,10 +30,10 @@ EditorUserSettings:
value: 0606045450065e5d55575a241522064444161c797f7b7234757c4f32e1b0353d
flags: 0
RecentlyUsedSceneGuid-7:
value: 5a07065703500c59585e0e7748770d44444f4a737d2d7f35787d4f63e0b26668
value: 5001560504060c590f5b0f7245725a44404f1d7c297e2233787e4a36b5e4666b
flags: 0
RecentlyUsedSceneGuid-8:
value: 5001560504060c590f5b0f7245725a44404f1d7c297e2233787e4a36b5e4666b
value: 5a07065703500c59585e0e7748770d44444f4a737d2d7f35787d4f63e0b26668
flags: 0
RecentlyUsedSceneGuid-9:
value: 50500404540c580d0f0b5e7543725b44424f4c7a7b7c7734747e4f36e4b1676d

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