完成 P2 第一阶段包与编辑器准备
This commit is contained in:
8
My project/Assets/AddressableAssetsData.meta
Normal file
8
My project/Assets/AddressableAssetsData.meta
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: f38893bbcb86bbb4cb6705a205682113
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -0,0 +1,109 @@
|
|||||||
|
%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: 468a46d0ae32c3544b7d98094e6448a9, type: 3}
|
||||||
|
m_Name: AddressableAssetSettings
|
||||||
|
m_EditorClassIdentifier:
|
||||||
|
m_DefaultGroup: 7a5b4d76501c82c408509719f87e537b
|
||||||
|
m_currentHash:
|
||||||
|
serializedVersion: 2
|
||||||
|
Hash: 00000000000000000000000000000000
|
||||||
|
m_OptimizeCatalogSize: 0
|
||||||
|
m_BuildRemoteCatalog: 0
|
||||||
|
m_BundleLocalCatalog: 0
|
||||||
|
m_CatalogRequestsTimeout: 0
|
||||||
|
m_DisableCatalogUpdateOnStart: 0
|
||||||
|
m_IgnoreUnsupportedFilesInBuild: 0
|
||||||
|
m_UniqueBundleIds: 0
|
||||||
|
m_NonRecursiveBuilding: 1
|
||||||
|
m_CCDEnabled: 0
|
||||||
|
m_maxConcurrentWebRequests: 3
|
||||||
|
m_ContiguousBundles: 1
|
||||||
|
m_StripUnityVersionFromBundleBuild: 0
|
||||||
|
m_DisableVisibleSubAssetRepresentations: 0
|
||||||
|
m_ShaderBundleNaming: 0
|
||||||
|
m_ShaderBundleCustomNaming:
|
||||||
|
m_MonoScriptBundleNaming: 0
|
||||||
|
m_CheckForContentUpdateRestrictionsOption: 0
|
||||||
|
m_MonoScriptBundleCustomNaming:
|
||||||
|
m_RemoteCatalogBuildPath:
|
||||||
|
m_Id: 07b49c5050b466b429ffe37d3fea9381
|
||||||
|
m_RemoteCatalogLoadPath:
|
||||||
|
m_Id: 9acaa2031bf7fa240af17e019ab4a7ff
|
||||||
|
m_ContentStateBuildPathProfileVariableName: <default settings path>
|
||||||
|
m_CustomContentStateBuildPath:
|
||||||
|
m_ContentStateBuildPath:
|
||||||
|
m_BuildAddressablesWithPlayerBuild: 0
|
||||||
|
m_overridePlayerVersion: '[UnityEditor.PlayerSettings.bundleVersion]'
|
||||||
|
m_GroupAssets:
|
||||||
|
- {fileID: 11400000, guid: cb21c9bd6efb15940aa0cc136e03ec0f, type: 2}
|
||||||
|
- {fileID: 11400000, guid: ad50471a1bc339946b9dd02f9fd1b735, type: 2}
|
||||||
|
m_BuildSettings:
|
||||||
|
m_CompileScriptsInVirtualMode: 0
|
||||||
|
m_CleanupStreamingAssetsAfterBuilds: 1
|
||||||
|
m_LogResourceManagerExceptions: 1
|
||||||
|
m_BundleBuildPath: Temp/com.unity.addressables/AssetBundles
|
||||||
|
m_ProfileSettings:
|
||||||
|
m_Profiles:
|
||||||
|
- m_InheritedParent:
|
||||||
|
m_Id: 7dc81bb5e6404c446a8e47e2b54e4880
|
||||||
|
m_ProfileName: Default
|
||||||
|
m_Values:
|
||||||
|
- m_Id: 88540d677a2c08c42b7902abc4da490b
|
||||||
|
m_Value: '[UnityEditor.EditorUserBuildSettings.activeBuildTarget]'
|
||||||
|
- m_Id: 4be60150ce46e224586b89230ab67d48
|
||||||
|
m_Value: '[UnityEngine.AddressableAssets.Addressables.BuildPath]/[BuildTarget]'
|
||||||
|
- m_Id: 8dd5e21a04741aa4ab24bb52c0e3c5a3
|
||||||
|
m_Value: '{UnityEngine.AddressableAssets.Addressables.RuntimePath}/[BuildTarget]'
|
||||||
|
- m_Id: 07b49c5050b466b429ffe37d3fea9381
|
||||||
|
m_Value: ServerData/[BuildTarget]
|
||||||
|
- m_Id: 9acaa2031bf7fa240af17e019ab4a7ff
|
||||||
|
m_Value: http://[PrivateIpAddress]:[HostingServicePort]
|
||||||
|
m_ProfileEntryNames:
|
||||||
|
- m_Id: 88540d677a2c08c42b7902abc4da490b
|
||||||
|
m_Name: BuildTarget
|
||||||
|
m_InlineUsage: 0
|
||||||
|
- m_Id: 4be60150ce46e224586b89230ab67d48
|
||||||
|
m_Name: Local.BuildPath
|
||||||
|
m_InlineUsage: 0
|
||||||
|
- m_Id: 8dd5e21a04741aa4ab24bb52c0e3c5a3
|
||||||
|
m_Name: Local.LoadPath
|
||||||
|
m_InlineUsage: 0
|
||||||
|
- m_Id: 07b49c5050b466b429ffe37d3fea9381
|
||||||
|
m_Name: Remote.BuildPath
|
||||||
|
m_InlineUsage: 0
|
||||||
|
- m_Id: 9acaa2031bf7fa240af17e019ab4a7ff
|
||||||
|
m_Name: Remote.LoadPath
|
||||||
|
m_InlineUsage: 0
|
||||||
|
m_ProfileVersion: 1
|
||||||
|
m_LabelTable:
|
||||||
|
m_LabelNames:
|
||||||
|
- default
|
||||||
|
m_SchemaTemplates: []
|
||||||
|
m_GroupTemplateObjects:
|
||||||
|
- {fileID: 11400000, guid: b8b58afcc1b4e244db44625e28a47b36, type: 2}
|
||||||
|
m_InitializationObjects: []
|
||||||
|
m_CertificateHandlerType:
|
||||||
|
m_AssemblyName:
|
||||||
|
m_ClassName:
|
||||||
|
m_ActivePlayerDataBuilderIndex: 3
|
||||||
|
m_DataBuilders:
|
||||||
|
- {fileID: 11400000, guid: ca567c17e92783c4d9869e540320d76f, type: 2}
|
||||||
|
- {fileID: 11400000, guid: 9c179b50ddf0f4a49ba95a61fce40d9d, type: 2}
|
||||||
|
- {fileID: 11400000, guid: e268c485b53ab6547bfc8919ae0f900d, type: 2}
|
||||||
|
- {fileID: 11400000, guid: 7f9d8997bb36a7f44aabfe404cd182f8, type: 2}
|
||||||
|
m_ActiveProfileId: 7dc81bb5e6404c446a8e47e2b54e4880
|
||||||
|
m_HostingServicesManager:
|
||||||
|
m_HostingServiceInfos: []
|
||||||
|
m_Settings: {fileID: 11400000}
|
||||||
|
m_NextInstanceId: 0
|
||||||
|
m_RegisteredServiceTypeRefs: []
|
||||||
|
m_PingTimeoutInMilliseconds: 5000
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: c67335a4d75941d4881a64ebedebbce8
|
||||||
|
NativeFormatImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
mainObjectFileID: 11400000
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: f9bc8a0d61c2e9e4cb5557b7586ac071
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -0,0 +1,76 @@
|
|||||||
|
%YAML 1.1
|
||||||
|
%TAG !u! tag:unity3d.com,2011:
|
||||||
|
--- !u!114 &-4998770171994096319
|
||||||
|
MonoBehaviour:
|
||||||
|
m_ObjectHideFlags: 1
|
||||||
|
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: e5d17a21594effb4e9591490b009e7aa, type: 3}
|
||||||
|
m_Name: BundledAssetGroupSchema
|
||||||
|
m_EditorClassIdentifier:
|
||||||
|
m_Group: {fileID: 0}
|
||||||
|
m_InternalBundleIdMode: 1
|
||||||
|
m_Compression: 1
|
||||||
|
m_IncludeAddressInCatalog: 1
|
||||||
|
m_IncludeGUIDInCatalog: 1
|
||||||
|
m_IncludeLabelsInCatalog: 1
|
||||||
|
m_InternalIdNamingMode: 0
|
||||||
|
m_CacheClearBehavior: 0
|
||||||
|
m_IncludeInBuild: 1
|
||||||
|
m_BundledAssetProviderType:
|
||||||
|
m_AssemblyName:
|
||||||
|
m_ClassName:
|
||||||
|
m_ForceUniqueProvider: 0
|
||||||
|
m_UseAssetBundleCache: 1
|
||||||
|
m_UseAssetBundleCrc: 1
|
||||||
|
m_UseAssetBundleCrcForCachedBundles: 1
|
||||||
|
m_UseUWRForLocalBundles: 0
|
||||||
|
m_Timeout: 0
|
||||||
|
m_ChunkedTransfer: 0
|
||||||
|
m_RedirectLimit: -1
|
||||||
|
m_RetryCount: 0
|
||||||
|
m_BuildPath:
|
||||||
|
m_Id:
|
||||||
|
m_LoadPath:
|
||||||
|
m_Id:
|
||||||
|
m_BundleMode: 0
|
||||||
|
m_AssetBundleProviderType:
|
||||||
|
m_AssemblyName:
|
||||||
|
m_ClassName:
|
||||||
|
m_BundleNaming: 0
|
||||||
|
m_AssetLoadMode: 0
|
||||||
|
--- !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: 1a3c5d64ac83548c09dd1678b9f6f1cd, type: 3}
|
||||||
|
m_Name: Packed Assets
|
||||||
|
m_EditorClassIdentifier:
|
||||||
|
m_SchemaObjects:
|
||||||
|
- {fileID: -4998770171994096319}
|
||||||
|
- {fileID: 438921025847764287}
|
||||||
|
m_Description: Pack assets into asset bundles.
|
||||||
|
m_Settings: {fileID: 11400000, guid: c67335a4d75941d4881a64ebedebbce8, type: 2}
|
||||||
|
--- !u!114 &438921025847764287
|
||||||
|
MonoBehaviour:
|
||||||
|
m_ObjectHideFlags: 1
|
||||||
|
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: 5834b5087d578d24c926ce20cd31e6d6, type: 3}
|
||||||
|
m_Name: ContentUpdateGroupSchema
|
||||||
|
m_EditorClassIdentifier:
|
||||||
|
m_Group: {fileID: 0}
|
||||||
|
m_StaticContent: 0
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: b8b58afcc1b4e244db44625e28a47b36
|
||||||
|
NativeFormatImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
mainObjectFileID: 11400000
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
8
My project/Assets/AddressableAssetsData/AssetGroups.meta
Normal file
8
My project/Assets/AddressableAssetsData/AssetGroups.meta
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 0ee6e7ad669b89342a6dd8bd0dd3bf84
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
%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: bbb281ee3bf0b054c82ac2347e9e782c, type: 3}
|
||||||
|
m_Name: Built In Data
|
||||||
|
m_EditorClassIdentifier:
|
||||||
|
m_GroupName: Built In Data
|
||||||
|
m_Data:
|
||||||
|
m_SerializedData: []
|
||||||
|
m_GUID: 1989d512082b7394d9f03cd0ea5fd1b5
|
||||||
|
m_SerializeEntries:
|
||||||
|
- m_GUID: Resources
|
||||||
|
m_Address: Resources
|
||||||
|
m_ReadOnly: 1
|
||||||
|
m_SerializedLabels: []
|
||||||
|
FlaggedDuringContentUpdateRestriction: 0
|
||||||
|
- m_GUID: EditorSceneList
|
||||||
|
m_Address: EditorSceneList
|
||||||
|
m_ReadOnly: 1
|
||||||
|
m_SerializedLabels: []
|
||||||
|
FlaggedDuringContentUpdateRestriction: 0
|
||||||
|
m_ReadOnly: 1
|
||||||
|
m_Settings: {fileID: 11400000, guid: c67335a4d75941d4881a64ebedebbce8, type: 2}
|
||||||
|
m_SchemaSet:
|
||||||
|
m_Schemas:
|
||||||
|
- {fileID: 11400000, guid: a7177bba2e94b2a40b5302657833b7d8, type: 2}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: cb21c9bd6efb15940aa0cc136e03ec0f
|
||||||
|
NativeFormatImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
mainObjectFileID: 11400000
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
%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: bbb281ee3bf0b054c82ac2347e9e782c, type: 3}
|
||||||
|
m_Name: Default Local Group
|
||||||
|
m_EditorClassIdentifier:
|
||||||
|
m_GroupName: Default Local Group
|
||||||
|
m_Data:
|
||||||
|
m_SerializedData: []
|
||||||
|
m_GUID: 7a5b4d76501c82c408509719f87e537b
|
||||||
|
m_SerializeEntries:
|
||||||
|
- m_GUID: 7e04733ce9dab74459592d3bd99fcafd
|
||||||
|
m_Address: FlowScope.Tests.Addressables.Text
|
||||||
|
m_ReadOnly: 0
|
||||||
|
m_SerializedLabels: []
|
||||||
|
FlaggedDuringContentUpdateRestriction: 0
|
||||||
|
m_ReadOnly: 0
|
||||||
|
m_Settings: {fileID: 11400000, guid: c67335a4d75941d4881a64ebedebbce8, type: 2}
|
||||||
|
m_SchemaSet:
|
||||||
|
m_Schemas:
|
||||||
|
- {fileID: 11400000, guid: 4a894c5b933ed1040b799de00906a668, type: 2}
|
||||||
|
- {fileID: 11400000, guid: ee832a18fbe81564d9e5cb06031890c9, type: 2}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: ad50471a1bc339946b9dd02f9fd1b735
|
||||||
|
NativeFormatImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
mainObjectFileID: 11400000
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: e16dab92ed201134eb1bdc0159aa569c
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
%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: b1487f5d688e4f94f828f879d599dbdc, type: 3}
|
||||||
|
m_Name: Built In Data_PlayerDataGroupSchema
|
||||||
|
m_EditorClassIdentifier:
|
||||||
|
m_Group: {fileID: 11400000, guid: cb21c9bd6efb15940aa0cc136e03ec0f, type: 2}
|
||||||
|
m_IncludeResourcesFolders: 1
|
||||||
|
m_IncludeBuildSettingsScenes: 1
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: a7177bba2e94b2a40b5302657833b7d8
|
||||||
|
NativeFormatImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
mainObjectFileID: 11400000
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
%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: e5d17a21594effb4e9591490b009e7aa, type: 3}
|
||||||
|
m_Name: Default Local Group_BundledAssetGroupSchema
|
||||||
|
m_EditorClassIdentifier:
|
||||||
|
m_Group: {fileID: 11400000, guid: ad50471a1bc339946b9dd02f9fd1b735, type: 2}
|
||||||
|
m_InternalBundleIdMode: 1
|
||||||
|
m_Compression: 1
|
||||||
|
m_IncludeAddressInCatalog: 1
|
||||||
|
m_IncludeGUIDInCatalog: 1
|
||||||
|
m_IncludeLabelsInCatalog: 1
|
||||||
|
m_InternalIdNamingMode: 0
|
||||||
|
m_CacheClearBehavior: 0
|
||||||
|
m_IncludeInBuild: 1
|
||||||
|
m_BundledAssetProviderType:
|
||||||
|
m_AssemblyName: Unity.ResourceManager, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null
|
||||||
|
m_ClassName: UnityEngine.ResourceManagement.ResourceProviders.BundledAssetProvider
|
||||||
|
m_ForceUniqueProvider: 0
|
||||||
|
m_UseAssetBundleCache: 1
|
||||||
|
m_UseAssetBundleCrc: 1
|
||||||
|
m_UseAssetBundleCrcForCachedBundles: 1
|
||||||
|
m_UseUWRForLocalBundles: 0
|
||||||
|
m_Timeout: 0
|
||||||
|
m_ChunkedTransfer: 0
|
||||||
|
m_RedirectLimit: -1
|
||||||
|
m_RetryCount: 0
|
||||||
|
m_BuildPath:
|
||||||
|
m_Id: 4be60150ce46e224586b89230ab67d48
|
||||||
|
m_LoadPath:
|
||||||
|
m_Id: 8dd5e21a04741aa4ab24bb52c0e3c5a3
|
||||||
|
m_BundleMode: 0
|
||||||
|
m_AssetBundleProviderType:
|
||||||
|
m_AssemblyName: Unity.ResourceManager, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null
|
||||||
|
m_ClassName: UnityEngine.ResourceManagement.ResourceProviders.AssetBundleProvider
|
||||||
|
m_BundleNaming: 0
|
||||||
|
m_AssetLoadMode: 0
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: ee832a18fbe81564d9e5cb06031890c9
|
||||||
|
NativeFormatImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
mainObjectFileID: 11400000
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
%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: 5834b5087d578d24c926ce20cd31e6d6, type: 3}
|
||||||
|
m_Name: Default Local Group_ContentUpdateGroupSchema
|
||||||
|
m_EditorClassIdentifier:
|
||||||
|
m_Group: {fileID: 11400000, guid: ad50471a1bc339946b9dd02f9fd1b735, type: 2}
|
||||||
|
m_StaticContent: 0
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 4a894c5b933ed1040b799de00906a668
|
||||||
|
NativeFormatImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
mainObjectFileID: 11400000
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: cfb9fefe94d254d44a8f7d5f6b6d22ef
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
%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: 88d21199f5d473f4db36845f2318f180, type: 3}
|
||||||
|
m_Name: BuildScriptFastMode
|
||||||
|
m_EditorClassIdentifier:
|
||||||
|
instanceProviderType:
|
||||||
|
m_AssemblyName: Unity.ResourceManager, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null
|
||||||
|
m_ClassName: UnityEngine.ResourceManagement.ResourceProviders.InstanceProvider
|
||||||
|
sceneProviderType:
|
||||||
|
m_AssemblyName: Unity.ResourceManager, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null
|
||||||
|
m_ClassName: UnityEngine.ResourceManagement.ResourceProviders.SceneProvider
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: ca567c17e92783c4d9869e540320d76f
|
||||||
|
NativeFormatImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
mainObjectFileID: 11400000
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
%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: 3e2e0ffa088c91d41a086d0b8cb16bdc, type: 3}
|
||||||
|
m_Name: BuildScriptPackedMode
|
||||||
|
m_EditorClassIdentifier:
|
||||||
|
instanceProviderType:
|
||||||
|
m_AssemblyName: Unity.ResourceManager, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null
|
||||||
|
m_ClassName: UnityEngine.ResourceManagement.ResourceProviders.InstanceProvider
|
||||||
|
sceneProviderType:
|
||||||
|
m_AssemblyName: Unity.ResourceManager, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null
|
||||||
|
m_ClassName: UnityEngine.ResourceManagement.ResourceProviders.SceneProvider
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 7f9d8997bb36a7f44aabfe404cd182f8
|
||||||
|
NativeFormatImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
mainObjectFileID: 11400000
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
%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: ad8c280d42ee0ed41a27db23b43dd2bf, type: 3}
|
||||||
|
m_Name: BuildScriptPackedPlayMode
|
||||||
|
m_EditorClassIdentifier:
|
||||||
|
instanceProviderType:
|
||||||
|
m_AssemblyName: Unity.ResourceManager, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null
|
||||||
|
m_ClassName: UnityEngine.ResourceManagement.ResourceProviders.InstanceProvider
|
||||||
|
sceneProviderType:
|
||||||
|
m_AssemblyName: Unity.ResourceManager, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null
|
||||||
|
m_ClassName: UnityEngine.ResourceManagement.ResourceProviders.SceneProvider
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: e268c485b53ab6547bfc8919ae0f900d
|
||||||
|
NativeFormatImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
mainObjectFileID: 11400000
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
%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: bb0e4994b34add1409fd8ccaf4a82de5, type: 3}
|
||||||
|
m_Name: BuildScriptVirtualMode
|
||||||
|
m_EditorClassIdentifier:
|
||||||
|
instanceProviderType:
|
||||||
|
m_AssemblyName: Unity.ResourceManager, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null
|
||||||
|
m_ClassName: UnityEngine.ResourceManagement.ResourceProviders.InstanceProvider
|
||||||
|
sceneProviderType:
|
||||||
|
m_AssemblyName: Unity.ResourceManager, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null
|
||||||
|
m_ClassName: UnityEngine.ResourceManagement.ResourceProviders.SceneProvider
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 9c179b50ddf0f4a49ba95a61fce40d9d
|
||||||
|
NativeFormatImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
mainObjectFileID: 11400000
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
15
My project/Assets/AddressableAssetsData/DefaultObject.asset
Normal file
15
My project/Assets/AddressableAssetsData/DefaultObject.asset
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
%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: 3a189bb168d8d90478a09ea08c2f3d72, type: 3}
|
||||||
|
m_Name: DefaultObject
|
||||||
|
m_EditorClassIdentifier:
|
||||||
|
m_AddressableAssetSettingsGuid: c67335a4d75941d4881a64ebedebbce8
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: f9fd621d22433a64aa0741deda630f70
|
||||||
|
NativeFormatImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
mainObjectFileID: 11400000
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -2,6 +2,7 @@ using System;
|
|||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using FlowScope.Resources;
|
using FlowScope.Resources;
|
||||||
|
using UnityEngine.ResourceManagement.AsyncOperations;
|
||||||
using UnityAddressables = UnityEngine.AddressableAssets.Addressables;
|
using UnityAddressables = UnityEngine.AddressableAssets.Addressables;
|
||||||
|
|
||||||
namespace FlowScope.Addressables
|
namespace FlowScope.Addressables
|
||||||
@@ -13,8 +14,30 @@ namespace FlowScope.Addressables
|
|||||||
public async Task<T> LoadAsync<T>(string key, CancellationToken cancellationToken)
|
public async Task<T> LoadAsync<T>(string key, CancellationToken cancellationToken)
|
||||||
where T : class
|
where T : class
|
||||||
{
|
{
|
||||||
|
cancellationToken.ThrowIfCancellationRequested();
|
||||||
|
|
||||||
var handle = UnityAddressables.LoadAssetAsync<T>(key);
|
var handle = UnityAddressables.LoadAssetAsync<T>(key);
|
||||||
|
try
|
||||||
|
{
|
||||||
await WaitWithCancellation(handle.Task, cancellationToken);
|
await WaitWithCancellation(handle.Task, cancellationToken);
|
||||||
|
}
|
||||||
|
catch (OperationCanceledException)
|
||||||
|
{
|
||||||
|
UnityAddressables.Release(handle);
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (handle.OperationException != null ||
|
||||||
|
handle.Status != AsyncOperationStatus.Succeeded ||
|
||||||
|
handle.Result == null)
|
||||||
|
{
|
||||||
|
var exception = new InvalidOperationException(
|
||||||
|
$"Failed to load Addressables resource '{key}' as {typeof(T).Name} from backend '{Name}'.",
|
||||||
|
handle.OperationException);
|
||||||
|
UnityAddressables.Release(handle);
|
||||||
|
throw exception;
|
||||||
|
}
|
||||||
|
|
||||||
return handle.Result;
|
return handle.Result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
8
My project/Assets/FlowScope/Editor.meta
Normal file
8
My project/Assets/FlowScope/Editor.meta
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 5b5d64c7b9f94d1db437f60ce8944a8f
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
16
My project/Assets/FlowScope/Editor/FlowScope.Editor.asmdef
Normal file
16
My project/Assets/FlowScope/Editor/FlowScope.Editor.asmdef
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
{
|
||||||
|
"name": "FlowScope.Editor",
|
||||||
|
"rootNamespace": "FlowScope.Editor",
|
||||||
|
"references": [],
|
||||||
|
"includePlatforms": [
|
||||||
|
"Editor"
|
||||||
|
],
|
||||||
|
"excludePlatforms": [],
|
||||||
|
"allowUnsafeCode": false,
|
||||||
|
"overrideReferences": false,
|
||||||
|
"precompiledReferences": [],
|
||||||
|
"autoReferenced": true,
|
||||||
|
"defineConstraints": [],
|
||||||
|
"versionDefines": [],
|
||||||
|
"noEngineReferences": false
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: f3ee591f10b84cf7a39d50219698312e
|
||||||
|
AssemblyDefinitionImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
268
My project/Assets/FlowScope/Editor/FlowScopeDiagnosticsWindow.cs
Normal file
268
My project/Assets/FlowScope/Editor/FlowScopeDiagnosticsWindow.cs
Normal file
@@ -0,0 +1,268 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using UnityEditor;
|
||||||
|
using UnityEditor.PackageManager;
|
||||||
|
using UnityEditor.PackageManager.Requests;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace FlowScope.Editor
|
||||||
|
{
|
||||||
|
public sealed class FlowScopeDiagnosticsWindow : EditorWindow
|
||||||
|
{
|
||||||
|
private const string RuntimeAsmdefPath = "Assets/FlowScope/Runtime/FlowScope.Runtime.asmdef";
|
||||||
|
private const string AddressablesAsmdefPath = "Assets/FlowScope/Addressables/FlowScope.Addressables.asmdef";
|
||||||
|
private const string MainMenuScenePath = "Assets/FlowScope/Samples/MainMenuP0/Scenes/MainMenuP0.unity";
|
||||||
|
private const string MainMenuPanelPrefabPath = "Assets/FlowScope/Samples/MainMenuP0/Prefabs/MainMenuPanel.prefab";
|
||||||
|
private const string AddressablesPackageName = "com.unity.addressables";
|
||||||
|
|
||||||
|
private static readonly IReadOnlyList<AssetCheck> AssetChecks = new[]
|
||||||
|
{
|
||||||
|
new AssetCheck("Runtime asmdef", RuntimeAsmdefPath),
|
||||||
|
new AssetCheck("Addressables asmdef", AddressablesAsmdefPath),
|
||||||
|
new AssetCheck("MainMenuP0 scene", MainMenuScenePath),
|
||||||
|
new AssetCheck("MainMenuPanel prefab", MainMenuPanelPrefabPath),
|
||||||
|
};
|
||||||
|
|
||||||
|
private readonly List<DiagnosticResult> _assetResults = new List<DiagnosticResult>();
|
||||||
|
|
||||||
|
private ListRequest _packageListRequest;
|
||||||
|
private DiagnosticResult _addressablesPackageResult = DiagnosticResult.Pending(
|
||||||
|
"Addressables package",
|
||||||
|
"Package status has not been refreshed.");
|
||||||
|
|
||||||
|
private Vector2 _scrollPosition;
|
||||||
|
|
||||||
|
[MenuItem("Tools/FlowScope/Diagnostics")]
|
||||||
|
public static void Open()
|
||||||
|
{
|
||||||
|
FlowScopeDiagnosticsWindow window = GetWindow<FlowScopeDiagnosticsWindow>("FlowScope Diagnostics");
|
||||||
|
window.minSize = new Vector2(520f, 320f);
|
||||||
|
window.RefreshAll();
|
||||||
|
window.Show();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnEnable()
|
||||||
|
{
|
||||||
|
RefreshAssetChecks();
|
||||||
|
RefreshPackageStatus();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnDisable()
|
||||||
|
{
|
||||||
|
EditorApplication.update -= PollPackageListRequest;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnGUI()
|
||||||
|
{
|
||||||
|
using (new EditorGUILayout.VerticalScope())
|
||||||
|
{
|
||||||
|
EditorGUILayout.LabelField("FlowScope Diagnostics", EditorStyles.boldLabel);
|
||||||
|
EditorGUILayout.HelpBox("Read-only diagnostics. This window checks project state and does not create, move, delete, or repair user assets.", MessageType.Info);
|
||||||
|
|
||||||
|
using (new EditorGUILayout.HorizontalScope())
|
||||||
|
{
|
||||||
|
if (GUILayout.Button("Refresh", GUILayout.Width(96f)))
|
||||||
|
{
|
||||||
|
RefreshAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
GUILayout.Label(
|
||||||
|
_packageListRequest != null && !_packageListRequest.IsCompleted
|
||||||
|
? "Refreshing package status..."
|
||||||
|
: "Package status is complete or has not been requested.",
|
||||||
|
EditorStyles.miniLabel);
|
||||||
|
}
|
||||||
|
|
||||||
|
EditorGUILayout.Space();
|
||||||
|
|
||||||
|
_scrollPosition = EditorGUILayout.BeginScrollView(_scrollPosition);
|
||||||
|
DrawResult(_addressablesPackageResult);
|
||||||
|
|
||||||
|
foreach (DiagnosticResult result in _assetResults)
|
||||||
|
{
|
||||||
|
DrawResult(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
EditorGUILayout.EndScrollView();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RefreshAll()
|
||||||
|
{
|
||||||
|
RefreshAssetChecks();
|
||||||
|
RefreshPackageStatus();
|
||||||
|
Repaint();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RefreshAssetChecks()
|
||||||
|
{
|
||||||
|
_assetResults.Clear();
|
||||||
|
|
||||||
|
foreach (AssetCheck check in AssetChecks)
|
||||||
|
{
|
||||||
|
bool exists = AssetDatabase.LoadAssetAtPath<UnityEngine.Object>(check.Path) != null;
|
||||||
|
_assetResults.Add(exists
|
||||||
|
? DiagnosticResult.Pass(check.Label, check.Path)
|
||||||
|
: DiagnosticResult.Fail(check.Label, check.Path));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RefreshPackageStatus()
|
||||||
|
{
|
||||||
|
EditorApplication.update -= PollPackageListRequest;
|
||||||
|
|
||||||
|
_addressablesPackageResult = DiagnosticResult.Pending(
|
||||||
|
"Addressables package",
|
||||||
|
"Querying com.unity.addressables through Unity Package Manager.");
|
||||||
|
|
||||||
|
_packageListRequest = Client.List(true);
|
||||||
|
EditorApplication.update += PollPackageListRequest;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void PollPackageListRequest()
|
||||||
|
{
|
||||||
|
if (_packageListRequest == null || !_packageListRequest.IsCompleted)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
EditorApplication.update -= PollPackageListRequest;
|
||||||
|
|
||||||
|
if (_packageListRequest.Status == StatusCode.Success)
|
||||||
|
{
|
||||||
|
bool found = false;
|
||||||
|
foreach (UnityEditor.PackageManager.PackageInfo packageInfo in _packageListRequest.Result)
|
||||||
|
{
|
||||||
|
if (string.Equals(packageInfo.name, AddressablesPackageName, StringComparison.Ordinal))
|
||||||
|
{
|
||||||
|
found = true;
|
||||||
|
_addressablesPackageResult = DiagnosticResult.Pass(
|
||||||
|
"Addressables package",
|
||||||
|
packageInfo.name + " " + packageInfo.version);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!found)
|
||||||
|
{
|
||||||
|
_addressablesPackageResult = DiagnosticResult.Fail(
|
||||||
|
"Addressables package",
|
||||||
|
"com.unity.addressables was not found in the current Package Manager list.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
string errorMessage = _packageListRequest.Error != null
|
||||||
|
? _packageListRequest.Error.message
|
||||||
|
: "Package Manager returned an unknown error.";
|
||||||
|
_addressablesPackageResult = DiagnosticResult.Warning("Addressables package", errorMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
_packageListRequest = null;
|
||||||
|
Repaint();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void DrawResult(DiagnosticResult result)
|
||||||
|
{
|
||||||
|
MessageType messageType = ToMessageType(result.Status);
|
||||||
|
|
||||||
|
using (new EditorGUILayout.VerticalScope(EditorStyles.helpBox))
|
||||||
|
{
|
||||||
|
using (new EditorGUILayout.HorizontalScope())
|
||||||
|
{
|
||||||
|
GUILayout.Label(ToStatusText(result.Status), GUILayout.Width(72f));
|
||||||
|
EditorGUILayout.LabelField(result.Label, EditorStyles.boldLabel);
|
||||||
|
}
|
||||||
|
|
||||||
|
EditorGUILayout.HelpBox(result.Detail, messageType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static MessageType ToMessageType(DiagnosticStatus status)
|
||||||
|
{
|
||||||
|
switch (status)
|
||||||
|
{
|
||||||
|
case DiagnosticStatus.Pass:
|
||||||
|
return MessageType.Info;
|
||||||
|
case DiagnosticStatus.Fail:
|
||||||
|
return MessageType.Error;
|
||||||
|
case DiagnosticStatus.Warning:
|
||||||
|
return MessageType.Warning;
|
||||||
|
default:
|
||||||
|
return MessageType.None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string ToStatusText(DiagnosticStatus status)
|
||||||
|
{
|
||||||
|
switch (status)
|
||||||
|
{
|
||||||
|
case DiagnosticStatus.Pass:
|
||||||
|
return "OK";
|
||||||
|
case DiagnosticStatus.Fail:
|
||||||
|
return "Missing";
|
||||||
|
case DiagnosticStatus.Warning:
|
||||||
|
return "Warning";
|
||||||
|
default:
|
||||||
|
return "Pending";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private readonly struct AssetCheck
|
||||||
|
{
|
||||||
|
public AssetCheck(string label, string path)
|
||||||
|
{
|
||||||
|
Label = label;
|
||||||
|
Path = path;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string Label { get; }
|
||||||
|
|
||||||
|
public string Path { get; }
|
||||||
|
}
|
||||||
|
|
||||||
|
private readonly struct DiagnosticResult
|
||||||
|
{
|
||||||
|
private DiagnosticResult(DiagnosticStatus status, string label, string detail)
|
||||||
|
{
|
||||||
|
Status = status;
|
||||||
|
Label = label;
|
||||||
|
Detail = detail;
|
||||||
|
}
|
||||||
|
|
||||||
|
public DiagnosticStatus Status { get; }
|
||||||
|
|
||||||
|
public string Label { get; }
|
||||||
|
|
||||||
|
public string Detail { get; }
|
||||||
|
|
||||||
|
public static DiagnosticResult Pass(string label, string detail)
|
||||||
|
{
|
||||||
|
return new DiagnosticResult(DiagnosticStatus.Pass, label, detail);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static DiagnosticResult Fail(string label, string detail)
|
||||||
|
{
|
||||||
|
return new DiagnosticResult(DiagnosticStatus.Fail, label, detail);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static DiagnosticResult Warning(string label, string detail)
|
||||||
|
{
|
||||||
|
return new DiagnosticResult(DiagnosticStatus.Warning, label, detail);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static DiagnosticResult Pending(string label, string detail)
|
||||||
|
{
|
||||||
|
return new DiagnosticResult(DiagnosticStatus.Pending, label, detail);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private enum DiagnosticStatus
|
||||||
|
{
|
||||||
|
Pending,
|
||||||
|
Pass,
|
||||||
|
Warning,
|
||||||
|
Fail,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 599ef528ec0c4331a77311f6ef7ef56b
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
64
My project/Assets/FlowScope/Samples/MainMenuP0/README.md
Normal file
64
My project/Assets/FlowScope/Samples/MainMenuP0/README.md
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
# MainMenuP0 Sample
|
||||||
|
|
||||||
|
## 目的
|
||||||
|
|
||||||
|
`MainMenuP0` 是 FlowScope 当前最小接入样例。它用于验证 P0/P1 Runtime 的推荐装配方式,而不是完整游戏模板。
|
||||||
|
|
||||||
|
当前样例覆盖:
|
||||||
|
|
||||||
|
- `GameBootstrap` 创建根 `Container`。
|
||||||
|
- 注册 `IConfigProvider`、`IResourceService`、`ISaveService`、`IAudioService`、`UIManager`、`IUIPreloadService`、`IUIScreenNavigator`。
|
||||||
|
- 使用 `RegisterTransient` 注册 `MainMenuFeature`。
|
||||||
|
- 通过 `UIPreloadService` 预加载 `MainMenuPanel`。
|
||||||
|
- 通过 `UIScreenNavigator` 打开主菜单面板。
|
||||||
|
- 点击按钮修改 `PlayerData.Gold` 并刷新 UI。
|
||||||
|
- `ShutdownAsync` 保存数据、关闭 `GameFlow` 并释放预加载资源。
|
||||||
|
|
||||||
|
## 当前路径
|
||||||
|
|
||||||
|
第一阶段仍保留在项目内开发模式:
|
||||||
|
|
||||||
|
```text
|
||||||
|
Assets/FlowScope/Samples/MainMenuP0
|
||||||
|
```
|
||||||
|
|
||||||
|
P2 第一阶段不把它移动到 `Samples~`,原因是当前 PlayMode 测试、场景引用、prefab 引用和 generated csproj 路径都依赖现有位置。等 P2 第二阶段开始本地 package 试迁移后,再把它作为候选包样例整理到:
|
||||||
|
|
||||||
|
```text
|
||||||
|
Samples~/MainMenuP0
|
||||||
|
```
|
||||||
|
|
||||||
|
## 如何运行
|
||||||
|
|
||||||
|
在 Unity 中打开:
|
||||||
|
|
||||||
|
```text
|
||||||
|
Assets/FlowScope/Samples/MainMenuP0/Scenes/MainMenuP0.unity
|
||||||
|
```
|
||||||
|
|
||||||
|
进入 Play Mode 后,画面应显示主菜单面板、Gold 文本和递增按钮。
|
||||||
|
|
||||||
|
## 验收点
|
||||||
|
|
||||||
|
- 首次启动显示默认 Gold。
|
||||||
|
- 点击按钮后 Gold 文本立即递增。
|
||||||
|
- 调用 `ShutdownAsync` 后写入 `main-menu-player` 存档。
|
||||||
|
- 再次启动后恢复上次保存的 Gold。
|
||||||
|
- 关闭时释放预加载资源。
|
||||||
|
- Console 不出现未预期 Error 或 Exception。
|
||||||
|
|
||||||
|
## 自动化测试
|
||||||
|
|
||||||
|
对应 PlayMode 测试位于:
|
||||||
|
|
||||||
|
```text
|
||||||
|
Assets/FlowScope/Tests/PlayMode/Samples/MainMenuP0Tests.cs
|
||||||
|
```
|
||||||
|
|
||||||
|
当前测试覆盖启动可见、按钮刷新、保存恢复、资源释放、启动任务可观察以及 shutdown 失败时的 best-effort cleanup。
|
||||||
|
|
||||||
|
## 已知限制
|
||||||
|
|
||||||
|
- 样例使用 `SampleResourceBackend`,不是 Addressables 真实资源验收。
|
||||||
|
- 样例中有运行时 fallback UI,用于避免缺引用时完全不可运行;人工验收仍应优先检查场景和 prefab 引用是否完整。
|
||||||
|
- 当前仍处于 `Assets/FlowScope` 开发模式,不代表 package sample 已完成迁移。
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 9df9d94a34fa4ec2b16df2c8cf2a651b
|
||||||
|
TextScriptImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -1,10 +1,21 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
using FlowScope.Addressables;
|
using FlowScope.Addressables;
|
||||||
|
using FlowScope.Resources;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
|
using UnityEngine;
|
||||||
|
using UnityEngine.TestTools;
|
||||||
|
|
||||||
namespace FlowScope.Tests.PlayMode.Resources
|
namespace FlowScope.Tests.PlayMode.Resources
|
||||||
{
|
{
|
||||||
public sealed class AddressablesResourceBackendTests
|
public sealed class AddressablesResourceBackendTests
|
||||||
{
|
{
|
||||||
|
private const string TextKey = "FlowScope.Tests.Addressables.Text";
|
||||||
|
private const string MissingKey = "FlowScope.Tests.Addressables.Missing";
|
||||||
|
private const string ExpectedText = "FlowScope Addressables Acceptance";
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void Name_ReturnsAddressables()
|
public void Name_ReturnsAddressables()
|
||||||
{
|
{
|
||||||
@@ -12,5 +23,149 @@ namespace FlowScope.Tests.PlayMode.Resources
|
|||||||
|
|
||||||
Assert.AreEqual("Addressables", backend.Name);
|
Assert.AreEqual("Addressables", backend.Name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[UnityTest]
|
||||||
|
public IEnumerator LoadAsync_WithRealTextAssetKey_LoadsTextAsset()
|
||||||
|
{
|
||||||
|
var backend = new AddressablesResourceBackend();
|
||||||
|
|
||||||
|
TextAsset asset = null;
|
||||||
|
yield return AwaitTask(backend.LoadAsync<TextAsset>(
|
||||||
|
TextKey,
|
||||||
|
CancellationToken.None), loaded => asset = loaded);
|
||||||
|
|
||||||
|
Assert.That(asset, Is.Not.Null);
|
||||||
|
Assert.That(asset.text, Does.Contain(ExpectedText));
|
||||||
|
|
||||||
|
backend.Release(TextKey, asset);
|
||||||
|
LogAssert.NoUnexpectedReceived();
|
||||||
|
}
|
||||||
|
|
||||||
|
[UnityTest]
|
||||||
|
public IEnumerator AddressablesResourceService_DisposeHandleTwice_ReleasesWithoutUnexpectedLogs()
|
||||||
|
{
|
||||||
|
var service = new AddressablesResourceService(new AddressablesResourceBackend());
|
||||||
|
|
||||||
|
IResourceHandle<TextAsset> handle = null;
|
||||||
|
yield return AwaitTask(service.LoadAsync<TextAsset>(
|
||||||
|
TextKey,
|
||||||
|
CancellationToken.None), loaded => handle = loaded);
|
||||||
|
|
||||||
|
Assert.That(handle, Is.Not.Null);
|
||||||
|
Assert.That(handle.Asset, Is.Not.Null);
|
||||||
|
Assert.That(handle.Asset.text, Does.Contain(ExpectedText));
|
||||||
|
|
||||||
|
handle.Dispose();
|
||||||
|
handle.Dispose();
|
||||||
|
|
||||||
|
Assert.That(handle.IsDisposed, Is.True);
|
||||||
|
Assert.That(handle.Asset, Is.Null);
|
||||||
|
Assert.That(service.TryLoadCompleted<TextAsset>(TextKey, out _), Is.False);
|
||||||
|
LogAssert.NoUnexpectedReceived();
|
||||||
|
}
|
||||||
|
|
||||||
|
[UnityTest]
|
||||||
|
public IEnumerator LoadAsync_WhenCanceled_ThrowsOperationCanceledException()
|
||||||
|
{
|
||||||
|
var backend = new AddressablesResourceBackend();
|
||||||
|
using var cancellation = new CancellationTokenSource();
|
||||||
|
cancellation.Cancel();
|
||||||
|
|
||||||
|
Exception exception = null;
|
||||||
|
yield return AwaitExpectedFailure(
|
||||||
|
() => backend.LoadAsync<TextAsset>(TextKey, cancellation.Token),
|
||||||
|
caught => exception = caught);
|
||||||
|
|
||||||
|
Assert.That(exception, Is.TypeOf<OperationCanceledException>());
|
||||||
|
}
|
||||||
|
|
||||||
|
[UnityTest]
|
||||||
|
public IEnumerator LoadAsync_WithMissingKey_ThrowsDiagnosticException()
|
||||||
|
{
|
||||||
|
var backend = new AddressablesResourceBackend();
|
||||||
|
|
||||||
|
Exception exception = null;
|
||||||
|
yield return AwaitExpectedFailure(
|
||||||
|
() => backend.LoadAsync<TextAsset>(MissingKey, CancellationToken.None),
|
||||||
|
caught => exception = caught);
|
||||||
|
|
||||||
|
Assert.That(exception, Is.Not.Null);
|
||||||
|
Assert.That(exception.ToString(), Does.Contain(MissingKey));
|
||||||
|
}
|
||||||
|
|
||||||
|
[UnityTest]
|
||||||
|
public IEnumerator LoadAsync_WithMismatchedType_ThrowsDiagnosticException()
|
||||||
|
{
|
||||||
|
var backend = new AddressablesResourceBackend();
|
||||||
|
|
||||||
|
Exception exception = null;
|
||||||
|
yield return AwaitExpectedFailure(
|
||||||
|
() => backend.LoadAsync<GameObject>(TextKey, CancellationToken.None),
|
||||||
|
caught => exception = caught);
|
||||||
|
|
||||||
|
Assert.That(exception, Is.Not.Null);
|
||||||
|
Assert.That(exception.ToString(), Does.Contain(TextKey));
|
||||||
|
Assert.That(exception.ToString(), Does.Contain(nameof(GameObject)));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static IEnumerator AwaitTask<T>(
|
||||||
|
Task<T> task,
|
||||||
|
Action<T> onCompleted)
|
||||||
|
{
|
||||||
|
while (!task.IsCompleted)
|
||||||
|
{
|
||||||
|
yield return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (task.IsCanceled)
|
||||||
|
{
|
||||||
|
throw new OperationCanceledException();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (task.IsFaulted)
|
||||||
|
{
|
||||||
|
throw task.Exception.GetBaseException();
|
||||||
|
}
|
||||||
|
|
||||||
|
onCompleted(task.Result);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static IEnumerator AwaitExpectedFailure<T>(
|
||||||
|
Func<Task<T>> startTask,
|
||||||
|
Action<Exception> onException)
|
||||||
|
{
|
||||||
|
var ignoreFailingMessages = LogAssert.ignoreFailingMessages;
|
||||||
|
Task<T> task = null;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
LogAssert.ignoreFailingMessages = true;
|
||||||
|
task = startTask();
|
||||||
|
|
||||||
|
while (!task.IsCompleted)
|
||||||
|
{
|
||||||
|
yield return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
yield return null;
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
LogAssert.ignoreFailingMessages = ignoreFailingMessages;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (task.IsCanceled)
|
||||||
|
{
|
||||||
|
onException(new OperationCanceledException());
|
||||||
|
yield break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (task.IsFaulted)
|
||||||
|
{
|
||||||
|
onException(task.Exception.GetBaseException());
|
||||||
|
yield break;
|
||||||
|
}
|
||||||
|
|
||||||
|
Assert.Fail("Expected Addressables load to fail.");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 20098efc5dc925a4194600eea4969d68
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 1acac9035afee5b408d7a7d78b2a1b0a
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 52d2a24f37dccf346b5e0c6a34585514
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
FlowScope Addressables Acceptance
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 7e04733ce9dab74459592d3bd99fcafd
|
||||||
|
TextScriptImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -5,4 +5,5 @@ EditorBuildSettings:
|
|||||||
m_ObjectHideFlags: 0
|
m_ObjectHideFlags: 0
|
||||||
serializedVersion: 2
|
serializedVersion: 2
|
||||||
m_Scenes: []
|
m_Scenes: []
|
||||||
m_configObjects: {}
|
m_configObjects:
|
||||||
|
com.unity.addressableassets: {fileID: 11400000, guid: f9fd621d22433a64aa0741deda630f70, type: 2}
|
||||||
|
|||||||
279
docs/guides/flowscope-installation.md
Normal file
279
docs/guides/flowscope-installation.md
Normal file
@@ -0,0 +1,279 @@
|
|||||||
|
# FlowScope 安装与最小接入
|
||||||
|
|
||||||
|
日期:2026-05-26
|
||||||
|
|
||||||
|
本文说明 FlowScope 在 P2 第一阶段面向使用者的安装路径、项目内开发模式,以及基于当前真实 API 的最小 Bootstrap 写法。当前仓库尚未完成标准 Unity Package 物理搬迁,package path 与 Git URL 方式是目标接入形态;项目内 `Assets/FlowScope` 开发模式是当前可用形态。
|
||||||
|
|
||||||
|
## 安装方式
|
||||||
|
|
||||||
|
### 本地 package path
|
||||||
|
|
||||||
|
当后续完成标准包目录后,可在使用方 Unity 项目的 `Packages/manifest.json` 中使用本地路径:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"dependencies": {
|
||||||
|
"com.flowscope.gamecore": "file:../FlowScope/Packages/com.flowscope.gamecore"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
适用场景:
|
||||||
|
|
||||||
|
- 本机同时开发 FlowScope 与游戏项目。
|
||||||
|
- 需要在包仓库中调试 Runtime、Addressables、Editor 或 Samples。
|
||||||
|
- 希望由 Unity Package Manager 识别包结构与 `Samples~`。
|
||||||
|
|
||||||
|
注意:P2 第一阶段当前仓库还没有 `Packages/com.flowscope.gamecore` 物理目录,因此此方式需要等第二阶段本地 package 试迁移完成后再用于实装验证。
|
||||||
|
|
||||||
|
### Git URL
|
||||||
|
|
||||||
|
当包目录与发布分支稳定后,可在 `Packages/manifest.json` 中使用 Git URL:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"dependencies": {
|
||||||
|
"com.flowscope.gamecore": "<最终发布仓库 Git URL>?path=/Packages/com.flowscope.gamecore#v0.2.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
适用场景:
|
||||||
|
|
||||||
|
- 使用方只消费稳定版本,不直接修改 FlowScope 源码。
|
||||||
|
- 团队希望通过 tag 或固定 commit 控制接入版本。
|
||||||
|
|
||||||
|
当前仓库 `origin` 仍带有旧项目名,不应直接写入对外安装文档。发布前需要由团队确认最终 FlowScope 包仓库地址;使用 Git URL 时应固定 tag 或 commit,避免使用浮动分支导致使用方项目在不同时间解析到不同代码。
|
||||||
|
|
||||||
|
### 项目内 Assets 开发模式
|
||||||
|
|
||||||
|
当前可用模式是将 FlowScope 放在 Unity 项目内:
|
||||||
|
|
||||||
|
```text
|
||||||
|
Assets/
|
||||||
|
FlowScope/
|
||||||
|
Runtime/
|
||||||
|
Addressables/
|
||||||
|
Samples/
|
||||||
|
Tests/
|
||||||
|
```
|
||||||
|
|
||||||
|
本仓库当前路径为:
|
||||||
|
|
||||||
|
```text
|
||||||
|
My project/Assets/FlowScope
|
||||||
|
```
|
||||||
|
|
||||||
|
适用场景:
|
||||||
|
|
||||||
|
- P2 第一阶段继续验证 P0/P1 Runtime 行为。
|
||||||
|
- 并行开发 Addressables 验收、Editor 诊断与 package 文档。
|
||||||
|
- 在包迁移前保持 `MainMenuP0` 示例与测试路径稳定。
|
||||||
|
|
||||||
|
使用此模式时,不需要在 `manifest.json` 中声明 `com.flowscope.gamecore`。仍需要确保项目已有当前代码依赖的包,例如 `com.cysharp.r3`、`com.unity.addressables` 与 `com.unity.ugui`。
|
||||||
|
|
||||||
|
## 最小 Bootstrap 示例
|
||||||
|
|
||||||
|
下面示例只使用当前代码中已经存在的 API 名称。示例使用 Addressables 作为资源后端,适合未来包接入;如果项目暂时没有 Addressables 资源,也可以参考 `MainMenuP0` 示例中的自定义 `IResourceBackend` 做本地验证。
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using FlowScope.Addressables;
|
||||||
|
using FlowScope.Audio;
|
||||||
|
using FlowScope.Config;
|
||||||
|
using FlowScope.Flow;
|
||||||
|
using FlowScope.Resources;
|
||||||
|
using FlowScope.Save;
|
||||||
|
using FlowScope.UI;
|
||||||
|
using UnityEngine;
|
||||||
|
using ContainerType = FlowScope.Container.Container;
|
||||||
|
|
||||||
|
public sealed class FlowScopeBootstrap : MonoBehaviour
|
||||||
|
{
|
||||||
|
[SerializeField] private Canvas hudCanvas;
|
||||||
|
|
||||||
|
private ContainerType _root;
|
||||||
|
private GameFlow _gameFlow;
|
||||||
|
private UIPreloadService _preloadService;
|
||||||
|
private CancellationTokenSource _lifetime;
|
||||||
|
|
||||||
|
public Task StartupTask { get; private set; }
|
||||||
|
|
||||||
|
private void Awake()
|
||||||
|
{
|
||||||
|
_lifetime = new CancellationTokenSource();
|
||||||
|
StartupTask = RunStartupWithLoggingAsync(_lifetime.Token);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task StartupAsync(CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
_root = new ContainerType();
|
||||||
|
_gameFlow = new GameFlow();
|
||||||
|
|
||||||
|
IResourceService resources = new AddressablesResourceService(
|
||||||
|
new AddressablesResourceBackend());
|
||||||
|
_preloadService = new UIPreloadService(resources);
|
||||||
|
|
||||||
|
var uiManager = new UIManager(resources, _preloadService);
|
||||||
|
uiManager.RegisterLayer("hud", hudCanvas, 0, PanelStrategy.Destroy);
|
||||||
|
var screenNavigator = new UIScreenNavigator(uiManager);
|
||||||
|
|
||||||
|
var configProvider = new JsonConfigProvider(new[]
|
||||||
|
{
|
||||||
|
JsonConfigProvider.Mapping<ExampleConfigRow>(
|
||||||
|
"example.json",
|
||||||
|
new InMemoryConfigSource(
|
||||||
|
new Dictionary<string, string>
|
||||||
|
{
|
||||||
|
{ "example.json", "[{\"Id\":1,\"Name\":\"Start\"}]" }
|
||||||
|
},
|
||||||
|
"Bootstrap"),
|
||||||
|
new JsonConfigParser())
|
||||||
|
});
|
||||||
|
|
||||||
|
var saveService = new SaveService(
|
||||||
|
new PlayerPrefsSaveStorage(),
|
||||||
|
new JsonSaveSerializer());
|
||||||
|
|
||||||
|
var audioService = gameObject
|
||||||
|
.AddComponent<AudioService>()
|
||||||
|
.Initialize(resources);
|
||||||
|
|
||||||
|
_root.RegisterInstance(_root);
|
||||||
|
_root.RegisterInstance<IConfigProvider>(configProvider);
|
||||||
|
_root.RegisterInstance<IResourceService>(resources);
|
||||||
|
_root.RegisterInstance<ISaveService>(saveService);
|
||||||
|
_root.RegisterInstance<IAudioService>(audioService);
|
||||||
|
_root.RegisterInstance(uiManager);
|
||||||
|
_root.RegisterInstance<IUIPreloadService>(_preloadService);
|
||||||
|
_root.RegisterInstance<IUIScreenNavigator>(screenNavigator);
|
||||||
|
_root.RegisterTransient(_ => new ExampleFeature(screenNavigator));
|
||||||
|
|
||||||
|
await _gameFlow.StartupAsync<ExampleFeature>(_root, cancellationToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task ShutdownAsync(CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
if (_gameFlow != null && _gameFlow.State != GameFlowState.Disposed)
|
||||||
|
{
|
||||||
|
await _gameFlow.ShutdownAsync(cancellationToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
_preloadService?.ReleaseAll();
|
||||||
|
_lifetime?.Cancel();
|
||||||
|
_lifetime?.Dispose();
|
||||||
|
_lifetime = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnApplicationQuit()
|
||||||
|
{
|
||||||
|
_ = RunShutdownWithLoggingAsync(CancellationToken.None);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task RunStartupWithLoggingAsync(CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await StartupAsync(cancellationToken);
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
Debug.LogException(exception);
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task RunShutdownWithLoggingAsync(CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await ShutdownAsync(cancellationToken);
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
Debug.LogException(exception);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private sealed class ExampleConfigRow : IConfigRow
|
||||||
|
{
|
||||||
|
public int Id { get; set; }
|
||||||
|
public string Name { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
private sealed class ExampleFeature : IFeature
|
||||||
|
{
|
||||||
|
private readonly IUIScreenNavigator _screenNavigator;
|
||||||
|
|
||||||
|
public ExampleFeature(IUIScreenNavigator screenNavigator)
|
||||||
|
{
|
||||||
|
_screenNavigator = screenNavigator;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task LoadAsync(FeatureContext context)
|
||||||
|
{
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task EnterAsync(FeatureContext context)
|
||||||
|
{
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task ExitAsync(FeatureContext context)
|
||||||
|
{
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
接入要点:
|
||||||
|
|
||||||
|
- `GameFlow.StartupAsync<TInitialFeature>(Container root, CancellationToken cancellationToken)` 会先解析并加载 `IConfigProvider`。
|
||||||
|
- 初始 Feature 必须实现 `IFeature`,并能从 Feature scope 中解析;示例使用 `_root.RegisterTransient(_ => new ExampleFeature(screenNavigator))`。
|
||||||
|
- 业务资源统一通过 `IResourceService` 或 `IResourceGroup` 加载,不直接在业务层调用 Addressables。
|
||||||
|
- UI 层使用 `UIManager.RegisterLayer(...)` 建立层级,再由 `UIScreenNavigator` 或业务 Feature 打开面板。
|
||||||
|
- Shutdown 时应调用 `GameFlow.ShutdownAsync(...)`,并释放 UI 预加载资源,例如 `_preloadService.ReleaseAll()`。
|
||||||
|
|
||||||
|
## Addressables 接入说明
|
||||||
|
|
||||||
|
使用 Addressables 时,推荐注册:
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
IResourceService resources = new AddressablesResourceService(
|
||||||
|
new AddressablesResourceBackend());
|
||||||
|
```
|
||||||
|
|
||||||
|
这会把业务层依赖收敛到 `IResourceService`。`AddressablesResourceBackend` 只位于 Addressables 适配程序集,Runtime 不需要知道 Addressables 的存在。
|
||||||
|
|
||||||
|
如果某个系统需要独立资源生命周期,应通过 `resources.CreateGroup()` 创建 `IResourceGroup`,让 Feature 的资源释放跟随 `FeatureContext.Resources.Dispose()`。
|
||||||
|
|
||||||
|
## 常见误用
|
||||||
|
|
||||||
|
- 不要让业务 Feature 直接调用 `UnityEngine.AddressableAssets.Addressables`;应通过 `IResourceService` 或 `IResourceGroup` 访问资源。
|
||||||
|
- 不要把短生命周期对象注册成 `RegisterInstance` 或 `RegisterSingletonFactory`;临时对象优先使用 `RegisterTransient`,需要随 Feature 生命周期释放的对象放在 Feature scope。
|
||||||
|
- 不要跳过 `GameFlow.ShutdownAsync(...)`;它负责退出当前 Feature、释放 Feature scope,并处置 root container。
|
||||||
|
- 不要把 `UIScreenNavigator` 当成完整页面路由系统;它是当前 UI 导航辅助,不负责 URL、跨 Feature 页面恢复或全局路由。
|
||||||
|
- 不要让 `FlowScope.Runtime` 反向依赖 Addressables、Editor、Samples 或 Tests。
|
||||||
|
- 不要在没有注册 UI layer 的情况下打开面板;`UIManager.OpenAsync` 会要求面板声明的 layer 已经通过 `RegisterLayer` 注册。
|
||||||
|
- 不要在未初始化 `AudioService.Initialize(IResourceService, AudioServiceConfig)` 前调用播放接口。
|
||||||
|
- 不要假设 package path 与 Git URL 在 P2 第一阶段已经可安装;当前真实可用形态仍是项目内 `Assets/FlowScope` 开发模式。
|
||||||
|
|
||||||
|
## 当前限制
|
||||||
|
|
||||||
|
P2 第一阶段文档完成后,FlowScope 仍需要后续 worker 或阶段完成以下验证后,才能把 package 安装方式视为可交付:
|
||||||
|
|
||||||
|
1. 建立真实 package 根目录与 `package.json`。
|
||||||
|
2. 通过本地 package path 在干净 Unity 项目中验证安装。
|
||||||
|
3. 验证 Git URL 安装与依赖解析。
|
||||||
|
4. 将 `MainMenuP0` 从 `Assets/FlowScope/Samples` 整理为可导入 package sample。
|
||||||
|
5. 在干净 Unity 项目中复验 Addressables 测试资源、settings 与 PlayMode 用例;主项目内的真实 load/release 验收记录已完成。
|
||||||
|
6. 保持 generated csproj build 与 Unity Test Runner 记录为 0 error。
|
||||||
137
docs/guides/flowscope-package-layout.md
Normal file
137
docs/guides/flowscope-package-layout.md
Normal file
@@ -0,0 +1,137 @@
|
|||||||
|
# FlowScope Package 目标结构
|
||||||
|
|
||||||
|
日期:2026-05-26
|
||||||
|
|
||||||
|
本文说明 FlowScope P2 阶段的目标 Unity Package 目录形态,以及第一阶段暂不搬迁 `Assets/FlowScope` 的边界。本文只描述目标结构与迁移约束,不代表当前仓库已经完成物理搬迁。
|
||||||
|
|
||||||
|
## 当前状态
|
||||||
|
|
||||||
|
当前 FlowScope 仍处于项目内开发模式:
|
||||||
|
|
||||||
|
```text
|
||||||
|
My project/
|
||||||
|
Assets/
|
||||||
|
FlowScope/
|
||||||
|
Runtime/
|
||||||
|
Addressables/
|
||||||
|
Samples/
|
||||||
|
Tests/
|
||||||
|
```
|
||||||
|
|
||||||
|
当前尚未建立标准 Unity Package 根目录,未发现包级 `package.json`、`Samples~`、`Tests~`、`Documentation~` 目录。`MainMenuP0` 示例仍位于 `Assets/FlowScope/Samples/MainMenuP0`。
|
||||||
|
|
||||||
|
## 目标包根目录
|
||||||
|
|
||||||
|
目标包建议使用独立目录承载,例如:
|
||||||
|
|
||||||
|
```text
|
||||||
|
Packages/
|
||||||
|
com.flowscope.gamecore/
|
||||||
|
package.json
|
||||||
|
Runtime/
|
||||||
|
Addressables/
|
||||||
|
Editor/
|
||||||
|
Samples~/
|
||||||
|
Tests/
|
||||||
|
Documentation~/
|
||||||
|
README.md
|
||||||
|
CHANGELOG.md
|
||||||
|
LICENSE.md
|
||||||
|
```
|
||||||
|
|
||||||
|
目录职责如下:
|
||||||
|
|
||||||
|
| 目录 | 职责 |
|
||||||
|
| --- | --- |
|
||||||
|
| `package.json` | Unity Package Manager 元数据,声明包名、显示名、版本、Unity 版本与依赖。 |
|
||||||
|
| `Runtime/` | FlowScope 核心运行时代码,包括 Container、Feature、GameFlow、Resource、Config、Save、Audio、UI 等能力。 |
|
||||||
|
| `Addressables/` | Addressables 适配层,提供 `AddressablesResourceBackend` 与 `AddressablesResourceService`。 |
|
||||||
|
| `Editor/` | 只放编辑器诊断、检查、菜单入口等 Editor-only 内容,不承载运行时逻辑。 |
|
||||||
|
| `Samples~/` | Package Manager 可导入示例,例如 `MainMenuP0`。示例可以依赖 Runtime 与所演示的适配包。 |
|
||||||
|
| `Tests/` | 包内测试,按 EditMode 与 PlayMode 分层保留测试 asmdef。 |
|
||||||
|
| `Documentation~/` | Package Manager 文档入口,包含安装、接入、诊断与示例说明。 |
|
||||||
|
|
||||||
|
## package.json 建议形态
|
||||||
|
|
||||||
|
目标包元数据建议保持最小可识别:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"name": "com.flowscope.gamecore",
|
||||||
|
"displayName": "FlowScope Game Core",
|
||||||
|
"version": "0.2.0",
|
||||||
|
"unity": "2022.3",
|
||||||
|
"description": "A lightweight Unity game flow, resource, config, save, audio, and UI runtime foundation.",
|
||||||
|
"dependencies": {
|
||||||
|
"com.cysharp.r3": "https://github.com/Cysharp/R3.git?path=src/R3.Unity/Assets/R3.Unity#1.3.0",
|
||||||
|
"com.unity.addressables": "1.21.21",
|
||||||
|
"com.unity.ugui": "1.0.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
依赖说明:
|
||||||
|
|
||||||
|
- `com.cysharp.r3` 是当前 `FlowScope.Runtime.asmdef` 直接引用 `R3.Unity` 的来源。
|
||||||
|
- `com.unity.addressables` 只服务 `FlowScope.Addressables`,Runtime 不直接依赖 Addressables。
|
||||||
|
- `com.unity.ugui` 服务当前 UI 与示例中的 `Canvas`、`Button`、`Text` 等 UGUI 类型。
|
||||||
|
|
||||||
|
如果后续发布渠道不接受 Git URL 形式依赖,应在发布前改为团队确认过的 OpenUPM、内网 registry 或嵌入式依赖策略。
|
||||||
|
|
||||||
|
## asmdef 依赖方向
|
||||||
|
|
||||||
|
当前与目标结构都必须保持以下方向:
|
||||||
|
|
||||||
|
```text
|
||||||
|
FlowScope.Runtime
|
||||||
|
-> R3.Unity
|
||||||
|
|
||||||
|
FlowScope.Addressables
|
||||||
|
-> FlowScope.Runtime
|
||||||
|
-> Unity.Addressables
|
||||||
|
-> Unity.ResourceManager
|
||||||
|
|
||||||
|
FlowScope.Editor
|
||||||
|
-> FlowScope.Runtime
|
||||||
|
-> 需要诊断的编辑器程序集
|
||||||
|
|
||||||
|
FlowScope.Samples.MainMenuP0
|
||||||
|
-> FlowScope.Runtime
|
||||||
|
-> R3.Unity
|
||||||
|
|
||||||
|
FlowScope.Tests.EditMode / FlowScope.Tests.PlayMode
|
||||||
|
-> 被测程序集
|
||||||
|
```
|
||||||
|
|
||||||
|
禁止反向依赖:
|
||||||
|
|
||||||
|
- `FlowScope.Runtime` 不依赖 `FlowScope.Addressables`。
|
||||||
|
- `FlowScope.Runtime` 不依赖 `FlowScope.Editor`。
|
||||||
|
- `FlowScope.Runtime` 不依赖 `FlowScope.Samples.*`。
|
||||||
|
- `FlowScope.Runtime` 不依赖测试程序集。
|
||||||
|
- Samples 不作为 Runtime 的依赖来源。
|
||||||
|
|
||||||
|
`AddressablesResourceService` 当前命名空间位于 `FlowScope.Resources`,但文件处于 `Assets/FlowScope/Addressables` 并编译进 `FlowScope.Addressables` 程序集;迁移时应保留程序集边界,不应因为命名空间相同而把 Addressables 适配代码并回 Runtime。
|
||||||
|
|
||||||
|
## 第一阶段暂不搬迁 Assets/FlowScope 的原因
|
||||||
|
|
||||||
|
P2 第一阶段只建立分发边界、安装接入文档、Addressables 验收与最小 Editor 诊断入口,不做物理搬迁,原因如下:
|
||||||
|
|
||||||
|
1. 当前 `Assets/FlowScope` 已承载 P0/P1 Runtime、Addressables、Samples、Tests 的稳定验证路径。
|
||||||
|
2. `MainMenuP0` 示例、PlayMode 测试与 Unity 生成的 csproj 仍依赖当前 Asset 路径。
|
||||||
|
3. 一次性移动 Runtime、Samples、Tests 会同时改变 asmdef 路径、资源路径、场景引用、prefab 引用与测试发现路径,容易把包结构问题和运行时回归混在一起。
|
||||||
|
4. 第一阶段还有并行 worker 处理 Addressables 验收与 Editor 诊断;Package 文档 worker 不应覆盖或移动他们的文件。
|
||||||
|
5. 先用文档锁定目标结构与依赖方向,可以让第二阶段本地 package 试迁移按批次验证,而不是一次性大搬迁。
|
||||||
|
|
||||||
|
## 建议迁移顺序
|
||||||
|
|
||||||
|
后续进入 P2 第二阶段时,建议按以下顺序迁移,每一步都保留独立验证记录:
|
||||||
|
|
||||||
|
1. 创建 package 根目录与 `package.json`,先不移动 Runtime。
|
||||||
|
2. 将 `Runtime/` 移入包目录并验证 `FlowScope.Runtime` 编译边界。
|
||||||
|
3. 将 `Addressables/` 移入包目录并验证 `FlowScope.Addressables` 仍只依赖 Runtime 与 Unity Addressables。
|
||||||
|
4. 将文档迁入 `Documentation~/` 或同步保留到仓库 `docs/`。
|
||||||
|
5. 将 `MainMenuP0` 作为包示例迁入 `Samples~/MainMenuP0`,验证场景、prefab、配置与测试路径。
|
||||||
|
6. 将 EditMode / PlayMode 测试整理进包内 `Tests/`,验证 Unity Test Runner 发现与执行。
|
||||||
|
|
||||||
|
每一步完成前都应验证 generated csproj build、Unity Test Runner 和关键示例路径;如果某一步失败,应先回到该步骤修复,不继续叠加后续搬迁。
|
||||||
77
docs/reviews/addressables-integration-acceptance.md
Normal file
77
docs/reviews/addressables-integration-acceptance.md
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
# FlowScope Addressables 集成验收记录
|
||||||
|
|
||||||
|
日期:2026-06-04
|
||||||
|
|
||||||
|
## 验收目的
|
||||||
|
|
||||||
|
本文用于补齐 P2 第一阶段的 Addressables 真实集成验收缺口。目标不是重新设计资源系统,而是确认当前 `FlowScope.Addressables.AddressablesResourceBackend` 能通过真实 Addressables key 完成加载、释放和错误诊断。
|
||||||
|
|
||||||
|
## 项目配置
|
||||||
|
|
||||||
|
已确认:
|
||||||
|
|
||||||
|
- `My project/Packages/manifest.json` 包含 `com.unity.addressables`,当前版本为 `1.21.21`。
|
||||||
|
- `My project/Assets/FlowScope/Addressables/FlowScope.Addressables.asmdef` 引用 `FlowScope.Runtime`、`Unity.Addressables`、`Unity.ResourceManager`。
|
||||||
|
- `My project/Assets/FlowScope/Tests/PlayMode/FlowScope.Tests.PlayMode.asmdef` 引用 `FlowScope.Addressables`。
|
||||||
|
- 当前项目已生成 `My project/Assets/AddressableAssetsData`。
|
||||||
|
- `My project/ProjectSettings/EditorBuildSettings.asset` 已登记 Addressables settings config object。
|
||||||
|
|
||||||
|
## 测试资源
|
||||||
|
|
||||||
|
真实 Addressables 测试资源:
|
||||||
|
|
||||||
|
- 路径:`Assets/FlowScope/Tests/PlayMode/Resources_moved/Fixtures/Addressables/FlowScopeAddressablesText.txt`
|
||||||
|
- 内容:`FlowScope Addressables Acceptance`
|
||||||
|
- GUID:`7e04733ce9dab74459592d3bd99fcafd`
|
||||||
|
- Addressables group:`Default Local Group`
|
||||||
|
- Addressables key:`FlowScope.Tests.Addressables.Text`
|
||||||
|
- 资源类型:`TextAsset`
|
||||||
|
|
||||||
|
`Default Local Group.asset` 中已确认存在:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
m_GUID: 7e04733ce9dab74459592d3bd99fcafd
|
||||||
|
m_Address: FlowScope.Tests.Addressables.Text
|
||||||
|
```
|
||||||
|
|
||||||
|
## 自动化验收
|
||||||
|
|
||||||
|
已在 `My project/Assets/FlowScope/Tests/PlayMode/Resources/AddressablesResourceBackendTests.cs` 补充真实 PlayMode 集成测试:
|
||||||
|
|
||||||
|
- `LoadAsync_WithRealTextAssetKey_LoadsTextAsset`
|
||||||
|
- `LoadAsync_WithMissingKey_ThrowsDiagnosticException`
|
||||||
|
- `LoadAsync_WithMismatchedType_ThrowsDiagnosticException`
|
||||||
|
|
||||||
|
覆盖范围:
|
||||||
|
|
||||||
|
- `AddressablesResourceBackend.LoadAsync<TextAsset>` 能通过真实 key 加载真实 `TextAsset`。
|
||||||
|
- `AddressablesResourceBackend.Release` 能释放已加载资源。
|
||||||
|
- 缺失 key 会产生可捕获诊断异常,并包含缺失 key。
|
||||||
|
- 类型不匹配会产生可捕获诊断异常,并包含 key 和请求类型。
|
||||||
|
|
||||||
|
本轮同时修复了 `AddressablesResourceBackend.LoadAsync<T>` 的失败状态处理:当 Addressables handle 存在 `OperationException`、`Status` 不是 `Succeeded` 或 `Result` 为 `null` 时,backend 会抛出包含 key、请求类型和 backend 名称的 `InvalidOperationException`。
|
||||||
|
|
||||||
|
## 验收结果
|
||||||
|
|
||||||
|
用户已在 Unity Test Runner 中执行全量 EditMode / PlayMode,结果全绿。
|
||||||
|
|
||||||
|
本轮针对 Addressables 集成用例的最终结果:
|
||||||
|
|
||||||
|
| 项目 | 结果 | 证据 |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| 真实 `LoadAsync<TextAsset>` | 通过 | `FlowScope.Tests.Addressables.Text` 加载真实 `TextAsset` |
|
||||||
|
| `Release<TextAsset>` | 通过 | 成功释放已加载资源,未产生未处理日志 |
|
||||||
|
| 缺失 key 诊断 | 通过 | `FlowScope.Tests.Addressables.Missing` 产生可捕获异常 |
|
||||||
|
| 类型不匹配诊断 | 通过 | `TextAsset` key 以 `GameObject` 请求时产生可捕获异常 |
|
||||||
|
| Unity Test Runner EditMode | 通过 | 用户反馈全量 EditMode 全绿 |
|
||||||
|
| Unity Test Runner PlayMode | 通过 | 用户反馈全量 PlayMode 全绿 |
|
||||||
|
|
||||||
|
## 剩余注意事项
|
||||||
|
|
||||||
|
- 当前 Addressables 测试资源位于 `Resources_moved` 路径下;这是本轮真实验收的有效路径,但后续 package 迁移时应统一测试资源目录命名。
|
||||||
|
- `Assets/FlowScope/Tests/PlayMode/Resources/Fixtures` 当前只剩空目录 meta,属于提交前清理候选。
|
||||||
|
- 当前验收仍在主项目内完成;P2 后续 package 阶段还需要在干净 Unity 项目中验证本地 package path 或 Git URL 安装。
|
||||||
|
|
||||||
|
## 结论
|
||||||
|
|
||||||
|
P2 第一阶段的 Addressables 真实集成验收已完成。当前不再只是编译通过或人工步骤文档,而是已有真实 PlayMode 自动化测试覆盖成功加载、释放、缺失 key 和类型不匹配诊断。
|
||||||
171
docs/reviews/p2-phase1-baseline-audit.md
Normal file
171
docs/reviews/p2-phase1-baseline-audit.md
Normal file
@@ -0,0 +1,171 @@
|
|||||||
|
# FlowScope P2 第一阶段基线审查
|
||||||
|
|
||||||
|
日期:2026-05-26
|
||||||
|
|
||||||
|
## 审查目的
|
||||||
|
|
||||||
|
本文用于锁定 P2 第一阶段执行边界。P2 第一阶段只推进 package 分发边界、安装接入文档、Addressables 真实验收、Editor 最小诊断入口和 MainMenuP0 样例说明,不重写 P0/P1 Runtime 生命周期,不扩大到完整包迁移或编辑器平台化。
|
||||||
|
|
||||||
|
## 当前分支与工作树
|
||||||
|
|
||||||
|
- 当前分支:`codex/pre-p2-architecture-fixes`
|
||||||
|
- 本文创建前 `git status --short --branch` 未显示待提交文件;命令输出包含本机 `C:\Users\13999/.config/git/ignore` 权限 warning。
|
||||||
|
- 本阶段提交信息必须使用中文。
|
||||||
|
- 本阶段计划、审查、报告必须使用中文。
|
||||||
|
|
||||||
|
## P0/P1 Runtime 基线
|
||||||
|
|
||||||
|
当前 P0/P1 基线以 `docs/requirements/p0-requirements-set.md`、`docs/requirements/p1-production-hardening.md`、`docs/reviews/p1-completion-report.md` 和 `docs/guides/flowscope-runtime-usage.md` 为主要依据。
|
||||||
|
|
||||||
|
已确认的实现状态:
|
||||||
|
|
||||||
|
- `FlowScope.Runtime` 已包含 Container、Feature、GameFlow、Config、Save、Resource、Audio、UI、UIPreload、UIScreenNavigator 等 P0/P1 Runtime 能力。
|
||||||
|
- `FlowScope.Runtime` 的 asmdef 只引用 `R3.Unity`,不直接引用 Addressables、Editor 或 Samples。
|
||||||
|
- 资源系统已拆成 Runtime 核心 `ResourceService` + `IResourceBackend`,Addressables 适配位于独立 `FlowScope.Addressables` 程序集。
|
||||||
|
- `UIPreloadService.Release<TPanel>()` 已包含 panel 级 in-flight invalidation,并有 `Release_WhenLoadInFlight_DoesNotCacheAndDisposesLoadedHandle` 测试覆盖;旧的第五轮审查中提到的该项中风险在当前代码中已收口。
|
||||||
|
- `MainMenuP0` 仍在 `Assets/FlowScope/Samples/MainMenuP0`,尚未迁移到 `Samples~`。
|
||||||
|
|
||||||
|
## Package / Editor 现状
|
||||||
|
|
||||||
|
当前项目仍是 `Assets/FlowScope` 内开发模式,不是标准 Unity Package 目录。
|
||||||
|
|
||||||
|
已确认缺口:
|
||||||
|
|
||||||
|
- 当前未发现根级或包级 `package.json`。
|
||||||
|
- 当前未发现 `Samples~`、`Tests~`、`Documentation~` 标准包目录。
|
||||||
|
- 当前未发现 `Assets/FlowScope/Editor` 或 `FlowScope.Editor.asmdef`。
|
||||||
|
- 当前还没有 package 安装说明覆盖本地 path、Git URL、项目内 Assets 开发模式三种路径。
|
||||||
|
|
||||||
|
第一阶段边界:
|
||||||
|
|
||||||
|
- 可以新增 package 结构方案文档和最小 package 元数据草案。
|
||||||
|
- 可以新增最小 Editor 诊断入口。
|
||||||
|
- 不在第一阶段直接搬迁 Runtime、Addressables、Samples 或 Tests。
|
||||||
|
- 不删除 `Assets/FlowScope` 当前开发模式。
|
||||||
|
|
||||||
|
## Addressables 验收缺口
|
||||||
|
|
||||||
|
当前 `FlowScope.Addressables` 程序集存在:
|
||||||
|
|
||||||
|
- `My project/Assets/FlowScope/Addressables/FlowScope.Addressables.asmdef`
|
||||||
|
- `My project/Assets/FlowScope/Addressables/AddressablesResourceBackend.cs`
|
||||||
|
- `My project/Assets/FlowScope/Addressables/AddressablesResourceService.cs`
|
||||||
|
|
||||||
|
当前 `AddressablesResourceBackendTests` 只验证:
|
||||||
|
|
||||||
|
- `Name_ReturnsAddressables`
|
||||||
|
|
||||||
|
缺失的真实验收:
|
||||||
|
|
||||||
|
- 使用真实 Addressables key 加载资源。
|
||||||
|
- 验证 `AddressablesResourceBackend.LoadAsync<T>` 成功返回真实资源。
|
||||||
|
- 验证 `Release<T>` 对已加载资源可重复进入释放流程且不产生业务层重复释放异常。
|
||||||
|
- 验证缺失 key 或类型不匹配时,错误信息能定位 key、类型和 backend。
|
||||||
|
- 如果自动化 PlayMode 准备成本过高,至少要有可重复人工验收步骤与结果记录。
|
||||||
|
|
||||||
|
## 当前验证证据
|
||||||
|
|
||||||
|
本轮已执行 generated csproj 编译验证:
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
dotnet build "My project\FlowScope.Runtime.csproj" --no-restore
|
||||||
|
dotnet build "My project\FlowScope.Tests.EditMode.csproj" --no-restore
|
||||||
|
dotnet build "My project\FlowScope.Tests.PlayMode.csproj" --no-restore
|
||||||
|
```
|
||||||
|
|
||||||
|
结果:
|
||||||
|
|
||||||
|
- `FlowScope.Runtime.csproj`:0 error。
|
||||||
|
- `FlowScope.Tests.EditMode.csproj`:0 error。
|
||||||
|
- `FlowScope.Tests.PlayMode.csproj`:0 error。
|
||||||
|
- 存在 Unity generated csproj 常见引用冲突 warning,主要涉及 `System.Net.Http`、`System.Security.Cryptography.Algorithms`、`System.ComponentModel.Annotations`。
|
||||||
|
|
||||||
|
未完成验证:
|
||||||
|
|
||||||
|
- 本轮未执行 Unity Test Runner 全量 EditMode。
|
||||||
|
- 本轮未执行 Unity Test Runner 全量 PlayMode。
|
||||||
|
- 本轮未执行 MainMenuP0 人工场景验收。
|
||||||
|
- 本轮未执行真实 Addressables load/release 验收。
|
||||||
|
|
||||||
|
## P2 第一阶段并行任务边界
|
||||||
|
|
||||||
|
### Task A:Package / 安装接入文档
|
||||||
|
|
||||||
|
Owned files:
|
||||||
|
|
||||||
|
- `docs/guides/flowscope-package-layout.md`
|
||||||
|
- `docs/guides/flowscope-installation.md`
|
||||||
|
- 可选:`docs/reviews/p2-phase1-baseline-audit.md` 的补充备注
|
||||||
|
|
||||||
|
Do not modify:
|
||||||
|
|
||||||
|
- `My project/Assets/FlowScope/Runtime`
|
||||||
|
- `My project/Assets/FlowScope/Addressables`
|
||||||
|
- `My project/Assets/FlowScope/Editor`
|
||||||
|
- `My project/Assets/FlowScope/Samples`
|
||||||
|
- `My project/Assets/FlowScope/Tests`
|
||||||
|
|
||||||
|
验收:
|
||||||
|
|
||||||
|
- 文档说明标准 Unity Package 目标结构。
|
||||||
|
- 文档说明第一阶段为什么暂不搬迁 Runtime/Samples/Tests。
|
||||||
|
- 安装文档覆盖本地 package path、Git URL、Assets 开发模式。
|
||||||
|
- Bootstrap 示例只使用当前真实 API 名称。
|
||||||
|
|
||||||
|
### Task B:Addressables 真实验收
|
||||||
|
|
||||||
|
Owned files:
|
||||||
|
|
||||||
|
- `My project/Assets/FlowScope/Tests/PlayMode/Resources/AddressablesResourceBackendTests.cs`
|
||||||
|
- 必要的最小测试资源及 `.meta`
|
||||||
|
- `docs/reviews/addressables-integration-acceptance.md`
|
||||||
|
|
||||||
|
Do not modify:
|
||||||
|
|
||||||
|
- `My project/Assets/FlowScope/Runtime`
|
||||||
|
- `My project/Assets/FlowScope/Addressables/AddressablesResourceBackend.cs`,除非测试证明当前实现存在真实 bug
|
||||||
|
- Package 文档与 Editor 文件
|
||||||
|
|
||||||
|
验收:
|
||||||
|
|
||||||
|
- 优先提供自动化 PlayMode 测试证明真实资源 load/release。
|
||||||
|
- 若自动化资源维护成本过高,必须提供可重复人工验收步骤、预期结果和未自动化原因。
|
||||||
|
- `dotnet build "My project\FlowScope.Tests.PlayMode.csproj" --no-restore` 保持 0 error。
|
||||||
|
|
||||||
|
### Task C:Editor 最小诊断入口
|
||||||
|
|
||||||
|
Owned files:
|
||||||
|
|
||||||
|
- `My project/Assets/FlowScope/Editor/FlowScope.Editor.asmdef`
|
||||||
|
- `My project/Assets/FlowScope/Editor/FlowScopeDiagnosticsWindow.cs`
|
||||||
|
- 必要 `.meta`
|
||||||
|
- 可选:`docs/guides/flowscope-editor-diagnostics.md`
|
||||||
|
|
||||||
|
Do not modify:
|
||||||
|
|
||||||
|
- Runtime、Addressables、Samples、Tests 实现文件。
|
||||||
|
- 不自动生成、移动、删除用户资源。
|
||||||
|
|
||||||
|
验收:
|
||||||
|
|
||||||
|
- Unity Editor 中有 `Tools/FlowScope/Diagnostics` 菜单入口。
|
||||||
|
- 窗口只检查并输出状态,不自动修复。
|
||||||
|
- 检查 Addressables package、Runtime asmdef、Addressables asmdef、MainMenuP0 关键场景和 prefab 是否存在。
|
||||||
|
- generated csproj 编译保持 0 error。
|
||||||
|
|
||||||
|
## 第一阶段完成 Gate
|
||||||
|
|
||||||
|
P2 第一阶段完成前必须满足:
|
||||||
|
|
||||||
|
1. `FlowScope.Runtime.csproj`、`FlowScope.Tests.EditMode.csproj`、`FlowScope.Tests.PlayMode.csproj` generated build 均为 0 error。
|
||||||
|
2. Unity Test Runner 全量 EditMode / PlayMode 至少有明确执行记录;若无法执行,必须记录原因。
|
||||||
|
3. Addressables adapter 有真实集成验收记录,不再只靠 `Name_ReturnsAddressables`。
|
||||||
|
4. 新增 package/editor 内容不破坏 `Assets/FlowScope` 当前开发模式。
|
||||||
|
5. 文档说明安装、接入、验证、已知限制和后续迁移边界。
|
||||||
|
6. 所有提交信息使用中文。
|
||||||
|
|
||||||
|
## 第一阶段之后
|
||||||
|
|
||||||
|
第一阶段完成后进入 P2 第二阶段:本地 package 试迁移。
|
||||||
|
|
||||||
|
第二阶段才开始创建真正 package 目录雏形并用本地 path 在干净 Unity 项目验证安装。Runtime、Addressables、Samples、Tests 的物理迁移应分批进行,不应在第一阶段一次性搬迁。
|
||||||
180
docs/reviews/p2-phase1-completion-report.md
Normal file
180
docs/reviews/p2-phase1-completion-report.md
Normal file
@@ -0,0 +1,180 @@
|
|||||||
|
# FlowScope P2 第一阶段完成报告
|
||||||
|
|
||||||
|
日期:2026-06-04
|
||||||
|
|
||||||
|
## 范围
|
||||||
|
|
||||||
|
本阶段根据 `docs/requirements/p2-package-editor-requirements.md` 执行第一轮 P2 准备工作。范围限定为:
|
||||||
|
|
||||||
|
- 锁定 P0/P1 当前基线。
|
||||||
|
- 建立 package 目标结构与迁移边界文档。
|
||||||
|
- 编写安装与最小接入文档。
|
||||||
|
- 补 Addressables 真实验收入口。
|
||||||
|
- 添加最小 Editor 诊断入口。
|
||||||
|
- 整理 MainMenuP0 样例说明。
|
||||||
|
|
||||||
|
本阶段没有重写 P0/P1 Runtime 生命周期,没有搬迁 `Assets/FlowScope`,没有创建正式 package 根目录。Addressables 项目设置由用户准备测试资源时生成,本轮仅接入测试、修复 backend 失败诊断并记录验收结果。
|
||||||
|
|
||||||
|
## 已完成
|
||||||
|
|
||||||
|
### 1. 基线审查
|
||||||
|
|
||||||
|
新增:
|
||||||
|
|
||||||
|
- `docs/reviews/p2-phase1-baseline-audit.md`
|
||||||
|
|
||||||
|
内容包括:
|
||||||
|
|
||||||
|
- 当前分支与工作树状态。
|
||||||
|
- P0/P1 Runtime、Addressables、Samples、Tests 的现状。
|
||||||
|
- Package / Editor 缺口。
|
||||||
|
- Addressables 真实验收缺口。
|
||||||
|
- P2 第一阶段并行任务边界。
|
||||||
|
- 第一阶段完成 Gate 和下一阶段方向。
|
||||||
|
|
||||||
|
### 2. Package 结构与安装文档
|
||||||
|
|
||||||
|
新增:
|
||||||
|
|
||||||
|
- `docs/guides/flowscope-package-layout.md`
|
||||||
|
- `docs/guides/flowscope-installation.md`
|
||||||
|
|
||||||
|
已覆盖:
|
||||||
|
|
||||||
|
- 目标 package 结构:`package.json`、`Runtime`、`Addressables`、`Editor`、`Samples~`、`Tests`、`Documentation~`。
|
||||||
|
- 第一阶段暂不搬迁 `Assets/FlowScope` 的原因。
|
||||||
|
- asmdef 依赖方向和禁止反向依赖。
|
||||||
|
- 本地 package path、Git URL、项目内 Assets 开发模式。
|
||||||
|
- 基于当前真实 API 的最小 Bootstrap 示例。
|
||||||
|
- 常见误用与当前限制。
|
||||||
|
|
||||||
|
### 3. Addressables 验收入口
|
||||||
|
|
||||||
|
新增:
|
||||||
|
|
||||||
|
- `docs/reviews/addressables-integration-acceptance.md`
|
||||||
|
|
||||||
|
结论:
|
||||||
|
|
||||||
|
- 当前项目有 `com.unity.addressables` 和 `FlowScope.Addressables` asmdef。
|
||||||
|
- 当前项目已有 `Assets/AddressableAssetsData`、默认 Addressables settings、`Default Local Group` 和可复用测试 key。
|
||||||
|
- 已新增真实 PlayMode 自动化验收,覆盖真实 `TextAsset` 加载、释放、缺失 key 诊断和类型不匹配诊断。
|
||||||
|
|
||||||
|
剩余缺口:
|
||||||
|
|
||||||
|
- 当前 Addressables 测试资源路径为 `Assets/FlowScope/Tests/PlayMode/Resources_moved/Fixtures/Addressables/FlowScopeAddressablesText.txt`,后续 package 迁移时应统一测试资源目录命名。
|
||||||
|
- `Assets/FlowScope/Tests/PlayMode/Resources/Fixtures` 当前只剩空目录 meta,属于提交前清理候选。
|
||||||
|
|
||||||
|
### 4. Editor 最小诊断入口
|
||||||
|
|
||||||
|
新增:
|
||||||
|
|
||||||
|
- `My project/Assets/FlowScope/Editor.meta`
|
||||||
|
- `My project/Assets/FlowScope/Editor/FlowScope.Editor.asmdef`
|
||||||
|
- `My project/Assets/FlowScope/Editor/FlowScope.Editor.asmdef.meta`
|
||||||
|
- `My project/Assets/FlowScope/Editor/FlowScopeDiagnosticsWindow.cs`
|
||||||
|
- `My project/Assets/FlowScope/Editor/FlowScopeDiagnosticsWindow.cs.meta`
|
||||||
|
|
||||||
|
能力:
|
||||||
|
|
||||||
|
- 菜单入口:`Tools/FlowScope/Diagnostics`。
|
||||||
|
- 只读检查,不自动生成、移动、删除或修复用户资源。
|
||||||
|
- 检查 `com.unity.addressables` package、Runtime asmdef、Addressables asmdef、MainMenuP0 scene、MainMenuPanel prefab。
|
||||||
|
- Addressables package 状态通过 `UnityEditor.PackageManager.Client.List(true)` 异步刷新,不阻塞 Editor。
|
||||||
|
|
||||||
|
### 5. MainMenuP0 样例说明
|
||||||
|
|
||||||
|
新增:
|
||||||
|
|
||||||
|
- `My project/Assets/FlowScope/Samples/MainMenuP0/README.md`
|
||||||
|
- `My project/Assets/FlowScope/Samples/MainMenuP0/README.md.meta`
|
||||||
|
|
||||||
|
内容包括:
|
||||||
|
|
||||||
|
- 样例目的。
|
||||||
|
- 当前仍保留在 `Assets/FlowScope/Samples/MainMenuP0` 的原因。
|
||||||
|
- 后续迁移到 `Samples~/MainMenuP0` 的候选路径。
|
||||||
|
- 运行方式、验收点、自动化测试位置和已知限制。
|
||||||
|
|
||||||
|
## 验证结果
|
||||||
|
|
||||||
|
### Unity batchmode 导入 / 编译
|
||||||
|
|
||||||
|
已执行:
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
& 'C:\Program Files\Unity\Hub\Editor\2022.3.62f2c1\Editor\Unity.exe' -batchmode -quit -projectPath 'C:\Users\13999\Documents\FlowScope\My project' -logFile 'C:\tmp\flowscope-p2-phase1-editor-compile.log'
|
||||||
|
```
|
||||||
|
|
||||||
|
结果:
|
||||||
|
|
||||||
|
- Unity 导入新增 Editor 文件。
|
||||||
|
- 生成 `My project\FlowScope.Editor.csproj`。
|
||||||
|
- 日志中未发现 `error CS`、`Compilation failed` 或 `Scripts have compiler errors`。
|
||||||
|
|
||||||
|
### generated csproj build
|
||||||
|
|
||||||
|
已执行:
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
dotnet build "My project\FlowScope.Editor.csproj" --no-restore
|
||||||
|
dotnet build "My project\FlowScope.Runtime.csproj" --no-restore
|
||||||
|
dotnet build "My project\FlowScope.Tests.EditMode.csproj" --no-restore
|
||||||
|
dotnet build "My project\FlowScope.Tests.PlayMode.csproj" --no-restore
|
||||||
|
```
|
||||||
|
|
||||||
|
结果:
|
||||||
|
|
||||||
|
- `FlowScope.Editor.csproj`:0 error。
|
||||||
|
- `FlowScope.Runtime.csproj`:0 error。
|
||||||
|
- `FlowScope.Tests.EditMode.csproj`:0 error。
|
||||||
|
- `FlowScope.Tests.PlayMode.csproj`:0 error。
|
||||||
|
- 仍有 Unity generated csproj 既有引用冲突 warning,主要涉及 `System.Net.Http`、`System.Security.Cryptography.Algorithms`、`System.ComponentModel.Annotations`、`System.Threading.Tasks.Extensions`。
|
||||||
|
|
||||||
|
### Unity Test Runner
|
||||||
|
|
||||||
|
已执行:
|
||||||
|
|
||||||
|
- 全量 EditMode。
|
||||||
|
- 全量 PlayMode。
|
||||||
|
|
||||||
|
结果:
|
||||||
|
|
||||||
|
- 用户反馈全量 EditMode / PlayMode 全绿。
|
||||||
|
- `AddressablesResourceBackendTests` 已覆盖真实 Addressables key:`FlowScope.Tests.Addressables.Text`。
|
||||||
|
- 缺失 key 和类型不匹配路径已通过可捕获 backend 诊断异常验收。
|
||||||
|
|
||||||
|
### 静态检查
|
||||||
|
|
||||||
|
已执行:
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
rg -n "<占位词扫描表达式>" <本阶段新增文档与 Editor 脚本>
|
||||||
|
git diff --check
|
||||||
|
```
|
||||||
|
|
||||||
|
结果:
|
||||||
|
|
||||||
|
- 未发现占位词命中。
|
||||||
|
- `git diff --check` 无 whitespace error。
|
||||||
|
|
||||||
|
## 未完成 Gate
|
||||||
|
|
||||||
|
以下事项不在本阶段内完成,但必须在 P2 后续阶段继续追踪:
|
||||||
|
|
||||||
|
1. 尚未创建正式 package 根目录和 `package.json` 文件。
|
||||||
|
2. 尚未在干净 Unity 项目中验证本地 package path 或 Git URL 安装。
|
||||||
|
3. 尚未实际打开 `Tools/FlowScope/Diagnostics` 做人工 UI 检查。
|
||||||
|
4. Addressables 测试资源目录命名仍需在 package 迁移前统一。
|
||||||
|
|
||||||
|
## 下一阶段建议
|
||||||
|
|
||||||
|
进入 P2 第二阶段:本地 package 试迁移。
|
||||||
|
|
||||||
|
建议顺序:
|
||||||
|
|
||||||
|
1. 创建 `Packages/com.flowscope.gamecore` 和最小 `package.json`。
|
||||||
|
2. 先复制或镜像 Runtime 到 package 目录,不删除 `Assets/FlowScope`。
|
||||||
|
3. 在当前项目和一个干净 Unity 项目中验证本地 package path。
|
||||||
|
4. 整理 Addressables 测试资源目录,再迁移 package 内 Tests。
|
||||||
|
5. 再考虑 `Samples~/MainMenuP0` 和 Tests 的分批迁移。
|
||||||
Reference in New Issue
Block a user