diff --git a/Assets/Editor/Inspector/BlockPuzzleGridEditor.cs b/Assets/Editor/Inspector/BlockShaprEditor.cs similarity index 51% rename from Assets/Editor/Inspector/BlockPuzzleGridEditor.cs rename to Assets/Editor/Inspector/BlockShaprEditor.cs index 2ad6302c4..021b9e91f 100644 --- a/Assets/Editor/Inspector/BlockPuzzleGridEditor.cs +++ b/Assets/Editor/Inspector/BlockShaprEditor.cs @@ -3,26 +3,23 @@ using UnityEngine; namespace AibisDream.SystemEditor { - [CustomEditor(typeof(BlockPuzzleGrid))] - public class BlockPuzzleGridEditor : Editor + [CustomEditor(typeof(BlockShape))] + public class BlockShapeEditor : Editor { public override void OnInspectorGUI() { base.OnInspectorGUI(); - var example = (BlockPuzzleGrid)target; - EditorGUILayout.Separator(); + var example = (BlockShape)target; EditorGUILayout.BeginHorizontal(); - // 保存按钮 + // 增加绘制形状和保存形状按钮 + if (GUILayout.Button("绘制")) + { + example.DrawShape(); + } if (GUILayout.Button("保存")) { - // example.SaveStyle(); - } - - // 加载按钮 - if (GUILayout.Button("加载")) - { - // example.LoadStyle(); + example.SaveShape(); } EditorGUILayout.EndHorizontal(); } diff --git a/Assets/Editor/Inspector/BlockPuzzleGridEditor.cs.meta b/Assets/Editor/Inspector/BlockShaprEditor.cs.meta similarity index 83% rename from Assets/Editor/Inspector/BlockPuzzleGridEditor.cs.meta rename to Assets/Editor/Inspector/BlockShaprEditor.cs.meta index fb40dde14..6ab775eca 100644 --- a/Assets/Editor/Inspector/BlockPuzzleGridEditor.cs.meta +++ b/Assets/Editor/Inspector/BlockShaprEditor.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 7eb275cadab046c44a9f46f3f475145e +guid: 73fc356b22fa6a147be7a2eb9c78f123 MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/Assets/Language/UIText/UIText_zh-Hans.asset b/Assets/Language/UIText/UIText_zh-Hans.asset index df174b5f0..270bd717d 100644 --- a/Assets/Language/UIText/UIText_zh-Hans.asset +++ b/Assets/Language/UIText/UIText_zh-Hans.asset @@ -24,7 +24,7 @@ MonoBehaviour: m_Metadata: m_Items: [] - m_Id: 22296654778368 - m_Localized: "\u7231&\u673A\u5668\u4EBA\\n\u7EF4\u4FEE\u6280\u672F" + m_Localized: "\u7231\u4E0E\u673A\u5668\u4EBA\\n\u7EF4\u4FEE\u6280\u672F" m_Metadata: m_Items: - rid: 7917695939544875419 diff --git a/Assets/Scenes/网格测试.unity b/Assets/Scenes/网格测试.unity index c8dffcca1..cee6a404e 100644 --- a/Assets/Scenes/网格测试.unity +++ b/Assets/Scenes/网格测试.unity @@ -123,6 +123,552 @@ NavMeshSettings: debug: m_Flags: 0 m_NavMeshData: {fileID: 0} +--- !u!1 &199240525 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 199240526} + - component: {fileID: 199240527} + m_Layer: 0 + m_Name: Cell_0_2 + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &199240526 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 199240525} + serializedVersion: 2 + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 2, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 1147676901} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!212 &199240527 +SpriteRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 199240525} + m_Enabled: 1 + m_CastShadows: 0 + m_ReceiveShadows: 0 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 0 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: a97c105638bdf8b4a8650670310a4cd3, type: 2} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 1 + m_SelectedEditorRenderState: 0 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 + m_Sprite: {fileID: 431031536} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_FlipX: 0 + m_FlipY: 0 + m_DrawMode: 0 + m_Size: {x: 1, y: 1} + m_AdaptiveModeThreshold: 0.5 + m_SpriteTileMode: 0 + m_WasSpriteAssigned: 1 + m_MaskInteraction: 0 + m_SpriteSortPoint: 0 +--- !u!1 &224755568 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 224755569} + - component: {fileID: 224755570} + m_Layer: 0 + m_Name: Cell_1_1 + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &224755569 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 224755568} + serializedVersion: 2 + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 1, y: 1, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 1147676901} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!212 &224755570 +SpriteRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 224755568} + m_Enabled: 1 + m_CastShadows: 0 + m_ReceiveShadows: 0 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 0 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: a97c105638bdf8b4a8650670310a4cd3, type: 2} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 1 + m_SelectedEditorRenderState: 0 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 + m_Sprite: {fileID: 431031536} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_FlipX: 0 + m_FlipY: 0 + m_DrawMode: 0 + m_Size: {x: 1, y: 1} + m_AdaptiveModeThreshold: 0.5 + m_SpriteTileMode: 0 + m_WasSpriteAssigned: 1 + m_MaskInteraction: 0 + m_SpriteSortPoint: 0 +--- !u!1 &397665472 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 397665473} + - component: {fileID: 397665474} + m_Layer: 0 + m_Name: Cell_0_1 + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &397665473 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 397665472} + serializedVersion: 2 + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0.01, y: 1, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 1147676901} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!212 &397665474 +SpriteRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 397665472} + m_Enabled: 1 + m_CastShadows: 0 + m_ReceiveShadows: 0 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 0 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: a97c105638bdf8b4a8650670310a4cd3, type: 2} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 1 + m_SelectedEditorRenderState: 0 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 + m_Sprite: {fileID: 431031536} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_FlipX: 0 + m_FlipY: 0 + m_DrawMode: 0 + m_Size: {x: 1, y: 1} + m_AdaptiveModeThreshold: 0.5 + m_SpriteTileMode: 0 + m_WasSpriteAssigned: 1 + m_MaskInteraction: 0 + m_SpriteSortPoint: 0 +--- !u!213 &431031536 +Sprite: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: + m_Rect: + serializedVersion: 2 + x: 0 + y: 0 + width: 1 + height: 1 + m_Offset: {x: 0, y: 0} + m_Border: {x: 0, y: 0, z: 0, w: 0} + m_PixelsToUnits: 1 + m_Pivot: {x: 0.5, y: 0.5} + m_Extrude: 0 + m_IsPolygon: 0 + m_AtlasName: + m_PackingTag: + m_RenderDataKey: + 00000000000000000000000000000000: 0 + m_AtlasTags: [] + m_SpriteAtlas: {fileID: 0} + m_RD: + serializedVersion: 3 + texture: {fileID: 952406368} + alphaTexture: {fileID: 0} + secondaryTextures: [] + m_SubMeshes: + - serializedVersion: 2 + firstByte: 0 + indexCount: 6 + topology: 0 + baseVertex: 0 + firstVertex: 0 + vertexCount: 4 + localAABB: + m_Center: {x: 0, y: 0, z: 0} + m_Extent: {x: 0, y: 0, z: 0} + m_IndexBuffer: 000001000200020001000300 + m_VertexData: + serializedVersion: 3 + m_VertexCount: 4 + m_Channels: + - stream: 0 + offset: 0 + format: 0 + dimension: 3 + - stream: 0 + offset: 0 + format: 0 + dimension: 0 + - stream: 0 + offset: 0 + format: 0 + dimension: 0 + - stream: 0 + offset: 0 + format: 0 + dimension: 0 + - stream: 1 + offset: 0 + format: 0 + dimension: 2 + - stream: 0 + offset: 0 + format: 0 + dimension: 0 + - stream: 0 + offset: 0 + format: 0 + dimension: 0 + - stream: 0 + offset: 0 + format: 0 + dimension: 0 + - stream: 0 + offset: 0 + format: 0 + dimension: 0 + - stream: 0 + offset: 0 + format: 0 + dimension: 0 + - stream: 0 + offset: 0 + format: 0 + dimension: 0 + - stream: 0 + offset: 0 + format: 0 + dimension: 0 + - stream: 0 + offset: 0 + format: 0 + dimension: 0 + - stream: 0 + offset: 0 + format: 0 + dimension: 0 + m_DataSize: 80 + _typelessdata: 000000bf0000003f000000000000003f0000003f00000000000000bf000000bf000000000000003f000000bf00000000000000000000803f0000803f0000803f00000000000000000000803f00000000 + m_Bindpose: [] + textureRect: + serializedVersion: 2 + x: 0 + y: 0 + width: 1 + height: 1 + textureRectOffset: {x: 0, y: 0} + atlasRectOffset: {x: -1, y: -1} + settingsRaw: 64 + uvTransform: {x: 1, y: 0.5, z: 1, w: 0.5} + downscaleMultiplier: 1 + m_AtlasRD: + serializedVersion: 3 + texture: {fileID: 952406368} + alphaTexture: {fileID: 0} + secondaryTextures: [] + m_SubMeshes: + - serializedVersion: 2 + firstByte: 0 + indexCount: 6 + topology: 0 + baseVertex: 0 + firstVertex: 0 + vertexCount: 4 + localAABB: + m_Center: {x: 0, y: 0, z: 0} + m_Extent: {x: 0, y: 0, z: 0} + m_IndexBuffer: 000001000200020001000300 + m_VertexData: + serializedVersion: 3 + m_VertexCount: 4 + m_Channels: + - stream: 0 + offset: 0 + format: 0 + dimension: 3 + - stream: 0 + offset: 0 + format: 0 + dimension: 0 + - stream: 0 + offset: 0 + format: 0 + dimension: 0 + - stream: 0 + offset: 0 + format: 0 + dimension: 0 + - stream: 1 + offset: 0 + format: 0 + dimension: 2 + - stream: 0 + offset: 0 + format: 0 + dimension: 0 + - stream: 0 + offset: 0 + format: 0 + dimension: 0 + - stream: 0 + offset: 0 + format: 0 + dimension: 0 + - stream: 0 + offset: 0 + format: 0 + dimension: 0 + - stream: 0 + offset: 0 + format: 0 + dimension: 0 + - stream: 0 + offset: 0 + format: 0 + dimension: 0 + - stream: 0 + offset: 0 + format: 0 + dimension: 0 + - stream: 0 + offset: 0 + format: 0 + dimension: 0 + - stream: 0 + offset: 0 + format: 0 + dimension: 0 + m_DataSize: 80 + _typelessdata: 000000bf0000003f000000000000003f0000003f00000000000000bf000000bf000000000000003f000000bf00000000000000000000803f0000803f0000803f00000000000000000000803f00000000 + m_Bindpose: [] + textureRect: + serializedVersion: 2 + x: 0 + y: 0 + width: 1 + height: 1 + textureRectOffset: {x: 0, y: 0} + atlasRectOffset: {x: -1, y: -1} + settingsRaw: 64 + uvTransform: {x: 1, y: 0.5, z: 1, w: 0.5} + downscaleMultiplier: 1 + m_PhysicsShape: [] + m_Bones: [] + m_SpriteID: +--- !u!1 &451361381 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 451361382} + - component: {fileID: 451361383} + m_Layer: 0 + m_Name: Cell_1_0 + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &451361382 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 451361381} + serializedVersion: 2 + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 1, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 1147676901} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!212 &451361383 +SpriteRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 451361381} + m_Enabled: 1 + m_CastShadows: 0 + m_ReceiveShadows: 0 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 0 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: a97c105638bdf8b4a8650670310a4cd3, type: 2} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 1 + m_SelectedEditorRenderState: 0 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 + m_Sprite: {fileID: 431031536} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_FlipX: 0 + m_FlipY: 0 + m_DrawMode: 0 + m_Size: {x: 1, y: 1} + m_AdaptiveModeThreshold: 0.5 + m_SpriteTileMode: 0 + m_WasSpriteAssigned: 1 + m_MaskInteraction: 0 + m_SpriteSortPoint: 0 --- !u!1 &791020753 GameObject: m_ObjectHideFlags: 0 @@ -215,6 +761,370 @@ Transform: m_Children: [] m_Father: {fileID: 0} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &822843321 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 822843322} + - component: {fileID: 822843323} + m_Layer: 0 + m_Name: Cell_0_0 + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &822843322 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 822843321} + 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: 1147676901} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!212 &822843323 +SpriteRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 822843321} + m_Enabled: 0 + m_CastShadows: 0 + m_ReceiveShadows: 0 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 0 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: a97c105638bdf8b4a8650670310a4cd3, type: 2} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 1 + m_SelectedEditorRenderState: 0 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 + m_Sprite: {fileID: 431031536} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_FlipX: 0 + m_FlipY: 0 + m_DrawMode: 0 + m_Size: {x: 1, y: 1} + m_AdaptiveModeThreshold: 0.5 + m_SpriteTileMode: 0 + m_WasSpriteAssigned: 1 + m_MaskInteraction: 0 + m_SpriteSortPoint: 0 +--- !u!28 &952406368 +Texture2D: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: + m_ImageContentsHash: + serializedVersion: 2 + Hash: 00000000000000000000000000000000 + m_ForcedFallbackFormat: 4 + m_DownscaleFallback: 0 + m_IsAlphaChannelOptional: 0 + serializedVersion: 2 + m_Width: 1 + m_Height: 1 + m_CompleteImageSize: 4 + m_MipsStripped: 0 + m_TextureFormat: 4 + m_MipCount: 1 + m_IsReadable: 1 + m_IsPreProcessed: 0 + m_IgnoreMipmapLimit: 0 + m_MipmapLimitGroupName: + m_StreamingMipmaps: 0 + m_StreamingMipmapsPriority: 0 + m_VTOnly: 0 + m_AlphaIsTransparency: 0 + m_ImageCount: 1 + m_TextureDimension: 2 + m_TextureSettings: + serializedVersion: 2 + m_FilterMode: 1 + m_Aniso: 1 + m_MipBias: 0 + m_WrapU: 0 + m_WrapV: 0 + m_WrapW: 0 + m_LightmapFormat: 0 + m_ColorSpace: 1 + m_PlatformBlob: + image data: 4 + _typelessdata: ff0000ff + m_StreamData: + serializedVersion: 2 + offset: 0 + size: 0 + path: +--- !u!1 &1147676900 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1147676901} + - component: {fileID: 1147676902} + - component: {fileID: 1147676903} + - component: {fileID: 1147676904} + - component: {fileID: 1147676905} + - component: {fileID: 1147676906} + m_Layer: 0 + m_Name: Shape + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &1147676901 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1147676900} + serializedVersion: 2 + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 2.06, y: -0.14, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 822843322} + - {fileID: 397665473} + - {fileID: 199240526} + - {fileID: 451361382} + - {fileID: 224755569} + - {fileID: 1662947689} + m_Father: {fileID: 0} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!114 &1147676902 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1147676900} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 4be33b2e5baf96447a7df0d10cb22064, type: 3} + m_Name: + m_EditorClassIdentifier: + shapeId: 0 + shapeName: "\u793A\u4F8B\u6A21\u5757" + currentRotation: 0 + width: 2 + height: 3 + cellSize: 1 + originalPatternStr: 0,1,1;1,1,0 +--- !u!212 &1147676903 +SpriteRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1147676900} + m_Enabled: 1 + m_CastShadows: 0 + m_ReceiveShadows: 0 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 0 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: a97c105638bdf8b4a8650670310a4cd3, type: 2} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 1 + m_SelectedEditorRenderState: 0 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 + m_Sprite: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_FlipX: 0 + m_FlipY: 0 + m_DrawMode: 0 + m_Size: {x: 1, y: 1} + m_AdaptiveModeThreshold: 0.5 + m_SpriteTileMode: 0 + m_WasSpriteAssigned: 0 + m_MaskInteraction: 0 + m_SpriteSortPoint: 0 +--- !u!60 &1147676904 +PolygonCollider2D: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1147676900} + m_Enabled: 1 + m_Density: 1 + m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 + m_ForceSendLayers: + serializedVersion: 2 + m_Bits: 4294967295 + m_ForceReceiveLayers: + serializedVersion: 2 + m_Bits: 4294967295 + m_ContactCaptureLayers: + serializedVersion: 2 + m_Bits: 4294967295 + m_CallbackLayers: + serializedVersion: 2 + m_Bits: 4294967295 + m_IsTrigger: 0 + m_UsedByEffector: 0 + m_UsedByComposite: 0 + m_Offset: {x: 0, y: 0} + m_SpriteTilingProperty: + border: {x: 0, y: 0, z: 0, w: 0} + pivot: {x: 0, y: 0} + oldSize: {x: 0, y: 0} + newSize: {x: 0, y: 0} + adaptiveTilingThreshold: 0 + drawMode: 0 + adaptiveTiling: 0 + m_AutoTiling: 0 + m_Points: + m_Paths: + - - {x: -0.5, y: 0.5} + - {x: 0.5, y: 0.5} + - {x: 0.5, y: -0.5} + - {x: 1.5, y: -0.5} + - {x: 1.5, y: 0.5} + - {x: 1.5, y: 1.5} + - {x: 0.5, y: 1.5} + - {x: 0.5, y: 2.5} + - {x: -0.5, y: 2.5} + - {x: -0.5, y: 1.5} + m_UseDelaunayMesh: 0 +--- !u!114 &1147676905 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1147676900} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 6ad01079e32d2314eb3719ead5f50ddb, type: 3} + m_Name: + m_EditorClassIdentifier: + isDraggable: 1 + snapDistance: 0.5 + targetGridComponent: {fileID: 1928610078} + snapColor: {r: 0, g: 1, b: 0, a: 0.7} + invalidColor: {r: 1, g: 0, b: 0, a: 0.7} + normalColor: {r: 1, g: 1, b: 1, a: 1} + dragScale: 1.1 +--- !u!114 &1147676906 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1147676900} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 0c4fbab01ad40c2449c849cd2a8c620f, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Delegates: + - eventID: 0 + callback: + m_PersistentCalls: + m_Calls: [] + - eventID: 1 + callback: + m_PersistentCalls: + m_Calls: [] + - eventID: 2 + callback: + m_PersistentCalls: + m_Calls: [] + - eventID: 3 + callback: + m_PersistentCalls: + m_Calls: [] + - eventID: 4 + callback: + m_PersistentCalls: + m_Calls: [] + - eventID: 5 + callback: + m_PersistentCalls: + m_Calls: [] + - eventID: 14 + callback: + m_PersistentCalls: + m_Calls: [] + - eventID: 13 + callback: + m_PersistentCalls: + m_Calls: [] --- !u!1 &1429030510 GameObject: m_ObjectHideFlags: 0 @@ -224,7 +1134,6 @@ GameObject: serializedVersion: 6 m_Component: - component: {fileID: 1429030511} - - component: {fileID: 1429030512} m_Layer: 0 m_Name: Block Puzzle Game m_TagString: Untagged @@ -248,22 +1157,90 @@ Transform: - {fileID: 1928610076} m_Father: {fileID: 0} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} ---- !u!114 &1429030512 -MonoBehaviour: +--- !u!1 &1662947688 +GameObject: 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 + serializedVersion: 6 + m_Component: + - component: {fileID: 1662947689} + - component: {fileID: 1662947690} + m_Layer: 0 + m_Name: Cell_1_2 + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &1662947689 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1662947688} + serializedVersion: 2 + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 1, y: 2, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 1147676901} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!212 &1662947690 +SpriteRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1662947688} + m_Enabled: 0 + m_CastShadows: 0 + m_ReceiveShadows: 0 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 0 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: a97c105638bdf8b4a8650670310a4cd3, type: 2} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 1 + m_SelectedEditorRenderState: 0 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 + m_Sprite: {fileID: 431031536} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_FlipX: 0 + m_FlipY: 0 + m_DrawMode: 0 + m_Size: {x: 1, y: 1} + m_AdaptiveModeThreshold: 0.5 + m_SpriteTileMode: 0 + m_WasSpriteAssigned: 1 + m_MaskInteraction: 0 + m_SpriteSortPoint: 0 --- !u!1 &1928610075 GameObject: m_ObjectHideFlags: 0 @@ -291,7 +1268,7 @@ Transform: 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_LocalPosition: {x: -6.5, y: -2.5, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} m_ConstrainProportionsScale: 0 m_Children: [] @@ -310,8 +1287,8 @@ MonoBehaviour: m_Name: m_EditorClassIdentifier: gridComponent: {fileID: 1928610078} - gridWidth: 10 - gridHeight: 10 + gridWidth: 5 + gridHeight: 5 cellSize: 1 showGridLines: 1 showCellSprites: 1 @@ -333,13 +1310,19 @@ MonoBehaviour: m_Script: {fileID: 11500000, guid: 990de8ef2a6774544b9bc9b64af0a8a5, type: 3} m_Name: m_EditorClassIdentifier: - width: 10 - height: 10 + width: 5 + height: 5 cellSize: 1 initializeOnStart: 1 + showGizmos: 1 + gridLineColor: {r: 0.5, g: 0.5, b: 0.5, a: 0.8} + emptyCellColor: {r: 0.9, g: 0.9, b: 0.9, a: 0.2} + occupiedCellColor: {r: 0.2, g: 0.6, b: 1, a: 0.5} + showCellStates: 1 --- !u!1660057539 &9223372036854775807 SceneRoots: m_ObjectHideFlags: 0 m_Roots: - {fileID: 791020756} - {fileID: 1429030511} + - {fileID: 1147676901} diff --git a/Assets/Scripts/MiniGame/BlockPuzzle/Class.meta b/Assets/Scripts/MiniGame/BlockPuzzle/Class.meta new file mode 100644 index 000000000..822c6c2df --- /dev/null +++ b/Assets/Scripts/MiniGame/BlockPuzzle/Class.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 0eb5cc55dcdc0f244a5cb635090c0bd3 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/MiniGame/BlockPuzzle/BlockPuzzleGrid.cs b/Assets/Scripts/MiniGame/BlockPuzzle/Class/BlockPuzzleGrid.cs similarity index 54% rename from Assets/Scripts/MiniGame/BlockPuzzle/BlockPuzzleGrid.cs rename to Assets/Scripts/MiniGame/BlockPuzzle/Class/BlockPuzzleGrid.cs index 4936c995c..6f61d4257 100644 --- a/Assets/Scripts/MiniGame/BlockPuzzle/BlockPuzzleGrid.cs +++ b/Assets/Scripts/MiniGame/BlockPuzzle/Class/BlockPuzzleGrid.cs @@ -14,6 +14,13 @@ namespace AibisDream [SerializeField] private float cellSize = 1f; [SerializeField] private bool initializeOnStart = true; + [Header("编辑器预览设置")] + [SerializeField] private bool showGizmos = true; + [SerializeField] private Color gridLineColor = new Color(0.5f, 0.5f, 0.5f, 0.8f); + [SerializeField] private Color emptyCellColor = new Color(0.9f, 0.9f, 0.9f, 0.2f); + [SerializeField] private Color occupiedCellColor = new Color(0.2f, 0.6f, 1f, 0.5f); + [SerializeField] private bool showCellStates = true; + private GridCellState[,] _grid; private bool _isInitialized = false; @@ -157,6 +164,24 @@ namespace AibisDream return new Vector3(worldX, worldY, Origin.z); } + public Vector3[] GridVerticesToWorld(int x, int y) + { + if (!_isInitialized) + { + return new Vector3[4]; + } + + // 先获取网格中心位置 + Vector3 center = GridToWorld(x, y); + // 然后获取网格四个顶点位置 + Vector3[] vertices = new Vector3[4]; + vertices[0] = center + new Vector3(-CellSize / 2, -CellSize / 2, 0); + vertices[1] = center + new Vector3(CellSize / 2, -CellSize / 2, 0); + vertices[2] = center + new Vector3(CellSize / 2, CellSize / 2, 0); + vertices[3] = center + new Vector3(-CellSize / 2, CellSize / 2, 0); + return vertices; + } + public void Clear() { if (!_isInitialized || _grid == null) return; @@ -209,6 +234,93 @@ namespace AibisDream height = Mathf.Max(1, height); cellSize = Mathf.Max(0.1f, cellSize); } + + /// + /// 在编辑器中绘制网格预览 + /// + private void OnDrawGizmos() + { + if (!showGizmos) return; + + // 使用当前Inspector中的参数或已初始化的参数 + int drawWidth = _isInitialized ? Width : width; + int drawHeight = _isInitialized ? Height : height; + float drawCellSize = _isInitialized ? CellSize : cellSize; + Vector3 drawOrigin = _isInitialized ? Origin : transform.position; + + if (drawWidth <= 0 || drawHeight <= 0 || drawCellSize <= 0) return; + + // 绘制网格线 + Gizmos.color = gridLineColor; + for (int x = 0; x <= drawWidth; x++) + { + Vector3 start = drawOrigin + new Vector3(x * drawCellSize, 0, 0); + Vector3 end = drawOrigin + new Vector3(x * drawCellSize, drawHeight * drawCellSize, 0); + Gizmos.DrawLine(start, end); + } + + for (int y = 0; y <= drawHeight; y++) + { + Vector3 start = drawOrigin + new Vector3(0, y * drawCellSize, 0); + Vector3 end = drawOrigin + new Vector3(drawWidth * drawCellSize, y * drawCellSize, 0); + Gizmos.DrawLine(start, end); + } + + // 绘制单元格状态(如果已初始化且启用) + if (showCellStates && _isInitialized && _grid != null) + { + float cellDisplaySize = drawCellSize * 0.9f; // 稍微小一点,留出网格线空间 + Vector3 cellSizeVec = new Vector3(cellDisplaySize, cellDisplaySize, 0.01f); + + for (int x = 0; x < drawWidth; x++) + { + for (int y = 0; y < drawHeight; y++) + { + if (IsValidPosition(x, y)) + { + Vector3 cellCenter = GridToWorld(x, y); + GridCellState state = GetCellState(x, y); + + Gizmos.color = state == GridCellState.Occupied ? occupiedCellColor : emptyCellColor; + Gizmos.DrawCube(cellCenter, cellSizeVec); + } + } + } + } + } + + /// + /// 选中时绘制更明显的网格边界 + /// + private void OnDrawGizmosSelected() + { + if (!showGizmos) return; + + // 使用当前Inspector中的参数或已初始化的参数 + int drawWidth = _isInitialized ? Width : width; + int drawHeight = _isInitialized ? Height : height; + float drawCellSize = _isInitialized ? CellSize : cellSize; + Vector3 drawOrigin = _isInitialized ? Origin : transform.position; + + if (drawWidth <= 0 || drawHeight <= 0 || drawCellSize <= 0) return; + + // 绘制网格边界(更粗的线) + Gizmos.color = new Color(gridLineColor.r, gridLineColor.g, gridLineColor.b, 1f); + Vector3 bottomLeft = drawOrigin; + Vector3 bottomRight = drawOrigin + new Vector3(drawWidth * drawCellSize, 0, 0); + Vector3 topLeft = drawOrigin + new Vector3(0, drawHeight * drawCellSize, 0); + Vector3 topRight = drawOrigin + new Vector3(drawWidth * drawCellSize, drawHeight * drawCellSize, 0); + + // 绘制边界框 + Gizmos.DrawLine(bottomLeft, bottomRight); + Gizmos.DrawLine(bottomRight, topRight); + Gizmos.DrawLine(topRight, topLeft); + Gizmos.DrawLine(topLeft, bottomLeft); + + // 绘制原点标记 + Gizmos.color = Color.yellow; + Gizmos.DrawWireSphere(drawOrigin, 0.1f); + } } } diff --git a/Assets/Scripts/MiniGame/BlockPuzzle/BlockPuzzleGrid.cs.meta b/Assets/Scripts/MiniGame/BlockPuzzle/Class/BlockPuzzleGrid.cs.meta similarity index 100% rename from Assets/Scripts/MiniGame/BlockPuzzle/BlockPuzzleGrid.cs.meta rename to Assets/Scripts/MiniGame/BlockPuzzle/Class/BlockPuzzleGrid.cs.meta diff --git a/Assets/Scripts/MiniGame/BlockPuzzle/BlockPuzzleGridExample.cs b/Assets/Scripts/MiniGame/BlockPuzzle/Class/BlockPuzzleGridExample.cs similarity index 100% rename from Assets/Scripts/MiniGame/BlockPuzzle/BlockPuzzleGridExample.cs rename to Assets/Scripts/MiniGame/BlockPuzzle/Class/BlockPuzzleGridExample.cs diff --git a/Assets/Scripts/MiniGame/BlockPuzzle/BlockPuzzleGridExample.cs.meta b/Assets/Scripts/MiniGame/BlockPuzzle/Class/BlockPuzzleGridExample.cs.meta similarity index 100% rename from Assets/Scripts/MiniGame/BlockPuzzle/BlockPuzzleGridExample.cs.meta rename to Assets/Scripts/MiniGame/BlockPuzzle/Class/BlockPuzzleGridExample.cs.meta diff --git a/Assets/Scripts/MiniGame/BlockPuzzle/Class/BlockShape.cs b/Assets/Scripts/MiniGame/BlockPuzzle/Class/BlockShape.cs new file mode 100644 index 000000000..792658e6b --- /dev/null +++ b/Assets/Scripts/MiniGame/BlockPuzzle/Class/BlockShape.cs @@ -0,0 +1,381 @@ +using System.Collections.Generic; +using AibisDream.Utility; +using UnityEngine; + +namespace AibisDream +{ + public class BlockShape : MonoBehaviour, IBlockShape + { + private SpriteRenderer[,] _cellRenderers; + + // 模块数据 + [Header("模块数据")] + [SerializeField] private int shapeId; + [SerializeField] private string shapeName; + [SerializeField] private RotationAngle currentRotation; + [SerializeField] private int width; + [SerializeField] private int height; + [SerializeField] private float cellSize; + [SerializeField] private string originalPatternStr; + + // 接口实现 + public int ShapeId => shapeId; + public string ShapeName => shapeName; + public RotationAngle CurrentRotation => currentRotation; + public int Width => width; + public int Height => height; + public int BlockCount + { + get + { + var pattern = GetOriginalPattern(); + if (pattern == null) return 0; + + int count = 0; + for (int i = 0; i < width; i++) + { + for (int j = 0; j < height; j++) + { + if (pattern[i, j] != 0) + { + count++; + } + } + } + return count; + } + } + + public Vector2Int[] GetBlockPositions() + { + var pattern = GetOriginalPattern(); + if (pattern == null) return new Vector2Int[0]; + + List positions = new List(); + + for (int i = 0; i < width; i++) + { + for (int j = 0; j < height; j++) + { + if (pattern[i, j] != 0) + { + positions.Add(new Vector2Int(i, j)); + } + } + } + + return positions.ToArray(); + } + + /// + /// 获取单元格渲染器数组(供拖拽组件使用) + /// + public SpriteRenderer[,] GetCellRenderers() + { + return _cellRenderers; + } + + public int[,] GetCurrentPattern() + { + throw new System.NotImplementedException(); + } + + public int[,] GetOriginalPattern() + { + return CommonUtil.DeserializeIntArray(originalPatternStr); + } + + public bool HasBlockAt(int x, int y) + { + throw new System.NotImplementedException(); + } + + public void ResetRotation() + { + throw new System.NotImplementedException(); + } + + public void Rotate(RotationAngle angle) + { + throw new System.NotImplementedException(); + } + + public void RotateClockwise() + { + throw new System.NotImplementedException(); + } + + public void RotateCounterClockwise() + { + throw new System.NotImplementedException(); + } + + private Sprite CreateDefaultSprite() + { + Texture2D texture = new Texture2D(1, 1); + texture.SetPixel(0, 0, Color.red); + texture.Apply(); + return Sprite.Create(texture, new Rect(0, 0, 1, 1), new Vector2(0.5f, 0.5f), 1f); + } + + // 仅限编辑器下使用 + public void DrawShape() + { + transform.DestroyAllChildren(); + _cellRenderers = null; + + // 根据Width和Height,绘制Cells,Cell为SpriteRenderer + _cellRenderers = new SpriteRenderer[width, height]; + var defaultSprite = CreateDefaultSprite(); + for (int i = 0; i < width; i++) + { + for (int j = 0; j < height; j++) + { + var cell = new GameObject($"Cell_{i}_{j}"); + var cellRenderer = cell.AddComponent(); + cellRenderer.sprite = defaultSprite; + _cellRenderers[i, j] = cellRenderer; + + cell.transform.parent = transform; + // 计算Cell应处的位置 + var cellPosX = i * cellSize; + var cellPosY = j * cellSize; + cell.transform.localPosition = new Vector3(cellPosX, cellPosY, 0); + } + } + + // 反序列化并处理 + var pattern = CommonUtil.DeserializeIntArray(originalPatternStr); + // 先判断pattern的维度是否与width和height一致 + if (pattern.GetLength(0) != width || pattern.GetLength(1) != height) + { + // 如果不一致,就不处理 + Debug.LogWarning($"Pattern dimension mismatch: {pattern.GetLength(0)}x{pattern.GetLength(1)} != {width}x{height}"); + return; + } + // 如果一致,就读取pattern,并设置对应的cell的SpriteRenderer是否激活 + for (int i = 0; i < width; i++) + { + for (int j = 0; j < height; j++) + { + _cellRenderers[i, j].enabled = pattern[i, j] != 0; + } + } + + // 更新碰撞体形状 + UpdatePolygonCollider(); + } + + public void SaveShape() + { + // 如果_cellRenderers不为空,则序列化并保存 + if (_cellRenderers != null) + { + var pattern = new int[width, height]; + for (int i = 0; i < width; i++) + { + for (int j = 0; j < height; j++) + { + pattern[i, j] = _cellRenderers[i, j].enabled ? 1 : 0; + } + } + Debug.Log($"SaveShape: {CommonUtil.SerializeIntArray(pattern)}"); + originalPatternStr = CommonUtil.SerializeIntArray(pattern); + + // 保存后更新碰撞体 + UpdatePolygonCollider(); + } + } + + /// + /// 根据originalPattern更新PolygonCollider2D的形状 + /// + public void UpdatePolygonCollider() + { + var pattern = GetOriginalPattern(); + if (pattern == null) return; + + // 获取或添加PolygonCollider2D组件 + PolygonCollider2D polygonCollider = GetComponent(); + if (polygonCollider == null) + { + polygonCollider = gameObject.AddComponent(); + } + + // 生成多边形顶点 + List vertices = GeneratePolygonVertices(pattern); + + if (vertices.Count > 0) + { + // 设置多边形路径 + polygonCollider.pathCount = 1; + polygonCollider.SetPath(0, vertices.ToArray()); + } + else + { + // 如果没有有效格子,清除路径 + polygonCollider.pathCount = 0; + } + } + + /// + /// 根据pattern生成多边形顶点 + /// + private List GeneratePolygonVertices(int[,] pattern) + { + List vertices = new List(); + + if (pattern == null || width <= 0 || height <= 0 || cellSize <= 0) + return vertices; + + // 生成精确的外轮廓(只包含边界点) + vertices = GenerateOutlineVertices(pattern); + + return vertices; + } + + /// + /// 生成精确的外轮廓顶点(只包含边界点) + /// 使用Marching Squares算法的简化版本 + /// + private List GenerateOutlineVertices(int[,] pattern) + { + List outlineVertices = new List(); + HashSet visitedPoints = new HashSet(); + + // 遍历所有格子,找到边界点 + for (int i = 0; i <= width; i++) + { + for (int j = 0; j <= height; j++) + { + // 检查这个角点是否在边界上 + bool isBoundaryCorner = IsBoundaryCorner(pattern, i, j); + + if (isBoundaryCorner) + { + // 计算角点的本地坐标 + // cornerX 和 cornerY 是角点索引(0到width/height) + // 需要转换为格子的角点坐标 + // 角点 (i, j) 对应格子 (i-0.5, j-0.5) 的角点位置 + float x = (i - 0.5f) * cellSize; + float y = (j - 0.5f) * cellSize; + + Vector2Int key = new Vector2Int(Mathf.RoundToInt(x * 100), Mathf.RoundToInt(y * 100)); + if (!visitedPoints.Contains(key)) + { + outlineVertices.Add(new Vector2(x, y)); + visitedPoints.Add(key); + } + } + } + } + + // 如果没有找到边界点,使用简单包围盒 + if (outlineVertices.Count == 0) + { + return GenerateBoundingBox(pattern); + } + + // 对顶点进行排序,形成闭合多边形 + return OrderVerticesForPolygon(outlineVertices); + } + + /// + /// 检查角点是否在边界上 + /// + private bool IsBoundaryCorner(int[,] pattern, int cornerX, int cornerY) + { + // 角点位于四个格子的交汇处 + // 检查周围四个格子的状态 + bool topLeft = (cornerX > 0 && cornerY > 0) ? pattern[cornerX - 1, cornerY - 1] != 0 : false; + bool topRight = (cornerX < width && cornerY > 0) ? pattern[cornerX, cornerY - 1] != 0 : false; + bool bottomLeft = (cornerX > 0 && cornerY < height) ? pattern[cornerX - 1, cornerY] != 0 : false; + bool bottomRight = (cornerX < width && cornerY < height) ? pattern[cornerX, cornerY] != 0 : false; + + // 如果四个格子状态不一致,说明这个角点在边界上 + int filledCount = (topLeft ? 1 : 0) + (topRight ? 1 : 0) + + (bottomLeft ? 1 : 0) + (bottomRight ? 1 : 0); + + // 边界角点:不是全部填充也不是全部空 + return filledCount > 0 && filledCount < 4; + } + + + /// + /// 生成简单的包围盒(备用方法) + /// + private List GenerateBoundingBox(int[,] pattern) + { + List vertices = new List(); + + // 找到有效格子的边界 + int minX = width, maxX = -1, minY = height, maxY = -1; + bool hasValidCell = false; + + for (int i = 0; i < width; i++) + { + for (int j = 0; j < height; j++) + { + if (pattern[i, j] != 0) + { + hasValidCell = true; + minX = Mathf.Min(minX, i); + maxX = Mathf.Max(maxX, i); + minY = Mathf.Min(minY, j); + maxY = Mathf.Max(maxY, j); + } + } + } + + if (!hasValidCell) + return vertices; + + // 计算包围盒的四个角点(本地坐标) + float halfSize = cellSize / 2f; + float minXWorld = minX * cellSize - width * cellSize / 2f - halfSize; + float maxXWorld = (maxX + 1) * cellSize - width * cellSize / 2f + halfSize; + float minYWorld = minY * cellSize - height * cellSize / 2f - halfSize; + float maxYWorld = (maxY + 1) * cellSize - height * cellSize / 2f + halfSize; + + // 按逆时针顺序添加顶点(Unity的PolygonCollider2D要求) + vertices.Add(new Vector2(minXWorld, minYWorld)); // 左下 + vertices.Add(new Vector2(maxXWorld, minYWorld)); // 右下 + vertices.Add(new Vector2(maxXWorld, maxYWorld)); // 右上 + vertices.Add(new Vector2(minXWorld, maxYWorld)); // 左上 + + return vertices; + } + + /// + /// 对顶点进行排序,形成闭合多边形 + /// + private List OrderVerticesForPolygon(List vertices) + { + if (vertices.Count <= 2) + return vertices; + + // 找到中心点 + Vector2 center = Vector2.zero; + foreach (var v in vertices) + { + center += v; + } + center /= vertices.Count; + + // 按角度排序(相对于中心点) + vertices.Sort((a, b) => + { + float angleA = Mathf.Atan2(a.y - center.y, a.x - center.x); + float angleB = Mathf.Atan2(b.y - center.y, b.x - center.x); + if (angleA.CompareTo(angleB) != 0) + { + return angleA.CompareTo(angleB); + } + + return (a - center).sqrMagnitude.CompareTo((b - center).sqrMagnitude); + }); + + return vertices; + } + } +} \ No newline at end of file diff --git a/Assets/Scripts/MiniGame/BlockPuzzle/Class/BlockShape.cs.meta b/Assets/Scripts/MiniGame/BlockPuzzle/Class/BlockShape.cs.meta new file mode 100644 index 000000000..0b9f4bd46 --- /dev/null +++ b/Assets/Scripts/MiniGame/BlockPuzzle/Class/BlockShape.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 4be33b2e5baf96447a7df0d10cb22064 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/MiniGame/BlockPuzzle/Class/BlockShapeDragger.cs b/Assets/Scripts/MiniGame/BlockPuzzle/Class/BlockShapeDragger.cs new file mode 100644 index 000000000..5414fa0d1 --- /dev/null +++ b/Assets/Scripts/MiniGame/BlockPuzzle/Class/BlockShapeDragger.cs @@ -0,0 +1,405 @@ +using System; +using AibisDream.Framework; +using UnityEngine; + +namespace AibisDream +{ + /// + /// 形状拖拽组件 + /// 实现IBlockShapeDragger接口,处理形状的拖拽、吸附和放置逻辑 + /// + [RequireComponent(typeof(BlockShape))] + [RequireComponent(typeof(EventTriggerEx))] + public class BlockShapeDragger : MonoBehaviour, IBlockShapeDragger + { + [Header("拖拽设置")] + [SerializeField] private bool isDraggable = true; + [SerializeField] private float snapDistance = 0.5f; + [SerializeField] private BlockPuzzleGrid targetGridComponent; // Unity组件引用 + + [Header("视觉反馈")] + [SerializeField] private Color snapColor = new Color(0f, 1f, 0f, 0.7f); + [SerializeField] private Color invalidColor = new Color(1f, 0f, 0f, 0.7f); + [SerializeField] private Color normalColor = Color.white; + [SerializeField] private float dragScale = 1.1f; + + // 拖拽状态 + private DragState _currentState = DragState.Idle; + private bool _isDragging = false; + private Vector3 _dragOffset; + private Vector3 _originalPosition; + private Vector3 _originalScale; + private int _originalSortingOrder; + private bool _isSnapping = false; + private int _snapGridX; + private int _snapGridY; + + // 组件引用 + private BlockShape _blockShape; + private SpriteRenderer _spriteRenderer; + private Collider2D _collider2D; + + // 事件 + public event Action OnDragStart; + public event Action OnDragUpdate; + public event Action OnDragEnd; + public event Action OnSnap; + public event Action OnUnsnap; + + // 接口实现 + public DragState CurrentState => _currentState; + public bool IsDraggable { get => isDraggable; set => isDraggable = value; } + public IBlockPuzzleGrid TargetGrid + { + get => targetGridComponent; + set => targetGridComponent = value as BlockPuzzleGrid; + } + public float SnapDistance { get => snapDistance; set => snapDistance = value; } + + // 内部使用的网格引用 + private IBlockPuzzleGrid targetGrid => targetGridComponent; + + private void Awake() + { + _blockShape = GetComponent(); + _spriteRenderer = GetComponent(); + } + + public void StartDrag() + { + if (!isDraggable || _isDragging) return; + + _isDragging = true; + _currentState = DragState.Dragging; + _originalPosition = transform.position; + _originalScale = transform.localScale; + + // 计算拖拽偏移量 + Vector3 mouseWorldPos = GetMouseWorldPosition(); + _dragOffset = transform.position - mouseWorldPos; + + // 提升层级和缩放 + if (_spriteRenderer != null) + { + _originalSortingOrder = _spriteRenderer.sortingOrder; + _spriteRenderer.sortingOrder = 100; + } + + transform.localScale = _originalScale * dragScale; + + OnDragStart?.Invoke(_blockShape); + } + + public void UpdateDrag() + { + if (!_isDragging) return; + + Vector3 mouseWorldPos = GetMouseWorldPosition(); + Vector3 targetPosition = mouseWorldPos + _dragOffset; + + // 检测是否靠近网格 + if (targetGrid != null) + { + bool canSnap = CheckSnapToGrid(targetPosition, out Vector3 snapPos, out int gridX, out int gridY); + + if (canSnap) + { + // 吸附到网格 + transform.position = snapPos; + _isSnapping = true; + _snapGridX = gridX; + _snapGridY = gridY; + _currentState = DragState.Snapping; + + // 重要:更新拖拽偏移量,使鼠标继续移动时能正确计算位置 + _dragOffset = snapPos - mouseWorldPos; + + // 检查是否可以放置 + bool canPlace = CanPlaceAt(gridX, gridY); + ShowSnapPreview(true, canPlace); + + OnSnap?.Invoke(_blockShape, snapPos, gridX, gridY); + } + else + { + // 自由拖拽 + transform.position = targetPosition; + if (_isSnapping) + { + _isSnapping = false; + ShowSnapPreview(false, false); + OnUnsnap?.Invoke(_blockShape); + } + _currentState = DragState.Dragging; + } + } + else + { + // 没有网格,自由拖拽 + transform.position = targetPosition; + _currentState = DragState.Dragging; + } + + OnDragUpdate?.Invoke(_blockShape, transform.position); + } + + public void EndDrag() + { + if (!_isDragging) return; + + bool placed = false; + + if (_isSnapping && targetGrid != null) + { + // 尝试放置到网格 + if (CanPlaceAt(_snapGridX, _snapGridY)) + { + if (TryPlaceOnGrid(_snapGridX, _snapGridY)) + { + placed = true; + _currentState = DragState.Placed; + OnShapePlaced(); + } + } + } + + if (!placed) + { + // 返回原位置 + transform.position = _originalPosition; + _currentState = DragState.Idle; + } + + // 恢复原始状态 + transform.localScale = _originalScale; + if (_spriteRenderer != null) + { + _spriteRenderer.sortingOrder = _originalSortingOrder; + } + + ShowSnapPreview(false, false); + + _isDragging = false; + _isSnapping = false; + + OnDragEnd?.Invoke(_blockShape, placed); + } + + public void CancelDrag() + { + if (!_isDragging) return; + + transform.position = _originalPosition; + transform.localScale = _originalScale; + if (_spriteRenderer != null) + { + _spriteRenderer.sortingOrder = _originalSortingOrder; + } + + ShowSnapPreview(false, false); + + _isDragging = false; + _isSnapping = false; + _currentState = DragState.Idle; + + OnDragEnd?.Invoke(_blockShape, false); + } + + public bool CanPlaceAt(int gridX, int gridY) + { + if (targetGrid == null || _blockShape == null) return false; + + Vector2Int[] blockPositions = _blockShape.GetBlockPositions(); + if (blockPositions == null || blockPositions.Length == 0) return false; + + // 形状的坐标系统: + // - GetBlockPositions() 返回的坐标中,(0, 0) 是形状的左上角 + // - gridX, gridY 是形状左下角在网格中的位置 + // - 需要将形状的相对坐标转换为网格坐标 + foreach (var blockPos in blockPositions) + { + // 将形状的相对坐标转换为网格坐标(左下角为基准) + // 形状的 (0, 0) 对应网格的 (gridX, gridY + shape.Height - 1) + // 形状的 (i, j) 对应网格的 (gridX + i, gridY + shape.Height - 1 - j) + int checkX = gridX + blockPos.x; + int checkY = gridY + _blockShape.Height - 1 - blockPos.y; + + if (!targetGrid.IsValidPosition(checkX, checkY) || + !targetGrid.IsCellEmpty(checkX, checkY)) + { + return false; + } + } + + return true; + } + + public bool GetSnapGridPosition(out int gridX, out int gridY) + { + if (_isSnapping) + { + gridX = _snapGridX; + gridY = _snapGridY; + return true; + } + + gridX = 0; + gridY = 0; + return false; + } + + /// + /// 检查是否可以吸附到网格 + /// + private bool CheckSnapToGrid(Vector3 worldPos, out Vector3 snapPos, out int gridX, out int gridY) + { + snapPos = worldPos; + gridX = 0; + gridY = 0; + + if (targetGrid == null) return false; + + // 将世界坐标转换为网格坐标 + // 注意:WorldToGrid 需要世界坐标在网格范围内才能返回true + // 但我们需要检查所有附近的网格单元格,不仅仅是当前所在的单元格 + // 所以先尝试直接转换,如果失败则查找最近的网格单元格 + + int tempGridX, tempGridY; + bool inGrid = targetGrid.WorldToGrid(worldPos, out tempGridX, out tempGridY); + + if (!inGrid) + { + // 如果不在网格内,尝试查找最近的网格单元格 + // 计算最近的网格坐标 + Vector3 localPos = worldPos - targetGrid.Origin; + tempGridX = Mathf.FloorToInt(localPos.x / targetGrid.CellSize); + tempGridY = Mathf.FloorToInt(localPos.y / targetGrid.CellSize); + + // 检查是否在有效范围内 + if (!targetGrid.IsValidPosition(tempGridX, tempGridY)) + { + return false; + } + } + + // 获取网格单元格中心的世界坐标 + Vector3 gridCenter = targetGrid.GridToWorld(tempGridX, tempGridY); + + // 计算距离 + float distance = Vector3.Distance(worldPos, gridCenter); + + if (distance <= snapDistance) + { + // 计算形状应该放置的网格位置 + // 形状左下角对齐到网格单元格左下角 + gridX = tempGridX; + gridY = tempGridY; + + // 计算形状左下角的世界坐标 + // 网格单元格左下角的世界坐标 = Origin + (gridX * CellSize, gridY * CellSize) + Vector3 gridBottomLeft = targetGrid.Origin + new Vector3( + gridX * targetGrid.CellSize, + gridY * targetGrid.CellSize, + 0 + ); + + // 计算形状中心相对于形状左下角的偏移 + // 形状的 pattern 中,(0, 0) 是左上角,所以形状左下角在 pattern 中的相对位置是 (0, height-1) + // 在形状的本地坐标系中(假设形状中心在 (0, 0)),形状左下角的位置是: + // localX = -width * cellSize / 2 + // localY = -height * cellSize / 2 + (height-1) * cellSize = (height-1) * cellSize - height * cellSize / 2 + // 形状中心相对于形状左下角的偏移 = (0, 0) - (localX, localY) = (width * cellSize / 2, height * cellSize / 2 - (height-1) * cellSize) + + // 为了简化,我们假设形状的中心在形状的几何中心 + // 形状中心相对于形状左下角的偏移 = (width * cellSize / 2, height * cellSize / 2) + // 但考虑到形状的 pattern 中 (0, 0) 是左上角,形状左下角在 pattern 中的位置是 (0, height-1) + // 所以形状中心相对于形状左下角的偏移 = (width * cellSize / 2, (height-1) * cellSize / 2) + + // 实际上,我们需要根据形状的实际布局计算形状中心的位置 + // 但为了简化,我们直接使用网格单元格左下角作为形状左下角的位置 + // 然后根据形状的实际布局计算形状中心的位置 + + // 形状左下角对齐到网格单元格左下角 + // 形状中心的世界坐标 = 网格单元格左下角的世界坐标 + 形状中心相对于形状左下角的偏移 + // 使用网格的 CellSize,因为形状的 cellSize 应该与网格的 CellSize 相同 + float shapeCenterOffsetX = _blockShape.Width * targetGrid.CellSize / 2f; + float shapeCenterOffsetY = (_blockShape.Height - 1) * targetGrid.CellSize / 2f; + snapPos = gridBottomLeft + new Vector3(shapeCenterOffsetX, shapeCenterOffsetY, 0); + + return true; + } + + return false; + } + + /// + /// 尝试放置到网格 + /// + /// 网格X坐标(形状左下角位置) + /// 网格Y坐标(形状左下角位置) + private bool TryPlaceOnGrid(int gridX, int gridY) + { + if (!CanPlaceAt(gridX, gridY)) return false; + + Vector2Int[] blockPositions = _blockShape.GetBlockPositions(); + if (blockPositions == null) return false; + + // 形状的坐标系统: + // - GetBlockPositions() 返回的坐标中,(0, 0) 是形状的左上角 + // - gridX, gridY 是形状左下角在网格中的位置 + // - 需要将形状的相对坐标转换为网格坐标 + foreach (var blockPos in blockPositions) + { + // 将形状的相对坐标转换为网格坐标(左下角为基准) + // 形状的 (0, 0) 对应网格的 (gridX, gridY + shape.Height - 1) + // 形状的 (i, j) 对应网格的 (gridX + i, gridY + shape.Height - 1 - j) + int x = gridX + blockPos.x; + int y = gridY + _blockShape.Height - 1 - blockPos.y; + targetGrid.SetCellState(x, y, GridCellState.Occupied); + } + + return true; + } + + /// + /// 显示吸附预览效果 + /// + private void ShowSnapPreview(bool show, bool isValid) + { + Color previewColor = show ? (isValid ? snapColor : invalidColor) : normalColor; + + // 更新主SpriteRenderer + if (_spriteRenderer != null) + { + _spriteRenderer.color = previewColor; + } + } + + /// + /// 形状放置完成 + /// + private void OnShapePlaced() + { + // 可以在这里添加放置后的逻辑 + // 例如:禁用拖拽、播放动画等 + isDraggable = false; + } + + /// + /// 获取鼠标世界坐标 + /// + private Vector3 GetMouseWorldPosition() + { + return CameraKit.GetMouseWorldPos(Input.mousePosition); + } + + private void OnDisable() + { + if (_isDragging) + { + CancelDrag(); + } + } + } +} + diff --git a/Assets/Scripts/MiniGame/BlockPuzzle/Class/BlockShapeDragger.cs.meta b/Assets/Scripts/MiniGame/BlockPuzzle/Class/BlockShapeDragger.cs.meta new file mode 100644 index 000000000..58d046e4f --- /dev/null +++ b/Assets/Scripts/MiniGame/BlockPuzzle/Class/BlockShapeDragger.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 6ad01079e32d2314eb3719ead5f50ddb +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/Class/GridVisualizer.cs similarity index 97% rename from Assets/Scripts/MiniGame/BlockPuzzle/GridVisualizer.cs rename to Assets/Scripts/MiniGame/BlockPuzzle/Class/GridVisualizer.cs index 1e6838497..b3708fbf9 100644 --- a/Assets/Scripts/MiniGame/BlockPuzzle/GridVisualizer.cs +++ b/Assets/Scripts/MiniGame/BlockPuzzle/Class/GridVisualizer.cs @@ -135,8 +135,8 @@ namespace AibisDream for (int x = 0; x <= _grid.Width; x++) { CreateLine( - _grid.GridToWorld(x, 0), - _grid.GridToWorld(x, _grid.Height), + _grid.GridVerticesToWorld(x, 0)[0], + _grid.GridVerticesToWorld(x, _grid.Height)[0], gridLineWidth, gridLineColor ); @@ -145,8 +145,8 @@ namespace AibisDream for (int y = 0; y <= _grid.Height; y++) { CreateLine( - _grid.GridToWorld(0, y), - _grid.GridToWorld(_grid.Width, y), + _grid.GridVerticesToWorld(0, y)[0], + _grid.GridVerticesToWorld(_grid.Width, y)[0], gridLineWidth, gridLineColor ); diff --git a/Assets/Scripts/MiniGame/BlockPuzzle/GridVisualizer.cs.meta b/Assets/Scripts/MiniGame/BlockPuzzle/Class/GridVisualizer.cs.meta similarity index 100% rename from Assets/Scripts/MiniGame/BlockPuzzle/GridVisualizer.cs.meta rename to Assets/Scripts/MiniGame/BlockPuzzle/Class/GridVisualizer.cs.meta diff --git a/Assets/Scripts/MiniGame/BlockPuzzle/Class/PlacementValidator.cs b/Assets/Scripts/MiniGame/BlockPuzzle/Class/PlacementValidator.cs new file mode 100644 index 000000000..c07ab0a86 --- /dev/null +++ b/Assets/Scripts/MiniGame/BlockPuzzle/Class/PlacementValidator.cs @@ -0,0 +1,112 @@ +using System.Collections.Generic; +using UnityEngine; + +namespace AibisDream +{ + /// + /// 放置验证器实现 + /// 负责验证形状是否可以放置在指定位置 + /// + public class PlacementValidator : IPlacementValidator + { + /// + /// 检查形状是否可以放置在指定位置 + /// + /// 网格系统 + /// 要放置的形状 + /// 网格X坐标(形状左下角位置) + /// 网格Y坐标(形状左下角位置) + /// 放置验证结果 + public PlacementResult CanPlace(IBlockPuzzleGrid grid, IBlockShape shape, int gridX, int gridY) + { + if (grid == null || shape == null) + { + return PlacementResult.InvalidPosition; + } + + // 获取形状的所有格子位置(相对坐标,基于形状的本地坐标系) + Vector2Int[] blockPositions = shape.GetBlockPositions(); + if (blockPositions == null || blockPositions.Length == 0) + { + return PlacementResult.InvalidPosition; + } + + // 形状的坐标系统: + // - GetBlockPositions() 返回的坐标中,(0, 0) 是形状的左上角(在pattern数组中) + // - pattern[i, j] 中,i 是 x(从左到右),j 是 y(从上到下,j=0 是顶部) + // - gridX, gridY 是形状左下角在网格中的位置 + // - 需要将形状的相对坐标转换为网格坐标 + foreach (var blockPos in blockPositions) + { + // 将形状的相对坐标转换为网格坐标 + // 形状的 (0, 0) 对应网格的 (gridX, gridY + shape.Height - 1) + // 形状的 (i, j) 对应网格的 (gridX + i, gridY + shape.Height - 1 - j) + int gridCellX = gridX + blockPos.x; + int gridCellY = gridY + shape.Height - 1 - blockPos.y; + + // 检查是否超出边界 + if (!grid.IsValidPosition(gridCellX, gridCellY)) + { + return PlacementResult.OutOfBounds; + } + + // 检查是否与已有块重叠 + if (!grid.IsCellEmpty(gridCellX, gridCellY)) + { + return PlacementResult.Overlap; + } + } + + return PlacementResult.Success; + } + + /// + /// 检查形状是否可以放置在指定位置(详细版本) + /// + /// 网格系统 + /// 要放置的形状 + /// 网格X坐标(形状左下角位置) + /// 网格Y坐标(形状左下角位置) + /// 失败原因(如果返回false) + /// 是否可以放置 + public bool CanPlaceDetailed(IBlockPuzzleGrid grid, IBlockShape shape, int gridX, int gridY, out PlacementResult failureReason) + { + failureReason = CanPlace(grid, shape, gridX, gridY); + return failureReason == PlacementResult.Success; + } + + /// + /// 获取形状在指定位置的所有占用格子坐标 + /// + /// 形状 + /// 网格X坐标(形状左下角位置) + /// 网格Y坐标(形状左下角位置) + /// 占用格子的网格坐标数组 + public (int x, int y)[] GetOccupiedCells(IBlockShape shape, int gridX, int gridY) + { + if (shape == null) + { + return new (int x, int y)[0]; + } + + Vector2Int[] blockPositions = shape.GetBlockPositions(); + if (blockPositions == null || blockPositions.Length == 0) + { + return new (int x, int y)[0]; + } + + List<(int x, int y)> occupiedCells = new List<(int x, int y)>(); + + foreach (var blockPos in blockPositions) + { + // 将形状的相对坐标转换为网格坐标(左下角为基准) + int gridCellX = gridX + blockPos.x; + int gridCellY = gridY + shape.Height - 1 - blockPos.y; + occupiedCells.Add((gridCellX, gridCellY)); + } + + return occupiedCells.ToArray(); + } + } +} + diff --git a/Assets/Scripts/MiniGame/BlockPuzzle/Class/PlacementValidator.cs.meta b/Assets/Scripts/MiniGame/BlockPuzzle/Class/PlacementValidator.cs.meta new file mode 100644 index 000000000..5333b0e2a --- /dev/null +++ b/Assets/Scripts/MiniGame/BlockPuzzle/Class/PlacementValidator.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 826c5312f504d944c924549de95babe7 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/MiniGame/BlockPuzzle/Interface.meta b/Assets/Scripts/MiniGame/BlockPuzzle/Interface.meta new file mode 100644 index 000000000..e0a9cbce1 --- /dev/null +++ b/Assets/Scripts/MiniGame/BlockPuzzle/Interface.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: abf557018657fa642a0d79187ed450eb +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/MiniGame/BlockPuzzle/IBlockPuzzleGameLogic.cs b/Assets/Scripts/MiniGame/BlockPuzzle/Interface/IBlockPuzzleGameLogic.cs similarity index 100% rename from Assets/Scripts/MiniGame/BlockPuzzle/IBlockPuzzleGameLogic.cs rename to Assets/Scripts/MiniGame/BlockPuzzle/Interface/IBlockPuzzleGameLogic.cs diff --git a/Assets/Scripts/MiniGame/BlockPuzzle/IBlockPuzzleGameLogic.cs.meta b/Assets/Scripts/MiniGame/BlockPuzzle/Interface/IBlockPuzzleGameLogic.cs.meta similarity index 100% rename from Assets/Scripts/MiniGame/BlockPuzzle/IBlockPuzzleGameLogic.cs.meta rename to Assets/Scripts/MiniGame/BlockPuzzle/Interface/IBlockPuzzleGameLogic.cs.meta diff --git a/Assets/Scripts/MiniGame/BlockPuzzle/IBlockPuzzleGrid.cs b/Assets/Scripts/MiniGame/BlockPuzzle/Interface/IBlockPuzzleGrid.cs similarity index 95% rename from Assets/Scripts/MiniGame/BlockPuzzle/IBlockPuzzleGrid.cs rename to Assets/Scripts/MiniGame/BlockPuzzle/Interface/IBlockPuzzleGrid.cs index 1005788f9..33fef4914 100644 --- a/Assets/Scripts/MiniGame/BlockPuzzle/IBlockPuzzleGrid.cs +++ b/Assets/Scripts/MiniGame/BlockPuzzle/Interface/IBlockPuzzleGrid.cs @@ -117,6 +117,12 @@ namespace AibisDream /// /// 总单元格数量 int GetTotalCellCount(); + + /// + /// 获取网格顶点信息 + /// + /// 网格四个顶点位置 + Vector3[] GridVerticesToWorld(int x, int y); } } diff --git a/Assets/Scripts/MiniGame/BlockPuzzle/IBlockPuzzleGrid.cs.meta b/Assets/Scripts/MiniGame/BlockPuzzle/Interface/IBlockPuzzleGrid.cs.meta similarity index 100% rename from Assets/Scripts/MiniGame/BlockPuzzle/IBlockPuzzleGrid.cs.meta rename to Assets/Scripts/MiniGame/BlockPuzzle/Interface/IBlockPuzzleGrid.cs.meta diff --git a/Assets/Scripts/MiniGame/BlockPuzzle/IBlockShape.cs b/Assets/Scripts/MiniGame/BlockPuzzle/Interface/IBlockShape.cs similarity index 98% rename from Assets/Scripts/MiniGame/BlockPuzzle/IBlockShape.cs rename to Assets/Scripts/MiniGame/BlockPuzzle/Interface/IBlockShape.cs index f0e51bc91..19cc7635d 100644 --- a/Assets/Scripts/MiniGame/BlockPuzzle/IBlockShape.cs +++ b/Assets/Scripts/MiniGame/BlockPuzzle/Interface/IBlockShape.cs @@ -100,7 +100,7 @@ namespace AibisDream /// 创建形状的副本 /// /// 形状副本 - IBlockShape Clone(); + // IBlockShape Clone(); } } diff --git a/Assets/Scripts/MiniGame/BlockPuzzle/IBlockShape.cs.meta b/Assets/Scripts/MiniGame/BlockPuzzle/Interface/IBlockShape.cs.meta similarity index 100% rename from Assets/Scripts/MiniGame/BlockPuzzle/IBlockShape.cs.meta rename to Assets/Scripts/MiniGame/BlockPuzzle/Interface/IBlockShape.cs.meta diff --git a/Assets/Scripts/MiniGame/BlockPuzzle/Interface/IBlockShapeDragger.cs b/Assets/Scripts/MiniGame/BlockPuzzle/Interface/IBlockShapeDragger.cs new file mode 100644 index 000000000..37b1cfd9b --- /dev/null +++ b/Assets/Scripts/MiniGame/BlockPuzzle/Interface/IBlockShapeDragger.cs @@ -0,0 +1,105 @@ +using UnityEngine; +using System; + +namespace AibisDream +{ + /// + /// 拖拽状态 + /// + public enum DragState + { + Idle, // 空闲 + Dragging, // 拖拽中 + Snapping, // 吸附中 + Placed // 已放置 + } + + /// + /// 形状拖拽接口 + /// 负责处理形状的拖拽、吸附和放置逻辑 + /// + public interface IBlockShapeDragger + { + /// + /// 当前拖拽状态 + /// + DragState CurrentState { get; } + + /// + /// 是否可拖拽 + /// + bool IsDraggable { get; set; } + + /// + /// 目标网格 + /// + IBlockPuzzleGrid TargetGrid { get; set; } + + /// + /// 吸附距离阈值 + /// + float SnapDistance { get; set; } + + /// + /// 拖拽开始事件 + /// + event Action OnDragStart; + + /// + /// 拖拽更新事件 + /// + event Action OnDragUpdate; + + /// + /// 拖拽结束事件 + /// + event Action OnDragEnd; // bool表示是否成功放置 + + /// + /// 吸附事件 + /// + event Action OnSnap; // 形状, 位置, 网格X, 网格Y + + /// + /// 取消吸附事件 + /// + event Action OnUnsnap; + + /// + /// 开始拖拽 + /// + void StartDrag(); + + /// + /// 更新拖拽位置 + /// + void UpdateDrag(); + + /// + /// 结束拖拽 + /// + void EndDrag(); + + /// + /// 取消拖拽(返回原位置) + /// + void CancelDrag(); + + /// + /// 检查是否可以放置到指定位置 + /// + /// 网格X坐标 + /// 网格Y坐标 + /// 是否可以放置 + bool CanPlaceAt(int gridX, int gridY); + + /// + /// 获取当前吸附的网格坐标 + /// + /// 输出的网格X坐标 + /// 输出的网格Y坐标 + /// 是否正在吸附 + bool GetSnapGridPosition(out int gridX, out int gridY); + } +} + diff --git a/Assets/Scripts/MiniGame/BlockPuzzle/Interface/IBlockShapeDragger.cs.meta b/Assets/Scripts/MiniGame/BlockPuzzle/Interface/IBlockShapeDragger.cs.meta new file mode 100644 index 000000000..5053d37b4 --- /dev/null +++ b/Assets/Scripts/MiniGame/BlockPuzzle/Interface/IBlockShapeDragger.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 5b2cbf04f1d9ef6448d01501a7861a46 +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/Interface/IBlockShapeFactory.cs similarity index 100% rename from Assets/Scripts/MiniGame/BlockPuzzle/IBlockShapeFactory.cs rename to Assets/Scripts/MiniGame/BlockPuzzle/Interface/IBlockShapeFactory.cs diff --git a/Assets/Scripts/MiniGame/BlockPuzzle/IBlockShapeFactory.cs.meta b/Assets/Scripts/MiniGame/BlockPuzzle/Interface/IBlockShapeFactory.cs.meta similarity index 100% rename from Assets/Scripts/MiniGame/BlockPuzzle/IBlockShapeFactory.cs.meta rename to Assets/Scripts/MiniGame/BlockPuzzle/Interface/IBlockShapeFactory.cs.meta diff --git a/Assets/Scripts/MiniGame/BlockPuzzle/IPlacementValidator.cs b/Assets/Scripts/MiniGame/BlockPuzzle/Interface/IPlacementValidator.cs similarity index 82% rename from Assets/Scripts/MiniGame/BlockPuzzle/IPlacementValidator.cs rename to Assets/Scripts/MiniGame/BlockPuzzle/Interface/IPlacementValidator.cs index 72ed54e51..8020246ce 100644 --- a/Assets/Scripts/MiniGame/BlockPuzzle/IPlacementValidator.cs +++ b/Assets/Scripts/MiniGame/BlockPuzzle/Interface/IPlacementValidator.cs @@ -22,8 +22,8 @@ namespace AibisDream /// /// 网格系统 /// 要放置的形状 - /// 网格X坐标(形状左上角位置) - /// 网格Y坐标(形状左上角位置) + /// 网格X坐标(形状左下角位置) + /// 网格Y坐标(形状左下角位置) /// 放置验证结果 PlacementResult CanPlace(IBlockPuzzleGrid grid, IBlockShape shape, int gridX, int gridY); @@ -32,8 +32,8 @@ namespace AibisDream /// /// 网格系统 /// 要放置的形状 - /// 网格X坐标(形状左上角位置) - /// 网格Y坐标(形状左上角位置) + /// 网格X坐标(形状左下角位置) + /// 网格Y坐标(形状左下角位置) /// 失败原因(如果返回false) /// 是否可以放置 bool CanPlaceDetailed(IBlockPuzzleGrid grid, IBlockShape shape, int gridX, int gridY, out PlacementResult failureReason); @@ -42,10 +42,9 @@ namespace AibisDream /// 获取形状在指定位置的所有占用格子坐标 /// /// 形状 - /// 网格X坐标(形状左上角位置) - /// 网格Y坐标(形状左上角位置) + /// 网格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/Interface/IPlacementValidator.cs.meta similarity index 100% rename from Assets/Scripts/MiniGame/BlockPuzzle/IPlacementValidator.cs.meta rename to Assets/Scripts/MiniGame/BlockPuzzle/Interface/IPlacementValidator.cs.meta diff --git a/Assets/Scripts/Utility/CommonUtil.cs b/Assets/Scripts/Utility/CommonUtil.cs index 92550a5f8..1bdc69c5e 100644 --- a/Assets/Scripts/Utility/CommonUtil.cs +++ b/Assets/Scripts/Utility/CommonUtil.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Text; using System.Linq; using System.Reflection; using System.Text.RegularExpressions; @@ -7,6 +8,8 @@ using System.Threading.Tasks; using Unity.VisualScripting; using UnityEngine; using Object = UnityEngine.Object; +using System.Globalization; +using UnityEditor; namespace AibisDream.Utility { @@ -189,8 +192,21 @@ namespace AibisDream.Utility return; } + List children = new List(); foreach (Transform child in self) { + children.Add(child); + } + + foreach (var child in children) + { +#if UNITY_EDITOR + if (!EditorApplication.isPlaying) + { + Object.DestroyImmediate(child.gameObject); + continue; + } +#endif Object.Destroy(child.gameObject); } } @@ -243,5 +259,47 @@ namespace AibisDream.Utility { return Regex.Replace(str.Replace("
", "\n"), "[<|{].*?[>|}]", string.Empty); } + + public static int[,] DeserializeIntArray(string str) + { + if (string.IsNullOrWhiteSpace(str)) + { + return new int[0, 0]; + } + + var array = str.Trim().Split(';'); + var result = new int[array.Length, array[0].Split(',').Length]; + for (int i = 0; i < array.Length; i++) + { + var row = array[i].Split(','); + for (int j = 0; j < row.Length; j++) + { + result[i, j] = int.Parse(row[j], NumberStyles.Integer, CultureInfo.InvariantCulture); + } + } + return result; + } + + public static string SerializeIntArray(int[,] array) + { + var result = new StringBuilder(); + for (int i = 0; i < array.GetLength(0); i++) + { + for (int j = 0; j < array.GetLength(1); j++) + { + result.Append(array[i, j]); + if (j < array.GetLength(1) - 1) + { + result.Append(','); + } + } + + if (i < array.GetLength(0) - 1) + { + result.Append(';'); + } + } + return result.ToString(); + } } } \ No newline at end of file