Dev_logroome

Three.js 헥사곤 그리드 배치 문제 해결 – RoomE #4

2025-03-31
ProjectReactTrouble

📌ㅤ헥사곤 그리드 배치

🚨 10번째 방부터 중복 배치되는 현상

10번째 방부터 중복 배치되는 현상

✅ㅤ큐브 좌표계

  • 형식 : [q, r, s]
  • 항상 q + r + s = 0 조건을 만족해야 한다.
  • 3개의 축(q, r, s)을 사용하며, 각 축은 6방향 중 3개의 방향을 표현
  • 장점 : 방향 계산이 직관적이며, 인접 셀 계산이 쉬움.

중앙: [0, 0, 0] | 왼쪽: [-1, 0, 1] | 오른쪽: [1, 0, -1]

jsx
1
const directions = [
  [1, -1, 0],    // 6. 위 오른쪽 대각선 상단
  [0, -1, 1],   // 5. 위 왼쪽 대각선 상단
  [0, 1, 1],    // 4. 아래 왼쪽 대각선 하단
  [-1, 1, 1],    // 3. 아래 오른쪽 대각선 하단
  [-1, 0, 1],    // 2. 왼쪽
  [1, 0, -1],    // 1. 오른쪽
];
jsx
1
for (let step = 0; step < steps && result.length < roomCount; step++) {
  const posKey = `${cubeX},${cubeY},${cubeZ}`;
 
  if (!visited.has(posKey)) {  // ✅ 방문 여부만 확인
    visited.add(posKey);
    result.push({ position: [cubeX, cubeY, cubeZ], room: rooms[roomIndex] });
    roomIndex++;
  }
  
  cubeX += directions[side][0];
  cubeY += directions[side][1];
  cubeZ += directions[side][2];
}
  • 일부 dx, dy, dz 값이 q + r + s = 0 조건을 만족하지 않음
  • 그로인해 헥사곤 그리드 내에서 유효한 위치로 간주되지 않음ㅤ➡️ㅤ중복 체크가 제대로 되지 않음
  • visited.has(posKey) 만으로는 잘못된 위치의 중복 배치를 방지할 수 없음
jsx
1
const directions = [
  [-1, 0, 1],    // 1. 왼쪽
  [1, 0, -1],    // 2. 오른쪽
  [0, -1, 1],   // 3. 위 왼쪽 대각선 상단
  [1, -1, 0],    // 4. 위 오른쪽 대각선 상단
  [-1, 1, 0],    // 5. 아래 왼쪽 대각선 하단
  [0, 1, -1],    // 6. 아래 오른쪽 대각선 하단
];
jsx
1
for (let step = 0; step < (ring - 1) && placedInRing < positionsInRing; step++) {
  const [x, y, z] = currentPos;
  const posKey = `${x},${y},${z}`;
 
  if (!visited.has(posKey) && (x + y + z) === 0) {
    visited.add(posKey);
    result.push({ position: [x, y, z], room: rooms[roomIndex] });
    roomIndex++;
    placedInRing++;
  }
 
  currentPos = [x + dx, y + dy, z + dz];
}
 

🚨 18번째 방부터 아래쪽에서만 배치되는 현상

18번째 방부터 아래쪽에서만 배치되는 현상
jsx
1
const baseIndex = previousRing[directionIndex % previousRing.length];
const basePos = result[baseIndex].position;
const [dx, dy, dz] = dir;
const newPos: Position = [basePos[0] + dx, basePos[1] + dy, basePos[2] + dz];
const posKey = `${newPos[0]},${newPos[1]},${newPos[2]}`;
 
if (!visited.has(posKey) && newPos[0] + newPos[1] + newPos[2] === 0) {
  visited.add(posKey);
  result.push({ position: newPos, room: rooms[roomIndex] });
  return { roomIndex: roomIndex + 1, placedInRing: placedInRing + 1 };
}
jsx
1
for (let i = 0; i < previousRing.length; i++) {
  const baseIndex = previousRing[(directionIndex + i) % previousRing.length];
  const basePos = result[baseIndex].position;
  const [dx, dy, dz] = dir;
  const newPos: Position = [basePos[0] + dx, basePos[1] + dy, basePos[2] + dz];
  const posKey = `${newPos[0]},${newPos[1]},${newPos[2]}`;
 
  if (!visited.has(posKey) && newPos[0] + newPos[1] + newPos[2] === 0) {
    visited.add(posKey);
    result.push({ position: newPos, room: rooms[roomIndex] });
    return { roomIndex: roomIndex + 1, placedInRing: placedInRing + 1 };
  }
}
return { roomIndex, placedInRing };

💬ㅤ비하인드

어지러운 x,y,z 좌표계.... 최상의 배치를 맞추기 위한 노력...

Front ViewTop View

첫 번째 링을 완성하기 까지의 여정...

문어발 스타일아래는 왜...

첫 번째 링 이후의 방황하는 방들...

12

여러가지 확장 방식을 시도해보면서 겪은 다양한 형태

문어발 스타일아래는 왜...