This commit is contained in:
journaliciouz
2025-11-19 23:51:15 +01:00
39 changed files with 1010 additions and 244 deletions

View File

@@ -40,7 +40,7 @@ RectTransform:
m_AnchorMin: {x: 0.5, y: 0.5} m_AnchorMin: {x: 0.5, y: 0.5}
m_AnchorMax: {x: 0.5, y: 0.5} m_AnchorMax: {x: 0.5, y: 0.5}
m_AnchoredPosition: {x: 0, y: 0} m_AnchoredPosition: {x: 0, y: 0}
m_SizeDelta: {x: 200, y: 270} m_SizeDelta: {x: 200, y: 200}
m_Pivot: {x: 0.5, y: 0.5} m_Pivot: {x: 0.5, y: 0.5}
--- !u!114 &2127389269465269351 --- !u!114 &2127389269465269351
MonoBehaviour: MonoBehaviour:
@@ -143,6 +143,7 @@ GameObject:
- component: {fileID: 581410895551808339} - component: {fileID: 581410895551808339}
- component: {fileID: 9061673992343081870} - component: {fileID: 9061673992343081870}
- component: {fileID: 2391223799317068879} - component: {fileID: 2391223799317068879}
- component: {fileID: 3335061902473626590}
m_Layer: 0 m_Layer: 0
m_Name: Visual m_Name: Visual
m_TagString: Untagged m_TagString: Untagged
@@ -164,10 +165,10 @@ RectTransform:
m_Children: [] m_Children: []
m_Father: {fileID: 8730990344497138252} m_Father: {fileID: 8730990344497138252}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0.5, y: 0.5} m_AnchorMin: {x: 0, y: 0}
m_AnchorMax: {x: 0.5, y: 0.5} m_AnchorMax: {x: 1, y: 1}
m_AnchoredPosition: {x: 0, y: 0} m_AnchoredPosition: {x: 0, y: 0}
m_SizeDelta: {x: 200, y: 200} m_SizeDelta: {x: 0, y: 0}
m_Pivot: {x: 0.5, y: 0.5} m_Pivot: {x: 0.5, y: 0.5}
--- !u!114 &581410895551808339 --- !u!114 &581410895551808339
MonoBehaviour: MonoBehaviour:
@@ -220,6 +221,20 @@ MonoBehaviour:
m_FillOrigin: 0 m_FillOrigin: 0
m_UseSpriteMesh: 0 m_UseSpriteMesh: 0
m_PixelsPerUnitMultiplier: 1 m_PixelsPerUnitMultiplier: 1
--- !u!114 &3335061902473626590
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 3963317030246886356}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 86710e43de46f6f4bac7c8e50813a599, type: 3}
m_Name:
m_EditorClassIdentifier: UnityEngine.UI::UnityEngine.UI.AspectRatioFitter
m_AspectMode: 1
m_AspectRatio: 1
--- !u!1 &4127399957670380340 --- !u!1 &4127399957670380340
GameObject: GameObject:
m_ObjectHideFlags: 0 m_ObjectHideFlags: 0

View File

@@ -55,7 +55,6 @@ MonoBehaviour:
occupantScale: {x: 1, y: 1, z: 1} occupantScale: {x: 1, y: 1, z: 1}
scaleTransitionDuration: 0.3 scaleTransitionDuration: 0.3
boxType: 0 boxType: 0
boxSprite: {fileID: 0}
--- !u!1 &6923066319076554151 --- !u!1 &6923066319076554151
GameObject: GameObject:
m_ObjectHideFlags: 0 m_ObjectHideFlags: 0
@@ -132,8 +131,8 @@ SpriteRenderer:
m_AutoUVMaxAngle: 89 m_AutoUVMaxAngle: 89
m_LightmapParameters: {fileID: 0} m_LightmapParameters: {fileID: 0}
m_GlobalIlluminationMeshLod: 0 m_GlobalIlluminationMeshLod: 0
m_SortingLayerID: -1132846201 m_SortingLayerID: 0
m_SortingLayer: 1 m_SortingLayer: 0
m_SortingOrder: 0 m_SortingOrder: 0
m_Sprite: {fileID: -7843813406500067289, guid: bd1c641e7bfe53145820bb64b08f8fc8, type: 3} m_Sprite: {fileID: -7843813406500067289, guid: bd1c641e7bfe53145820bb64b08f8fc8, type: 3}
m_Color: {r: 1, g: 1, b: 1, a: 1} m_Color: {r: 1, g: 1, b: 1, a: 1}

View File

@@ -35,7 +35,7 @@ RectTransform:
m_AnchorMin: {x: 0, y: 0} m_AnchorMin: {x: 0, y: 0}
m_AnchorMax: {x: 0, y: 0} m_AnchorMax: {x: 0, y: 0}
m_AnchoredPosition: {x: 0, y: 0} m_AnchoredPosition: {x: 0, y: 0}
m_SizeDelta: {x: 200, y: 50} m_SizeDelta: {x: 0, y: 0}
m_Pivot: {x: 0.5, y: 0.5} m_Pivot: {x: 0.5, y: 0.5}
--- !u!222 &6535475175108777945 --- !u!222 &6535475175108777945
CanvasRenderer: CanvasRenderer:
@@ -65,7 +65,7 @@ MonoBehaviour:
m_OnCullStateChanged: m_OnCullStateChanged:
m_PersistentCalls: m_PersistentCalls:
m_Calls: [] m_Calls: []
m_text: 0000 m_text: 'Score: 0'
m_isRightToLeft: 0 m_isRightToLeft: 0
m_fontAsset: {fileID: 11400000, guid: 4aca0db6ec111b5418bdc747168f9474, type: 2} m_fontAsset: {fileID: 11400000, guid: 4aca0db6ec111b5418bdc747168f9474, type: 2}
m_sharedMaterial: {fileID: -1441574381962284772, guid: 4aca0db6ec111b5418bdc747168f9474, type: 2} m_sharedMaterial: {fileID: -1441574381962284772, guid: 4aca0db6ec111b5418bdc747168f9474, type: 2}
@@ -92,15 +92,15 @@ MonoBehaviour:
m_faceColor: m_faceColor:
serializedVersion: 2 serializedVersion: 2
rgba: 4294967295 rgba: 4294967295
m_fontSize: 100 m_fontSize: 75
m_fontSizeBase: 100 m_fontSizeBase: 75
m_fontWeight: 400 m_fontWeight: 400
m_enableAutoSizing: 0 m_enableAutoSizing: 0
m_fontSizeMin: 18 m_fontSizeMin: 18
m_fontSizeMax: 72 m_fontSizeMax: 72
m_fontStyle: 1 m_fontStyle: 1
m_HorizontalAlignment: 1 m_HorizontalAlignment: 2
m_VerticalAlignment: 256 m_VerticalAlignment: 512
m_textAlignment: 65535 m_textAlignment: 65535
m_characterSpacing: 0 m_characterSpacing: 0
m_wordSpacing: 0 m_wordSpacing: 0
@@ -171,7 +171,7 @@ RectTransform:
m_AnchorMin: {x: 0, y: 0} m_AnchorMin: {x: 0, y: 0}
m_AnchorMax: {x: 0, y: 0} m_AnchorMax: {x: 0, y: 0}
m_AnchoredPosition: {x: 0, y: 0} m_AnchoredPosition: {x: 0, y: 0}
m_SizeDelta: {x: 200, y: 50} m_SizeDelta: {x: 0, y: 0}
m_Pivot: {x: 0.5, y: 0.5} m_Pivot: {x: 0.5, y: 0.5}
--- !u!222 &6186147173729892847 --- !u!222 &6186147173729892847
CanvasRenderer: CanvasRenderer:
@@ -201,7 +201,7 @@ MonoBehaviour:
m_OnCullStateChanged: m_OnCullStateChanged:
m_PersistentCalls: m_PersistentCalls:
m_Calls: [] m_Calls: []
m_text: 0% m_text: 'Accuracy: 0%'
m_isRightToLeft: 0 m_isRightToLeft: 0
m_fontAsset: {fileID: 11400000, guid: 4aca0db6ec111b5418bdc747168f9474, type: 2} m_fontAsset: {fileID: 11400000, guid: 4aca0db6ec111b5418bdc747168f9474, type: 2}
m_sharedMaterial: {fileID: -1441574381962284772, guid: 4aca0db6ec111b5418bdc747168f9474, type: 2} m_sharedMaterial: {fileID: -1441574381962284772, guid: 4aca0db6ec111b5418bdc747168f9474, type: 2}
@@ -228,15 +228,15 @@ MonoBehaviour:
m_faceColor: m_faceColor:
serializedVersion: 2 serializedVersion: 2
rgba: 4294967295 rgba: 4294967295
m_fontSize: 100 m_fontSize: 75
m_fontSizeBase: 100 m_fontSizeBase: 75
m_fontWeight: 400 m_fontWeight: 400
m_enableAutoSizing: 0 m_enableAutoSizing: 0
m_fontSizeMin: 18 m_fontSizeMin: 18
m_fontSizeMax: 72 m_fontSizeMax: 72
m_fontStyle: 1 m_fontStyle: 1
m_HorizontalAlignment: 1 m_HorizontalAlignment: 2
m_VerticalAlignment: 256 m_VerticalAlignment: 512
m_textAlignment: 65535 m_textAlignment: 65535
m_characterSpacing: 0 m_characterSpacing: 0
m_wordSpacing: 0 m_wordSpacing: 0
@@ -307,10 +307,10 @@ RectTransform:
m_Father: {fileID: 707190640386266950} m_Father: {fileID: 707190640386266950}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0, y: 1} m_AnchorMin: {x: 0, y: 1}
m_AnchorMax: {x: 1, y: 1} m_AnchorMax: {x: 0, y: 1}
m_AnchoredPosition: {x: 0, y: 0} m_AnchoredPosition: {x: 0, y: 0}
m_SizeDelta: {x: 0, y: 150} m_SizeDelta: {x: 1200, y: 94}
m_Pivot: {x: 0.5, y: 1} m_Pivot: {x: 0, y: 1}
--- !u!114 &3416581373676611538 --- !u!114 &3416581373676611538
MonoBehaviour: MonoBehaviour:
m_ObjectHideFlags: 0 m_ObjectHideFlags: 0
@@ -324,19 +324,98 @@ MonoBehaviour:
m_Name: m_Name:
m_EditorClassIdentifier: UnityEngine.UI::UnityEngine.UI.HorizontalLayoutGroup m_EditorClassIdentifier: UnityEngine.UI::UnityEngine.UI.HorizontalLayoutGroup
m_Padding: m_Padding:
m_Left: 0 m_Left: 150
m_Right: 0 m_Right: 0
m_Top: 0 m_Top: 50
m_Bottom: 0 m_Bottom: 0
m_ChildAlignment: 4 m_ChildAlignment: 4
m_Spacing: 150 m_Spacing: 25
m_ChildForceExpandWidth: 0 m_ChildForceExpandWidth: 1
m_ChildForceExpandHeight: 1 m_ChildForceExpandHeight: 1
m_ChildControlWidth: 1
m_ChildControlHeight: 1
m_ChildScaleWidth: 0
m_ChildScaleHeight: 0
m_ReverseArrangement: 0
--- !u!1 &3087548688493085045
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 3245381937614859990}
- component: {fileID: 1428588550372337553}
- component: {fileID: 7088646692690443534}
m_Layer: 5
m_Name: HealthBar
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!224 &3245381937614859990
RectTransform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 3087548688493085045}
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: 3256183770156166523}
m_Father: {fileID: 707190640386266950}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 1, y: 1}
m_AnchorMax: {x: 1, y: 1}
m_AnchoredPosition: {x: 0, y: 0}
m_SizeDelta: {x: 559.7, y: 150}
m_Pivot: {x: 1, y: 1}
--- !u!114 &1428588550372337553
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 3087548688493085045}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 30649d3a9faa99c48a7b1166b86bf2a0, type: 3}
m_Name:
m_EditorClassIdentifier: UnityEngine.UI::UnityEngine.UI.HorizontalLayoutGroup
m_Padding:
m_Left: 0
m_Right: 50
m_Top: 50
m_Bottom: 0
m_ChildAlignment: 2
m_Spacing: 25
m_ChildForceExpandWidth: 0
m_ChildForceExpandHeight: 0
m_ChildControlWidth: 0 m_ChildControlWidth: 0
m_ChildControlHeight: 0 m_ChildControlHeight: 0
m_ChildScaleWidth: 0 m_ChildScaleWidth: 0
m_ChildScaleHeight: 0 m_ChildScaleHeight: 0
m_ReverseArrangement: 0 m_ReverseArrangement: 0
--- !u!114 &7088646692690443534
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 3087548688493085045}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: e8bcb252540442e593cc6467c518ff75, type: 3}
m_Name:
m_EditorClassIdentifier: AppleHillsScripts::Minigames.CardSorting.UI.LivesDisplay
layoutGroup: {fileID: 1428588550372337553}
animationDuration: 0.8
scaleMultiplier: 1.5
--- !u!1 &5517642253100401386 --- !u!1 &5517642253100401386
GameObject: GameObject:
m_ObjectHideFlags: 0 m_ObjectHideFlags: 0
@@ -372,7 +451,7 @@ RectTransform:
m_AnchorMin: {x: 0, y: 0} m_AnchorMin: {x: 0, y: 0}
m_AnchorMax: {x: 0, y: 0} m_AnchorMax: {x: 0, y: 0}
m_AnchoredPosition: {x: 0, y: 0} m_AnchoredPosition: {x: 0, y: 0}
m_SizeDelta: {x: 200, y: 50} m_SizeDelta: {x: 0, y: 0}
m_Pivot: {x: 0.5, y: 0.5} m_Pivot: {x: 0.5, y: 0.5}
--- !u!222 &6941393494789352809 --- !u!222 &6941393494789352809
CanvasRenderer: CanvasRenderer:
@@ -429,15 +508,15 @@ MonoBehaviour:
m_faceColor: m_faceColor:
serializedVersion: 2 serializedVersion: 2
rgba: 4294967295 rgba: 4294967295
m_fontSize: 100 m_fontSize: 75
m_fontSizeBase: 100 m_fontSizeBase: 75
m_fontWeight: 400 m_fontWeight: 400
m_enableAutoSizing: 0 m_enableAutoSizing: 0
m_fontSizeMin: 18 m_fontSizeMin: 18
m_fontSizeMax: 72 m_fontSizeMax: 72
m_fontStyle: 1 m_fontStyle: 1
m_HorizontalAlignment: 1 m_HorizontalAlignment: 2
m_VerticalAlignment: 256 m_VerticalAlignment: 512
m_textAlignment: 65535 m_textAlignment: 65535
m_characterSpacing: 0 m_characterSpacing: 0
m_wordSpacing: 0 m_wordSpacing: 0
@@ -473,6 +552,81 @@ MonoBehaviour:
m_hasFontAssetChanged: 0 m_hasFontAssetChanged: 0
m_baseMaterial: {fileID: 0} m_baseMaterial: {fileID: 0}
m_maskOffset: {x: 0, y: 0, z: 0, w: 0} m_maskOffset: {x: 0, y: 0, z: 0, w: 0}
--- !u!1 &5874089027455915553
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 3256183770156166523}
- component: {fileID: 8780034033757607403}
- component: {fileID: 5667348465997950205}
m_Layer: 5
m_Name: Image
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!224 &3256183770156166523
RectTransform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 5874089027455915553}
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: 3245381937614859990}
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: 100, y: 100}
m_Pivot: {x: 0.5, y: 0.5}
--- !u!222 &8780034033757607403
CanvasRenderer:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 5874089027455915553}
m_CullTransparentMesh: 1
--- !u!114 &5667348465997950205
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 5874089027455915553}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3}
m_Name:
m_EditorClassIdentifier: UnityEngine.UI::UnityEngine.UI.Image
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_Sprite: {fileID: 6741082507358000137, guid: 2406a20d35d3e8946b1e3f83ecd269c2, type: 3}
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 &7537653696112211670 --- !u!1 &7537653696112211670
GameObject: GameObject:
m_ObjectHideFlags: 0 m_ObjectHideFlags: 0
@@ -503,6 +657,7 @@ RectTransform:
m_ConstrainProportionsScale: 0 m_ConstrainProportionsScale: 0
m_Children: m_Children:
- {fileID: 8952171418799244803} - {fileID: 8952171418799244803}
- {fileID: 3245381937614859990}
m_Father: {fileID: 0} m_Father: {fileID: 0}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0, y: 0} m_AnchorMin: {x: 0, y: 0}

View File

@@ -1059,8 +1059,8 @@ RectTransform:
- {fileID: 5196078022198364906} - {fileID: 5196078022198364906}
m_Father: {fileID: 5616388183832902964} m_Father: {fileID: 5616388183832902964}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0.2, y: 0.12} m_AnchorMin: {x: 0.15, y: 0.15}
m_AnchorMax: {x: 0.9, y: 0.85} m_AnchorMax: {x: 0.9, y: 0.8}
m_AnchoredPosition: {x: 0, y: 0} m_AnchoredPosition: {x: 0, y: 0}
m_SizeDelta: {x: 0, y: 0} m_SizeDelta: {x: 0, y: 0}
m_Pivot: {x: 0.5, y: 0.5} m_Pivot: {x: 0.5, y: 0.5}
@@ -1083,7 +1083,7 @@ MonoBehaviour:
m_Bottom: 0 m_Bottom: 0
m_ChildAlignment: 0 m_ChildAlignment: 0
m_Spacing: 10 m_Spacing: 10
m_ChildForceExpandWidth: 1 m_ChildForceExpandWidth: 0
m_ChildForceExpandHeight: 0 m_ChildForceExpandHeight: 0
m_ChildControlWidth: 1 m_ChildControlWidth: 1
m_ChildControlHeight: 0 m_ChildControlHeight: 0

View File

@@ -160,6 +160,7 @@ MonoBehaviour:
- {fileID: 254614580} - {fileID: 254614580}
- {fileID: 222800744} - {fileID: 222800744}
impulseSource: {fileID: 90351797} impulseSource: {fileID: 90351797}
livesDisplay: {fileID: 934831599}
--- !u!4 &90351796 --- !u!4 &90351796
Transform: Transform:
m_ObjectHideFlags: 0 m_ObjectHideFlags: 0
@@ -1025,6 +1026,134 @@ MonoBehaviour:
m_PostInfinity: 2 m_PostInfinity: 2
m_RotationOrder: 4 m_RotationOrder: 4
CustomBlends: {fileID: 0} CustomBlends: {fileID: 0}
--- !u!114 &934831599 stripped
MonoBehaviour:
m_CorrespondingSourceObject: {fileID: 7088646692690443534, guid: acc5a752dcc18834b984fe78b6926dad, type: 3}
m_PrefabInstance: {fileID: 384779534298969466}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: e8bcb252540442e593cc6467c518ff75, type: 3}
m_Name:
m_EditorClassIdentifier: AppleHillsScripts::Minigames.CardSorting.UI.LivesDisplay
--- !u!1 &1018422025
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 1018422026}
- component: {fileID: 1018422030}
- component: {fileID: 1018422029}
- component: {fileID: 1018422028}
- component: {fileID: 1018422027}
m_Layer: 0
m_Name: CanvasHitbox
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!224 &1018422026
RectTransform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1018422025}
m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 0.010527978, y: 0.04, z: 0.08}
m_ConstrainProportionsScale: 1
m_Children: []
m_Father: {fileID: 1020017635}
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: 800, y: 113.2}
m_Pivot: {x: 0.5, y: 0.5}
--- !u!114 &1018422027
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1018422025}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3}
m_Name:
m_EditorClassIdentifier: UnityEngine.UI::UnityEngine.UI.Image
m_Material: {fileID: 0}
m_Color: {r: 1, g: 1, b: 1, a: 0.003921569}
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!222 &1018422028
CanvasRenderer:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1018422025}
m_CullTransparentMesh: 1
--- !u!114 &1018422029
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1018422025}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: dc42784cf147c0c48a680349fa168899, type: 3}
m_Name:
m_EditorClassIdentifier: UnityEngine.UI::UnityEngine.UI.GraphicRaycaster
m_IgnoreReversedGraphics: 1
m_BlockingObjects: 0
m_BlockingMask:
serializedVersion: 2
m_Bits: 4294967295
--- !u!223 &1018422030
Canvas:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1018422025}
m_Enabled: 1
serializedVersion: 3
m_RenderMode: 2
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!1 &1020017633 --- !u!1 &1020017633
GameObject: GameObject:
m_ObjectHideFlags: 0 m_ObjectHideFlags: 0
@@ -1034,6 +1163,7 @@ GameObject:
serializedVersion: 6 serializedVersion: 6
m_Component: m_Component:
- component: {fileID: 1020017635} - component: {fileID: 1020017635}
- component: {fileID: 1020017636}
- component: {fileID: 1020017634} - component: {fileID: 1020017634}
m_Layer: 0 m_Layer: 0
m_Name: ConveyorBeltVisual m_Name: ConveyorBeltVisual
@@ -1086,8 +1216,8 @@ SpriteRenderer:
m_AutoUVMaxAngle: 89 m_AutoUVMaxAngle: 89
m_LightmapParameters: {fileID: 0} m_LightmapParameters: {fileID: 0}
m_GlobalIlluminationMeshLod: 0 m_GlobalIlluminationMeshLod: 0
m_SortingLayerID: 0 m_SortingLayerID: 622133659
m_SortingLayer: 0 m_SortingLayer: -1
m_SortingOrder: 0 m_SortingOrder: 0
m_Sprite: {fileID: -7171755534009967257, guid: b382c7219ff059c499bdeba018f5a93f, type: 3} m_Sprite: {fileID: -7171755534009967257, guid: b382c7219ff059c499bdeba018f5a93f, type: 3}
m_Color: {r: 1, g: 1, b: 1, a: 1} m_Color: {r: 1, g: 1, b: 1, a: 1}
@@ -1109,15 +1239,36 @@ Transform:
m_GameObject: {fileID: 1020017633} m_GameObject: {fileID: 1020017633}
serializedVersion: 2 serializedVersion: 2
m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
m_LocalPosition: {x: 0, y: -10.39, z: 0} m_LocalPosition: {x: 0, y: -10.08, z: 0}
m_LocalScale: {x: 7.5988007, y: 1.4, z: 1} m_LocalScale: {x: 7.5988007, y: 2, z: 1}
m_ConstrainProportionsScale: 0 m_ConstrainProportionsScale: 0
m_Children: m_Children:
- {fileID: 1122663370} - {fileID: 1122663370}
- {fileID: 1595343397} - {fileID: 1595343397}
- {fileID: 1992305141} - {fileID: 1992305141}
- {fileID: 1018422026}
m_Father: {fileID: 0} m_Father: {fileID: 0}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!114 &1020017636
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1020017633}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 9304d17587314133a4d8d1e582cfbf81, type: 3}
m_Name:
m_EditorClassIdentifier: AppleHillsScripts::Minigames.CardSorting.Core.ConveyorBeltSlot
slotIndex: -1
isLocked: 0
hideImageOnPlay: 0
filterByType: 0
allowedTypeNames: []
occupantSizeMode: 0
occupantScale: {x: 1, y: 1, z: 1}
scaleTransitionDuration: 0.3
--- !u!1 &1122663369 --- !u!1 &1122663369
GameObject: GameObject:
m_ObjectHideFlags: 0 m_ObjectHideFlags: 0
@@ -1438,34 +1589,6 @@ PrefabInstance:
propertyPath: m_LocalEulerAnglesHint.z propertyPath: m_LocalEulerAnglesHint.z
value: 0 value: 0
objectReference: {fileID: 0} objectReference: {fileID: 0}
- target: {fileID: 1653089319504497253, guid: acc5a752dcc18834b984fe78b6926dad, type: 3}
propertyPath: m_fontSize
value: 100
objectReference: {fileID: 0}
- target: {fileID: 1653089319504497253, guid: acc5a752dcc18834b984fe78b6926dad, type: 3}
propertyPath: m_enableAutoSizing
value: 0
objectReference: {fileID: 0}
- target: {fileID: 1653089319504497253, guid: acc5a752dcc18834b984fe78b6926dad, type: 3}
propertyPath: m_VerticalAlignment
value: 512
objectReference: {fileID: 0}
- target: {fileID: 1653089319504497253, guid: acc5a752dcc18834b984fe78b6926dad, type: 3}
propertyPath: m_HorizontalAlignment
value: 2
objectReference: {fileID: 0}
- target: {fileID: 1929638711813240478, guid: acc5a752dcc18834b984fe78b6926dad, type: 3}
propertyPath: m_text
value: 'Accuracy: 0%'
objectReference: {fileID: 0}
- target: {fileID: 1929638711813240478, guid: acc5a752dcc18834b984fe78b6926dad, type: 3}
propertyPath: m_VerticalAlignment
value: 512
objectReference: {fileID: 0}
- target: {fileID: 1929638711813240478, guid: acc5a752dcc18834b984fe78b6926dad, type: 3}
propertyPath: m_HorizontalAlignment
value: 2
objectReference: {fileID: 0}
- target: {fileID: 3193587684358315289, guid: acc5a752dcc18834b984fe78b6926dad, type: 3} - target: {fileID: 3193587684358315289, guid: acc5a752dcc18834b984fe78b6926dad, type: 3}
propertyPath: m_AnchorMax.y propertyPath: m_AnchorMax.y
value: 0 value: 0
@@ -1476,7 +1599,7 @@ PrefabInstance:
objectReference: {fileID: 0} objectReference: {fileID: 0}
- target: {fileID: 3193587684358315289, guid: acc5a752dcc18834b984fe78b6926dad, type: 3} - target: {fileID: 3193587684358315289, guid: acc5a752dcc18834b984fe78b6926dad, type: 3}
propertyPath: m_SizeDelta.x propertyPath: m_SizeDelta.x
value: 400 value: 0
objectReference: {fileID: 0} objectReference: {fileID: 0}
- target: {fileID: 3193587684358315289, guid: acc5a752dcc18834b984fe78b6926dad, type: 3} - target: {fileID: 3193587684358315289, guid: acc5a752dcc18834b984fe78b6926dad, type: 3}
propertyPath: m_SizeDelta.y propertyPath: m_SizeDelta.y
@@ -1490,25 +1613,21 @@ PrefabInstance:
propertyPath: m_AnchoredPosition.y propertyPath: m_AnchoredPosition.y
value: 0 value: 0
objectReference: {fileID: 0} objectReference: {fileID: 0}
- target: {fileID: 3416581373676611538, guid: acc5a752dcc18834b984fe78b6926dad, type: 3} - target: {fileID: 3256183770156166523, guid: acc5a752dcc18834b984fe78b6926dad, type: 3}
propertyPath: m_ChildControlHeight propertyPath: m_AnchorMax.y
value: 1 value: 0
objectReference: {fileID: 0} objectReference: {fileID: 0}
- target: {fileID: 3416581373676611538, guid: acc5a752dcc18834b984fe78b6926dad, type: 3} - target: {fileID: 3256183770156166523, guid: acc5a752dcc18834b984fe78b6926dad, type: 3}
propertyPath: m_ChildForceExpandHeight propertyPath: m_AnchorMin.y
value: 1 value: 0
objectReference: {fileID: 0} objectReference: {fileID: 0}
- target: {fileID: 4139981089598238079, guid: acc5a752dcc18834b984fe78b6926dad, type: 3} - target: {fileID: 3256183770156166523, guid: acc5a752dcc18834b984fe78b6926dad, type: 3}
propertyPath: m_text propertyPath: m_AnchoredPosition.x
value: 'Score: 0' value: 0
objectReference: {fileID: 0} objectReference: {fileID: 0}
- target: {fileID: 4139981089598238079, guid: acc5a752dcc18834b984fe78b6926dad, type: 3} - target: {fileID: 3256183770156166523, guid: acc5a752dcc18834b984fe78b6926dad, type: 3}
propertyPath: m_VerticalAlignment propertyPath: m_AnchoredPosition.y
value: 512 value: 0
objectReference: {fileID: 0}
- target: {fileID: 4139981089598238079, guid: acc5a752dcc18834b984fe78b6926dad, type: 3}
propertyPath: m_HorizontalAlignment
value: 2
objectReference: {fileID: 0} objectReference: {fileID: 0}
- target: {fileID: 4881154979794857029, guid: acc5a752dcc18834b984fe78b6926dad, type: 3} - target: {fileID: 4881154979794857029, guid: acc5a752dcc18834b984fe78b6926dad, type: 3}
propertyPath: m_AnchorMax.y propertyPath: m_AnchorMax.y
@@ -1518,6 +1637,10 @@ PrefabInstance:
propertyPath: m_AnchorMin.y propertyPath: m_AnchorMin.y
value: 0 value: 0
objectReference: {fileID: 0} objectReference: {fileID: 0}
- target: {fileID: 4881154979794857029, guid: acc5a752dcc18834b984fe78b6926dad, type: 3}
propertyPath: m_SizeDelta.x
value: 0
objectReference: {fileID: 0}
- target: {fileID: 4881154979794857029, guid: acc5a752dcc18834b984fe78b6926dad, type: 3} - target: {fileID: 4881154979794857029, guid: acc5a752dcc18834b984fe78b6926dad, type: 3}
propertyPath: m_SizeDelta.y propertyPath: m_SizeDelta.y
value: 0 value: 0
@@ -1544,7 +1667,7 @@ PrefabInstance:
objectReference: {fileID: 0} objectReference: {fileID: 0}
- target: {fileID: 8294328820939081367, guid: acc5a752dcc18834b984fe78b6926dad, type: 3} - target: {fileID: 8294328820939081367, guid: acc5a752dcc18834b984fe78b6926dad, type: 3}
propertyPath: m_SizeDelta.x propertyPath: m_SizeDelta.x
value: 600 value: 0
objectReference: {fileID: 0} objectReference: {fileID: 0}
- target: {fileID: 8294328820939081367, guid: acc5a752dcc18834b984fe78b6926dad, type: 3} - target: {fileID: 8294328820939081367, guid: acc5a752dcc18834b984fe78b6926dad, type: 3}
propertyPath: m_SizeDelta.y propertyPath: m_SizeDelta.y
@@ -2013,11 +2136,11 @@ PrefabInstance:
objectReference: {fileID: 0} objectReference: {fileID: 0}
- target: {fileID: 9145507277845748449, guid: c8846509eba59f84aa047197fe02375b, type: 3} - target: {fileID: 9145507277845748449, guid: c8846509eba59f84aa047197fe02375b, type: 3}
propertyPath: m_SizeDelta.x propertyPath: m_SizeDelta.x
value: 1000 value: 1200
objectReference: {fileID: 0} objectReference: {fileID: 0}
- target: {fileID: 9145507277845748449, guid: c8846509eba59f84aa047197fe02375b, type: 3} - target: {fileID: 9145507277845748449, guid: c8846509eba59f84aa047197fe02375b, type: 3}
propertyPath: m_SizeDelta.y propertyPath: m_SizeDelta.y
value: 1000 value: 1200
objectReference: {fileID: 0} objectReference: {fileID: 0}
- target: {fileID: 9145507277845748449, guid: c8846509eba59f84aa047197fe02375b, type: 3} - target: {fileID: 9145507277845748449, guid: c8846509eba59f84aa047197fe02375b, type: 3}
propertyPath: m_LocalPosition.x propertyPath: m_LocalPosition.x

View File

@@ -55,10 +55,24 @@ namespace Core.Settings
[Tooltip("Points deducted when item falls off belt")] [Tooltip("Points deducted when item falls off belt")]
[SerializeField] private int missedItemPenalty = -3; [SerializeField] private int missedItemPenalty = -3;
[Header("Lives")]
[Tooltip("Maximum number of lives player has")]
[SerializeField] private int maxLives = 3;
[Header("Rewards")] [Header("Rewards")]
[Tooltip("Booster packs awarded per correct sort")] [Tooltip("Booster packs awarded per correct sort")]
[SerializeField] private int boosterPacksPerCorrectItem = 1; [SerializeField] private int boosterPacksPerCorrectItem = 1;
[Header("Visual")]
[Tooltip("Size of spawned cards (width, height)")]
[SerializeField] private Vector2 cardSize = new Vector2(100f, 140f);
[Tooltip("Random offset range for spawn distance X (min, max). Range: -10 to 10")]
[SerializeField] private Vector2 spawnOffsetX = new Vector2(-5f, 5f);
[Tooltip("Random offset range for spawn Y position (min, max). Range: -10 to 10")]
[SerializeField] private Vector2 spawnOffsetY = new Vector2(-10f, 10f);
// Interface implementation // Interface implementation
public float GameDuration => gameDuration; public float GameDuration => gameDuration;
@@ -75,7 +89,11 @@ namespace Core.Settings
public int CorrectSortPoints => correctSortPoints; public int CorrectSortPoints => correctSortPoints;
public int IncorrectSortPenalty => incorrectSortPenalty; public int IncorrectSortPenalty => incorrectSortPenalty;
public int MissedItemPenalty => missedItemPenalty; public int MissedItemPenalty => missedItemPenalty;
public int MaxLives => maxLives;
public int BoosterPacksPerCorrectItem => boosterPacksPerCorrectItem; public int BoosterPacksPerCorrectItem => boosterPacksPerCorrectItem;
public Vector2 CardSize => cardSize;
public Vector2 SpawnOffsetX => spawnOffsetX;
public Vector2 SpawnOffsetY => spawnOffsetY;
public override void OnValidate() public override void OnValidate()
{ {
@@ -86,6 +104,15 @@ namespace Core.Settings
maxBeltSpeed = Mathf.Max(initialBeltSpeed, maxBeltSpeed); maxBeltSpeed = Mathf.Max(initialBeltSpeed, maxBeltSpeed);
correctSortPoints = Mathf.Max(0, correctSortPoints); correctSortPoints = Mathf.Max(0, correctSortPoints);
boosterPacksPerCorrectItem = Mathf.Max(0, boosterPacksPerCorrectItem); boosterPacksPerCorrectItem = Mathf.Max(0, boosterPacksPerCorrectItem);
maxLives = Mathf.Max(1, maxLives);
cardSize.x = Mathf.Max(1f, cardSize.x);
cardSize.y = Mathf.Max(1f, cardSize.y);
// Clamp spawn offsets between -10 and 10
spawnOffsetX.x = Mathf.Clamp(spawnOffsetX.x, -10f, 10f);
spawnOffsetX.y = Mathf.Clamp(spawnOffsetX.y, -10f, 10f);
spawnOffsetY.x = Mathf.Clamp(spawnOffsetY.x, -10f, 10f);
spawnOffsetY.y = Mathf.Clamp(spawnOffsetY.y, -10f, 10f);
} }
} }
} }

View File

@@ -32,8 +32,16 @@ namespace Core.Settings
int IncorrectSortPenalty { get; } int IncorrectSortPenalty { get; }
int MissedItemPenalty { get; } int MissedItemPenalty { get; }
// Lives
int MaxLives { get; }
// Rewards // Rewards
int BoosterPacksPerCorrectItem { get; } int BoosterPacksPerCorrectItem { get; }
// Visual
Vector2 CardSize { get; }
Vector2 SpawnOffsetX { get; } // Min/Max range for X offset (distance)
Vector2 SpawnOffsetY { get; } // Min/Max range for Y offset (position)
} }
} }

View File

@@ -4,6 +4,7 @@ using Data.CardSystem;
using Minigames.CardSorting.Core; using Minigames.CardSorting.Core;
using Minigames.CardSorting.Data; using Minigames.CardSorting.Data;
using System.Collections.Generic; using System.Collections.Generic;
using UI.DragAndDrop.Core;
using UnityEngine; using UnityEngine;
namespace Minigames.CardSorting.Controllers namespace Minigames.CardSorting.Controllers
@@ -27,6 +28,8 @@ namespace Minigames.CardSorting.Controllers
private HashSet<SortableItem> missedItems = new HashSet<SortableItem>(); // Items past visual end, moving to despawn private HashSet<SortableItem> missedItems = new HashSet<SortableItem>(); // Items past visual end, moving to despawn
private float currentSpeed; private float currentSpeed;
private SortableItem lastSpawnedItem; // Track last spawned item for distance-based spawning private SortableItem lastSpawnedItem; // Track last spawned item for distance-based spawning
private float cachedSpawnOffsetX; // Cached random offset for next spawn
private bool isGameOver = false; // Flag to stop conveyor when game ends
// Events - conveyor owns item lifecycle // Events - conveyor owns item lifecycle
public event System.Action<SortableItem> OnItemSpawned; // Fired when new item spawns public event System.Action<SortableItem> OnItemSpawned; // Fired when new item spawns
@@ -37,6 +40,7 @@ namespace Minigames.CardSorting.Controllers
public float CurrentSpeed => currentSpeed; public float CurrentSpeed => currentSpeed;
public int ActiveItemCount => activeItems.Count; public int ActiveItemCount => activeItems.Count;
public bool IsGameOver => isGameOver;
public ConveyorBeltController( public ConveyorBeltController(
Transform spawnPoint, Transform spawnPoint,
@@ -55,6 +59,9 @@ namespace Minigames.CardSorting.Controllers
this.currentSpeed = settings.InitialBeltSpeed; this.currentSpeed = settings.InitialBeltSpeed;
this.lastSpawnedItem = null; // No items spawned yet this.lastSpawnedItem = null; // No items spawned yet
// Initialize first cached offset
this.cachedSpawnOffsetX = Random.Range(settings.SpawnOffsetX.x, settings.SpawnOffsetX.y);
} }
/// <summary> /// <summary>
@@ -62,6 +69,9 @@ namespace Minigames.CardSorting.Controllers
/// </summary> /// </summary>
public void Update(float deltaTime, float gameProgress) public void Update(float deltaTime, float gameProgress)
{ {
// Stop processing if game is over
if (isGameOver) return;
UpdateBeltSpeed(gameProgress); UpdateBeltSpeed(gameProgress);
CheckItemsOffBelt(); CheckItemsOffBelt();
CheckDistanceBasedSpawn(gameProgress); CheckDistanceBasedSpawn(gameProgress);
@@ -70,6 +80,7 @@ namespace Minigames.CardSorting.Controllers
/// <summary> /// <summary>
/// Check if we should spawn a new item based on distance from last spawn. /// Check if we should spawn a new item based on distance from last spawn.
/// Items spawn when last item has moved far enough from spawn point. /// Items spawn when last item has moved far enough from spawn point.
/// Uses cached random X offset for spawn distance variation.
/// </summary> /// </summary>
private void CheckDistanceBasedSpawn(float gameProgress) private void CheckDistanceBasedSpawn(float gameProgress)
{ {
@@ -77,15 +88,24 @@ namespace Minigames.CardSorting.Controllers
if (lastSpawnedItem == null) if (lastSpawnedItem == null)
{ {
SpawnNewItem(gameProgress); SpawnNewItem(gameProgress);
// Generate new offset for next spawn
cachedSpawnOffsetX = Random.Range(settings.SpawnOffsetX.x, settings.SpawnOffsetX.y);
return; return;
} }
// Check if last spawned item is far enough from spawn point // Check if last spawned item is far enough from spawn point
float distanceFromSpawn = Mathf.Abs(lastSpawnedItem.transform.position.x - spawnPoint.position.x); float distanceFromSpawn = Mathf.Abs(lastSpawnedItem.transform.position.x - spawnPoint.position.x);
if (distanceFromSpawn >= settings.SpawnDistance) // Using InitialSpawnInterval as distance threshold // Use cached offset for required distance
float requiredDistance = settings.SpawnDistance + cachedSpawnOffsetX;
if (distanceFromSpawn >= requiredDistance)
{ {
SpawnNewItem(gameProgress); SpawnNewItem(gameProgress);
// Generate new offset for next spawn
cachedSpawnOffsetX = Random.Range(settings.SpawnOffsetX.x, settings.SpawnOffsetX.y);
} }
} }
@@ -145,16 +165,27 @@ namespace Minigames.CardSorting.Controllers
GarbageItemDefinition garbage = SelectRandomGarbage(); GarbageItemDefinition garbage = SelectRandomGarbage();
GameObject obj = Object.Instantiate(garbagePrefab, spawnPoint.position, Quaternion.identity); // Apply random Y offset to spawn position
float randomOffsetY = Random.Range(settings.SpawnOffsetY.x, settings.SpawnOffsetY.y);
Vector3 spawnPos = spawnPoint.position + new Vector3(0f, randomOffsetY, 0f);
GameObject obj = Object.Instantiate(garbagePrefab, spawnPos, Quaternion.identity);
SortableItem item = obj.GetComponent<SortableItem>(); SortableItem item = obj.GetComponent<SortableItem>();
if (item != null) if (item != null)
{ {
item.SetupAsGarbage(garbage); item.SetupAsGarbage(garbage);
// Apply card size (garbage items use same size as cards)
ApplyCardSize(item);
// Subscribe to item events // Subscribe to item events
item.OnItemDroppedInBox += HandleItemDroppedInBox; item.OnItemDroppedInBox += HandleItemDroppedInBox;
item.OnItemDroppedOnFloor += HandleItemDroppedOnFloor;
item.OnItemReturnedToConveyor += HandleItemReturnedToConveyor; item.OnItemReturnedToConveyor += HandleItemReturnedToConveyor;
// Subscribe to drag events to remove from tracking
item.OnDragStarted += HandleItemDragStarted;
} }
else else
{ {
@@ -177,16 +208,27 @@ namespace Minigames.CardSorting.Controllers
return null; return null;
} }
GameObject obj = Object.Instantiate(cardPrefab, spawnPoint.position, Quaternion.identity); // Apply random Y offset to spawn position
float randomOffsetY = Random.Range(settings.SpawnOffsetY.x, settings.SpawnOffsetY.y);
Vector3 spawnPos = spawnPoint.position + new Vector3(0f, randomOffsetY, 0f);
GameObject obj = Object.Instantiate(cardPrefab, spawnPos, Quaternion.identity);
SortableItem item = obj.GetComponent<SortableItem>(); SortableItem item = obj.GetComponent<SortableItem>();
if (item != null) if (item != null)
{ {
item.SetupAsCard(cardData); item.SetupAsCard(cardData);
// Apply card size
ApplyCardSize(item);
// Subscribe to item events // Subscribe to item events
item.OnItemDroppedInBox += HandleItemDroppedInBox; item.OnItemDroppedInBox += HandleItemDroppedInBox;
item.OnItemDroppedOnFloor += HandleItemDroppedOnFloor;
item.OnItemReturnedToConveyor += HandleItemReturnedToConveyor; item.OnItemReturnedToConveyor += HandleItemReturnedToConveyor;
// Subscribe to drag events to remove from tracking
item.OnDragStarted += HandleItemDragStarted;
} }
else else
{ {
@@ -296,55 +338,86 @@ namespace Minigames.CardSorting.Controllers
} }
/// <summary>
/// Handle when an item starts being dragged.
/// Remove from tracking to prevent "fell off belt" detection while dragging.
/// </summary>
private void HandleItemDragStarted(DraggableObject draggableObj)
{
SortableItem item = draggableObj as SortableItem;
if (item == null) return;
RemoveItemFromTracking(item);
}
/// <summary> /// <summary>
/// Handle when an item is dropped in a box (correct or incorrect). /// Handle when an item is dropped in a box (correct or incorrect).
/// Note: Item was already removed from activeItems when dragging started (HandleItemDragStarted).
/// </summary> /// </summary>
private void HandleItemDroppedInBox(SortableItem item, SortingBox box, bool correct) private void HandleItemDroppedInBox(SortableItem item, SortingBox box, bool correct)
{ {
// Remove from tracking and unsubscribe // Clean up tracking (item already removed from activeItems when picked up)
if (activeItems.Remove(item)) activeItems.Remove(item);
missedItems.Remove(item);
// Clear lastSpawnedItem reference if this was it
if (lastSpawnedItem == item)
{ {
// Also remove from missed items if it was there lastSpawnedItem = null;
missedItems.Remove(item); }
// Clear lastSpawnedItem reference if this was it // Unsubscribe from events
if (lastSpawnedItem == item) item.OnItemDroppedInBox -= HandleItemDroppedInBox;
{ item.OnItemDroppedOnFloor -= HandleItemDroppedOnFloor;
lastSpawnedItem = null; item.OnItemReturnedToConveyor -= HandleItemReturnedToConveyor;
} item.OnDragStarted -= HandleItemDragStarted;
item.OnItemDroppedInBox -= HandleItemDroppedInBox; // Emit event for game manager to handle scoring, passing box and correctness
item.OnItemReturnedToConveyor -= HandleItemReturnedToConveyor; OnItemSorted?.Invoke(item, box, correct);
}
// Emit event for game manager to handle scoring, passing box and correctness /// <summary>
OnItemSorted?.Invoke(item, box, correct); /// Handle when an item is returned to conveyor (dropped back on conveyor belt).
/// Re-adds item to tracking so it continues moving along the belt.
/// </summary>
private void HandleItemReturnedToConveyor(SortableItem item)
{
if (item == null) return;
// Re-add to active tracking
if (!activeItems.Contains(item))
{
activeItems.Add(item);
Debug.Log($"[ConveyorBeltController] Item returned to conveyor: {item.CardData?.Name ?? item.GarbageItem?.DisplayName}");
} }
} }
/// <summary> /// <summary>
/// Handle when an item is returned to conveyor (dropped outside box). /// Handle when an item is dropped on floor (dropped outside box).
/// Item transitions to DroppedOnFloorState and gets destroyed. /// Item transitions to DroppedOnFloorState and gets destroyed.
/// Note: Item was already removed from activeItems when dragging started (HandleItemDragStarted).
/// </summary> /// </summary>
private void HandleItemReturnedToConveyor(SortableItem item) private void HandleItemDroppedOnFloor(SortableItem item)
{ {
// Remove from tracking and unsubscribe (item will be destroyed) // Clean up tracking (item already removed from activeItems when picked up)
if (activeItems.Remove(item)) activeItems.Remove(item);
missedItems.Remove(item);
if (lastSpawnedItem == item)
{ {
missedItems.Remove(item); lastSpawnedItem = null;
if (lastSpawnedItem == item)
{
lastSpawnedItem = null;
}
item.OnItemDroppedInBox -= HandleItemDroppedInBox;
item.OnItemReturnedToConveyor -= HandleItemReturnedToConveyor;
// Emit event for scoring
OnItemDroppedOnFloor?.Invoke(item);
Debug.Log($"[ConveyorBeltController] Item dropped on floor: {item.CardData?.Name ?? item.GarbageItem?.DisplayName}");
} }
// Unsubscribe from events
item.OnItemDroppedInBox -= HandleItemDroppedInBox;
item.OnItemDroppedOnFloor -= HandleItemDroppedOnFloor;
item.OnItemReturnedToConveyor -= HandleItemReturnedToConveyor;
item.OnDragStarted -= HandleItemDragStarted;
// Emit event for scoring
OnItemDroppedOnFloor?.Invoke(item);
Debug.Log($"[ConveyorBeltController] Item dropped on floor: {item.CardData?.Name ?? item.GarbageItem?.DisplayName}");
} }
private CardRarity DetermineRarity(float roll) private CardRarity DetermineRarity(float roll)
@@ -365,6 +438,65 @@ namespace Minigames.CardSorting.Controllers
{ {
return settings.GarbageItems[Random.Range(0, settings.GarbageItems.Length)]; return settings.GarbageItems[Random.Range(0, settings.GarbageItems.Length)];
} }
/// <summary>
/// Apply configured card size to spawned item.
/// </summary>
private void ApplyCardSize(SortableItem item)
{
if (item == null || item.Context == null || item.Context.RootTransform == null)
return;
// Get the RectTransform to resize (root object)
var rectTransform = item.Context.RootTransform.GetComponent<RectTransform>();
if (rectTransform != null)
{
rectTransform.sizeDelta = settings.CardSize;
}
}
/// <summary>
/// Remove an item from tracking (called when picked up via event).
/// Prevents item from being subject to belt end detection while being dragged.
/// </summary>
private void RemoveItemFromTracking(SortableItem item)
{
if (item == null) return;
bool wasTracked = activeItems.Remove(item);
missedItems.Remove(item);
// Clear lastSpawnedItem reference if this was it
if (lastSpawnedItem == item)
{
lastSpawnedItem = null;
}
if (wasTracked)
{
Debug.Log($"[ConveyorBeltController] Item removed from tracking (picked up): {item.CardData?.Name ?? item.GarbageItem?.DisplayName}");
}
}
/// <summary>
/// Stop the conveyor belt when game ends.
/// Disables dragging on all active items so they can't be interacted with.
/// </summary>
public void StopConveyor()
{
isGameOver = true;
// Disable dragging on all active items
foreach (var item in activeItems)
{
if (item != null)
{
item.SetDraggingEnabled(false);
}
}
Debug.Log("[ConveyorBeltController] Conveyor stopped - all items disabled");
}
} }
} }

View File

@@ -0,0 +1,35 @@
using UI.DragAndDrop.Core;
using UnityEngine;
namespace Minigames.CardSorting.Core
{
/// <summary>
/// Slot component for the conveyor belt.
/// Allows detection when items are dropped back onto the conveyor.
/// Place this on a UI element (Image/Panel) that covers the conveyor belt area.
/// </summary>
public class ConveyorBeltSlot : DraggableSlot
{
/// <summary>
/// Check if this slot can accept a specific draggable type.
/// ConveyorBeltSlot accepts all SortableItems.
/// </summary>
public new bool CanAccept(DraggableObject draggable)
{
// Accept all sortable items
return draggable is SortableItem;
}
/// <summary>
/// Called when an item is dropped on the conveyor.
/// Items dropped here should return to their conveyor state.
/// </summary>
public void OnItemDroppedOnConveyor(SortableItem item)
{
if (item == null) return;
Debug.Log($"[ConveyorBeltSlot] Item dropped back on conveyor: {item.CardData?.Name ?? item.GarbageItem?.DisplayName}");
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 9304d17587314133a4d8d1e582cfbf81
timeCreated: 1763590821

View File

@@ -28,7 +28,8 @@ namespace Minigames.CardSorting.Core
// Events - item emits notifications, conveyor subscribes // Events - item emits notifications, conveyor subscribes
public event System.Action<SortableItem, SortingBox, bool> OnItemDroppedInBox; public event System.Action<SortableItem, SortingBox, bool> OnItemDroppedInBox;
public event System.Action<SortableItem> OnItemReturnedToConveyor; public event System.Action<SortableItem> OnItemDroppedOnFloor;
public event System.Action<SortableItem> OnItemReturnedToConveyor; // Fired when dropped back on conveyor
// Public accessors // Public accessors
public SortableItemContext Context => context; public SortableItemContext Context => context;
@@ -142,9 +143,10 @@ namespace Minigames.CardSorting.Core
{ {
base.OnDragEndedHook(); base.OnDragEndedHook();
// Validate drop on sorting box // Check what type of slot we're over
if (CurrentSlot is SortingBox box) if (CurrentSlot is SortingBox box)
{ {
// Dropped in sorting box
bool correctSort = box.ValidateItem(this); bool correctSort = box.ValidateItem(this);
// Fire event IMMEDIATELY when card is released over bin // Fire event IMMEDIATELY when card is released over bin
@@ -162,10 +164,28 @@ namespace Minigames.CardSorting.Core
ChangeState("SortedIncorrectlyState"); ChangeState("SortedIncorrectlyState");
} }
} }
else if (CurrentSlot is ConveyorBeltSlot conveyorSlot)
{
// Dropped back on conveyor - return to conveyor state
Logging.Debug("[SortableItem] Dropped back on conveyor, returning to OnConveyorState");
// Notify conveyor slot
conveyorSlot.OnItemDroppedOnConveyor(this);
// Fire event for conveyor controller to re-add to tracking
OnItemReturnedToConveyor?.Invoke(this);
// Return to conveyor state
ChangeState("OnConveyorState");
}
else else
{ {
// Dropped outside valid box - transition to dropped on floor state // Dropped outside valid box or conveyor - fire event then transition to dropped on floor state
Logging.Debug("[SortableItem] Dropped outside box, transitioning to floor state"); Logging.Debug("[SortableItem] Dropped outside box/conveyor, transitioning to floor state");
// Fire event for conveyor controller to handle scoring
OnItemDroppedOnFloor?.Invoke(this);
ChangeState("DroppedOnFloorState"); ChangeState("DroppedOnFloorState");
} }
} }
@@ -174,6 +194,7 @@ namespace Minigames.CardSorting.Core
/// <summary> /// <summary>
/// Detect which slot (if any) is under the pointer during drag. /// Detect which slot (if any) is under the pointer during drag.
/// Updates CurrentSlot for drop detection. /// Updates CurrentSlot for drop detection.
/// Scans for both SortingBox and ConveyorBeltSlot.
/// </summary> /// </summary>
private void DetectSlotUnderPointer(UnityEngine.EventSystems.PointerEventData eventData) private void DetectSlotUnderPointer(UnityEngine.EventSystems.PointerEventData eventData)
{ {
@@ -181,29 +202,46 @@ namespace Minigames.CardSorting.Core
var raycastResults = new System.Collections.Generic.List<UnityEngine.EventSystems.RaycastResult>(); var raycastResults = new System.Collections.Generic.List<UnityEngine.EventSystems.RaycastResult>();
UnityEngine.EventSystems.EventSystem.current.RaycastAll(eventData, raycastResults); UnityEngine.EventSystems.EventSystem.current.RaycastAll(eventData, raycastResults);
SortingBox hoveredBox = null; DraggableSlot hoveredSlot = null;
// Find first SortingBox in raycast results // Find first slot (SortingBox or ConveyorBeltSlot) in raycast results
foreach (var result in raycastResults) foreach (var result in raycastResults)
{ {
// Check for SortingBox first (higher priority)
var box = result.gameObject.GetComponentInParent<SortingBox>(); var box = result.gameObject.GetComponentInParent<SortingBox>();
if (box != null) if (box != null)
{ {
hoveredBox = box; hoveredSlot = box;
break;
}
// Check for ConveyorBeltSlot if no box found
var conveyorSlot = result.gameObject.GetComponentInParent<ConveyorBeltSlot>();
if (conveyorSlot != null)
{
hoveredSlot = conveyorSlot;
break; break;
} }
} }
// Update current slot (used in OnDragEndedHook) // Update current slot (used in OnDragEndedHook)
if (hoveredBox != null && hoveredBox != CurrentSlot) if (hoveredSlot != null && hoveredSlot != CurrentSlot)
{ {
_currentSlot = hoveredBox; _currentSlot = hoveredSlot;
Logging.Debug($"[SortableItem] Now hovering over {hoveredBox.BoxType} box");
if (hoveredSlot is SortingBox sortBox)
{
Logging.Debug($"[SortableItem] Now hovering over {sortBox.BoxType} box");
}
else if (hoveredSlot is ConveyorBeltSlot)
{
Logging.Debug("[SortableItem] Now hovering over conveyor belt");
}
} }
else if (hoveredBox == null && CurrentSlot != null) else if (hoveredSlot == null && CurrentSlot != null)
{ {
_currentSlot = null; _currentSlot = null;
Logging.Debug("[SortableItem] No longer over any box"); Logging.Debug("[SortableItem] No longer over any slot");
} }
} }

View File

@@ -29,9 +29,15 @@ namespace Minigames.CardSorting.Core
[Header("Effects")] [Header("Effects")]
[SerializeField] private CinemachineImpulseSource impulseSource; // Screen shake on incorrect sort [SerializeField] private CinemachineImpulseSource impulseSource; // Screen shake on incorrect sort
[Header("UI References")]
[SerializeField] private UI.LivesDisplay livesDisplay;
// Settings // Settings
private ICardSortingSettings _settings; private ICardSortingSettings _settings;
// Lives tracking
private int currentLives;
// Controllers (lazy init) // Controllers (lazy init)
private ConveyorBeltController _conveyorController; private ConveyorBeltController _conveyorController;
private ConveyorBeltController Conveyor => _conveyorController ??= new ConveyorBeltController( private ConveyorBeltController Conveyor => _conveyorController ??= new ConveyorBeltController(
@@ -43,6 +49,9 @@ namespace Minigames.CardSorting.Core
_settings _settings
); );
// Public accessor for states to check game over status
public ConveyorBeltController ConveyorController => Conveyor;
private SortingScoreController _scoreController; private SortingScoreController _scoreController;
private SortingScoreController Score => _scoreController ??= new SortingScoreController(_settings); private SortingScoreController Score => _scoreController ??= new SortingScoreController(_settings);
@@ -152,6 +161,13 @@ namespace Minigames.CardSorting.Core
// Reset score // Reset score
Score.Reset(); Score.Reset();
// Initialize lives
currentLives = _settings.MaxLives;
if (livesDisplay != null)
{
livesDisplay.Initialize(currentLives);
}
OnGameStarted?.Invoke(); OnGameStarted?.Invoke();
// Set input mode to game // Set input mode to game
@@ -163,6 +179,61 @@ namespace Minigames.CardSorting.Core
Logging.Debug("[SortingGameManager] Game started!"); Logging.Debug("[SortingGameManager] Game started!");
} }
/// <summary>
/// Unified feedback for wrong actions (trash penalties).
/// Plays blink animation on item and triggers camera shake.
/// </summary>
public void PlayWrongStateFeedback(SortableItem item)
{
// Camera shake
if (impulseSource != null)
{
impulseSource.GenerateImpulse();
}
// Blink the item red (if it still exists)
if (item != null && item.Context != null && item.Context.Animator != null)
{
UnityEngine.UI.Image imageToBlink = null;
if (item.Context.CardDisplay != null)
{
imageToBlink = item.Context.CardDisplay.GetComponent<UnityEngine.UI.Image>();
}
else if (item.Context.GarbageVisual != null)
{
imageToBlink = item.Context.GarbageVisual.GetComponent<UnityEngine.UI.Image>();
}
if (imageToBlink != null)
{
item.Context.Animator.BlinkRed(imageToBlink, 0.15f);
}
}
}
/// <summary>
/// Lose a life. If lives reach 0, end the game.
/// </summary>
private void LoseLife()
{
currentLives--;
if (livesDisplay != null)
{
livesDisplay.RemoveLife();
}
Logging.Debug($"[SortingGameManager] Life lost! Remaining lives: {currentLives}");
// Check for game over
if (currentLives <= 0)
{
Logging.Debug("[SortingGameManager] No lives remaining - Game Over!");
EndGame();
}
}
public void EndGame() public void EndGame()
{ {
if (isGameOver) return; if (isGameOver) return;
@@ -170,6 +241,9 @@ namespace Minigames.CardSorting.Core
isGameOver = true; isGameOver = true;
isGameActive = false; isGameActive = false;
// Stop the conveyor and disable all items
Conveyor.StopConveyor();
// Calculate rewards // Calculate rewards
int boosterReward = Score.CalculateBoosterReward(); int boosterReward = Score.CalculateBoosterReward();
@@ -201,7 +275,7 @@ namespace Minigames.CardSorting.Core
/// Called when item reaches visual end of belt (via conveyor event). /// Called when item reaches visual end of belt (via conveyor event).
/// Item continues moving off-screen until despawn point. /// Item continues moving off-screen until despawn point.
/// Scoring rules: /// Scoring rules:
/// - Trash fell off: Negative score (penalty) /// - Trash fell off: Negative score (penalty) + lose life
/// - Card fell off: Neutral (no score change) /// - Card fell off: Neutral (no score change)
/// </summary> /// </summary>
private void OnConveyorItemFellOff(SortableItem item) private void OnConveyorItemFellOff(SortableItem item)
@@ -211,6 +285,8 @@ namespace Minigames.CardSorting.Core
if (item.IsGarbage) if (item.IsGarbage)
{ {
Score.RecordMissedItem(); Score.RecordMissedItem();
PlayWrongStateFeedback(item);
LoseLife();
Logging.Debug($"[SortingGameManager] Trash fell off belt! {item.GarbageItem?.DisplayName} - PENALTY"); Logging.Debug($"[SortingGameManager] Trash fell off belt! {item.GarbageItem?.DisplayName} - PENALTY");
} }
else else
@@ -228,7 +304,7 @@ namespace Minigames.CardSorting.Core
/// <summary> /// <summary>
/// Called when item is dropped on floor (via conveyor event). /// Called when item is dropped on floor (via conveyor event).
/// Scoring rules: /// Scoring rules:
/// - Trash dropped on floor: Negative score (penalty) /// - Trash dropped on floor: Negative score (penalty) + lose life
/// - Card dropped on floor: Neutral (no score change) /// - Card dropped on floor: Neutral (no score change)
/// </summary> /// </summary>
private void OnConveyorItemDroppedOnFloor(SortableItem item) private void OnConveyorItemDroppedOnFloor(SortableItem item)
@@ -238,13 +314,9 @@ namespace Minigames.CardSorting.Core
if (item.IsGarbage) if (item.IsGarbage)
{ {
Score.RecordIncorrectSort(); Score.RecordIncorrectSort();
PlayWrongStateFeedback(item);
LoseLife();
Logging.Debug($"[SortingGameManager] Trash dropped on floor! {item.GarbageItem?.DisplayName} - PENALTY"); Logging.Debug($"[SortingGameManager] Trash dropped on floor! {item.GarbageItem?.DisplayName} - PENALTY");
// Trigger screen shake for trash dropped on floor
if (impulseSource != null)
{
impulseSource.GenerateImpulse();
}
} }
else else
{ {
@@ -270,7 +342,7 @@ namespace Minigames.CardSorting.Core
/// Handles scoring only - the state (SortedCorrectlyState/SortedIncorrectlyState) handles animation and destruction. /// Handles scoring only - the state (SortedCorrectlyState/SortedIncorrectlyState) handles animation and destruction.
/// Scoring rules: /// Scoring rules:
/// - Correct sort: Positive score (cards or trash in correct box) /// - Correct sort: Positive score (cards or trash in correct box)
/// - Incorrect trash: Negative score (trash in wrong box) /// - Incorrect trash: Negative score (trash in wrong box) + lose life
/// - Incorrect card: Neutral (no score change) /// - Incorrect card: Neutral (no score change)
/// </summary> /// </summary>
private void OnConveyorItemSorted(SortableItem item, SortingBox box, bool correct) private void OnConveyorItemSorted(SortableItem item, SortingBox box, bool correct)
@@ -290,16 +362,12 @@ namespace Minigames.CardSorting.Core
if (item.IsGarbage) if (item.IsGarbage)
{ {
Score.RecordIncorrectSort(); Score.RecordIncorrectSort();
PlayWrongStateFeedback(item);
LoseLife();
Logging.Debug($"[SortingGameManager] Incorrect trash sort! {item.GarbageItem?.DisplayName} - PENALTY"); Logging.Debug($"[SortingGameManager] Incorrect trash sort! {item.GarbageItem?.DisplayName} - PENALTY");
// Fire global incorrect sort event for effects // Fire global incorrect sort event for effects
OnItemSortedIncorrectly?.Invoke(item); OnItemSortedIncorrectly?.Invoke(item);
// Trigger screen shake
if (impulseSource != null)
{
impulseSource.GenerateImpulse();
}
} }
else else
{ {

View File

@@ -13,10 +13,12 @@ namespace Minigames.CardSorting.StateMachine.States
public class BeingDraggedState : AppleState public class BeingDraggedState : AppleState
{ {
private SortableItemContext _context; private SortableItemContext _context;
private SortableItem _item;
private void Awake() private void Awake()
{ {
_context = GetComponentInParent<SortableItemContext>(); _context = GetComponentInParent<SortableItemContext>();
_item = GetComponentInParent<SortableItem>();
} }
public override void OnEnterState() public override void OnEnterState()
@@ -25,8 +27,6 @@ namespace Minigames.CardSorting.StateMachine.States
_context.IsOnConveyor = false; _context.IsOnConveyor = false;
// Visual feedback: scale up root transform by 10%
// Use OriginalScale from context (captured at spawn, preserves world-space Canvas scale)
if (_context.RootTransform != null && _context.Animator != null) if (_context.RootTransform != null && _context.Animator != null)
{ {
Vector3 targetScale = _context.OriginalScale * 1.1f; Vector3 targetScale = _context.OriginalScale * 1.1f;

View File

@@ -25,45 +25,11 @@ namespace Minigames.CardSorting.StateMachine.States
_context.IsOnConveyor = false; _context.IsOnConveyor = false;
Logging.Debug("[DroppedOnFloorState] Item dropped on floor, blinking red then disappearing"); Logging.Debug("[DroppedOnFloorState] Item dropped on floor, disappearing");
// Blink red briefly, then play disappear animation // Feedback (blink + shake) already triggered by game manager when scoring (for trash)
StartBlinkThenDisappear(); // Just play disappear animation
} PlayDisappearAnimation();
private void StartBlinkThenDisappear()
{
if (_context.Animator == null || _item == null) return;
// Get the image to blink
UnityEngine.UI.Image imageToBlink = null;
if (_context.CardDisplay != null)
{
imageToBlink = _context.CardDisplay.GetComponent<UnityEngine.UI.Image>();
}
else if (_context.GarbageVisual != null)
{
imageToBlink = _context.GarbageVisual.GetComponent<UnityEngine.UI.Image>();
}
if (imageToBlink != null)
{
// Blink red briefly (2-3 times), then stop and disappear
_context.Animator.BlinkRed(imageToBlink, 0.15f); // Fast blink
// After brief delay, stop blinking and play disappear animation
_context.Animator.AnimateScale(_context.RootTransform.localScale, 0.5f, () =>
{
_context.Animator.StopBlinking();
PlayDisappearAnimation();
});
}
else
{
// No image found, just disappear directly
PlayDisappearAnimation();
}
} }
private void PlayDisappearAnimation() private void PlayDisappearAnimation()

View File

@@ -44,6 +44,12 @@ namespace Minigames.CardSorting.StateMachine.States
{ {
if (_context == null || !_context.IsOnConveyor) return; if (_context == null || !_context.IsOnConveyor) return;
// Stop moving if game is over
if (SortingGameManager.Instance.ConveyorController.IsGameOver)
{
return;
}
// Continue moving item toward despawn point (same logic as OnConveyorState) // Continue moving item toward despawn point (same logic as OnConveyorState)
Vector3 movement = Vector3.right * _context.ConveyorSpeed * Time.deltaTime; Vector3 movement = Vector3.right * _context.ConveyorSpeed * Time.deltaTime;
_context.RootTransform.position += movement; _context.RootTransform.position += movement;

View File

@@ -40,6 +40,12 @@ namespace Minigames.CardSorting.StateMachine.States
{ {
if (_context == null || !_context.IsOnConveyor) return; if (_context == null || !_context.IsOnConveyor) return;
// Stop moving if game is over
if (SortingGameManager.Instance.ConveyorController.IsGameOver)
{
return;
}
// Move item along conveyor (right direction) // Move item along conveyor (right direction)
Vector3 movement = Vector3.right * _context.ConveyorSpeed * Time.deltaTime; Vector3 movement = Vector3.right * _context.ConveyorSpeed * Time.deltaTime;
_context.RootTransform.position += movement; _context.RootTransform.position += movement;

View File

@@ -26,45 +26,11 @@ namespace Minigames.CardSorting.StateMachine.States
_context.IsOnConveyor = false; _context.IsOnConveyor = false;
Logging.Debug("[SortedIncorrectlyState] Item incorrectly sorted, blinking red then tweening to box"); Logging.Debug("[SortedIncorrectlyState] Item incorrectly sorted, tweening to box then falling in");
// Start blinking red briefly, then tween to box // Feedback (blink + shake) already triggered by game manager when scoring
StartBlinkThenTween(); // Just proceed with animation
} TweenToBoxThenFall();
private void StartBlinkThenTween()
{
if (_context.Animator == null || _item == null) return;
// Get the image to blink
UnityEngine.UI.Image imageToBlink = null;
if (_context.CardDisplay != null)
{
imageToBlink = _context.CardDisplay.GetComponent<UnityEngine.UI.Image>();
}
else if (_context.GarbageVisual != null)
{
imageToBlink = _context.GarbageVisual.GetComponent<UnityEngine.UI.Image>();
}
if (imageToBlink != null)
{
// Blink red briefly (2-3 times), then stop and continue with tween
_context.Animator.BlinkRed(imageToBlink, 0.15f); // Fast blink
// After brief delay, stop blinking and tween to box
_context.Animator.AnimateScale(_context.RootTransform.localScale, 0.5f, () =>
{
_context.Animator.StopBlinking();
TweenToBoxThenFall();
});
}
else
{
// No image found, just tween directly
TweenToBoxThenFall();
}
} }
private void TweenToBoxThenFall() private void TweenToBoxThenFall()

View File

@@ -0,0 +1,208 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
namespace Minigames.CardSorting.UI
{
/// <summary>
/// Displays and animates lives for the card sorting minigame.
/// Manages a layout group of life icons with add/remove animations.
/// </summary>
public class LivesDisplay : MonoBehaviour
{
[Header("Configuration")]
[SerializeField] private LayoutGroup layoutGroup;
[Header("Animation Settings")]
[SerializeField] private float animationDuration = 0.3f;
[SerializeField] private float scaleMultiplier = 1.5f;
private GameObject lifeIconPrefab;
private List<GameObject> lifeIcons = new List<GameObject>();
private void Awake()
{
if (layoutGroup == null)
{
layoutGroup = GetComponent<LayoutGroup>();
}
// Find the prefab (first child) if not assigned
if (lifeIconPrefab == null && layoutGroup != null && layoutGroup.transform.childCount > 0)
{
lifeIconPrefab = layoutGroup.transform.GetChild(0).gameObject;
}
}
/// <summary>
/// Initialize the display with the maximum number of lives.
/// Clones the prefab element multiple times.
/// </summary>
public void Initialize(int maxLives)
{
// Clear existing lives
ClearAllLives();
if (lifeIconPrefab == null)
{
Debug.LogError("[LivesDisplay] No life icon prefab assigned!");
return;
}
if (layoutGroup == null)
{
Debug.LogError("[LivesDisplay] No layout group assigned!");
return;
}
// Ensure prefab is active to use as template
bool wasActive = lifeIconPrefab.activeSelf;
lifeIconPrefab.SetActive(true);
lifeIcons.Add(lifeIconPrefab);
// Create life icons
for (int i = 0; i < maxLives - 1; i++)
{
GameObject icon = Instantiate(lifeIconPrefab, layoutGroup.transform);
icon.SetActive(true);
lifeIcons.Add(icon);
}
// Hide the original prefab
lifeIconPrefab.SetActive(wasActive);
}
/// <summary>
/// Remove a life with animation.
/// </summary>
public void RemoveLife()
{
if (lifeIcons.Count == 0)
{
Debug.LogWarning("[LivesDisplay] No lives left to remove!");
return;
}
GameObject icon = lifeIcons[0];
lifeIcons.RemoveAt(0);
// Play removal animation
StartCoroutine(AnimateLifeRemoval(icon));
}
/// <summary>
/// Add a life with animation.
/// </summary>
public void AddLife()
{
if (lifeIconPrefab == null || layoutGroup == null)
{
Debug.LogError("[LivesDisplay] Cannot add life - missing prefab or layout group!");
return;
}
// Create new life icon
GameObject icon = Instantiate(lifeIconPrefab, layoutGroup.transform);
icon.SetActive(true);
lifeIcons.Add(icon);
// Play addition animation
StartCoroutine(AnimateLifeAddition(icon));
}
/// <summary>
/// Clear all lives (for reset).
/// </summary>
private void ClearAllLives()
{
foreach (var icon in lifeIcons)
{
if (icon != null)
{
Destroy(icon);
}
}
lifeIcons.Clear();
}
/// <summary>
/// Animate life removal: scale up, then scale down and destroy.
/// </summary>
private IEnumerator AnimateLifeRemoval(GameObject icon)
{
if (icon == null) yield break;
Vector3 originalScale = icon.transform.localScale;
Vector3 targetScale = originalScale * scaleMultiplier;
// Scale up
yield return AnimateScale(icon.transform, originalScale, targetScale, animationDuration * 0.5f);
// Scale down to zero
yield return AnimateScale(icon.transform, targetScale, Vector3.zero, animationDuration * 0.5f);
// Destroy
if (icon != null)
{
Destroy(icon);
}
}
/// <summary>
/// Animate life addition: start at zero, scale up beyond target, then settle to normal.
/// </summary>
private IEnumerator AnimateLifeAddition(GameObject icon)
{
if (icon == null) yield break;
Vector3 originalScale = icon.transform.localScale;
Vector3 overshootScale = originalScale * scaleMultiplier;
// Start at zero
icon.transform.localScale = Vector3.zero;
// Scale up to overshoot
yield return AnimateScale(icon.transform, Vector3.zero, overshootScale, animationDuration * 0.5f);
// Settle back to original scale
yield return AnimateScale(icon.transform, overshootScale, originalScale, animationDuration * 0.5f);
}
/// <summary>
/// Generic scale animation coroutine.
/// </summary>
private IEnumerator AnimateScale(Transform target, Vector3 from, Vector3 to, float duration)
{
float elapsed = 0f;
while (elapsed < duration)
{
elapsed += Time.deltaTime;
float t = Mathf.Clamp01(elapsed / duration);
// Use ease-in-out curve for smooth animation
t = t * t * (3f - 2f * t); // Smoothstep
if (target != null)
{
target.localScale = Vector3.Lerp(from, to, t);
}
yield return null;
}
// Ensure final scale is set
if (target != null)
{
target.localScale = to;
}
}
/// <summary>
/// Get the current number of lives displayed.
/// </summary>
public int CurrentLives => lifeIcons.Count;
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: e8bcb252540442e593cc6467c518ff75
timeCreated: 1763586518

View File

@@ -99,7 +99,8 @@ namespace Minigames.CardSorting.UI
{ {
if (scoreText == null) return; if (scoreText == null) return;
scoreText.text = $"Score: {newScore}"; // Only display numerical value (label is separate UI element)
scoreText.text = $"Score: {newScore.ToString()}";
// Color based on positive/negative // Color based on positive/negative
if (newScore >= 0) if (newScore >= 0)

View File

@@ -1,4 +1,6 @@
using Minigames.CardSorting.Core; using System;
using Core;
using Minigames.CardSorting.Core;
using TMPro; using TMPro;
using UnityEngine; using UnityEngine;
using UnityEngine.UI; using UnityEngine.UI;
@@ -70,26 +72,26 @@ namespace Minigames.CardSorting.UI
if (gameManager == null) return; if (gameManager == null) return;
// Populate data // Populate data - only numerical values (labels are separate UI elements)
if (finalScoreText != null) if (finalScoreText != null)
finalScoreText.text = $"Final Score: {gameManager.CurrentScore}"; finalScoreText.text = gameManager.CurrentScore.ToString();
if (correctSortsText != null) if (correctSortsText != null)
correctSortsText.text = $"Correct: {gameManager.CorrectSorts}"; correctSortsText.text = gameManager.CorrectSorts.ToString();
if (incorrectSortsText != null) if (incorrectSortsText != null)
incorrectSortsText.text = $"Incorrect: {gameManager.IncorrectSorts}"; incorrectSortsText.text = gameManager.IncorrectSorts.ToString();
if (missedItemsText != null) if (missedItemsText != null)
missedItemsText.text = $"Missed: {gameManager.MissedItems}"; missedItemsText.text = gameManager.MissedItems.ToString();
if (accuracyText != null) if (accuracyText != null)
accuracyText.text = $"Accuracy: {gameManager.Accuracy:P0}"; accuracyText.text = gameManager.Accuracy.ToString("P0");
// Calculate boosters (already granted by manager) // Calculate boosters (already granted by manager)
int boosters = gameManager.CorrectSorts; // Simple 1:1 ratio int boosters = gameManager.CorrectSorts; // Simple 1:1 ratio
if (boostersEarnedText != null) if (boostersEarnedText != null)
boostersEarnedText.text = $"Boosters Earned: {boosters}"; boostersEarnedText.text = boosters.ToString();
// Show screen // Show screen
if (canvasGroup != null) if (canvasGroup != null)
@@ -104,7 +106,7 @@ namespace Minigames.CardSorting.UI
} }
} }
private void OnCloseClicked() private async void OnCloseClicked()
{ {
// Hide screen // Hide screen
if (canvasGroup != null) if (canvasGroup != null)
@@ -118,8 +120,9 @@ namespace Minigames.CardSorting.UI
gameObject.SetActive(false); gameObject.SetActive(false);
} }
// Could also trigger scene transition, return to menu, etc. // TODO: Smarter replay?
Debug.Log("[SortingResultsScreen] Closed results screen"); var progress = new Progress<float>(p => Logging.Debug($"Loading progress: {p * 100:F0}%"));
await SceneManagerService.Instance.ReloadCurrentScene(progress);
} }
} }
} }

View File

@@ -46,7 +46,11 @@ MonoBehaviour:
rareCardWeight: 30 rareCardWeight: 30
legendCardWeight: 20 legendCardWeight: 20
garbageWeight: 40 garbageWeight: 40
correctSortPoints: 10 correctSortPoints: 1
incorrectSortPenalty: -5 incorrectSortPenalty: -1
missedItemPenalty: -3 missedItemPenalty: 0
maxLives: 3
boosterPacksPerCorrectItem: 1 boosterPacksPerCorrectItem: 1
cardSize: {x: 100, y: 140}
spawnOffsetX: {x: -5, y: 5}
spawnOffsetY: {x: -2.5, y: 2.5}