Воспроизведение симуляции в ускоренном режиме - PullRequest
1 голос
/ 15 января 2020

Я работаю над простой игрой, где пользователи создают структуры в секторе, который является сеткой 10x10. Некоторые структуры генерируют ресурсы, а некоторые потребляют ресурсы. Сам сектор может содержать некоторые ресурсы вне какой-либо структуры. Генераторы и потребители связаны между собой. Например, скважина может генерировать воду, затем сплиттер потребляет воду и производит водород и кислород, в то время как нефтеперерабатывающий завод потребляет водород и кислород и производит ракетное топливо, и т. Д. c.

Скорость, с которой они генерируют или потребляют ресурсы, могут варьироваться в зависимости от структуры - я называю это уровнем тиков. Каждый раз, когда потребитель тикает, он сначала пытается извлечь эти ресурсы из структур, окружающих его в секторе. Если их недостаточно, он попытается получить их из хранилища сектора. Если этого все еще недостаточно, структура остановится. Структуры удерживают ресурсы, которые они генерируют, до некоторого максимума. Как только они заполнены, они не будут генерировать больше, пока некоторые не будут потреблены. Если структура остановлена, она также не будет генерировать больше ресурсов, но ресурсы, которые она уже имеет, могут все еще использоваться другой смежной структурой.

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

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

const numTicks = (Date.now() - lastTickTime) / shortestTickTime;
let currentTickTime = lastTickTime;
for (i = 0; i < numTicks; i++) {
    currentTickTime += shortestTickTime;
    // check the consumers - all structures that are consumers
    for (curConsumer of consumers) {
       if (curConsumer.isRunning && 
           (currentTickTime - curConsumer.lastTickTime >= curConsumer.tickRate) {
           ... check surrounding structures for resources
           if (curConsumer.stillNeedsResources) {
               ... check sector for researches
           }
           if (curConsumer.stillNeedsResources) {
              ... no resources available
              curConsumer.isRunning = false;
           }
        }
        // check the generators - all structures that are generators
        for (curGenerator of generators) {
           if (curGenerator.isRunning && 
               (currentTickTime - curGenerator.lastTickTime >= curGenerator.tickRate) {
               ... add the generated resources
           }
        }
    }
}

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

Мне интересно, есть ли какие-либо подсказки или приемы для моделирования такого рода для вычисления net изменить, не играя каждый тик. Или, альтернативно, если есть изменения, которые я могу внести в симуляцию, чтобы это было легче вычислить. Спасибо!

1 Ответ

1 голос
/ 15 января 2020

Шаг 1: преобразовать необработанные данные в форму «графа узлов» , где каждый узел представляет машину, а производители находятся внизу, а потребители - вверху. Например, это может выглядеть следующим образом:

          |
        (fuel)
          |
       Refinery
        /   \
       /     \
(hydrogen) (oxygen)
       \     /
        \   /
       Splitter
          |
       (water)
          |
         well

Примечание: если у машины есть выходной буфер (или входной буфер / с); тогда эти буферы должны быть отдельными узлами. Например, если все имеет выходные буферы, это может выглядеть следующим образом:

          |
        (fuel)
          |
  Refinery output buffer
          |
        (fuel)
          |
       Refinery
        /   \
       /     \
(hydrogen) (oxygen)
    |         |
 Hydrogen   Oxygen
  output    output
  buffer    buffer
    |         |
(hydrogen) (oxygen)
       \     /
        \   /
       Splitter
          |
       (water)
          |
   Well output buffer
          |
       (water)
          |
         well

Шаг 2: Определить «текущие средние показатели устойчивого состояния» путем (изначально) работы снизу вверх (производители потребителям). Например, если скважина производит 1 единицу воды на каждые 4 тика, тогда предположим, что она производит в среднем 0,25 воды на тик; и если сплиттер может преобразовывать 1 единицу воды в 2 единицы водорода и 1 единицу кислорода каждые 3 тика, то это максимум. скорость 0,333 воды превращается в 0,666 водорода и 0,333 кислорода, но вы уже знаете, что скважина не производит воду достаточно быстро и может определить, что сплиттер будет фактически потреблять 0,25 воды для производства 0,5 водорода и 0,25 кислорода.

Обратите внимание, что если производитель перепроизводит, вам нужно будет вернуться назад. Например, если скважина производит 1 единицу воды на каждые 2 тика, тогда вы предполагаете, что она производит в среднем 0,5 воды на тик; и если сплиттер может преобразовывать 1 единицу воды в 2 единицы водорода и 1 единицу кислорода каждые 3 тика, то вы знаете, что скважина производит больше воды, чем сплиттер может потреблять, и ей необходимо go вернуться в скважину и зажать ее выведите 0,333 воды на тик.

Шаг 3: Определите, когда (сколько тиков) будет происходить следующая вещь , которая изменит «текущие средние показатели устойчивого состояния». Если ресурс может истощиться (например, скважина работает dry), вам нужно знать, когда это произойдет. Таким же образом, если «емкость для хранения» (выходной буфер, входной буфер, резервуар для воды, емкость для хранения топлива и т. Д.) Заполнится или опустеет, вам необходимо знать, когда это произойдет. Все это основано на «текущих средних скоростях устойчивого состояния», которые у вас есть - например, если выходной буфер скважины пуст и получает воду (из скважины) со скоростью 0,5 воды на тик и теряет воду (на сплиттер) со скоростью 0,33 воды на тик; затем вы можете рассчитать, что количество, которое он хранит, увеличивается со скоростью «0,5 - 0,33 = 0,17 за тик» и (в сочетании с емкостью буфера) рассчитать, когда выходной буфер заполнится.

Обратите внимание, что « количество тиков до следующего события "также должно быть ограничено тем, когда вы хотите прекратить симуляцию.

Шаг 4. Время опережения до следующего события. Это в основном означает обновление количество материала, хранящегося в «резервуарах для хранения» с использованием «текущих средних ставок устойчивого состояния», которые у вас есть; а затем измените любую информацию (например, установите для скважины значение «не работает, пробег dry»).

Шаг 5. Повторяйте предыдущие шаги, пока не достигнете желаемого времени. Это это просто "if(current_time < stop_time) goto step 2".

Шаг 6: Обновите мир с окончательным состоянием всего. Это в основном обратная сторона шага 1 - установка количеств в «резервуарах хранения», пометить ресурсы как исчерпанные и т. д. c.

Примечания:

  • Возможно, вам потребуется добавить «транспорт» в качестве типа узла. Например, если у вас есть конвейерные ленты, доставляющие воду из скважины в сплиттер, то это можно смоделировать как «сосуд для хранения», но с задержкой по времени (например, если лента пуста и предметы начинают надеваться на ленту, то она будет возьмите время «длина * скорость» до того, как предметы придут на другой конец).

  • Если хотите; Вы можете добавить «пробой» в игру (например, есть небольшая вероятность того, что сплиттер выйдет из строя и нуждается в ремонте). Это просто дополнительная вещь, которую необходимо учитывать на шаге 3.

  • Не забывайте, что вы можете изменить дизайн игры, чтобы упростить ее. Для начала я бы избежал петель обратной связи (например, если топливо из реактора подается в генератор для создания энергии, которая потребляется разделителем, который ...), потому что это делает все значительно сложнее. Мне также хотелось бы избегать вещей с «переменной скоростью» (например, шахтеры, путешествующие между рудным полем и точкой отвода, где расстояние, на котором путешествуют шахтеры, увеличивается по мере расходования более близких частей рудного поля, так что «средняя руда на тик» "увеличивается и никогда не бывает постоянным).

  • Не забывайте, что это игра - она ​​не должна быть на 100% точной, а должна быть достаточно убедительной, чтобы обмануть игрок. Если это «немного неправильно» (например, в буфере вывода должно быть 23 элемента, но только 21 элемент), вполне вероятно, что никто никогда не заметит.

  • В зависимости от других деталей; Вы могли бы (или не могли бы) остановиться рано и переключиться на «имитацию одного тика за раз». Например, если вам нужно смоделировать 12345600 тиков, то вы можете использовать подход, который я описал для первых 12345500 тиков, а затем использовать подход, который вы уже использовали, чтобы сделать последние 100 тиков. Это может помочь сделать некоторые вещи (например, положение предметов на конвейерной ленте) более реалистичными c.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...