diff --git a/Assets/AddressableAssetsData/link.xml b/Assets/AddressableAssetsData/link.xml
deleted file mode 100644
index 607d992ff..000000000
--- a/Assets/AddressableAssetsData/link.xml
+++ /dev/null
@@ -1,535 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/Assets/Editor/Inspector/BlockPuzzleGridEditor.cs b/Assets/Editor/Inspector/BlockPuzzleGridEditor.cs
new file mode 100644
index 000000000..2ad6302c4
--- /dev/null
+++ b/Assets/Editor/Inspector/BlockPuzzleGridEditor.cs
@@ -0,0 +1,30 @@
+using UnityEditor;
+using UnityEngine;
+
+namespace AibisDream.SystemEditor
+{
+ [CustomEditor(typeof(BlockPuzzleGrid))]
+ public class BlockPuzzleGridEditor : Editor
+ {
+ public override void OnInspectorGUI()
+ {
+ base.OnInspectorGUI();
+ var example = (BlockPuzzleGrid)target;
+
+ EditorGUILayout.Separator();
+ EditorGUILayout.BeginHorizontal();
+ // 保存按钮
+ if (GUILayout.Button("保存"))
+ {
+ // example.SaveStyle();
+ }
+
+ // 加载按钮
+ if (GUILayout.Button("加载"))
+ {
+ // example.LoadStyle();
+ }
+ EditorGUILayout.EndHorizontal();
+ }
+ }
+}
\ No newline at end of file
diff --git a/Assets/Editor/Inspector/BlockPuzzleGridEditor.cs.meta b/Assets/Editor/Inspector/BlockPuzzleGridEditor.cs.meta
new file mode 100644
index 000000000..fb40dde14
--- /dev/null
+++ b/Assets/Editor/Inspector/BlockPuzzleGridEditor.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 7eb275cadab046c44a9f46f3f475145e
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Scenes/网格测试.unity b/Assets/Scenes/网格测试.unity
new file mode 100644
index 000000000..c8dffcca1
--- /dev/null
+++ b/Assets/Scenes/网格测试.unity
@@ -0,0 +1,345 @@
+%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: 3
+ m_SubtractiveShadowColor: {r: 0.42, g: 0.478, b: 0.627, a: 1}
+ m_SkyboxMaterial: {fileID: 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_IndirectSpecularColor: {r: 0, g: 0, b: 0, a: 1}
+ 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: 0
+ 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 &791020753
+GameObject:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ serializedVersion: 6
+ m_Component:
+ - component: {fileID: 791020756}
+ - component: {fileID: 791020755}
+ - component: {fileID: 791020754}
+ m_Layer: 0
+ m_Name: Main Camera
+ m_TagString: MainCamera
+ m_Icon: {fileID: 0}
+ m_NavMeshLayer: 0
+ m_StaticEditorFlags: 0
+ m_IsActive: 1
+--- !u!81 &791020754
+AudioListener:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 791020753}
+ m_Enabled: 1
+--- !u!20 &791020755
+Camera:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 791020753}
+ m_Enabled: 1
+ serializedVersion: 2
+ m_ClearFlags: 1
+ m_BackGroundColor: {r: 0.19215687, g: 0.3019608, b: 0.4745098, a: 0}
+ 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: 1
+ orthographic size: 5
+ m_Depth: -1
+ 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 &791020756
+Transform:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 791020753}
+ serializedVersion: 2
+ m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+ m_LocalPosition: {x: 0, y: 0, z: -10}
+ 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 &1429030510
+GameObject:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ serializedVersion: 6
+ m_Component:
+ - component: {fileID: 1429030511}
+ - component: {fileID: 1429030512}
+ m_Layer: 0
+ m_Name: Block Puzzle Game
+ m_TagString: Untagged
+ m_Icon: {fileID: 0}
+ m_NavMeshLayer: 0
+ m_StaticEditorFlags: 0
+ m_IsActive: 1
+--- !u!4 &1429030511
+Transform:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 1429030510}
+ 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:
+ - {fileID: 1928610076}
+ m_Father: {fileID: 0}
+ m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!114 &1429030512
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 1429030510}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 11500000, guid: cfe4f41ce15355345b390aa2701f3f46, type: 3}
+ m_Name:
+ m_EditorClassIdentifier:
+ gridComponent: {fileID: 0}
+ gridVisualizer: {fileID: 0}
+ testPlaceKey: 32
+ testClearKey: 99
+--- !u!1 &1928610075
+GameObject:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ serializedVersion: 6
+ m_Component:
+ - component: {fileID: 1928610076}
+ - component: {fileID: 1928610078}
+ - component: {fileID: 1928610077}
+ m_Layer: 0
+ m_Name: Grid
+ m_TagString: Untagged
+ m_Icon: {fileID: 0}
+ m_NavMeshLayer: 0
+ m_StaticEditorFlags: 0
+ m_IsActive: 1
+--- !u!4 &1928610076
+Transform:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 1928610075}
+ 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: 1429030511}
+ m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!114 &1928610077
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 1928610075}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 11500000, guid: d364afd8a560b4447b86f55c52c73d7a, type: 3}
+ m_Name:
+ m_EditorClassIdentifier:
+ gridComponent: {fileID: 1928610078}
+ gridWidth: 10
+ gridHeight: 10
+ cellSize: 1
+ showGridLines: 1
+ showCellSprites: 1
+ emptyCellColor: {r: 0.9, g: 0.9, b: 0.9, a: 0.3}
+ occupiedCellColor: {r: 0.2, g: 0.6, b: 1, a: 1}
+ gridLineColor: {r: 0.5, g: 0.5, b: 0.5, a: 0.5}
+ gridLineWidth: 0.02
+ cellSprite: {fileID: 0}
+ cellMaterial: {fileID: 0}
+--- !u!114 &1928610078
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 1928610075}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 11500000, guid: 990de8ef2a6774544b9bc9b64af0a8a5, type: 3}
+ m_Name:
+ m_EditorClassIdentifier:
+ width: 10
+ height: 10
+ cellSize: 1
+ initializeOnStart: 1
+--- !u!1660057539 &9223372036854775807
+SceneRoots:
+ m_ObjectHideFlags: 0
+ m_Roots:
+ - {fileID: 791020756}
+ - {fileID: 1429030511}
diff --git a/Assets/AddressableAssetsData/link.xml.meta b/Assets/Scenes/网格测试.unity.meta
similarity index 62%
rename from Assets/AddressableAssetsData/link.xml.meta
rename to Assets/Scenes/网格测试.unity.meta
index a297ca980..6d9ec06a5 100644
--- a/Assets/AddressableAssetsData/link.xml.meta
+++ b/Assets/Scenes/网格测试.unity.meta
@@ -1,6 +1,6 @@
fileFormatVersion: 2
-guid: d54d55d9be1bc4d4b9eadd960d993a80
-TextScriptImporter:
+guid: d4b95f599ab563c45a074014a67459fb
+DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
diff --git a/Assets/Scripts/MiniGame/BlockPuzzle.meta b/Assets/Scripts/MiniGame/BlockPuzzle.meta
new file mode 100644
index 000000000..f936d9cbc
--- /dev/null
+++ b/Assets/Scripts/MiniGame/BlockPuzzle.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: f38a6a6bc9a88284496050743d6247e9
+folderAsset: yes
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Scripts/MiniGame/BlockPuzzle/BlockPuzzleGrid.cs b/Assets/Scripts/MiniGame/BlockPuzzle/BlockPuzzleGrid.cs
new file mode 100644
index 000000000..4936c995c
--- /dev/null
+++ b/Assets/Scripts/MiniGame/BlockPuzzle/BlockPuzzleGrid.cs
@@ -0,0 +1,214 @@
+using UnityEngine;
+
+namespace AibisDream
+{
+ ///
+ /// BlockPuzzle网格组件
+ /// 实现IBlockPuzzleGrid接口,管理整个游戏网格的状态和操作
+ ///
+ public class BlockPuzzleGrid : MonoBehaviour, IBlockPuzzleGrid
+ {
+ [Header("网格设置")]
+ [SerializeField] private int width = 10;
+ [SerializeField] private int height = 10;
+ [SerializeField] private float cellSize = 1f;
+ [SerializeField] private bool initializeOnStart = true;
+
+ private GridCellState[,] _grid;
+ private bool _isInitialized = false;
+
+ // IBlockPuzzleGrid 接口实现
+ public int Width { get; private set; }
+ public int Height { get; private set; }
+ public float CellSize { get; private set; }
+ public Vector3 Origin { get; private set; }
+
+ private void Start()
+ {
+ if (initializeOnStart)
+ {
+ Initialize(width, height, cellSize, transform.position);
+ }
+ }
+
+ ///
+ /// 初始化网格
+ ///
+ public void Initialize(int width, int height, float cellSize, Vector3 origin)
+ {
+ if (width <= 0 || height <= 0)
+ {
+ Debug.LogError($"BlockPuzzleGridComponent: 无效的网格尺寸 {width}x{height}");
+ return;
+ }
+
+ if (cellSize <= 0)
+ {
+ Debug.LogError($"BlockPuzzleGridComponent: 无效的单元格大小 {cellSize}");
+ return;
+ }
+
+ this.width = width;
+ this.height = height;
+ this.cellSize = cellSize;
+
+ Width = width;
+ Height = height;
+ CellSize = cellSize;
+ Origin = origin;
+ _grid = new GridCellState[width, height];
+ _isInitialized = true;
+
+ Clear();
+ }
+
+ ///
+ /// 使用Inspector中的参数初始化网格
+ ///
+ public void Initialize()
+ {
+ Initialize(width, height, cellSize, transform.position);
+ }
+
+ ///
+ /// 重新初始化网格(使用新的参数)
+ ///
+ public void Reinitialize(int newWidth, int newHeight, float newCellSize)
+ {
+ Initialize(newWidth, newHeight, newCellSize, transform.position);
+ }
+
+ public GridCellState GetCellState(int x, int y)
+ {
+ if (!_isInitialized)
+ {
+ Debug.LogWarning("BlockPuzzleGridComponent: 网格未初始化");
+ return GridCellState.Empty;
+ }
+
+ if (!IsValidPosition(x, y))
+ {
+ Debug.LogWarning($"BlockPuzzleGridComponent: 尝试访问无效位置 ({x}, {y})");
+ return GridCellState.Empty;
+ }
+
+ return _grid[x, y];
+ }
+
+ public void SetCellState(int x, int y, GridCellState state)
+ {
+ if (!_isInitialized)
+ {
+ Debug.LogWarning("BlockPuzzleGridComponent: 网格未初始化");
+ return;
+ }
+
+ if (!IsValidPosition(x, y))
+ {
+ Debug.LogWarning($"BlockPuzzleGridComponent: 尝试设置无效位置 ({x}, {y})");
+ return;
+ }
+
+ _grid[x, y] = state;
+ }
+
+ public bool IsValidPosition(int x, int y)
+ {
+ return _isInitialized && x >= 0 && x < Width && y >= 0 && y < Height;
+ }
+
+ public bool IsCellEmpty(int x, int y)
+ {
+ return IsValidPosition(x, y) && GetCellState(x, y) == GridCellState.Empty;
+ }
+
+ public bool WorldToGrid(Vector3 worldPos, out int gridX, out int gridY)
+ {
+ if (!_isInitialized)
+ {
+ gridX = 0;
+ gridY = 0;
+ return false;
+ }
+
+ Vector3 localPos = worldPos - Origin;
+ gridX = Mathf.FloorToInt(localPos.x / CellSize);
+ gridY = Mathf.FloorToInt(localPos.y / CellSize);
+
+ // 检查是否在有效范围内
+ if (!IsValidPosition(gridX, gridY))
+ {
+ return false;
+ }
+
+ return true;
+ }
+
+ public Vector3 GridToWorld(int gridX, int gridY)
+ {
+ if (!_isInitialized)
+ {
+ return transform.position;
+ }
+
+ // 返回单元格中心的世界坐标
+ float worldX = Origin.x + (gridX + 0.5f) * CellSize;
+ float worldY = Origin.y + (gridY + 0.5f) * CellSize;
+ return new Vector3(worldX, worldY, Origin.z);
+ }
+
+ public void Clear()
+ {
+ if (!_isInitialized || _grid == null) return;
+
+ for (int x = 0; x < Width; x++)
+ {
+ for (int y = 0; y < Height; y++)
+ {
+ _grid[x, y] = GridCellState.Empty;
+ }
+ }
+ }
+
+ public int GetOccupiedCount()
+ {
+ if (!_isInitialized || _grid == null) return 0;
+
+ int count = 0;
+ for (int x = 0; x < Width; x++)
+ {
+ for (int y = 0; y < Height; y++)
+ {
+ if (_grid[x, y] == GridCellState.Occupied)
+ {
+ count++;
+ }
+ }
+ }
+
+ return count;
+ }
+
+ public int GetTotalCellCount()
+ {
+ return Width * Height;
+ }
+
+ ///
+ /// 检查网格是否已初始化
+ ///
+ public bool IsInitialized()
+ {
+ return _isInitialized;
+ }
+
+ private void OnValidate()
+ {
+ // 确保参数有效
+ width = Mathf.Max(1, width);
+ height = Mathf.Max(1, height);
+ cellSize = Mathf.Max(0.1f, cellSize);
+ }
+ }
+}
+
diff --git a/Assets/Scripts/MiniGame/BlockPuzzle/BlockPuzzleGrid.cs.meta b/Assets/Scripts/MiniGame/BlockPuzzle/BlockPuzzleGrid.cs.meta
new file mode 100644
index 000000000..4d53546f0
--- /dev/null
+++ b/Assets/Scripts/MiniGame/BlockPuzzle/BlockPuzzleGrid.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 990de8ef2a6774544b9bc9b64af0a8a5
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Scripts/MiniGame/BlockPuzzle/BlockPuzzleGridExample.cs b/Assets/Scripts/MiniGame/BlockPuzzle/BlockPuzzleGridExample.cs
new file mode 100644
index 000000000..bf298d12a
--- /dev/null
+++ b/Assets/Scripts/MiniGame/BlockPuzzle/BlockPuzzleGridExample.cs
@@ -0,0 +1,148 @@
+using UnityEngine;
+
+namespace AibisDream
+{
+ ///
+ /// BlockPuzzle网格系统使用示例
+ /// 演示如何使用网格系统和可视化组件
+ ///
+ public class BlockPuzzleGridExample : MonoBehaviour
+ {
+ [Header("组件引用")]
+ [SerializeField] private BlockPuzzleGrid gridComponent;
+ [SerializeField] private GridVisualizer gridVisualizer;
+
+ [Header("测试设置")]
+ [SerializeField] private KeyCode testPlaceKey = KeyCode.Space;
+ [SerializeField] private KeyCode testClearKey = KeyCode.C;
+
+ private IBlockPuzzleGrid _grid;
+
+ private void Start()
+ {
+ // 初始化网格组件
+ if (gridComponent != null)
+ {
+ gridComponent.Initialize();
+ _grid = gridComponent; // 组件直接实现IBlockPuzzleGrid接口
+ }
+
+ // 设置可视化器
+ if (gridVisualizer != null && _grid != null)
+ {
+ gridVisualizer.SetGrid(_grid);
+ }
+ }
+
+ private void Update()
+ {
+ if (_grid == null) return;
+
+ // 测试:按空格键随机放置一个格子
+ if (Input.GetKeyDown(testPlaceKey))
+ {
+ TestPlaceRandomCell();
+ }
+
+ // 测试:按C键清空网格
+ if (Input.GetKeyDown(testClearKey))
+ {
+ TestClearGrid();
+ }
+
+ // 测试:鼠标点击放置格子
+ if (Input.GetMouseButtonDown(0))
+ {
+ TestPlaceCellAtMouse();
+ }
+ }
+
+ ///
+ /// 测试:在随机位置放置一个格子
+ ///
+ private void TestPlaceRandomCell()
+ {
+ if (_grid == null) return;
+
+ int x = Random.Range(0, _grid.Width);
+ int y = Random.Range(0, _grid.Height);
+
+ if (_grid.IsCellEmpty(x, y))
+ {
+ _grid.SetCellState(x, y, GridCellState.Occupied);
+ Debug.Log($"放置格子到位置 ({x}, {y})");
+
+ // 更新可视化
+ if (gridVisualizer != null)
+ {
+ gridVisualizer.UpdateCellVisual(x, y);
+ }
+ }
+ }
+
+ ///
+ /// 测试:在鼠标位置放置格子
+ ///
+ private void TestPlaceCellAtMouse()
+ {
+ if (_grid == null) return;
+
+ // 获取鼠标世界坐标(需要根据你的相机设置调整)
+ Camera cam = Camera.main;
+ if (cam == null) return;
+
+ Vector3 mousePos = Input.mousePosition;
+ mousePos.z = cam.nearClipPlane + 1f; // 设置合适的Z距离
+ Vector3 worldPos = cam.ScreenToWorldPoint(mousePos);
+
+ // 转换为网格坐标
+ if (_grid.WorldToGrid(worldPos, out int gridX, out int gridY))
+ {
+ if (_grid.IsCellEmpty(gridX, gridY))
+ {
+ _grid.SetCellState(gridX, gridY, GridCellState.Occupied);
+ Debug.Log($"鼠标点击放置格子到位置 ({gridX}, {gridY})");
+
+ // 更新可视化
+ if (gridVisualizer != null)
+ {
+ gridVisualizer.UpdateCellVisual(gridX, gridY);
+ }
+ }
+ }
+ }
+
+ ///
+ /// 测试:清空网格
+ ///
+ private void TestClearGrid()
+ {
+ if (_grid == null) return;
+
+ _grid.Clear();
+ Debug.Log("清空网格");
+
+ // 更新可视化
+ if (gridVisualizer != null)
+ {
+ gridVisualizer.UpdateAllCellsVisual();
+ }
+ }
+
+ private void OnGUI()
+ {
+ if (_grid == null) return;
+
+ GUILayout.BeginArea(new Rect(10, 10, 300, 200));
+ GUILayout.Label($"网格尺寸: {_grid.Width} x {_grid.Height}");
+ GUILayout.Label($"单元格大小: {_grid.CellSize}");
+ GUILayout.Label($"已占用: {_grid.GetOccupiedCount()} / {_grid.GetTotalCellCount()}");
+ GUILayout.Space(10);
+ GUILayout.Label($"按 {testPlaceKey} 键:随机放置格子");
+ GUILayout.Label($"按 {testClearKey} 键:清空网格");
+ GUILayout.Label("鼠标左键:在鼠标位置放置格子");
+ GUILayout.EndArea();
+ }
+ }
+}
+
diff --git a/Assets/Scripts/MiniGame/BlockPuzzle/BlockPuzzleGridExample.cs.meta b/Assets/Scripts/MiniGame/BlockPuzzle/BlockPuzzleGridExample.cs.meta
new file mode 100644
index 000000000..0fdf9130c
--- /dev/null
+++ b/Assets/Scripts/MiniGame/BlockPuzzle/BlockPuzzleGridExample.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: cfe4f41ce15355345b390aa2701f3f46
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Scripts/MiniGame/BlockPuzzle/BlockPuzzleSystem.cs b/Assets/Scripts/MiniGame/BlockPuzzle/BlockPuzzleSystem.cs
new file mode 100644
index 000000000..b77d7b446
--- /dev/null
+++ b/Assets/Scripts/MiniGame/BlockPuzzle/BlockPuzzleSystem.cs
@@ -0,0 +1,16 @@
+using System.Collections;
+using System.Collections.Generic;
+using UnityEngine;
+
+namespace AibisDream
+{
+ ///
+ /// BlockPuzzle游戏系统
+ /// 整合所有子系统的核心控制器
+ ///
+ public class BlockPuzzleSystem : MonoBehaviour
+ {
+ // 接口引用将在实现类中注入
+ // 这里先定义基础框架接口结构
+ }
+}
diff --git a/Assets/Scripts/MiniGame/BlockPuzzle/BlockPuzzleSystem.cs.meta b/Assets/Scripts/MiniGame/BlockPuzzle/BlockPuzzleSystem.cs.meta
new file mode 100644
index 000000000..12d2b626c
--- /dev/null
+++ b/Assets/Scripts/MiniGame/BlockPuzzle/BlockPuzzleSystem.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: c452a931d2651e5449cf3779882cad19
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Scripts/MiniGame/BlockPuzzle/GridVisualizer.cs b/Assets/Scripts/MiniGame/BlockPuzzle/GridVisualizer.cs
new file mode 100644
index 000000000..1e6838497
--- /dev/null
+++ b/Assets/Scripts/MiniGame/BlockPuzzle/GridVisualizer.cs
@@ -0,0 +1,337 @@
+using System.Collections.Generic;
+using UnityEngine;
+
+namespace AibisDream
+{
+ ///
+ /// 网格可视化组件
+ /// 负责在Unity场景中可视化显示网格和单元格状态
+ ///
+ public class GridVisualizer : MonoBehaviour
+ {
+ [Header("网格配置")]
+ [SerializeField] private BlockPuzzleGrid gridComponent;
+ [SerializeField] private int gridWidth = 10;
+ [SerializeField] private int gridHeight = 10;
+ [SerializeField] private float cellSize = 1f;
+
+ private IBlockPuzzleGrid _grid;
+
+ [Header("可视化设置")]
+ [SerializeField] private bool showGridLines = true;
+ [SerializeField] private bool showCellSprites = true;
+ [SerializeField] private Color emptyCellColor = new Color(0.9f, 0.9f, 0.9f, 0.3f);
+ [SerializeField] private Color occupiedCellColor = new Color(0.2f, 0.6f, 1f, 1f);
+ [SerializeField] private Color gridLineColor = new Color(0.5f, 0.5f, 0.5f, 0.5f);
+ [SerializeField] private float gridLineWidth = 0.02f;
+
+ [Header("预制体(可选)")]
+ [SerializeField] private Sprite cellSprite;
+ [SerializeField] private Material cellMaterial;
+
+ // 单元格可视化对象池
+ private Dictionary<(int x, int y), GameObject> _cellObjects = new Dictionary<(int x, int y), GameObject>();
+ private GameObject _gridLinesParent;
+ private GameObject _cellsParent;
+ private BlockPuzzleGrid _internalGrid;
+
+ private void Awake()
+ {
+ // 创建父对象用于组织层级
+ _gridLinesParent = new GameObject("GridLines");
+ _gridLinesParent.transform.SetParent(transform);
+ _gridLinesParent.transform.localPosition = Vector3.zero;
+
+ _cellsParent = new GameObject("Cells");
+ _cellsParent.transform.SetParent(transform);
+ _cellsParent.transform.localPosition = Vector3.zero;
+ }
+
+ private void Start()
+ {
+ InitializeGrid();
+ }
+
+ ///
+ /// 初始化网格
+ ///
+ public void InitializeGrid()
+ {
+ // 优先使用外部网格组件
+ if (gridComponent != null)
+ {
+ // 如果组件还没有初始化,先初始化它
+ if (!gridComponent.IsInitialized())
+ {
+ gridComponent.Initialize();
+ }
+ _grid = gridComponent;
+ }
+ else
+ {
+ // 如果没有外部网格,创建内部网格组件
+ if (_internalGrid == null)
+ {
+ _internalGrid = gameObject.GetComponent();
+ if (_internalGrid == null)
+ {
+ _internalGrid = gameObject.AddComponent();
+ }
+ }
+ _internalGrid.Initialize(gridWidth, gridHeight, cellSize, transform.position);
+ _grid = _internalGrid;
+ }
+
+ CreateVisualization();
+ }
+
+ ///
+ /// 设置网格引用
+ ///
+ public void SetGrid(IBlockPuzzleGrid grid)
+ {
+ _grid = grid;
+ ClearVisualization();
+ CreateVisualization();
+ }
+
+ ///
+ /// 创建可视化
+ ///
+ private void CreateVisualization()
+ {
+ if (_grid == null) return;
+
+ if (showGridLines)
+ {
+ DrawGridLines();
+ }
+
+ if (showCellSprites)
+ {
+ CreateCellSprites();
+ }
+ }
+
+ ///
+ /// 绘制网格线
+ ///
+ private void DrawGridLines()
+ {
+ // 清除旧的网格线
+ foreach (Transform child in _gridLinesParent.transform)
+ {
+ if (Application.isPlaying)
+ {
+ Destroy(child.gameObject);
+ }
+ else
+ {
+ DestroyImmediate(child.gameObject);
+ }
+ }
+
+ // 创建网格线(使用LineRenderer或简单的Quad)
+ for (int x = 0; x <= _grid.Width; x++)
+ {
+ CreateLine(
+ _grid.GridToWorld(x, 0),
+ _grid.GridToWorld(x, _grid.Height),
+ gridLineWidth,
+ gridLineColor
+ );
+ }
+
+ for (int y = 0; y <= _grid.Height; y++)
+ {
+ CreateLine(
+ _grid.GridToWorld(0, y),
+ _grid.GridToWorld(_grid.Width, y),
+ gridLineWidth,
+ gridLineColor
+ );
+ }
+ }
+
+ ///
+ /// 创建一条线
+ ///
+ private void CreateLine(Vector3 start, Vector3 end, float width, Color color)
+ {
+ GameObject lineObj = new GameObject("GridLine");
+ lineObj.transform.SetParent(_gridLinesParent.transform);
+ lineObj.transform.localPosition = Vector3.zero;
+
+ LineRenderer lineRenderer = lineObj.AddComponent();
+ lineRenderer.useWorldSpace = true;
+ lineRenderer.positionCount = 2;
+ lineRenderer.SetPosition(0, start);
+ lineRenderer.SetPosition(1, end);
+ lineRenderer.startWidth = width;
+ lineRenderer.endWidth = width;
+ lineRenderer.material = new Material(Shader.Find("Sprites/Default"));
+ lineRenderer.startColor = color;
+ lineRenderer.endColor = color;
+ lineRenderer.sortingOrder = 0;
+ }
+
+ ///
+ /// 创建单元格精灵
+ ///
+ private void CreateCellSprites()
+ {
+ // 清除旧的单元格
+ ClearCellSprites();
+
+ // 为每个单元格创建可视化对象
+ for (int x = 0; x < _grid.Width; x++)
+ {
+ for (int y = 0; y < _grid.Height; y++)
+ {
+ CreateCellSprite(x, y);
+ }
+ }
+ }
+
+ ///
+ /// 创建单个单元格精灵
+ ///
+ private void CreateCellSprite(int x, int y)
+ {
+ GameObject cellObj = new GameObject($"Cell_{x}_{y}");
+ cellObj.transform.SetParent(_cellsParent.transform);
+ cellObj.transform.position = _grid.GridToWorld(x, y);
+
+ SpriteRenderer spriteRenderer = cellObj.AddComponent();
+
+ // 如果没有指定Sprite,创建一个简单的白色Sprite
+ if (cellSprite == null)
+ {
+ cellSprite = CreateDefaultSprite();
+ }
+
+ spriteRenderer.sprite = cellSprite;
+ spriteRenderer.color = emptyCellColor;
+ spriteRenderer.sortingOrder = 1;
+
+ // 设置大小
+ float spriteSize = _grid.CellSize * 0.9f; // 稍微小一点,留出网格线空间
+ cellObj.transform.localScale = new Vector3(spriteSize, spriteSize, 1f);
+
+ // 如果有材质,应用材质
+ if (cellMaterial != null)
+ {
+ spriteRenderer.material = cellMaterial;
+ }
+
+ _cellObjects[(x, y)] = cellObj;
+ }
+
+ ///
+ /// 创建默认的白色Sprite
+ ///
+ private Sprite CreateDefaultSprite()
+ {
+ Texture2D texture = new Texture2D(1, 1);
+ texture.SetPixel(0, 0, Color.white);
+ texture.Apply();
+ return Sprite.Create(texture, new Rect(0, 0, 1, 1), new Vector2(0.5f, 0.5f), 1f);
+ }
+
+ ///
+ /// 更新单元格可视化状态
+ ///
+ public void UpdateCellVisual(int x, int y)
+ {
+ if (!_cellObjects.TryGetValue((x, y), out GameObject cellObj))
+ {
+ return;
+ }
+
+ SpriteRenderer spriteRenderer = cellObj.GetComponent();
+ if (spriteRenderer == null) return;
+
+ GridCellState state = _grid.GetCellState(x, y);
+ spriteRenderer.color = state == GridCellState.Occupied ? occupiedCellColor : emptyCellColor;
+ }
+
+ ///
+ /// 更新所有单元格可视化
+ ///
+ public void UpdateAllCellsVisual()
+ {
+ if (_grid == null) return;
+
+ for (int x = 0; x < _grid.Width; x++)
+ {
+ for (int y = 0; y < _grid.Height; y++)
+ {
+ UpdateCellVisual(x, y);
+ }
+ }
+ }
+
+ ///
+ /// 清除单元格精灵
+ ///
+ private void ClearCellSprites()
+ {
+ foreach (var kvp in _cellObjects)
+ {
+ if (Application.isPlaying)
+ {
+ Destroy(kvp.Value);
+ }
+ else
+ {
+ DestroyImmediate(kvp.Value);
+ }
+ }
+ _cellObjects.Clear();
+ }
+
+ ///
+ /// 清除所有可视化
+ ///
+ private void ClearVisualization()
+ {
+ ClearCellSprites();
+
+ foreach (Transform child in _gridLinesParent.transform)
+ {
+ if (Application.isPlaying)
+ {
+ Destroy(child.gameObject);
+ }
+ else
+ {
+ DestroyImmediate(child.gameObject);
+ }
+ }
+ }
+
+ private void OnDestroy()
+ {
+ ClearVisualization();
+ }
+
+ // 在编辑器中预览网格
+ private void OnDrawGizmos()
+ {
+ if (_grid == null) return;
+
+ // 绘制网格边界
+ Gizmos.color = gridLineColor;
+ Vector3 bottomLeft = _grid.GridToWorld(0, 0);
+ Vector3 bottomRight = _grid.GridToWorld(_grid.Width, 0);
+ Vector3 topLeft = _grid.GridToWorld(0, _grid.Height);
+ Vector3 topRight = _grid.GridToWorld(_grid.Width, _grid.Height);
+
+ Gizmos.DrawLine(bottomLeft, bottomRight);
+ Gizmos.DrawLine(bottomRight, topRight);
+ Gizmos.DrawLine(topRight, topLeft);
+ Gizmos.DrawLine(topLeft, bottomLeft);
+ }
+ }
+}
+
diff --git a/Assets/Scripts/MiniGame/BlockPuzzle/GridVisualizer.cs.meta b/Assets/Scripts/MiniGame/BlockPuzzle/GridVisualizer.cs.meta
new file mode 100644
index 000000000..447775593
--- /dev/null
+++ b/Assets/Scripts/MiniGame/BlockPuzzle/GridVisualizer.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: d364afd8a560b4447b86f55c52c73d7a
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Scripts/MiniGame/BlockPuzzle/IBlockPuzzleGameLogic.cs b/Assets/Scripts/MiniGame/BlockPuzzle/IBlockPuzzleGameLogic.cs
new file mode 100644
index 000000000..b5986dc1f
--- /dev/null
+++ b/Assets/Scripts/MiniGame/BlockPuzzle/IBlockPuzzleGameLogic.cs
@@ -0,0 +1,139 @@
+using System;
+
+namespace AibisDream
+{
+ ///
+ /// 游戏状态
+ ///
+ public enum GameState
+ {
+ NotStarted, // 未开始
+ Playing, // 游戏中
+ Paused, // 暂停
+ GameOver // 游戏结束
+ }
+
+ ///
+ /// 游戏逻辑接口
+ /// 负责管理游戏的整体流程和状态
+ ///
+ public interface IBlockPuzzleGameLogic
+ {
+ ///
+ /// 当前游戏状态
+ ///
+ GameState CurrentState { get; }
+
+ ///
+ /// 当前分数(已放置的格子数量)
+ ///
+ int Score { get; }
+
+ ///
+ /// 当前选中的形状
+ ///
+ IBlockShape CurrentShape { get; }
+
+ ///
+ /// 网格系统
+ ///
+ IBlockPuzzleGrid Grid { get; }
+
+ ///
+ /// 放置验证器
+ ///
+ IPlacementValidator Validator { get; }
+
+ ///
+ /// 游戏开始事件
+ ///
+ event Action OnGameStart;
+
+ ///
+ /// 游戏结束事件
+ ///
+ event Action OnGameOver;
+
+ ///
+ /// 形状放置成功事件
+ ///
+ event Action OnShapePlaced;
+
+ ///
+ /// 形状放置失败事件
+ ///
+ event Action OnShapePlaceFailed;
+
+ ///
+ /// 分数变化事件
+ ///
+ event Action OnScoreChanged;
+
+ ///
+ /// 初始化游戏
+ ///
+ /// 网格宽度
+ /// 网格高度
+ void InitializeGame(int gridWidth, int gridHeight);
+
+ ///
+ /// 开始游戏
+ ///
+ void StartGame();
+
+ ///
+ /// 暂停游戏
+ ///
+ void PauseGame();
+
+ ///
+ /// 继续游戏
+ ///
+ void ResumeGame();
+
+ ///
+ /// 结束游戏
+ ///
+ void EndGame();
+
+ ///
+ /// 重置游戏
+ ///
+ void ResetGame();
+
+ ///
+ /// 选择形状
+ ///
+ /// 要选择的形状
+ void SelectShape(IBlockShape shape);
+
+ ///
+ /// 尝试放置当前选中的形状到指定位置
+ ///
+ /// 网格X坐标
+ /// 网格Y坐标
+ /// 是否放置成功
+ bool TryPlaceShape(int gridX, int gridY);
+
+ ///
+ /// 旋转当前选中的形状
+ ///
+ /// 旋转角度
+ void RotateCurrentShape(RotationAngle angle);
+
+ ///
+ /// 检查是否还有可放置的形状
+ ///
+ /// 可用的形状列表
+ /// 是否还有可放置的形状
+ bool HasPlaceableShapes(IBlockShape[] availableShapes);
+
+ ///
+ /// 获取下一个可用的形状
+ ///
+ /// 形状池
+ /// 下一个形状,如果没有则返回null
+ IBlockShape GetNextShape(IBlockShape[] shapePool);
+ }
+}
+
diff --git a/Assets/Scripts/MiniGame/BlockPuzzle/IBlockPuzzleGameLogic.cs.meta b/Assets/Scripts/MiniGame/BlockPuzzle/IBlockPuzzleGameLogic.cs.meta
new file mode 100644
index 000000000..fa33a1d4a
--- /dev/null
+++ b/Assets/Scripts/MiniGame/BlockPuzzle/IBlockPuzzleGameLogic.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 4603b37a885792d4b85d6d5d392d4862
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Scripts/MiniGame/BlockPuzzle/IBlockPuzzleGrid.cs b/Assets/Scripts/MiniGame/BlockPuzzle/IBlockPuzzleGrid.cs
new file mode 100644
index 000000000..1005788f9
--- /dev/null
+++ b/Assets/Scripts/MiniGame/BlockPuzzle/IBlockPuzzleGrid.cs
@@ -0,0 +1,122 @@
+using UnityEngine;
+
+namespace AibisDream
+{
+ ///
+ /// 网格单元格状态
+ ///
+ public enum GridCellState
+ {
+ Empty = 0, // 空
+ Occupied = 1 // 已占用
+ }
+
+ ///
+ /// 游戏网格系统接口(整个网格)
+ /// 负责管理整个游戏网格的状态和操作,包括:
+ /// - 网格的整体尺寸和配置
+ /// - 所有单元格的状态管理
+ /// - 坐标转换(世界坐标 ↔ 网格坐标)
+ /// - 网格级别的操作(清空、统计等)
+ ///
+ /// 注意:单个单元格(Cell)通过坐标访问,没有独立接口
+ ///
+ public interface IBlockPuzzleGrid
+ {
+ ///
+ /// 网格宽度
+ ///
+ int Width { get; }
+
+ ///
+ /// 网格高度
+ ///
+ int Height { get; }
+
+ ///
+ /// 网格单元格大小(世界单位)
+ ///
+ float CellSize { get; }
+
+ ///
+ /// 网格原点位置(世界坐标)
+ ///
+ Vector3 Origin { get; }
+
+ ///
+ /// 初始化网格
+ ///
+ /// 宽度
+ /// 高度
+ /// 单元格大小
+ /// 原点位置
+ void Initialize(int width, int height, float cellSize, Vector3 origin);
+
+ ///
+ /// 获取指定位置的单元格状态
+ ///
+ /// 网格X坐标
+ /// 网格Y坐标
+ /// 单元格状态
+ GridCellState GetCellState(int x, int y);
+
+ ///
+ /// 设置指定位置的单元格状态
+ ///
+ /// 网格X坐标
+ /// 网格Y坐标
+ /// 单元格状态
+ void SetCellState(int x, int y, GridCellState state);
+
+ ///
+ /// 检查指定位置是否在网格范围内
+ ///
+ /// 网格X坐标
+ /// 网格Y坐标
+ /// 是否在范围内
+ bool IsValidPosition(int x, int y);
+
+ ///
+ /// 检查指定位置是否为空
+ ///
+ /// 网格X坐标
+ /// 网格Y坐标
+ /// 是否为空
+ bool IsCellEmpty(int x, int y);
+
+ ///
+ /// 世界坐标转换为网格坐标
+ ///
+ /// 世界坐标
+ /// 输出的网格X坐标
+ /// 输出的网格Y坐标
+ /// 是否转换成功
+ bool WorldToGrid(Vector3 worldPos, out int gridX, out int gridY);
+
+ ///
+ /// 网格坐标转换为世界坐标
+ ///
+ /// 网格X坐标
+ /// 网格Y坐标
+ /// 世界坐标
+ Vector3 GridToWorld(int gridX, int gridY);
+
+ ///
+ /// 清空整个网格
+ ///
+ void Clear();
+
+ ///
+ /// 获取已占用的单元格数量
+ ///
+ /// 已占用的单元格数量
+ int GetOccupiedCount();
+
+ ///
+ /// 获取总单元格数量
+ ///
+ /// 总单元格数量
+ int GetTotalCellCount();
+ }
+}
+
diff --git a/Assets/Scripts/MiniGame/BlockPuzzle/IBlockPuzzleGrid.cs.meta b/Assets/Scripts/MiniGame/BlockPuzzle/IBlockPuzzleGrid.cs.meta
new file mode 100644
index 000000000..286711bcc
--- /dev/null
+++ b/Assets/Scripts/MiniGame/BlockPuzzle/IBlockPuzzleGrid.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 44bbdf523b438ef4aa34868e92b91aed
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Scripts/MiniGame/BlockPuzzle/IBlockShape.cs b/Assets/Scripts/MiniGame/BlockPuzzle/IBlockShape.cs
new file mode 100644
index 000000000..f0e51bc91
--- /dev/null
+++ b/Assets/Scripts/MiniGame/BlockPuzzle/IBlockShape.cs
@@ -0,0 +1,106 @@
+using UnityEngine;
+
+namespace AibisDream
+{
+ ///
+ /// 形状旋转角度
+ ///
+ public enum RotationAngle
+ {
+ Rotate0 = 0, // 0度
+ Rotate90 = 90, // 90度
+ Rotate180 = 180, // 180度
+ Rotate270 = 270 // 270度
+ }
+
+ ///
+ /// 形状块接口
+ /// 负责管理形状的数据和操作
+ ///
+ public interface IBlockShape
+ {
+ ///
+ /// 形状的唯一ID
+ ///
+ int ShapeId { get; }
+
+ ///
+ /// 形状名称
+ ///
+ string ShapeName { get; }
+
+ ///
+ /// 当前旋转角度
+ ///
+ RotationAngle CurrentRotation { get; }
+
+ ///
+ /// 获取当前形状的宽度(考虑旋转)
+ ///
+ int Width { get; }
+
+ ///
+ /// 获取当前形状的高度(考虑旋转)
+ ///
+ int Height { get; }
+
+ ///
+ /// 获取形状占用的格子数量
+ ///
+ int BlockCount { get; }
+
+ ///
+ /// 获取原始形状模式(未旋转)
+ ///
+ /// 二维数组,1表示有格子,0表示空
+ int[,] GetOriginalPattern();
+
+ ///
+ /// 获取当前形状模式(考虑旋转)
+ ///
+ /// 二维数组,1表示有格子,0表示空
+ int[,] GetCurrentPattern();
+
+ ///
+ /// 旋转形状
+ ///
+ /// 旋转角度
+ void Rotate(RotationAngle angle);
+
+ ///
+ /// 顺时针旋转90度
+ ///
+ void RotateClockwise();
+
+ ///
+ /// 逆时针旋转90度
+ ///
+ void RotateCounterClockwise();
+
+ ///
+ /// 重置旋转角度为0
+ ///
+ void ResetRotation();
+
+ ///
+ /// 检查指定位置是否有格子
+ ///
+ /// 形状内的X坐标(相对坐标)
+ /// 形状内的Y坐标(相对坐标)
+ /// 是否有格子
+ bool HasBlockAt(int x, int y);
+
+ ///
+ /// 获取形状的所有格子位置(相对坐标)
+ ///
+ /// 格子位置列表
+ Vector2Int[] GetBlockPositions();
+
+ ///
+ /// 创建形状的副本
+ ///
+ /// 形状副本
+ IBlockShape Clone();
+ }
+}
+
diff --git a/Assets/Scripts/MiniGame/BlockPuzzle/IBlockShape.cs.meta b/Assets/Scripts/MiniGame/BlockPuzzle/IBlockShape.cs.meta
new file mode 100644
index 000000000..8387aaece
--- /dev/null
+++ b/Assets/Scripts/MiniGame/BlockPuzzle/IBlockShape.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: b3b17304622be4e46b270e3d475b1300
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Scripts/MiniGame/BlockPuzzle/IBlockShapeFactory.cs b/Assets/Scripts/MiniGame/BlockPuzzle/IBlockShapeFactory.cs
new file mode 100644
index 000000000..18d52bcb5
--- /dev/null
+++ b/Assets/Scripts/MiniGame/BlockPuzzle/IBlockShapeFactory.cs
@@ -0,0 +1,57 @@
+namespace AibisDream
+{
+ ///
+ /// 形状工厂接口
+ /// 负责创建和管理形状对象
+ ///
+ public interface IBlockShapeFactory
+ {
+ ///
+ /// 创建指定ID的形状
+ ///
+ /// 形状ID
+ /// 形状对象,如果ID不存在则返回null
+ IBlockShape CreateShape(int shapeId);
+
+ ///
+ /// 创建指定名称的形状
+ ///
+ /// 形状名称
+ /// 形状对象,如果名称不存在则返回null
+ IBlockShape CreateShape(string shapeName);
+
+ ///
+ /// 创建随机形状
+ ///
+ /// 随机形状对象
+ IBlockShape CreateRandomShape();
+
+ ///
+ /// 获取所有可用的形状ID列表
+ ///
+ /// 形状ID数组
+ int[] GetAllShapeIds();
+
+ ///
+ /// 获取所有可用的形状名称列表
+ ///
+ /// 形状名称数组
+ string[] GetAllShapeNames();
+
+ ///
+ /// 注册新形状
+ ///
+ /// 形状ID
+ /// 形状名称
+ /// 形状模式(二维数组)
+ void RegisterShape(int shapeId, string shapeName, int[,] pattern);
+
+ ///
+ /// 检查形状ID是否存在
+ ///
+ /// 形状ID
+ /// 是否存在
+ bool HasShape(int shapeId);
+ }
+}
+
diff --git a/Assets/Scripts/MiniGame/BlockPuzzle/IBlockShapeFactory.cs.meta b/Assets/Scripts/MiniGame/BlockPuzzle/IBlockShapeFactory.cs.meta
new file mode 100644
index 000000000..611459543
--- /dev/null
+++ b/Assets/Scripts/MiniGame/BlockPuzzle/IBlockShapeFactory.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 735c986ae1357ab489f5a5951ffb4bde
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Scripts/MiniGame/BlockPuzzle/IPlacementValidator.cs b/Assets/Scripts/MiniGame/BlockPuzzle/IPlacementValidator.cs
new file mode 100644
index 000000000..72ed54e51
--- /dev/null
+++ b/Assets/Scripts/MiniGame/BlockPuzzle/IPlacementValidator.cs
@@ -0,0 +1,51 @@
+namespace AibisDream
+{
+ ///
+ /// 放置验证结果
+ ///
+ public enum PlacementResult
+ {
+ Success, // 可以放置
+ OutOfBounds, // 超出边界
+ Overlap, // 与已有块重叠
+ InvalidPosition // 无效位置
+ }
+
+ ///
+ /// 放置检测接口
+ /// 负责验证形状是否可以放置在指定位置
+ ///
+ public interface IPlacementValidator
+ {
+ ///
+ /// 检查形状是否可以放置在指定位置
+ ///
+ /// 网格系统
+ /// 要放置的形状
+ /// 网格X坐标(形状左上角位置)
+ /// 网格Y坐标(形状左上角位置)
+ /// 放置验证结果
+ PlacementResult CanPlace(IBlockPuzzleGrid grid, IBlockShape shape, int gridX, int gridY);
+
+ ///
+ /// 检查形状是否可以放置在指定位置(详细版本)
+ ///
+ /// 网格系统
+ /// 要放置的形状
+ /// 网格X坐标(形状左上角位置)
+ /// 网格Y坐标(形状左上角位置)
+ /// 失败原因(如果返回false)
+ /// 是否可以放置
+ bool CanPlaceDetailed(IBlockPuzzleGrid grid, IBlockShape shape, int gridX, int gridY, out PlacementResult failureReason);
+
+ ///
+ /// 获取形状在指定位置的所有占用格子坐标
+ ///
+ /// 形状
+ /// 网格X坐标(形状左上角位置)
+ /// 网格Y坐标(形状左上角位置)
+ /// 占用格子的网格坐标数组
+ (int x, int y)[] GetOccupiedCells(IBlockShape shape, int gridX, int gridY);
+ }
+}
+
diff --git a/Assets/Scripts/MiniGame/BlockPuzzle/IPlacementValidator.cs.meta b/Assets/Scripts/MiniGame/BlockPuzzle/IPlacementValidator.cs.meta
new file mode 100644
index 000000000..04b726752
--- /dev/null
+++ b/Assets/Scripts/MiniGame/BlockPuzzle/IPlacementValidator.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: e4ebc4e9f404ead41ac713a7a31fe330
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant: