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: