Рубрики
Без рубрики

Загрузка Quake Engine Карты в Three.js: Часть № 1 – Диаграмм

Зачем использовать карты Quake для разработки игр в основном. Это действительно простой процесс для создания … Tagged с JavaScript, Quake.

Зачем использовать карты Quake

Для развития игры в основном. Это действительно простой процесс создания сложных трехмерных сцен с использованием популярных редакторов карты землетрясения, даже если вы не работаете над игрой FPS. Я могу представить, что вы можете использовать этот формат карты в других приложениях, а также при создании среды VR или просто прототипирование.

Требуемые навыки

Я предполагаю, что:

  1. Вы знаете, как настроить среду разработки JavaScript и использовать модули ES.
  2. Вы знаете, как использовать ES6 Генераторы Анкет
  3. Вы знаете, как использовать Принести

Чему я могу узнать здесь?

.map Формат файлов довольно легко понять, поэтому я покажу здесь, как написать простой анализатор, который не требует полного взорвания Lexer, при условии, что мы можем предположить несколько вещей, которые упрощают процесс.

Вы также можете узнать, как 3D -объекты были представлены в классических играх и как этот подход все еще может быть полезен в современном развитии.

Инструменты

Я использую Trenchbroom Редактор, чтобы создать карты и полагаться на это .карта Форматирование отдела пробела файла, поэтому, если вы хотите попробовать этот подход самостоятельно, вы должны использовать Trenchbroom также. Вам не нужно иметь классические игры, такие как Quake, Daikatana и т. Д., Чтобы создать карты.

Формат карты землетрясения

Это отличные ресурсы, чтобы узнать больше о формате карты землетрясения:

  1. http://www.gamers.org/dEngine/quake/QDP/qmapspec.html
  2. https://quakewiki.org/wiki/quake_map_format
  3. http://www.gamers.org/dengine/quake2/q2dp/q2dp_map/q2dp_map-2.html

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

Quake .карта Файлы представляют собой простые текстовые файлы с определенным синтаксисом, в небольшой степени, похожей на JSON. Они содержат список «сущностей», который может быть любым объектом, который может быть размещен на карте (стена, 3D -модель, абстрактные метаданные, такие как ящики местоположения игрока).

Грубо, .карта Файл представляет собой серию объектов с их свойствами и дополнительными кистями (кисть – это определение трехмерного объекта):

{
 // entity 0
 "property_key" "property_value"
 {
  // brush (optional)
 }
}
{
  // entity 1
}
// (...)
{
  // entity N
}

Пример карты

На изображении выше вы можете увидеть две сущности:

  1. Куб
  2. Световая коробка

.карта Файл выглядит так:

// Game: PersonalIdol
// Format: Standard
// entity 0
{
    "classname" "worldspawn"
    "light" "0.3"
    "_tb_textures" "./debug;./textures"
    // brush 0
    {
        ( -64 -64 128 ) ( -64 -64 -0 ) ( -64 64 -0 ) textures/texture-crate-128x128 0 0 0 1 1
        ( 64 -64 128 ) ( 64 -64 -0 ) ( -64 -64 -0 ) textures/texture-crate-128x128 0 0 0 1 1
        ( -64 -64 -0 ) ( 64 -64 -0 ) ( 64 64 -0 ) textures/texture-crate-128x128 0 0 0 1 1
        ( 64 -64 128 ) ( -64 -64 128 ) ( -64 64 128 ) textures/texture-crate-128x128 0 0 0 1 1
        ( -64 64 -0 ) ( 64 64 -0 ) ( -64 64 128 ) textures/texture-crate-128x128 0 64 0 1 1
        ( -64 64 128 ) ( 64 64 -0 ) ( 64 -64 128 ) debug/texture-uv-1024x1024 0 -0 0 1 1
        ( 64 64 -0 ) ( 64 -64 -0 ) ( 64 -64 128 ) textures/texture-crate-128x128 0 64 0 1 1
    }
}
// entity 1
{
    "classname" "light"
    "origin" "224 192 192"
    "decay" "2"
    "light" "1"
}

Я думаю, что само определение сущности довольно простое. Это набор свойств между кронштейнами {"foo" "bar"} Анкет Это как бы напоминает JSON, но между свойствами нет запятых и колонсов. Они организованы парами.

Щетки

Хитрое – как обращаться с кистями. Звук использовал BSP и другие алгоритмы, которые хорошо работали с половинами пространства.

Это означает, что определение кисти не дает вам набора вершин для визуализации, как вы, возможно, ожидали, вместо этого у вас есть набор не менее 4 половинок, определяемых тремя точками. Чтобы иметь список вершин, нам нужно найти перекрестки между этими половинами. Я покажу вам, как в следующих частях этой серии я просто сосредоточусь на анализе файла.

Диапазон

Предположения

Для анализа мы можем использовать несколько предположений, которые верны при использовании редактора карты Trenchbroom:

  1. Каждый кронштейн (открытие или закрытие) находится в новой линии
  2. Комментарии начинаются только в начале линии, и вся линия может быть проигнорирована.
  3. Каждое свойство объекта определяется в новой линии
  4. Каждое полупространство определяется в новой линии

Алгоритм

С этими предположениями мы можем проанализировать файл, используя этот алгоритм:

1. Split the `.map` file into the separate lines
2. Iterate over each line.
    1. If the line is a comment, then ignore it.
    2. If the line is empty, then ignore it.
    3. If the line is an opening bracket:
        1. If you are inside the entity definition:
            1. If you already are inside the brush definition, then it is an error.
            2. Start current brush buffer and store the current line inside it.
        2. If you are not inside the entity definition, start a new entity buffer.
    4. If it is a closing bracket: 
        1. If you have an opened brush buffer, then close it and save the brush.
        2. If you do not have an opened brush buffer:
            1. If you are not inside the entity definition, then it is an error.
            2. If you are inside the entity definition, then the entity definition is complete.
    5. If you are inside the brush, then it is the half-space definition.
    6. If you are inside the entity, but not in a brush, then it's the entity property.

Таким образом, вам не нужен сложный анализатор, Лекер и т. Д., И вы все равно будете сохранять информацию о номере строки.

Образец реализации JavaScript

Эта реализация следует приведенному выше алгоритму и дает новое определение сущностей каждый раз, когда она уверена, что она завершена с помощью генератора.

*parse() {
  const lines = this.content.split(/\r?\n/);


  let currentBrushSketch = null;
  let currentEntitySketch = null;

  // 2. Iterate over each line.
  for (let lineno = 0; lineno < lines.length; lineno += 1) {
    const line = lines[lineno];

    // 2.1. If the line is a comment, then ignore it.
    if (line.startsWith("//") || line.trim().length < 1) {
      continue;
    }

    // 3. If the line is an opening bracket:
    if (line.startsWith("{")) {
      // 3.1. Start current brush buffer and store the current line inside it.
      if (currentEntitySketch) {
        currentBrushSketch = [];
        continue;
      // 3.2. If you are not inside the entity definition, start a new entity buffer.
      } else if (!currentEntitySketch) {
        currentEntitySketch = {
          brushes: [],
          props: [],
        };
        continue;
      // 3.1.1. If you already are inside the brush definition, then it is an error.
      } else {
        throw new Error("Unexpected opening bracket.");
      }
    }

    // 2.4 If it is a closing bracket: 
    if (line.startsWith("}")) {
      // 2.4.1. If you have an opened brush buffer, then close it and save the brush.
      if (currentBrushSketch) {
        if (!currentEntitySketch) {
          throw new Error("Expected brush to be nested inside entity");
        }
        currentEntitySketch.brushes.push(new QuakeBrush(breadcrumbs.add("QuakeBrush"), currentBrushSketch));
        currentBrushSketch = null;
        continue;
      // 2.4.2. If you do not have an opened brush buffer:
      } else if (currentEntitySketch) {
        // 2.4.2.2. If you are inside the entity definition, then the entity definition is complete.
        yield {
          brushes: currentEntitySketch.brushes,
          properties: currentEntitySketch.props,
        }

        currentEntitySketch = null;
        continue;
      } else {
        // 2.4.2.1. If you are not inside the entity definition, then it is an error.
        throw new Error("Unexpected closing bracket.");
      }
    }

    if (currentBrushSketch) {
      // 5. If you are inside the brush, then it is the half-space definition.
      currentBrushSketch.push(line);
      continue;
    }

    // 6. If you are inside the entity, but not in a brush, then it's the entity property.
    if (currentEntitySketch) {
      currentEntitySketch.props.push(line);
      continue;
    }

    throw new Error("Unexpected line.");
  }

  // these two protect us from corrupted maps
  if (currentBrushSketch) {
    throw new Error("Unexpected end of brush data.");
  }

  if (currentEntitySketch) {
    throw new Error("Unexpected end of entity data.");
  }
}

Резюме

Подводя итог, теперь вы должны иметь по крайней мере основную идею, как подходить к картам Quake Satching по -настоящему простым способом. В следующей части я покажу, как найти вершины щетки, используя перекрестки наполовину пространства.

А пока вы также можете проверить мой проект, где я реализовал этот анализатор: https://github.com/mcharytoniuk/personalidol https://github.com/mcharytoniuk/personalidol/blob/b2e5d84b3d800eeaf0d7dae98d7108176eee33de/src/framework/classes/quakemapparser.js

Спасибо, что сделали со мной!:)

Оригинал: “https://dev.to/mcharytoniuk/loading-quake-engine-maps-in-three-js-part-1-parsing-55mp”