From ac8fcc3c2438d8699d6baa45fd93e3f6ecca1f32 Mon Sep 17 00:00:00 2001 From: "JSD\\13999" <1399945104@qq.com> Date: Thu, 4 Jun 2026 20:16:28 +0800 Subject: [PATCH] =?UTF-8?q?=E9=95=9C=E5=83=8F=20MainMenuP0=20=E5=8C=85?= =?UTF-8?q?=E6=A0=B7=E4=BE=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Packages/com.flowscope.gamecore/README.md | 1 + Packages/com.flowscope.gamecore/Samples~.meta | 8 + .../Samples~/MainMenuP0.meta | 8 + .../Samples~/MainMenuP0/Configs.meta | 8 + .../MainMenuP0/Configs/player_start.json | 3 + .../MainMenuP0/Configs/player_start.json.meta | 7 + .../FlowScope.Samples.MainMenuP0.asmdef | 17 + .../FlowScope.Samples.MainMenuP0.asmdef.meta | 7 + .../Samples~/MainMenuP0/Prefabs.meta | 8 + .../MainMenuP0/Prefabs/MainMenuPanel.prefab | 453 ++++++++++++++++++ .../Prefabs/MainMenuPanel.prefab.meta | 7 + .../Samples~/MainMenuP0/README.md | 44 ++ .../Samples~/MainMenuP0/README.md.meta | 7 + .../Samples~/MainMenuP0/Scenes.meta | 8 + .../MainMenuP0/Scenes/MainMenuP0.unity | 440 +++++++++++++++++ .../MainMenuP0/Scenes/MainMenuP0.unity.meta | 7 + .../Samples~/MainMenuP0/Scripts.meta | 8 + .../MainMenuP0/Scripts/GameBootstrap.cs | 303 ++++++++++++ .../MainMenuP0/Scripts/GameBootstrap.cs.meta | 11 + .../MainMenuP0/Scripts/MainMenuFeature.cs | 45 ++ .../Scripts/MainMenuFeature.cs.meta | 11 + .../MainMenuP0/Scripts/MainMenuPanel.cs | 62 +++ .../MainMenuP0/Scripts/MainMenuPanel.cs.meta | 11 + .../MainMenuP0/Scripts/MainMenuViewModel.cs | 24 + .../Scripts/MainMenuViewModel.cs.meta | 11 + .../Samples~/MainMenuP0/Scripts/PlayerData.cs | 9 + .../MainMenuP0/Scripts/PlayerData.cs.meta | 11 + Packages/com.flowscope.gamecore/package.json | 9 +- .../p2-phase2-package-preview-report.md | 2 +- 29 files changed, 1548 insertions(+), 2 deletions(-) create mode 100644 Packages/com.flowscope.gamecore/Samples~.meta create mode 100644 Packages/com.flowscope.gamecore/Samples~/MainMenuP0.meta create mode 100644 Packages/com.flowscope.gamecore/Samples~/MainMenuP0/Configs.meta create mode 100644 Packages/com.flowscope.gamecore/Samples~/MainMenuP0/Configs/player_start.json create mode 100644 Packages/com.flowscope.gamecore/Samples~/MainMenuP0/Configs/player_start.json.meta create mode 100644 Packages/com.flowscope.gamecore/Samples~/MainMenuP0/FlowScope.Samples.MainMenuP0.asmdef create mode 100644 Packages/com.flowscope.gamecore/Samples~/MainMenuP0/FlowScope.Samples.MainMenuP0.asmdef.meta create mode 100644 Packages/com.flowscope.gamecore/Samples~/MainMenuP0/Prefabs.meta create mode 100644 Packages/com.flowscope.gamecore/Samples~/MainMenuP0/Prefabs/MainMenuPanel.prefab create mode 100644 Packages/com.flowscope.gamecore/Samples~/MainMenuP0/Prefabs/MainMenuPanel.prefab.meta create mode 100644 Packages/com.flowscope.gamecore/Samples~/MainMenuP0/README.md create mode 100644 Packages/com.flowscope.gamecore/Samples~/MainMenuP0/README.md.meta create mode 100644 Packages/com.flowscope.gamecore/Samples~/MainMenuP0/Scenes.meta create mode 100644 Packages/com.flowscope.gamecore/Samples~/MainMenuP0/Scenes/MainMenuP0.unity create mode 100644 Packages/com.flowscope.gamecore/Samples~/MainMenuP0/Scenes/MainMenuP0.unity.meta create mode 100644 Packages/com.flowscope.gamecore/Samples~/MainMenuP0/Scripts.meta create mode 100644 Packages/com.flowscope.gamecore/Samples~/MainMenuP0/Scripts/GameBootstrap.cs create mode 100644 Packages/com.flowscope.gamecore/Samples~/MainMenuP0/Scripts/GameBootstrap.cs.meta create mode 100644 Packages/com.flowscope.gamecore/Samples~/MainMenuP0/Scripts/MainMenuFeature.cs create mode 100644 Packages/com.flowscope.gamecore/Samples~/MainMenuP0/Scripts/MainMenuFeature.cs.meta create mode 100644 Packages/com.flowscope.gamecore/Samples~/MainMenuP0/Scripts/MainMenuPanel.cs create mode 100644 Packages/com.flowscope.gamecore/Samples~/MainMenuP0/Scripts/MainMenuPanel.cs.meta create mode 100644 Packages/com.flowscope.gamecore/Samples~/MainMenuP0/Scripts/MainMenuViewModel.cs create mode 100644 Packages/com.flowscope.gamecore/Samples~/MainMenuP0/Scripts/MainMenuViewModel.cs.meta create mode 100644 Packages/com.flowscope.gamecore/Samples~/MainMenuP0/Scripts/PlayerData.cs create mode 100644 Packages/com.flowscope.gamecore/Samples~/MainMenuP0/Scripts/PlayerData.cs.meta diff --git a/Packages/com.flowscope.gamecore/README.md b/Packages/com.flowscope.gamecore/README.md index 1b2e533..f064db7 100644 --- a/Packages/com.flowscope.gamecore/README.md +++ b/Packages/com.flowscope.gamecore/README.md @@ -34,5 +34,6 @@ Add the package to the consuming project's `Packages/manifest.json`: - `FlowScope.Runtime` references `R3.Unity`. - `FlowScope.Addressables` references `FlowScope.Runtime`, `Unity.Addressables`, and `Unity.ResourceManager`. - `FlowScope.Editor` is Editor-only and only provides read-only diagnostics. +- `Samples~/MainMenuP0` is a package sample candidate mirrored from the project-internal MainMenuP0 sample. Runtime must not depend on Addressables, Editor, Samples, or Tests. diff --git a/Packages/com.flowscope.gamecore/Samples~.meta b/Packages/com.flowscope.gamecore/Samples~.meta new file mode 100644 index 0000000..62c9458 --- /dev/null +++ b/Packages/com.flowscope.gamecore/Samples~.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 49d9c024747f4af0b560e21d931a7816 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.flowscope.gamecore/Samples~/MainMenuP0.meta b/Packages/com.flowscope.gamecore/Samples~/MainMenuP0.meta new file mode 100644 index 0000000..cf2a6e0 --- /dev/null +++ b/Packages/com.flowscope.gamecore/Samples~/MainMenuP0.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: dffe7921ed374f83b8077b49644bc744 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.flowscope.gamecore/Samples~/MainMenuP0/Configs.meta b/Packages/com.flowscope.gamecore/Samples~/MainMenuP0/Configs.meta new file mode 100644 index 0000000..da5ef59 --- /dev/null +++ b/Packages/com.flowscope.gamecore/Samples~/MainMenuP0/Configs.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 76085fdd1da44ab08b69395b17064b59 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.flowscope.gamecore/Samples~/MainMenuP0/Configs/player_start.json b/Packages/com.flowscope.gamecore/Samples~/MainMenuP0/Configs/player_start.json new file mode 100644 index 0000000..a65d073 --- /dev/null +++ b/Packages/com.flowscope.gamecore/Samples~/MainMenuP0/Configs/player_start.json @@ -0,0 +1,3 @@ +{ + "Gold": 0 +} diff --git a/Packages/com.flowscope.gamecore/Samples~/MainMenuP0/Configs/player_start.json.meta b/Packages/com.flowscope.gamecore/Samples~/MainMenuP0/Configs/player_start.json.meta new file mode 100644 index 0000000..2a6786a --- /dev/null +++ b/Packages/com.flowscope.gamecore/Samples~/MainMenuP0/Configs/player_start.json.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: bd391f0c499b42a3a264d0a9de2483f8 +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.flowscope.gamecore/Samples~/MainMenuP0/FlowScope.Samples.MainMenuP0.asmdef b/Packages/com.flowscope.gamecore/Samples~/MainMenuP0/FlowScope.Samples.MainMenuP0.asmdef new file mode 100644 index 0000000..96b2c6e --- /dev/null +++ b/Packages/com.flowscope.gamecore/Samples~/MainMenuP0/FlowScope.Samples.MainMenuP0.asmdef @@ -0,0 +1,17 @@ +{ + "name": "FlowScope.Samples.MainMenuP0", + "rootNamespace": "FlowScope.Samples.MainMenuP0", + "references": [ + "FlowScope.Runtime", + "R3.Unity" + ], + "includePlatforms": [], + "excludePlatforms": [], + "allowUnsafeCode": false, + "overrideReferences": false, + "precompiledReferences": [], + "autoReferenced": true, + "defineConstraints": [], + "versionDefines": [], + "noEngineReferences": false +} diff --git a/Packages/com.flowscope.gamecore/Samples~/MainMenuP0/FlowScope.Samples.MainMenuP0.asmdef.meta b/Packages/com.flowscope.gamecore/Samples~/MainMenuP0/FlowScope.Samples.MainMenuP0.asmdef.meta new file mode 100644 index 0000000..dd25658 --- /dev/null +++ b/Packages/com.flowscope.gamecore/Samples~/MainMenuP0/FlowScope.Samples.MainMenuP0.asmdef.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: eb3b4bf3dfdc46e4a9352fc2203bbfd4 +AssemblyDefinitionImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.flowscope.gamecore/Samples~/MainMenuP0/Prefabs.meta b/Packages/com.flowscope.gamecore/Samples~/MainMenuP0/Prefabs.meta new file mode 100644 index 0000000..1b361d5 --- /dev/null +++ b/Packages/com.flowscope.gamecore/Samples~/MainMenuP0/Prefabs.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: c88fe653c15d473cac0029539a2ff355 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.flowscope.gamecore/Samples~/MainMenuP0/Prefabs/MainMenuPanel.prefab b/Packages/com.flowscope.gamecore/Samples~/MainMenuP0/Prefabs/MainMenuPanel.prefab new file mode 100644 index 0000000..826ce08 --- /dev/null +++ b/Packages/com.flowscope.gamecore/Samples~/MainMenuP0/Prefabs/MainMenuPanel.prefab @@ -0,0 +1,453 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!1 &4224295462618148274 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 2785319262204863001} + - component: {fileID: 6821847843857072034} + - component: {fileID: 8750438775342100074} + - component: {fileID: 8038936202738359261} + m_Layer: 0 + m_Name: MainMenuPanel + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &2785319262204863001 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4224295462618148274} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 6248245286658728259} + - {fileID: 6082938367091974448} + - {fileID: 8836034375789789371} + m_Father: {fileID: 0} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0.5, y: 0.5} + m_AnchorMax: {x: 0.5, y: 0.5} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 420, y: 240} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &6821847843857072034 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4224295462618148274} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 3a641bd6447847f2af2cd5af40305a5a, type: 3} + m_Name: + m_EditorClassIdentifier: + goldText: {fileID: 1736696561195866944} + incrementButton: {fileID: 4338913550008398238} +--- !u!222 &8750438775342100074 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4224295462618148274} + m_CullTransparentMesh: 1 +--- !u!114 &8038936202738359261 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4224295462618148274} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 0.08, g: 0.1, b: 0.12, a: 0.92} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 0} + m_Type: 0 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 +--- !u!1 &4373334964405757163 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 8836034375789789371} + - component: {fileID: 2680961909818563798} + - component: {fileID: 1639113149993436237} + - component: {fileID: 4338913550008398238} + m_Layer: 0 + m_Name: IncrementGoldButton + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &8836034375789789371 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4373334964405757163} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 9086714161511357950} + m_Father: {fileID: 2785319262204863001} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0.5, y: 0} + m_AnchorMax: {x: 0.5, y: 0} + m_AnchoredPosition: {x: 0, y: 48} + m_SizeDelta: {x: 220, y: 48} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!222 &2680961909818563798 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4373334964405757163} + m_CullTransparentMesh: 1 +--- !u!114 &1639113149993436237 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4373334964405757163} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 0.2, g: 0.54, b: 0.8, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 0} + m_Type: 0 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 +--- !u!114 &4338913550008398238 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4373334964405757163} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 4e29b1a8efbd4b44bb3f3716e73f07ff, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Navigation: + m_Mode: 3 + m_WrapAround: 0 + m_SelectOnUp: {fileID: 0} + m_SelectOnDown: {fileID: 0} + m_SelectOnLeft: {fileID: 0} + m_SelectOnRight: {fileID: 0} + m_Transition: 1 + m_Colors: + m_NormalColor: {r: 1, g: 1, b: 1, a: 1} + m_HighlightedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} + m_PressedColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 1} + m_SelectedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} + m_DisabledColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 0.5019608} + m_ColorMultiplier: 1 + m_FadeDuration: 0.1 + m_SpriteState: + m_HighlightedSprite: {fileID: 0} + m_PressedSprite: {fileID: 0} + m_SelectedSprite: {fileID: 0} + m_DisabledSprite: {fileID: 0} + m_AnimationTriggers: + m_NormalTrigger: Normal + m_HighlightedTrigger: Highlighted + m_PressedTrigger: Pressed + m_SelectedTrigger: Selected + m_DisabledTrigger: Disabled + m_Interactable: 1 + m_TargetGraphic: {fileID: 1639113149993436237} + m_OnClick: + m_PersistentCalls: + m_Calls: [] +--- !u!1 &5175570728316865318 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 6082938367091974448} + - component: {fileID: 901708792770677843} + - component: {fileID: 1736696561195866944} + m_Layer: 0 + m_Name: GoldText + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &6082938367091974448 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 5175570728316865318} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 2785319262204863001} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0.5} + m_AnchorMax: {x: 1, y: 0.5} + m_AnchoredPosition: {x: 0, y: 18} + m_SizeDelta: {x: -48, y: 60} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!222 &901708792770677843 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 5175570728316865318} + m_CullTransparentMesh: 1 +--- !u!114 &1736696561195866944 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 5175570728316865318} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_FontData: + m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0} + m_FontSize: 40 + m_FontStyle: 0 + m_BestFit: 0 + m_MinSize: 10 + m_MaxSize: 40 + m_Alignment: 4 + m_AlignByGeometry: 0 + m_RichText: 1 + m_HorizontalOverflow: 0 + m_VerticalOverflow: 0 + m_LineSpacing: 1 + m_Text: 0 +--- !u!1 &7474348118067574816 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 6248245286658728259} + - component: {fileID: 8827375201563443825} + - component: {fileID: 1429476040966027426} + m_Layer: 0 + m_Name: Title + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &6248245286658728259 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7474348118067574816} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 2785319262204863001} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 1} + m_AnchorMax: {x: 1, y: 1} + m_AnchoredPosition: {x: 0, y: -24} + m_SizeDelta: {x: -48, y: 46} + m_Pivot: {x: 0.5, y: 1} +--- !u!222 &8827375201563443825 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7474348118067574816} + m_CullTransparentMesh: 1 +--- !u!114 &1429476040966027426 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7474348118067574816} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_FontData: + m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0} + m_FontSize: 28 + m_FontStyle: 0 + m_BestFit: 0 + m_MinSize: 10 + m_MaxSize: 40 + m_Alignment: 4 + m_AlignByGeometry: 0 + m_RichText: 1 + m_HorizontalOverflow: 0 + m_VerticalOverflow: 0 + m_LineSpacing: 1 + m_Text: FlowScope Main Menu +--- !u!1 &8599479246113536964 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 9086714161511357950} + - component: {fileID: 2312868584952693925} + - component: {fileID: 9151466043238822226} + m_Layer: 0 + m_Name: Label + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &9086714161511357950 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 8599479246113536964} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 8836034375789789371} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 1, y: 1} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 0, y: 0} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!222 &2312868584952693925 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 8599479246113536964} + m_CullTransparentMesh: 1 +--- !u!114 &9151466043238822226 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 8599479246113536964} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_FontData: + m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0} + m_FontSize: 20 + m_FontStyle: 0 + m_BestFit: 0 + m_MinSize: 10 + m_MaxSize: 40 + m_Alignment: 4 + m_AlignByGeometry: 0 + m_RichText: 1 + m_HorizontalOverflow: 0 + m_VerticalOverflow: 0 + m_LineSpacing: 1 + m_Text: Add Gold diff --git a/Packages/com.flowscope.gamecore/Samples~/MainMenuP0/Prefabs/MainMenuPanel.prefab.meta b/Packages/com.flowscope.gamecore/Samples~/MainMenuP0/Prefabs/MainMenuPanel.prefab.meta new file mode 100644 index 0000000..fea0efa --- /dev/null +++ b/Packages/com.flowscope.gamecore/Samples~/MainMenuP0/Prefabs/MainMenuPanel.prefab.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 05cda67df190bb746b57ab6f944469c5 +PrefabImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.flowscope.gamecore/Samples~/MainMenuP0/README.md b/Packages/com.flowscope.gamecore/Samples~/MainMenuP0/README.md new file mode 100644 index 0000000..af222de --- /dev/null +++ b/Packages/com.flowscope.gamecore/Samples~/MainMenuP0/README.md @@ -0,0 +1,44 @@ +# MainMenuP0 Package Sample + +`MainMenuP0` is the minimal FlowScope package sample. It demonstrates the recommended bootstrap path for a small Unity project without introducing a full game template. + +## What It Covers + +- Creating a root `Container`. +- Registering config, resource, save, audio, UI, preload, and screen navigation services. +- Registering `MainMenuFeature` with `RegisterTransient`. +- Preloading `MainMenuPanel` through `UIPreloadService`. +- Opening the main menu through `UIScreenNavigator`. +- Updating `PlayerData.Gold` through R3-backed view data. +- Saving data and releasing preloaded resources during `ShutdownAsync`. + +## How To Import + +In a consuming Unity project, open Package Manager, select `FlowScope Game Core`, and import the `MainMenuP0` sample. + +Do not import this package sample into the FlowScope development project while `Assets/FlowScope/Samples/MainMenuP0` is still present. The development project keeps the Assets-based sample as the source of truth for tests during P2 migration. + +## How To Run + +After importing the sample, open: + +```text +Assets/Samples/FlowScope Game Core//MainMenuP0/Scenes/MainMenuP0.unity +``` + +Enter Play Mode. The scene should show the main menu panel, a Gold text, and an increment button. + +## Validation Points + +- The first startup shows default Gold. +- Clicking the button increments Gold immediately. +- `ShutdownAsync` writes the `main-menu-player` save. +- Restarting restores the last saved Gold. +- Shutdown releases preloaded UI resources. +- Console has no unexpected Error or Exception. + +## Current Limits + +- The sample uses `SampleResourceBackend` instead of real Addressables assets. +- Runtime fallback UI exists so the sample can still run if scene or prefab references are missing. +- P2 package tests are not migrated yet; current automated tests still live under the FlowScope development project's `Assets/FlowScope/Tests`. diff --git a/Packages/com.flowscope.gamecore/Samples~/MainMenuP0/README.md.meta b/Packages/com.flowscope.gamecore/Samples~/MainMenuP0/README.md.meta new file mode 100644 index 0000000..2146550 --- /dev/null +++ b/Packages/com.flowscope.gamecore/Samples~/MainMenuP0/README.md.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 9df9d94a34fa4ec2b16df2c8cf2a651b +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.flowscope.gamecore/Samples~/MainMenuP0/Scenes.meta b/Packages/com.flowscope.gamecore/Samples~/MainMenuP0/Scenes.meta new file mode 100644 index 0000000..d1cc351 --- /dev/null +++ b/Packages/com.flowscope.gamecore/Samples~/MainMenuP0/Scenes.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 946526d21b06440688088de21f383782 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.flowscope.gamecore/Samples~/MainMenuP0/Scenes/MainMenuP0.unity b/Packages/com.flowscope.gamecore/Samples~/MainMenuP0/Scenes/MainMenuP0.unity new file mode 100644 index 0000000..1e069fa --- /dev/null +++ b/Packages/com.flowscope.gamecore/Samples~/MainMenuP0/Scenes/MainMenuP0.unity @@ -0,0 +1,440 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!29 &1 +OcclusionCullingSettings: + m_ObjectHideFlags: 0 + serializedVersion: 2 + m_OcclusionBakeSettings: + smallestOccluder: 5 + smallestHole: 0.25 + backfaceThreshold: 100 + m_SceneGUID: 00000000000000000000000000000000 + m_OcclusionCullingData: {fileID: 0} +--- !u!104 &2 +RenderSettings: + m_ObjectHideFlags: 0 + serializedVersion: 9 + m_Fog: 0 + m_FogColor: {r: 0.5, g: 0.5, b: 0.5, a: 1} + m_FogMode: 3 + m_FogDensity: 0.01 + m_LinearFogStart: 0 + m_LinearFogEnd: 300 + m_AmbientSkyColor: {r: 0.212, g: 0.227, b: 0.259, a: 1} + m_AmbientEquatorColor: {r: 0.114, g: 0.125, b: 0.133, a: 1} + m_AmbientGroundColor: {r: 0.047, g: 0.043, b: 0.035, a: 1} + m_AmbientIntensity: 1 + m_AmbientMode: 0 + m_SubtractiveShadowColor: {r: 0.42, g: 0.478, b: 0.627, a: 1} + m_SkyboxMaterial: {fileID: 10304, guid: 0000000000000000f000000000000000, type: 0} + m_HaloStrength: 0.5 + m_FlareStrength: 1 + m_FlareFadeSpeed: 3 + m_HaloTexture: {fileID: 0} + m_SpotCookie: {fileID: 10001, guid: 0000000000000000e000000000000000, type: 0} + m_DefaultReflectionMode: 0 + m_DefaultReflectionResolution: 128 + m_ReflectionBounces: 1 + m_ReflectionIntensity: 1 + m_CustomReflection: {fileID: 0} + m_Sun: {fileID: 0} + m_UseRadianceAmbientProbe: 0 +--- !u!157 &3 +LightmapSettings: + m_ObjectHideFlags: 0 + serializedVersion: 12 + m_GIWorkflowMode: 1 + m_GISettings: + serializedVersion: 2 + m_BounceScale: 1 + m_IndirectOutputScale: 1 + m_AlbedoBoost: 1 + m_EnvironmentLightingMode: 0 + m_EnableBakedLightmaps: 1 + m_EnableRealtimeLightmaps: 0 + m_LightmapEditorSettings: + serializedVersion: 12 + m_Resolution: 2 + m_BakeResolution: 40 + m_AtlasSize: 1024 + m_AO: 0 + m_AOMaxDistance: 1 + m_CompAOExponent: 1 + m_CompAOExponentDirect: 0 + m_ExtractAmbientOcclusion: 0 + m_Padding: 2 + m_LightmapParameters: {fileID: 0} + m_LightmapsBakeMode: 1 + m_TextureCompression: 1 + m_FinalGather: 0 + m_FinalGatherFiltering: 1 + m_FinalGatherRayCount: 256 + m_ReflectionCompression: 2 + m_MixedBakeMode: 2 + m_BakeBackend: 1 + m_PVRSampling: 1 + m_PVRDirectSampleCount: 32 + m_PVRSampleCount: 512 + m_PVRBounces: 2 + m_PVREnvironmentSampleCount: 256 + m_PVREnvironmentReferencePointCount: 2048 + m_PVRFilteringMode: 1 + m_PVRDenoiserTypeDirect: 1 + m_PVRDenoiserTypeIndirect: 1 + m_PVRDenoiserTypeAO: 1 + m_PVRFilterTypeDirect: 0 + m_PVRFilterTypeIndirect: 0 + m_PVRFilterTypeAO: 0 + m_PVREnvironmentMIS: 1 + m_PVRCulling: 1 + m_PVRFilteringGaussRadiusDirect: 1 + m_PVRFilteringGaussRadiusIndirect: 5 + m_PVRFilteringGaussRadiusAO: 2 + m_PVRFilteringAtrousPositionSigmaDirect: 0.5 + m_PVRFilteringAtrousPositionSigmaIndirect: 2 + m_PVRFilteringAtrousPositionSigmaAO: 1 + m_ExportTrainingData: 0 + m_TrainingDataDestination: TrainingData + m_LightProbeSampleCountMultiplier: 4 + m_LightingDataAsset: {fileID: 0} + m_LightingSettings: {fileID: 0} +--- !u!196 &4 +NavMeshSettings: + serializedVersion: 2 + m_ObjectHideFlags: 0 + m_BuildSettings: + serializedVersion: 3 + agentTypeID: 0 + agentRadius: 0.5 + agentHeight: 2 + agentSlope: 45 + agentClimb: 0.4 + ledgeDropHeight: 0 + maxJumpAcrossDistance: 0 + minRegionArea: 2 + manualCellSize: 0 + cellSize: 0.16666667 + manualTileSize: 0 + tileSize: 256 + buildHeightMesh: 0 + maxJobWorkers: 0 + preserveTilesOutsideBounds: 0 + debug: + m_Flags: 0 + m_NavMeshData: {fileID: 0} +--- !u!1 &204902849 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 204902852} + - component: {fileID: 204902851} + - component: {fileID: 204902850} + m_Layer: 0 + m_Name: Main Camera + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!81 &204902850 +AudioListener: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 204902849} + m_Enabled: 1 +--- !u!20 &204902851 +Camera: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 204902849} + m_Enabled: 1 + serializedVersion: 2 + m_ClearFlags: 2 + m_BackGroundColor: {r: 0.05, g: 0.06, b: 0.07, a: 1} + m_projectionMatrixMode: 1 + m_GateFitMode: 2 + m_FOVAxisMode: 0 + m_Iso: 200 + m_ShutterSpeed: 0.005 + m_Aperture: 16 + m_FocusDistance: 10 + m_FocalLength: 50 + m_BladeCount: 5 + m_Curvature: {x: 2, y: 11} + m_BarrelClipping: 0.25 + m_Anamorphism: 0 + m_SensorSize: {x: 36, y: 24} + m_LensShift: {x: 0, y: 0} + m_NormalizedViewPortRect: + serializedVersion: 2 + x: 0 + y: 0 + width: 1 + height: 1 + near clip plane: 0.3 + far clip plane: 1000 + field of view: 60 + orthographic: 0 + orthographic size: 5 + m_Depth: 0 + m_CullingMask: + serializedVersion: 2 + m_Bits: 4294967295 + m_RenderingPath: -1 + m_TargetTexture: {fileID: 0} + m_TargetDisplay: 0 + m_TargetEye: 3 + m_HDR: 1 + m_AllowMSAA: 1 + m_AllowDynamicResolution: 0 + m_ForceIntoRT: 0 + m_OcclusionCulling: 1 + m_StereoConvergence: 10 + m_StereoSeparation: 0.022 +--- !u!4 &204902852 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 204902849} + serializedVersion: 2 + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 0} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &1061918572 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1061918574} + - component: {fileID: 1061918573} + m_Layer: 0 + m_Name: GameBootstrap + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!114 &1061918573 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1061918572} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: cd97432f2838454cbbf2d19dbe14d7f3, type: 3} + m_Name: + m_EditorClassIdentifier: + hudCanvas: {fileID: 1965407523} + mainMenuPanelPrefab: {fileID: 6821847843857072034, guid: 05cda67df190bb746b57ab6f944469c5, type: 3} + defaultGold: 0 +--- !u!4 &1061918574 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1061918572} + serializedVersion: 2 + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 0} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &1965407522 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1965407526} + - component: {fileID: 1965407523} + - component: {fileID: 1965407525} + - component: {fileID: 1965407524} + m_Layer: 0 + m_Name: HUD Canvas + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!223 &1965407523 +Canvas: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1965407522} + m_Enabled: 1 + serializedVersion: 3 + m_RenderMode: 0 + m_Camera: {fileID: 0} + m_PlaneDistance: 100 + m_PixelPerfect: 0 + m_ReceivesEvents: 1 + m_OverrideSorting: 0 + m_OverridePixelPerfect: 0 + m_SortingBucketNormalizedSize: 0 + m_VertexColorAlwaysGammaSpace: 0 + m_AdditionalShaderChannelsFlag: 0 + m_UpdateRectTransformForStandalone: 0 + m_SortingLayerID: 0 + m_SortingOrder: 0 + m_TargetDisplay: 0 +--- !u!114 &1965407524 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1965407522} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: dc42784cf147c0c48a680349fa168899, type: 3} + m_Name: + m_EditorClassIdentifier: + m_IgnoreReversedGraphics: 1 + m_BlockingObjects: 0 + m_BlockingMask: + serializedVersion: 2 + m_Bits: 4294967295 +--- !u!114 &1965407525 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1965407522} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 0cd44c1031e13a943bb63640046fad76, type: 3} + m_Name: + m_EditorClassIdentifier: + m_UiScaleMode: 0 + m_ReferencePixelsPerUnit: 100 + m_ScaleFactor: 1 + m_ReferenceResolution: {x: 800, y: 600} + m_ScreenMatchMode: 0 + m_MatchWidthOrHeight: 0 + m_PhysicalUnit: 3 + m_FallbackScreenDPI: 96 + m_DefaultSpriteDPI: 96 + m_DynamicPixelsPerUnit: 1 + m_PresetInfoIsWorld: 0 +--- !u!224 &1965407526 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1965407522} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 0, y: 0, z: 0} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 0} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 0, y: 0} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 0, y: 0} + m_Pivot: {x: 0, y: 0} +--- !u!1 &2023591418 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 2023591421} + - component: {fileID: 2023591420} + - component: {fileID: 2023591419} + m_Layer: 0 + m_Name: EventSystem + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!114 &2023591419 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2023591418} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 4f231c4fb786f3946a6b90b886c48677, type: 3} + m_Name: + m_EditorClassIdentifier: + m_SendPointerHoverToParent: 1 + m_HorizontalAxis: Horizontal + m_VerticalAxis: Vertical + m_SubmitButton: Submit + m_CancelButton: Cancel + m_InputActionsPerSecond: 10 + m_RepeatDelay: 0.5 + m_ForceModuleActive: 0 +--- !u!114 &2023591420 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2023591418} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 76c392e42b5098c458856cdf6ecaaaa1, type: 3} + m_Name: + m_EditorClassIdentifier: + m_FirstSelected: {fileID: 0} + m_sendNavigationEvents: 1 + m_DragThreshold: 10 +--- !u!4 &2023591421 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2023591418} + serializedVersion: 2 + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 0} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1660057539 &9223372036854775807 +SceneRoots: + m_ObjectHideFlags: 0 + m_Roots: + - {fileID: 204902852} + - {fileID: 2023591421} + - {fileID: 1965407526} + - {fileID: 1061918574} diff --git a/Packages/com.flowscope.gamecore/Samples~/MainMenuP0/Scenes/MainMenuP0.unity.meta b/Packages/com.flowscope.gamecore/Samples~/MainMenuP0/Scenes/MainMenuP0.unity.meta new file mode 100644 index 0000000..090c7c3 --- /dev/null +++ b/Packages/com.flowscope.gamecore/Samples~/MainMenuP0/Scenes/MainMenuP0.unity.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 03630d34cf304668afe00ab221f3557e +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.flowscope.gamecore/Samples~/MainMenuP0/Scripts.meta b/Packages/com.flowscope.gamecore/Samples~/MainMenuP0/Scripts.meta new file mode 100644 index 0000000..ff67383 --- /dev/null +++ b/Packages/com.flowscope.gamecore/Samples~/MainMenuP0/Scripts.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 93879cc02e2644039382a2006c5434b1 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.flowscope.gamecore/Samples~/MainMenuP0/Scripts/GameBootstrap.cs b/Packages/com.flowscope.gamecore/Samples~/MainMenuP0/Scripts/GameBootstrap.cs new file mode 100644 index 0000000..eb76c17 --- /dev/null +++ b/Packages/com.flowscope.gamecore/Samples~/MainMenuP0/Scripts/GameBootstrap.cs @@ -0,0 +1,303 @@ +using System; +using System.Collections.Generic; +using System.Runtime.ExceptionServices; +using System.Threading; +using System.Threading.Tasks; +using FlowScope.Audio; +using FlowScope.Config; +using FlowScope.Flow; +using FlowScope.Resources; +using FlowScope.Save; +using FlowScope.UI; +using UnityEngine; +using UnityEngine.UI; +using ContainerType = FlowScope.Container.Container; + +namespace FlowScope.Samples.MainMenuP0 +{ + public sealed class GameBootstrap : MonoBehaviour + { + private const string SaveKey = "main-menu-player"; + + [SerializeField] private Canvas hudCanvas; + [SerializeField] private MainMenuPanel mainMenuPanelPrefab; + [SerializeField] private int defaultGold; + + private ContainerType _root; + private GameFlow _gameFlow; + private ISaveService _saveService; + private UIPreloadService _preloadService; + private SampleResourceBackend _sampleBackend; + private PlayerData _playerData; + private CancellationTokenSource _lifetime; + private bool _started; + private bool _shutdownInProgress; + private bool _shutdown; + + public int ReleasedResourceCount => _sampleBackend?.ReleaseCount ?? 0; + /// + /// True after runtime cleanup has completed; shutdown sub-step failures are still reported by ShutdownAsync. + /// + public bool IsShutdown => _shutdown; + public Task StartupTask { get; private set; } + + private void Awake() + { + _lifetime = new CancellationTokenSource(); + StartupTask = RunStartupWithLoggingAsync(_lifetime.Token); + } + + public async Task StartupAsync(CancellationToken cancellationToken) + { + if (_started) + { + return; + } + + _started = true; + _shutdown = false; + _root = new ContainerType(); + _gameFlow = new GameFlow(); + + _sampleBackend = new SampleResourceBackend(); + _sampleBackend.Register( + "FlowScope/Samples/MainMenuP0/MainMenuPanel", + ResolveMainMenuPanelPrefab().gameObject); + var resources = new ResourceService(_sampleBackend); + _preloadService = new UIPreloadService(resources); + var uiManager = new UIManager(resources, _preloadService); + uiManager.RegisterLayer("hud", ResolveHudCanvas(), 0, PanelStrategy.Destroy); + var screenNavigator = new UIScreenNavigator(uiManager); + + var migrations = new SaveMigrationRegistry(); + migrations.Register(new PlayerDataV1ToV2Migration()); + _saveService = new SaveService( + new FileSaveStorage(), + new JsonSaveSerializer(2, migrations)); + _playerData = await _saveService.LoadAsync( + SaveKey, + new PlayerData { Gold = new R3.ReactiveProperty(defaultGold) }, + cancellationToken); + + await _preloadService.PreloadAsync(cancellationToken); + + var audioService = gameObject.AddComponent().Initialize(resources); + _root.RegisterInstance(_root); + _root.RegisterInstance( + new JsonConfigProvider(new[] + { + JsonConfigProvider.Mapping( + "main_menu.json", + new InMemoryConfigSource( + new Dictionary + { + { "main_menu.json", "[{\"Id\":1,\"Title\":\"Main Menu P1\"}]" } + }, + "MainMenuP0"), + new JsonConfigParser()) + })); + _root.RegisterInstance(resources); + _root.RegisterInstance(_saveService); + _root.RegisterInstance(audioService); + _root.RegisterInstance(uiManager); + _root.RegisterInstance(_preloadService); + _root.RegisterInstance(screenNavigator); + _root.RegisterInstance(_playerData); + _root.RegisterTransient(_ => new MainMenuFeature(screenNavigator, _playerData)); + + await _gameFlow.StartupAsync(_root, cancellationToken); + } + + public async Task ShutdownAsync(CancellationToken cancellationToken) + { + if (_shutdown || _shutdownInProgress) + { + return; + } + + _shutdownInProgress = true; + var exceptions = new List(); + + try + { + if (_saveService != null && _playerData != null) + { + try + { + await _saveService.SaveAsync(SaveKey, _playerData, cancellationToken); + } + catch (Exception exception) + { + exceptions.Add(exception); + } + } + + if (_gameFlow != null && _gameFlow.State != GameFlowState.Disposed) + { + try + { + await _gameFlow.ShutdownAsync(cancellationToken); + } + catch (Exception exception) + { + exceptions.Add(exception); + } + } + + try + { + _preloadService?.ReleaseAll(); + } + catch (Exception exception) + { + exceptions.Add(exception); + } + + _shutdown = true; + } + finally + { + _shutdownInProgress = false; + } + + if (exceptions.Count == 1) + { + ExceptionDispatchInfo.Capture(exceptions[0]).Throw(); + } + + if (exceptions.Count > 1) + { + throw new AggregateException("MainMenuP0 shutdown completed with failures.", exceptions); + } + } + + private void OnApplicationQuit() + { + _lifetime?.Cancel(); + _ = RunShutdownWithLoggingAsync(CancellationToken.None); + } + + private void OnDestroy() + { + _lifetime?.Cancel(); + _lifetime?.Dispose(); + } + + 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 Canvas ResolveHudCanvas() + { + if (hudCanvas != null) + { + return hudCanvas; + } + + var canvasObject = new GameObject("FlowScope HUD Canvas"); + canvasObject.transform.SetParent(transform, false); + hudCanvas = canvasObject.AddComponent(); + hudCanvas.renderMode = RenderMode.ScreenSpaceOverlay; + return hudCanvas; + } + + private MainMenuPanel ResolveMainMenuPanelPrefab() + { + if (mainMenuPanelPrefab != null) + { + return mainMenuPanelPrefab; + } + + var panelObject = new GameObject("MainMenuPanel Template"); + panelObject.transform.SetParent(transform, false); + panelObject.SetActive(false); + + var panel = panelObject.AddComponent(); + + var goldObject = new GameObject("GoldText"); + goldObject.transform.SetParent(panelObject.transform, false); + var goldText = goldObject.AddComponent(); + goldText.text = defaultGold.ToString(); + goldText.alignment = TextAnchor.MiddleCenter; + + var buttonObject = new GameObject("IncrementGoldButton"); + buttonObject.transform.SetParent(panelObject.transform, false); + buttonObject.AddComponent(); + var button = buttonObject.AddComponent