zlwu 10 месяцев назад
Родитель
Сommit
ce73de7bc4
1 измененных файлов с 38 добавлено и 7 удалено
  1. 38 7
      frontend/src/hooks/useEnergyStones.ts

+ 38 - 7
frontend/src/hooks/useEnergyStones.ts

@@ -70,7 +70,7 @@ const computePosition = (
   container: { width: number; height: number },
   stoneSize: StoneSize
 ): StonePosition => {
-  const maxAttempts = 12;
+  const maxAttempts = 24;
   const width = container.width || FALLBACK_SIZE.width;
   const height = container.height || FALLBACK_SIZE.height;
   const halfWidth = stoneSize.width / 2;
@@ -81,6 +81,10 @@ const computePosition = (
   const verticalMax = Math.max(verticalMin, Math.min(height * 0.5, height - halfHeight));
   let candidate: StonePosition = { xRatio: 0.5, yRatio: 0.2 };
 
+  const hasOverlap = (rect: StoneRect) =>
+    existing.some((stone) => rectanglesOverlap(rect, createRectFromStone(stone, { width, height })));
+
+  // 1) 随机采样若干次,先尝试随机避让
   for (let attempt = 0; attempt < maxAttempts; attempt++) {
     const usableWidth = Math.max(0, horizontalMax - horizontalMin);
     const usableHeight = Math.max(0, verticalMax - verticalMin);
@@ -95,20 +99,45 @@ const computePosition = (
       bottom: centerY + halfHeight,
     };
 
-    const overlap = existing.some((stone) =>
-      rectanglesOverlap(candidateRect, createRectFromStone(stone, { width, height }))
-    );
-
     candidate = {
       xRatio: width ? centerX / width : 0.5,
       yRatio: height ? centerY / height : 0.2,
     };
 
-    if (!overlap) {
+    if (!hasOverlap(candidateRect)) {
       return candidate;
     }
   }
 
+  // 2) 如果随机失败,使用网格扫描尝试找到第一个不重叠的点
+  const usableWidth = Math.max(0, horizontalMax - horizontalMin);
+  const usableHeight = Math.max(0, verticalMax - verticalMin);
+  const cols = Math.max(2, Math.floor(usableWidth / Math.max(halfWidth * 2, 1)));
+  const rows = Math.max(2, Math.floor(usableHeight / Math.max(halfHeight * 2, 1)));
+  const stepX = cols > 1 ? usableWidth / (cols - 1) : usableWidth;
+  const stepY = rows > 1 ? usableHeight / (rows - 1) : usableHeight;
+
+  for (let row = 0; row < rows; row++) {
+    for (let col = 0; col < cols; col++) {
+      const centerX = horizontalMin + col * stepX;
+      const centerY = verticalMin + row * stepY;
+      const candidateRect: StoneRect = {
+        left: centerX - halfWidth,
+        right: centerX + halfWidth,
+        top: centerY - halfHeight,
+        bottom: centerY + halfHeight,
+      };
+
+      if (!hasOverlap(candidateRect)) {
+        return {
+          xRatio: width ? centerX / width : 0.5,
+          yRatio: height ? centerY / height : 0.2,
+        };
+      }
+    }
+  }
+
+  // 3) 实在找不到,就使用最后一次随机的候选(可能存在轻微重叠,但概率更低)
   return candidate;
 };
 
@@ -257,7 +286,9 @@ export const useEnergyStones = (
         size: stoneSize,
       };
 
-      setStones((prev) => [...prev, stone]);
+      const nextStones = [...stonesRef.current, stone];
+      stonesRef.current = nextStones;
+      setStones(nextStones);
       scheduleLifecycle(stone);
     },
     [