Откуда у террарии такие большие миры и при этом шелковисто-плавный fps? - PullRequest
3 голосов
/ 16 июня 2020

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

Теперь, очевидно, мне нужно l oop через все массивы, чтобы рендеринг / обновление, это создает проблемы, я попытался решить эту проблему, применив условие, что если какой-либо объект виден, только затем рендеринг обновления, иначе нет, но это не имеет большого значения.

мой TILE_SIZE - 1 мировая единица

поэтому, когда мой размер мира был а) 500 на 500, частота кадров была 60 б) 2000 на 2000, частота кадров была 50 c) 5000 на 5000 кадров в секунду уменьшалась дальше и так on

Если бы я только мог знать, как Terraria это делает, или каким-то образом это действительно помогло, спасибо.

Ответы [ 2 ]

0 голосов
/ 11 июля 2020

Мне нужно l oop через все массивы для рендеринга / обновления материала

Вам не нужно этого делать.

Для рендеринга материала вам действительно нужно учитывать только те блоки, которые находятся рядом с игроком. Вот код (x, y - координаты игрока, width, height - размеры вашего мира, render(x, y) - это заполнитель для вашей функциональности рендеринга, и для простоты я предположил, что игрок может увидеть 100 блоков в каждом направлении):

for (int i = Math.max(x-100, 0); i ≤ Math.min(x+100, width); i++) {
    for (int j = Math.max(y-100, 0); j ≤ Math.min(y+100, height); j++) {
        render(i, j);
    }
}

Процесс обновления также можно упростить:

Для npcs вам нужно всего лишь проверить несколько блоков вокруг него.

Для обновлений блоков (протекание жидкости, падение песка, распространение порчи и т. Д.) На самом деле есть две возможности:

  1. Итерировать разные части массива при каждом обновлении:
    Обновления блоков не должно быть так часто. Для потока жидкости может быть достаточно обновлять только каждый 50-й блок в каждом обновлении.
  2. Вы можете сохранить блоки, которые, возможно, потребуется обновить, в ArrayList (я предлагаю сохранять только координаты, а не сам блокирует).
    Затем, когда функция обновления вызывается, вы go через этот список проверяете, действительно ли блоку требуется обновление. В этом случае вы добавляете соседние блоки в список, который затем будет обновлен на следующей итерации.
    Убедитесь, что вы удалили только что обновленные блоки из списка, и избегайте добавления дубликатов!

В то время как второй подход обычно быстрее (обычно список определяется только действиями игрока, и даже если игрок решит осушить весь океан, весь список, вероятно, займет менее ¹⁄₂₀ всего мира).

Но первый подход, безусловно, легче реализовать, и он будет быстрее, если по какой-либо причине у вас будет много обновлений блоков.

0 голосов
/ 19 июня 2020
  1. Все, что вы не видите на экране, сохраняется как значения. Это означает, что все плитки, которые не видны в вашем окне просмотра, не отображаются. Они хранятся как объекты (например, {x, y, type}); Убедитесь, что вы подсчитали, сколько плиток на экране и сколько вы рендерит каждый l oop в своем коде. Никогда не визуализируйте то, что не видно пользователю, так как это будет занимать вашу память.

  2. Любые группы плиток, которые не могут быть визуализированы в ближайшее время (например, перемещением влево / вправо), не зацикливаются . Допустим, вы используете секторы плиток для группировки ваших плиток, и вы находитесь в секторе D. При перемещении влево вы попадете в C, а при перемещении вправо - в E. Таким образом, эти секторы должны быть загружены в массивы (но не рендерится) Вам не нужно проходить l oop через какие-либо другие сектора (например, A или B). Таким образом, их можно хранить извне (например, в виде текстовых файлов). Так что они доступны, но не занимают места в вашей игровой памяти.

Удачи. :)

...