diff --git a/My project/Assets/AddressableAssetsData.meta b/My project/Assets/AddressableAssetsData.meta new file mode 100644 index 0000000..f37dde8 --- /dev/null +++ b/My project/Assets/AddressableAssetsData.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: f38893bbcb86bbb4cb6705a205682113 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/My project/Assets/AddressableAssetsData/AddressableAssetSettings.asset b/My project/Assets/AddressableAssetsData/AddressableAssetSettings.asset new file mode 100644 index 0000000..fa64eb5 --- /dev/null +++ b/My project/Assets/AddressableAssetsData/AddressableAssetSettings.asset @@ -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: + 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 diff --git a/My project/Assets/AddressableAssetsData/AddressableAssetSettings.asset.meta b/My project/Assets/AddressableAssetsData/AddressableAssetSettings.asset.meta new file mode 100644 index 0000000..686866c --- /dev/null +++ b/My project/Assets/AddressableAssetsData/AddressableAssetSettings.asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: c67335a4d75941d4881a64ebedebbce8 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/My project/Assets/AddressableAssetsData/AssetGroupTemplates.meta b/My project/Assets/AddressableAssetsData/AssetGroupTemplates.meta new file mode 100644 index 0000000..b074a48 --- /dev/null +++ b/My project/Assets/AddressableAssetsData/AssetGroupTemplates.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: f9bc8a0d61c2e9e4cb5557b7586ac071 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/My project/Assets/AddressableAssetsData/AssetGroupTemplates/Packed Assets.asset b/My project/Assets/AddressableAssetsData/AssetGroupTemplates/Packed Assets.asset new file mode 100644 index 0000000..0a3d8a9 --- /dev/null +++ b/My project/Assets/AddressableAssetsData/AssetGroupTemplates/Packed Assets.asset @@ -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 diff --git a/My project/Assets/AddressableAssetsData/AssetGroupTemplates/Packed Assets.asset.meta b/My project/Assets/AddressableAssetsData/AssetGroupTemplates/Packed Assets.asset.meta new file mode 100644 index 0000000..aca3b56 --- /dev/null +++ b/My project/Assets/AddressableAssetsData/AssetGroupTemplates/Packed Assets.asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: b8b58afcc1b4e244db44625e28a47b36 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/My project/Assets/AddressableAssetsData/AssetGroups.meta b/My project/Assets/AddressableAssetsData/AssetGroups.meta new file mode 100644 index 0000000..2ad9cc3 --- /dev/null +++ b/My project/Assets/AddressableAssetsData/AssetGroups.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 0ee6e7ad669b89342a6dd8bd0dd3bf84 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/My project/Assets/AddressableAssetsData/AssetGroups/Built In Data.asset b/My project/Assets/AddressableAssetsData/AssetGroups/Built In Data.asset new file mode 100644 index 0000000..ab8be0a --- /dev/null +++ b/My project/Assets/AddressableAssetsData/AssetGroups/Built In Data.asset @@ -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} diff --git a/My project/Assets/AddressableAssetsData/AssetGroups/Built In Data.asset.meta b/My project/Assets/AddressableAssetsData/AssetGroups/Built In Data.asset.meta new file mode 100644 index 0000000..5a10d87 --- /dev/null +++ b/My project/Assets/AddressableAssetsData/AssetGroups/Built In Data.asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: cb21c9bd6efb15940aa0cc136e03ec0f +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/My project/Assets/AddressableAssetsData/AssetGroups/Default Local Group.asset b/My project/Assets/AddressableAssetsData/AssetGroups/Default Local Group.asset new file mode 100644 index 0000000..8883749 --- /dev/null +++ b/My project/Assets/AddressableAssetsData/AssetGroups/Default Local Group.asset @@ -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} diff --git a/My project/Assets/AddressableAssetsData/AssetGroups/Default Local Group.asset.meta b/My project/Assets/AddressableAssetsData/AssetGroups/Default Local Group.asset.meta new file mode 100644 index 0000000..2623a51 --- /dev/null +++ b/My project/Assets/AddressableAssetsData/AssetGroups/Default Local Group.asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: ad50471a1bc339946b9dd02f9fd1b735 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/My project/Assets/AddressableAssetsData/AssetGroups/Schemas.meta b/My project/Assets/AddressableAssetsData/AssetGroups/Schemas.meta new file mode 100644 index 0000000..a1add0b --- /dev/null +++ b/My project/Assets/AddressableAssetsData/AssetGroups/Schemas.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: e16dab92ed201134eb1bdc0159aa569c +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/My project/Assets/AddressableAssetsData/AssetGroups/Schemas/Built In Data_PlayerDataGroupSchema.asset b/My project/Assets/AddressableAssetsData/AssetGroups/Schemas/Built In Data_PlayerDataGroupSchema.asset new file mode 100644 index 0000000..01f8893 --- /dev/null +++ b/My project/Assets/AddressableAssetsData/AssetGroups/Schemas/Built In Data_PlayerDataGroupSchema.asset @@ -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 diff --git a/My project/Assets/AddressableAssetsData/AssetGroups/Schemas/Built In Data_PlayerDataGroupSchema.asset.meta b/My project/Assets/AddressableAssetsData/AssetGroups/Schemas/Built In Data_PlayerDataGroupSchema.asset.meta new file mode 100644 index 0000000..8b640e1 --- /dev/null +++ b/My project/Assets/AddressableAssetsData/AssetGroups/Schemas/Built In Data_PlayerDataGroupSchema.asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: a7177bba2e94b2a40b5302657833b7d8 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/My project/Assets/AddressableAssetsData/AssetGroups/Schemas/Default Local Group_BundledAssetGroupSchema.asset b/My project/Assets/AddressableAssetsData/AssetGroups/Schemas/Default Local Group_BundledAssetGroupSchema.asset new file mode 100644 index 0000000..7b78294 --- /dev/null +++ b/My project/Assets/AddressableAssetsData/AssetGroups/Schemas/Default Local Group_BundledAssetGroupSchema.asset @@ -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 diff --git a/My project/Assets/AddressableAssetsData/AssetGroups/Schemas/Default Local Group_BundledAssetGroupSchema.asset.meta b/My project/Assets/AddressableAssetsData/AssetGroups/Schemas/Default Local Group_BundledAssetGroupSchema.asset.meta new file mode 100644 index 0000000..2ed6360 --- /dev/null +++ b/My project/Assets/AddressableAssetsData/AssetGroups/Schemas/Default Local Group_BundledAssetGroupSchema.asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: ee832a18fbe81564d9e5cb06031890c9 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/My project/Assets/AddressableAssetsData/AssetGroups/Schemas/Default Local Group_ContentUpdateGroupSchema.asset b/My project/Assets/AddressableAssetsData/AssetGroups/Schemas/Default Local Group_ContentUpdateGroupSchema.asset new file mode 100644 index 0000000..383ae75 --- /dev/null +++ b/My project/Assets/AddressableAssetsData/AssetGroups/Schemas/Default Local Group_ContentUpdateGroupSchema.asset @@ -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 diff --git a/My project/Assets/AddressableAssetsData/AssetGroups/Schemas/Default Local Group_ContentUpdateGroupSchema.asset.meta b/My project/Assets/AddressableAssetsData/AssetGroups/Schemas/Default Local Group_ContentUpdateGroupSchema.asset.meta new file mode 100644 index 0000000..0e7f65f --- /dev/null +++ b/My project/Assets/AddressableAssetsData/AssetGroups/Schemas/Default Local Group_ContentUpdateGroupSchema.asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 4a894c5b933ed1040b799de00906a668 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/My project/Assets/AddressableAssetsData/DataBuilders.meta b/My project/Assets/AddressableAssetsData/DataBuilders.meta new file mode 100644 index 0000000..1704809 --- /dev/null +++ b/My project/Assets/AddressableAssetsData/DataBuilders.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: cfb9fefe94d254d44a8f7d5f6b6d22ef +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/My project/Assets/AddressableAssetsData/DataBuilders/BuildScriptFastMode.asset b/My project/Assets/AddressableAssetsData/DataBuilders/BuildScriptFastMode.asset new file mode 100644 index 0000000..f567c44 --- /dev/null +++ b/My project/Assets/AddressableAssetsData/DataBuilders/BuildScriptFastMode.asset @@ -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 diff --git a/My project/Assets/AddressableAssetsData/DataBuilders/BuildScriptFastMode.asset.meta b/My project/Assets/AddressableAssetsData/DataBuilders/BuildScriptFastMode.asset.meta new file mode 100644 index 0000000..412456b --- /dev/null +++ b/My project/Assets/AddressableAssetsData/DataBuilders/BuildScriptFastMode.asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: ca567c17e92783c4d9869e540320d76f +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/My project/Assets/AddressableAssetsData/DataBuilders/BuildScriptPackedMode.asset b/My project/Assets/AddressableAssetsData/DataBuilders/BuildScriptPackedMode.asset new file mode 100644 index 0000000..008cc0a --- /dev/null +++ b/My project/Assets/AddressableAssetsData/DataBuilders/BuildScriptPackedMode.asset @@ -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 diff --git a/My project/Assets/AddressableAssetsData/DataBuilders/BuildScriptPackedMode.asset.meta b/My project/Assets/AddressableAssetsData/DataBuilders/BuildScriptPackedMode.asset.meta new file mode 100644 index 0000000..ff33a87 --- /dev/null +++ b/My project/Assets/AddressableAssetsData/DataBuilders/BuildScriptPackedMode.asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 7f9d8997bb36a7f44aabfe404cd182f8 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/My project/Assets/AddressableAssetsData/DataBuilders/BuildScriptPackedPlayMode.asset b/My project/Assets/AddressableAssetsData/DataBuilders/BuildScriptPackedPlayMode.asset new file mode 100644 index 0000000..ba61f06 --- /dev/null +++ b/My project/Assets/AddressableAssetsData/DataBuilders/BuildScriptPackedPlayMode.asset @@ -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 diff --git a/My project/Assets/AddressableAssetsData/DataBuilders/BuildScriptPackedPlayMode.asset.meta b/My project/Assets/AddressableAssetsData/DataBuilders/BuildScriptPackedPlayMode.asset.meta new file mode 100644 index 0000000..d05a68c --- /dev/null +++ b/My project/Assets/AddressableAssetsData/DataBuilders/BuildScriptPackedPlayMode.asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: e268c485b53ab6547bfc8919ae0f900d +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/My project/Assets/AddressableAssetsData/DataBuilders/BuildScriptVirtualMode.asset b/My project/Assets/AddressableAssetsData/DataBuilders/BuildScriptVirtualMode.asset new file mode 100644 index 0000000..0b19cef --- /dev/null +++ b/My project/Assets/AddressableAssetsData/DataBuilders/BuildScriptVirtualMode.asset @@ -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 diff --git a/My project/Assets/AddressableAssetsData/DataBuilders/BuildScriptVirtualMode.asset.meta b/My project/Assets/AddressableAssetsData/DataBuilders/BuildScriptVirtualMode.asset.meta new file mode 100644 index 0000000..3e97d9c --- /dev/null +++ b/My project/Assets/AddressableAssetsData/DataBuilders/BuildScriptVirtualMode.asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 9c179b50ddf0f4a49ba95a61fce40d9d +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/My project/Assets/AddressableAssetsData/DefaultObject.asset b/My project/Assets/AddressableAssetsData/DefaultObject.asset new file mode 100644 index 0000000..6eb160b --- /dev/null +++ b/My project/Assets/AddressableAssetsData/DefaultObject.asset @@ -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 diff --git a/My project/Assets/AddressableAssetsData/DefaultObject.asset.meta b/My project/Assets/AddressableAssetsData/DefaultObject.asset.meta new file mode 100644 index 0000000..5003748 --- /dev/null +++ b/My project/Assets/AddressableAssetsData/DefaultObject.asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: f9fd621d22433a64aa0741deda630f70 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/My project/Assets/FlowScope/Addressables/AddressablesResourceBackend.cs b/My project/Assets/FlowScope/Addressables/AddressablesResourceBackend.cs index 8ef9b1b..c59d7f8 100644 --- a/My project/Assets/FlowScope/Addressables/AddressablesResourceBackend.cs +++ b/My project/Assets/FlowScope/Addressables/AddressablesResourceBackend.cs @@ -2,6 +2,7 @@ using System; using System.Threading; using System.Threading.Tasks; using FlowScope.Resources; +using UnityEngine.ResourceManagement.AsyncOperations; using UnityAddressables = UnityEngine.AddressableAssets.Addressables; namespace FlowScope.Addressables @@ -13,8 +14,30 @@ namespace FlowScope.Addressables public async Task LoadAsync(string key, CancellationToken cancellationToken) where T : class { + cancellationToken.ThrowIfCancellationRequested(); + var handle = UnityAddressables.LoadAssetAsync(key); - await WaitWithCancellation(handle.Task, cancellationToken); + try + { + 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; } diff --git a/My project/Assets/FlowScope/Editor.meta b/My project/Assets/FlowScope/Editor.meta new file mode 100644 index 0000000..dcb1904 --- /dev/null +++ b/My project/Assets/FlowScope/Editor.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 5b5d64c7b9f94d1db437f60ce8944a8f +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/My project/Assets/FlowScope/Editor/FlowScope.Editor.asmdef b/My project/Assets/FlowScope/Editor/FlowScope.Editor.asmdef new file mode 100644 index 0000000..63cff93 --- /dev/null +++ b/My project/Assets/FlowScope/Editor/FlowScope.Editor.asmdef @@ -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 +} diff --git a/My project/Assets/FlowScope/Editor/FlowScope.Editor.asmdef.meta b/My project/Assets/FlowScope/Editor/FlowScope.Editor.asmdef.meta new file mode 100644 index 0000000..46174e8 --- /dev/null +++ b/My project/Assets/FlowScope/Editor/FlowScope.Editor.asmdef.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: f3ee591f10b84cf7a39d50219698312e +AssemblyDefinitionImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/My project/Assets/FlowScope/Editor/FlowScopeDiagnosticsWindow.cs b/My project/Assets/FlowScope/Editor/FlowScopeDiagnosticsWindow.cs new file mode 100644 index 0000000..420b7fd --- /dev/null +++ b/My project/Assets/FlowScope/Editor/FlowScopeDiagnosticsWindow.cs @@ -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 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 _assetResults = new List(); + + 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("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(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, + } + } +} diff --git a/My project/Assets/FlowScope/Editor/FlowScopeDiagnosticsWindow.cs.meta b/My project/Assets/FlowScope/Editor/FlowScopeDiagnosticsWindow.cs.meta new file mode 100644 index 0000000..7afa200 --- /dev/null +++ b/My project/Assets/FlowScope/Editor/FlowScopeDiagnosticsWindow.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 599ef528ec0c4331a77311f6ef7ef56b +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/My project/Assets/FlowScope/Samples/MainMenuP0/README.md b/My project/Assets/FlowScope/Samples/MainMenuP0/README.md new file mode 100644 index 0000000..93d3f88 --- /dev/null +++ b/My project/Assets/FlowScope/Samples/MainMenuP0/README.md @@ -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 已完成迁移。 diff --git a/My project/Assets/FlowScope/Samples/MainMenuP0/README.md.meta b/My project/Assets/FlowScope/Samples/MainMenuP0/README.md.meta new file mode 100644 index 0000000..2146550 --- /dev/null +++ b/My project/Assets/FlowScope/Samples/MainMenuP0/README.md.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 9df9d94a34fa4ec2b16df2c8cf2a651b +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/My project/Assets/FlowScope/Tests/PlayMode/Resources/AddressablesResourceBackendTests.cs b/My project/Assets/FlowScope/Tests/PlayMode/Resources/AddressablesResourceBackendTests.cs index d90986f..d07699b 100644 --- a/My project/Assets/FlowScope/Tests/PlayMode/Resources/AddressablesResourceBackendTests.cs +++ b/My project/Assets/FlowScope/Tests/PlayMode/Resources/AddressablesResourceBackendTests.cs @@ -1,10 +1,21 @@ +using System; +using System.Collections; +using System.Threading; +using System.Threading.Tasks; using FlowScope.Addressables; +using FlowScope.Resources; using NUnit.Framework; +using UnityEngine; +using UnityEngine.TestTools; namespace FlowScope.Tests.PlayMode.Resources { 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] public void Name_ReturnsAddressables() { @@ -12,5 +23,149 @@ namespace FlowScope.Tests.PlayMode.Resources Assert.AreEqual("Addressables", backend.Name); } + + [UnityTest] + public IEnumerator LoadAsync_WithRealTextAssetKey_LoadsTextAsset() + { + var backend = new AddressablesResourceBackend(); + + TextAsset asset = null; + yield return AwaitTask(backend.LoadAsync( + 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 handle = null; + yield return AwaitTask(service.LoadAsync( + 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(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(TextKey, cancellation.Token), + caught => exception = caught); + + Assert.That(exception, Is.TypeOf()); + } + + [UnityTest] + public IEnumerator LoadAsync_WithMissingKey_ThrowsDiagnosticException() + { + var backend = new AddressablesResourceBackend(); + + Exception exception = null; + yield return AwaitExpectedFailure( + () => backend.LoadAsync(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(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( + Task task, + Action 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( + Func> startTask, + Action onException) + { + var ignoreFailingMessages = LogAssert.ignoreFailingMessages; + Task 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."); + } } } diff --git a/My project/Assets/FlowScope/Tests/PlayMode/Resources_moved.meta b/My project/Assets/FlowScope/Tests/PlayMode/Resources_moved.meta new file mode 100644 index 0000000..51d2828 --- /dev/null +++ b/My project/Assets/FlowScope/Tests/PlayMode/Resources_moved.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 20098efc5dc925a4194600eea4969d68 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/My project/Assets/FlowScope/Tests/PlayMode/Resources_moved/Fixtures.meta b/My project/Assets/FlowScope/Tests/PlayMode/Resources_moved/Fixtures.meta new file mode 100644 index 0000000..73644a4 --- /dev/null +++ b/My project/Assets/FlowScope/Tests/PlayMode/Resources_moved/Fixtures.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 1acac9035afee5b408d7a7d78b2a1b0a +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/My project/Assets/FlowScope/Tests/PlayMode/Resources_moved/Fixtures/Addressables.meta b/My project/Assets/FlowScope/Tests/PlayMode/Resources_moved/Fixtures/Addressables.meta new file mode 100644 index 0000000..d3ab966 --- /dev/null +++ b/My project/Assets/FlowScope/Tests/PlayMode/Resources_moved/Fixtures/Addressables.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 52d2a24f37dccf346b5e0c6a34585514 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/My project/Assets/FlowScope/Tests/PlayMode/Resources_moved/Fixtures/Addressables/FlowScopeAddressablesText.txt b/My project/Assets/FlowScope/Tests/PlayMode/Resources_moved/Fixtures/Addressables/FlowScopeAddressablesText.txt new file mode 100644 index 0000000..ead420f --- /dev/null +++ b/My project/Assets/FlowScope/Tests/PlayMode/Resources_moved/Fixtures/Addressables/FlowScopeAddressablesText.txt @@ -0,0 +1 @@ +FlowScope Addressables Acceptance \ No newline at end of file diff --git a/My project/Assets/FlowScope/Tests/PlayMode/Resources_moved/Fixtures/Addressables/FlowScopeAddressablesText.txt.meta b/My project/Assets/FlowScope/Tests/PlayMode/Resources_moved/Fixtures/Addressables/FlowScopeAddressablesText.txt.meta new file mode 100644 index 0000000..0c03cee --- /dev/null +++ b/My project/Assets/FlowScope/Tests/PlayMode/Resources_moved/Fixtures/Addressables/FlowScopeAddressablesText.txt.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 7e04733ce9dab74459592d3bd99fcafd +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/My project/ProjectSettings/EditorBuildSettings.asset b/My project/ProjectSettings/EditorBuildSettings.asset index 0147887..6bff890 100644 --- a/My project/ProjectSettings/EditorBuildSettings.asset +++ b/My project/ProjectSettings/EditorBuildSettings.asset @@ -5,4 +5,5 @@ EditorBuildSettings: m_ObjectHideFlags: 0 serializedVersion: 2 m_Scenes: [] - m_configObjects: {} + m_configObjects: + com.unity.addressableassets: {fileID: 11400000, guid: f9fd621d22433a64aa0741deda630f70, type: 2} diff --git a/docs/guides/flowscope-installation.md b/docs/guides/flowscope-installation.md new file mode 100644 index 0000000..436c91f --- /dev/null +++ b/docs/guides/flowscope-installation.md @@ -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( + "example.json", + new InMemoryConfigSource( + new Dictionary + { + { "example.json", "[{\"Id\":1,\"Name\":\"Start\"}]" } + }, + "Bootstrap"), + new JsonConfigParser()) + }); + + var saveService = new SaveService( + new PlayerPrefsSaveStorage(), + new JsonSaveSerializer()); + + var audioService = gameObject + .AddComponent() + .Initialize(resources); + + _root.RegisterInstance(_root); + _root.RegisterInstance(configProvider); + _root.RegisterInstance(resources); + _root.RegisterInstance(saveService); + _root.RegisterInstance(audioService); + _root.RegisterInstance(uiManager); + _root.RegisterInstance(_preloadService); + _root.RegisterInstance(screenNavigator); + _root.RegisterTransient(_ => new ExampleFeature(screenNavigator)); + + await _gameFlow.StartupAsync(_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(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。 diff --git a/docs/guides/flowscope-package-layout.md b/docs/guides/flowscope-package-layout.md new file mode 100644 index 0000000..716a7b8 --- /dev/null +++ b/docs/guides/flowscope-package-layout.md @@ -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 和关键示例路径;如果某一步失败,应先回到该步骤修复,不继续叠加后续搬迁。 diff --git a/docs/reviews/addressables-integration-acceptance.md b/docs/reviews/addressables-integration-acceptance.md new file mode 100644 index 0000000..f27c1bb --- /dev/null +++ b/docs/reviews/addressables-integration-acceptance.md @@ -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` 能通过真实 key 加载真实 `TextAsset`。 +- `AddressablesResourceBackend.Release` 能释放已加载资源。 +- 缺失 key 会产生可捕获诊断异常,并包含缺失 key。 +- 类型不匹配会产生可捕获诊断异常,并包含 key 和请求类型。 + +本轮同时修复了 `AddressablesResourceBackend.LoadAsync` 的失败状态处理:当 Addressables handle 存在 `OperationException`、`Status` 不是 `Succeeded` 或 `Result` 为 `null` 时,backend 会抛出包含 key、请求类型和 backend 名称的 `InvalidOperationException`。 + +## 验收结果 + +用户已在 Unity Test Runner 中执行全量 EditMode / PlayMode,结果全绿。 + +本轮针对 Addressables 集成用例的最终结果: + +| 项目 | 结果 | 证据 | +| --- | --- | --- | +| 真实 `LoadAsync` | 通过 | `FlowScope.Tests.Addressables.Text` 加载真实 `TextAsset` | +| `Release` | 通过 | 成功释放已加载资源,未产生未处理日志 | +| 缺失 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 和类型不匹配诊断。 diff --git a/docs/reviews/p2-phase1-baseline-audit.md b/docs/reviews/p2-phase1-baseline-audit.md new file mode 100644 index 0000000..d825db6 --- /dev/null +++ b/docs/reviews/p2-phase1-baseline-audit.md @@ -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()` 已包含 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` 成功返回真实资源。 +- 验证 `Release` 对已加载资源可重复进入释放流程且不产生业务层重复释放异常。 +- 验证缺失 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 的物理迁移应分批进行,不应在第一阶段一次性搬迁。 diff --git a/docs/reviews/p2-phase1-completion-report.md b/docs/reviews/p2-phase1-completion-report.md new file mode 100644 index 0000000..773169c --- /dev/null +++ b/docs/reviews/p2-phase1-completion-report.md @@ -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 的分批迁移。