Лабиринт рисунок с холста - PullRequest
0 голосов
/ 12 марта 2019

В качестве побочного проекта я создал генератор лабиринта, используя график.Логика генерации работает нормально, но я сталкиваюсь с некоторыми проблемами при рендеринге.В моей логике каждая ячейка представлена ​​в виде массива из 4 ребер, где первый край - это верхняя стенка, второй - правая стенка и т. Д. По часовой стрелке.Если есть грань (что-либо отличное от -1), должна быть стена, если есть -1, эта сторона должна быть открыта.

Для того, чтобы следовать этой логике, я создал следующий класс Render

export class Renderer {
  private _context: CanvasRenderingContext2D;
  private _y: number;
  private _x: number;

  constructor(canvas: HTMLCanvasElement, xSize: number, ySize: number) {
    this._context = canvas.getContext('2d') as CanvasRenderingContext2D;
    this._x = xSize;
    this._y = ySize
  }


  public render(graphAdjacencyList: Array<Vertex>): void {
    for (let i = 0; i < this._x; i++) {
      for (let j = 0; j < this._y; j++) {
        const codedIndex: number = parseInt(i.toString() + j.toString());
        this._renderCell({ x: 20 * j, y: 20 * i }, graphAdjacencyList[codedIndex].getEdges(), 20)
      }
    }
  }


  private _renderCell(coords: Record<'x' | 'y', number>, cellWalls: Array<number>, size: number) {
    cellWalls.forEach((w: number, index: number) => {
      this._context.beginPath();
      switch (index) {
        case 0:
          this._context.moveTo(coords.x, coords.y);
          (w !== -1) ? this._context.lineTo(coords.x + size, coords.y) : null;
          break;
        case 1:
          this._context.moveTo(coords.x + size, coords.y);
          (w !== -1) ? this._context.lineTo(coords.x + size, coords.y + size) : null;
          break;
        case 2:
          this._context.moveTo(coords.x + size, coords.y + size);
          (w !== -1) ? this._context.lineTo(coords.x, coords.y + size) : null;
          break;
        case 3:
          this._context.moveTo(coords.x, coords.y + size);
          (w !== -1) ? this._context.lineTo(coords.x, coords.y - size) : this._context.moveTo(coords.x, coords.y - size);
          break;
      }

      this._context.closePath();
      this._context.stroke();
    });
  }
}

, который на первый взгляд кажется работоспособным, за исключением того факта, что он рендерит "призрачные стены"(светло-серые штрихи), как на этом изображении 5X5 maze

Если я взгляну по краям, я вижу, что, например, ячейка [3][3] должна иметь только верхнюю и левуюстена, потому что ее края [23, -1, -1, 32].Я убежден, что ошибка в том, как я перемещаю точку, но я не могу точно определить проблему.

минимальный пример: https://stackblitz.com/edit/js-ys9a1j

в минимальном примере график не рандомизирован, но разработан таким образом, что результат должен состоять из всех блоков, чтобы иметь только нижнюю и левую стенки ([-1, -1, 1, 1])

Ответы [ 2 ]

2 голосов
/ 12 марта 2019

Здесь у вас логическая проблема: вы определяете четыре ребра каждой ячейки независимо.

Если, например, у нас ячейка B2 равна -1, -1, -1, -1 (все открыто) и C2 равно 1,1,1,1 (все закрыто), то грань между этими двумя ячейками будет закрыто объявлением C2, хотя декларации B2 говорят, что оно должно быть открыто.

 _A_|_B_|_C_|_D_|
|        
1        
|    ooo|‾‾‾|
2    o o|   |                – -> closed (wall)
|    ooo|___|                o -> open (no wall)

Чтобы избежать этого, вам нужно переосмыслить свою логику, чтобы либо проверить, не была ли упомянутая стена объявлена ​​ранее, либо даже лучше, чтобы хранить ее только один раз.

Два способа, которые я могу придумать, хотя и не тестировал:

- Создать график. От входа в ваш лабиринт вы генерируете узел, который будет иметь четыре свойства: N, S, W, E, каждое из которых указывает на другой узел, если он открыт, или на null, если должна быть стена. После тестирования я обнаружил, что идея графа действительно должна иметь метод поиска, или она может идти только в двух направлениях ...

  • 2 2D массива, один только для стен в столбцах, один только для строк в строках.

const cellWidth = 20;
const maze = generateMaze(12, 12);
const ctx = canvas.getContext('2d');
ctx.translate(cellWidth + 0.5, cellWidth + 0.5);
ctx.beginPath();
drawCols(maze.cols);
drawRows(maze.rows);
ctx.stroke();

function generateMaze(width, height) {
  const rows = generateCells(width, height);
  const cols = generateCells(height, width);
  return { cols, rows};

  function generateCells(a, b) {
    return Array.from({ length: a })
      .map(_ => Array.from({ length: b })
        .map(_ => Math.random() < 0.5)
      );
  }

}

function drawCols(list) {
  list.forEach((arr, x) => {
    arr.forEach((bool, y) => {
      if (bool) {
        ctx.moveTo(x * cellWidth, y * cellWidth);
        ctx.lineTo(x * cellWidth, y * cellWidth + cellWidth);
      }
    });
  });
}

function drawRows(list) {
  list.forEach((arr, y) => {
    arr.forEach((bool, x) => {
      if (bool) {
        ctx.moveTo(x * cellWidth, y * cellWidth);
        ctx.lineTo(x * cellWidth + cellWidth, y * cellWidth);
      }
    });
  });
}
<canvas id="canvas" width="300" height="300"></canvas>
0 голосов
/ 12 марта 2019

Я обнаружил проблему, просматривая код с другом, и мы заметили, что случай 3 ячейки рендеринга рисовал неправильную линию. Вот как код был исправлен.

Благодаря Кайидо я решил даже проблему с более светлыми серыми штрихами с его советом сместить на 0,5 координаты.

  case 3:
          this._adjustedMoveTo(coords.x, coords.y + size);
          (w !== -1) ? this._adjustedLineTo(coords.x, coords.y) : null
          break;
      }
...