Re[7]: Представление трехмерного массива в виде одномерного
От: Pavel Dvorkin Россия  
Дата: 09.12.13 03:36
Оценка:
Здравствуйте, Javaec, Вы писали:


J>Вот например одномерный массив температур:

J>{15,16,16,17}
J>чтобы его заскроллить нужно пройтись по всей его длине и переприсваивать элементы.

Нет, не нужно. Нужно ввести переменную xOrigin и присвоить ей сначала 0. Элементы берем от xOrigin до конца массива, а потом от 0 до xOrigin-1, всего N штук. То есть вначале

a[xOrigin] = 15
a[xOrigin+1] = 16
a[xOrigin+2] = 16
a[xOrigin+3]=17

А теперь нужно сделать скроллинг. Выполняем xOrigin++, xOrigin становится равным 1, и это все. Теперь элементы берем в следующем порядке

a[xOrigin] = 16
a[xOrigin+1] = 16
a[xOrigin+2] = 17
а следующий элемент a[0] изменяем, ставим 0



J>Получим:

J>{16,16,17,0}

Именно это и получим

Массив закольцован — за последним (N-1) элементом следует 0-й. Логическое начало массива не в 0-м элементе, а в элементе с номером xOrigin.
With best regards
Pavel Dvorkin
Re[7]: Представление трехмерного массива в виде одномерного
От: Pavel Dvorkin Россия  
Дата: 09.12.13 03:40
Оценка:
Здравствуйте, Nikolay_Ch, Вы писали:

N_C>Пока мне вообще непонятно, что автор топика хранит внутри массива, более того, я не понял, заполнен ли массив целиком или только частично.


Мне тоже не совсем ясно, но он хочет трехмерный массив, по-видимому, не слишком большой, и с возможностью скроллинга. ИМХО в этом случае не стоит заводить что-то иное, кроме массива, а скроллинг сделать с помощью wrap index.

N_C>Ссылочные типы не всегда чудовищны, особенно, когда надо делать кластеризацию. Или в случае, если массив слабо заполнен.


Истина конкретна, да, есть, конечно, случаи, когда стоит хранить в виде списка или хеш-таблицы. Но мне как-то претит превращать O(1) во что бы то ни было еще.
With best regards
Pavel Dvorkin
Re[3]: Представление трехмерного массива в виде одномерного
От: Ромашка Украина  
Дата: 09.12.13 13:04
Оценка:
Здравствуйте, Javaec, Вы писали:
J>Задача в рамках разработки компьютерной игры. Игрок двигается — пространство скроллится.

То есть никакого рандомного доступа к элементам массива? Нафиг массивы, делать списками.

J>А по индексу можно же обращаться, т.к. мы храним min и max векторы и благодаря им можем вызвать Get(-4, 3, 7) и взять его по какому-нибудь, 27му индексу в одномерном массиве.


Кому может понадобиться элемент [-4, 3, 7] без [-4, 3, 6] и [-4, 3, 8]?

J>Всякая математика по вычислению индекса выполняется на порядок быстрее, чем само обращение к памяти.


Да не нужно вообще математики.


Всё, что нас не убивает, ещё горько об этом пожалеет.
Re[3]: Представление трехмерного массива в виде одномерного
От: alexzz  
Дата: 11.12.13 21:15
Оценка: 3 (1) +1
Здравствуйте, Javaec, Вы писали:

J>Размер пространства примерно 15x15x15 (3375). Но добавление новых элементов и "скроллинг" с удалением выходящих за границу — операции очень частые.


Не знаю, конечно, всех имеющихся ограничений, но по идее если даже делать тупо трёхмерным массивом со скроллингом 60 раз в секунду, то 15х15х15 ― это не тот объём данных, на котором что-то может тормозить. Мне кажется, с этим справится даже ZX-Spectrum со своим CPU Z80 3,5МГц, не говоря о мобилках и тем более PC.

Я писал свой воксельный движок под Unity (https://vimeo.com/album/1836529). Много экспериментировал с разными способами хранения данных и в конечном итоге пришёл к такому варианту:
1. Весь окружающий игрока трёхмерный мир (1024х128х1024 = 128Мб) хранится в единой закольцованной в горизонтальных направлениях структуре данных, реализованной одномерным массивом. Можно сказать, что это закольцованный одномерный массив, но это несколько двусмысленная формулировка.
2. Так как структура закольцована, по мере перемещения игрока по миру скроллировать её не надо. Достаточно только скинуть на диск области, которые вышли из поля зрения, и после этого записать в массив данные для областей, которые вошли в поле зрения.

Вот так примерно выглядит устройство массива:
internal class World
{
    public const int WORLD_WIDTH_SHIFT = 10; // 10 -> the world is 1024 blocks wide
    public const int WORLD_WIDTH = 1 << WORLD_WIDTH_SHIFT;
    public const int WORLD_WIDTH_MASK = WORLD_WIDTH - 1;

    public const int WORLD_HEIGHT_SHIFT = 7; // 7 -> the world is 128 blocks high
    public const int WORLD_HEIGHT = 1 << WORLD_HEIGHT_SHIFT;
    public const int WORLD_HEIGHT_MASK = WORLD_HEIGHT - 1;

    private const int WORLD_ARRAY_SIZE = WORLD_WIDTH * WORLD_HEIGHT * WORLD_WIDTH;
    private const int WORLD_ARRAY_MASK = WORLD_ARRAY_SIZE - 1;

    public readonly Block[] blocks = new Block[WORLD_ARRAY_SIZE];

    public static int CalculatePointer(int wx, int wy, int wz)
    {
        wx &= WORLD_WIDTH_MASK;
        wz &= WORLD_WIDTH_MASK;

        return (((wx << WORLD_WIDTH_SHIFT) + wz) << WORLD_HEIGHT_SHIFT) + wy;
    }

    public Block GetBlock(int wx, int wy, int wz)
    {
        int pointer = CalculatePointer(wx, wy, wz);
        return blocks[pointer];
    }

    public void SetBlock(int wx, int wy, int wz, Block value)
    {
        int pointer = CalculatePointer(wx, wy, wz);
        blocks[pointer] = value;
    }
}


Также прелесть в том, что все обращения к такой структуре можно проводить прямо в мировых координатах. Например, если изначально в массиве хранятся данные начиная с x=0 по x=1023 включительно, то при перемещении игрока вправо все ячейки с координатами x=0 выходят из поля зрения, а ячейки с x=1024 появляются. Мы должны ячейки с x=0 прочитать и скинуть куда-нибудь на диск:
var oldValue = world.GetBlock(wx: 0, wy: ..., wz: ...);

а потом ячейки с x=1024 подгрузить с диска и записать в массив:
world.SetBlock(wx: 1024, wy: ..., wz: ..., value: newValue);

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