Настройка мира

4,045
63
645
Привет всем!
Начал тут разбираться в создании мира.
Задача следующая: свой мир, и полностью свои настраиваемые биомы.

В этой теме хочу попробовать разобраться во всех тонкостях этого процесса.
Буду публиковать свои наработки, как только появится что-то работающее ))) а так же задавать кучу вопросов )

Кому интересно, участвуйте )

На данный момент удалось создать мир и разместить в нём два собственных биома.
Пока не очень понимаю как настроить их размер и форму, но я в процессе.

Наткнулся на один интересный метод, очень хотел бы, чтобы знающие люди объяснили принцип его работы.
Метод зовётся getInts, в нём 4 параметра (как я понял, 2 координаты и 2 размер по осям???) и он присутствует в большинстве классов, отвечающих за мир: чанк провайдере, слоях и т.д. Как я понял, с помощью него как раз и осуществляется тонкая настройка местности.

Какие именно параметры он возвращает, и как с ним работать?

Ясно, что всё это в итоге складывается в тот самый массив чанка из 65536-ти блоков.
Но вот как всё это работает, пока не пойму... Этот массив разбивается на столбы, или наоборот режется по горизонтали и они вычисляются отдельно?

В общем, хелп.
 
586
1
2
Liahim написал(а):
Привет всем!
Начал тут разбираться в создании мира.
Задача следующая: свой мир, и полностью свои настраиваемые биомы.

В этой теме хочу попробовать разобраться во всех тонкостях этого процесса.
Буду публиковать свои наработки, как только появится что-то работающее ))) а так же задавать кучу вопросов )

Кому интересно, участвуйте )

На данный момент удалось создать мир и разместить в нём два собственных биома.
Пока не очень понимаю как настроить их размер и форму, но я в процессе.

Наткнулся на один интересный метод, очень хотел бы, чтобы знающие люди объяснили принцип его работы.
Метод зовётся getInts, в нём 4 параметра (как я понял, 2 координаты и 2 размер по осям???) и он присутствует в большинстве классов, отвечающих за мир: чанк провайдере, слоях и т.д. Как я понял, с помощью него как раз и осуществляется тонкая настройка местности.

Какие именно параметры он возвращает, и как с ним работать?

Ясно, что всё это в итоге складывается в тот самый массив чанка из 65536-ти блоков.
Но вот как всё это работает, пока не пойму... Этот массив разбивается на столбы, или наоборот режется по горизонтали и они вычисляются отдельно?

В общем, хелп.
Измерение?.
Очень много информаци можно найти в дивайне
 
4,045
63
645
Да и в ваниле её много... Меня сам принцип интересует...
Пытаюсь себе его представить геометрически, что б проще было понять... Как именно складывается чанк и какую роль в этом играет метод getInts?
 
167
3
23
getInts в лейерах? 
Объясню, но не всё.(к тому же, мне кажется никто не поймет что я сейчас напишу =( (Ведь у меня по русскому тройка(и книжек я не читаю xD ))))
Для начала хочу сказать, что в лейерах y - это z, а height - length.

1)Ну, сначала Идёт GenLayerIsland. в getInts он возвращает массив нолей и единиц. Нули показывают где будут океаны, единички - острова.

2)GenLayerZoom зумит область.
Например:

P.S: число >> 1 = число / 2
int[] parentInts = parent.getInts(x >> 1, y >> 1, (width >> 1) + 2, (height >> 1) + 2) // Получаем регион в два раза меньше с родительского лейера(Например, того же GenLayerIsland'а, который генерирует нули и единицы)
Допустим, вот как выглядит массив parentInts:

width = 4

1001
0100  height(lenght) = 4
1000
0010

А вот какой массив получится, и какой мы вернем:
зумим \/

width = 8

11000011
10010001
00110000
10000000   height(length) = 8
11000000
10000000
00001100
00001100

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

3)GenLayerVoronoiZoom работает почти так же как и обычный зум, только зумит сразу в 4 раза а не в 2. 
Оба масштабируют не абы как, а по сиду мира.(То есть, если родительский массив родительского лейера будет всегда одним и тем же, то и результат(массив который надо вернуть в методе getInts) этих зумеров будет всегда одним и тем же).

4)GenLayerSmooth. Сглаживает всё к чертям. Опять же по сиду.(Если честно, то почти во всех лейерах все делается по сиду).
Пример(скопление однёрок сверху справа, и снизу слева(если кто не понял)):
11010000
01100000
11100011
11001101
00001111
-->
11110000
11110000
11100011
11000111
00001111
**Выходной массив не обязательно должен получится именно как таким же, как и здесь, это лишь пример(сделанный своими ручками). Как что отгладится зависит только от сида.
И опять же, "этот лейер может обрабатывать и другие числа, а не только нули и единицы".

5)GenLayerAddIsland(Не путайте с GenLayerIsland!). Однёрки, полученные GenLayerIsland'ом, заменяет либо опять же на единицу, либо на 2, на 3, или 4.
1 - это значит, что на этой территории будет пустынный(Засушливый) биом
2 - жаркие/норм биомы(обчный лес, болото, или джунги)
3 - прохладные биомы(леса, горы и поляна туда входят)
4 - биомы с холодным климатом(например, тайга)
0 - так и остается океаном

6)GenLayerAddMushroomIsland. Ищёт, куда же можно впихнуть грибной биом(приоритетно в океане). В массиве сохраняет id грибного биома.
Пример:
0200
1300
4000
2101
Точку 3;3 со всех сторон окуружает океан, значит есть шанс что там будет грибной островочек
0200
1300
40X0
2101
** То есть есть шанс, что в массиве там где X, сохранится id грибочного биомчика

7)GenLayerBiomes. Превращает массив, который содержит 0, 1, 2, 3, 4, (и иногда другие циферки(например, тот же ид грибного острова)) в массив идишек биомов(финалли!)(но пока без идишек рек!)

8)GenLayerRiver. В массиве содержатся либо -1, либо id биома реки. Как эти числа генерируются? Загляните в сырцы, объяснять долго =P

9)GenLayerRiverMix. Скрещивает массивы GenLayerRiver'a и GenLayerBiomes. Это, можно сказать, готовый массив идишек биомов.
 
4,045
63
645
О! Честно, не ожидал такого развёрнутого ответа ) Спасибо!
Теперь буду копаться и пытаться всё это понять.
Но! Что первое понять удалось.
Каждый бит - это отдельный чанк? И размер биома как раз таки зависит от порядка цифр, а не от их содержания?
Или каждый бит - это готовый биом? Тогда в какой момент всё это переходит в генерацию чанков?

Или же система уж совсем ТАКАЯ:
Сперва из битов накидываем основные мазки, затем увеличиваем, добавляем деталей и, как только картина нас устраивает, увеличиваем всё это настолько, чтобы биты из биомов превратились в чанки? Или вплоть до блоков? О_о

Именно поэтому важно соблюсти именно последовательность зуммирования и новой генерации?
 
167
3
23
Сначала биты, потом всё это зумим несколько раз и сглаживаем.(Получаются небольшие группы однерок и нулей)

Потом острова(там где однёрки) делятся на четыре типа(в тексте выше всё написано), и опять зумятся и сглаживаются несколько раз.

Потом GanLayerBiomes в зависимости от типа (1 2 3 4) выбирает, какой будет биом будет тута, и сохраняет в массиве идишки. (На самом деле он не только это делает...)

Потом миксер скрещивает всё с реками, и вот тебе результат.
 
4,045
63
645
А кто-нибудь может объяснить принцип формирования самой поверхности?Мне нужно настроить жёсткость перепада высот между разными биомами... Нужно получить практически отвесный обрыв. А сейчас граница сглаживается...
Это какие параметры нужно крутить?
Как я понял, это различные нойсы в ЧанкПровайдере? Но вот как они работают?
Спасибо.
[merge_posts_bbcode]Добавлено: 29.05.2016 22:36:18[/merge_posts_bbcode]

Кстати, что удалось понять...
Все вышеописанные зумы (обычный или Fuzzy) применяются для работы над картой биомов, то есть для задания самого 2д рисунка биомов. Однако, для финального превращения этой карты в карту блоков, они почему-то не годятся... Пробовал, но получается очень криво и границы биомов не согласуются с их высотой.
Для этого как раз и придуман Вороной Зум. Он работает аккуратнее и, видимо, имеет какую-то связь с финальной генерацией поверхности. Поэтому его всегда нужно пихать в самый конец.
 
167
3
23
Liahim написал(а):
А кто-нибудь может объяснить принцип формирования самой поверхности?Мне нужно настроить жёсткость перепада высот между разными биомами... Нужно получить практически отвесный обрыв. А сейчас граница сглаживается...
Это какие параметры нужно крутить?
Как я понял, это различные нойсы в ЧанкПровайдере? Но вот как они работают?
Спасибо.
[merge_posts_bbcode]Добавлено: 29.05.2016 22:36:18[/merge_posts_bbcode]

Кстати, что удалось понять...
Все вышеописанные зумы (обычный или Fuzzy) применяются для работы над картой биомов, то есть для задания самого 2д рисунка биомов. Однако, для финального превращения этой карты в карту блоков, они почему-то не годятся... Пробовал, но получается очень криво и границы биомов не согласуются с их высотой.
Для этого как раз и придуман Вороной Зум. Он работает аккуратнее и, видимо, имеет какую-то связь с финальной генерацией поверхности. Поэтому его всегда нужно пихать в самый конец.
Не забывай, еще сглаживатели есть. 
Насчет сглаживания границ... Скинь пжлст код чанкпровайдера на гист, скажу где и как сглаживается(сейчас нет возможности в код смотреть(к тому же я не знаю какую версию майна используешь ты)).
 
4,045
63
645
Да, знаю про сглаживание и всё такое.
После твоего объяснения, всё пошло очень легко ) Спасибо...
Это я сейчас про карту биомов.
А по поводу ландшафта и чанкпровайдера, пока что использую целиком копи-паст с ванили... Смотрю туда и глаза разбегаются... Не знаю, с чего и начать.
Меня опять интересует тупо принцип работы нойсов ) Дальше, думаю, разберусь сам )
Версия у меня 1.7.10... Пока экспериментирую с ней.
 
167
3
23
1.7.10? Это хорошо. Есть там в провайдере метод func_147423_a, и есть часть кода:

Код:
                float f = 0.0F;
                float f1 = 0.0F;
                float f2 = 0.0F;
                byte b0 = 2;
                BiomeGenBase biomegenbase = this.biomesForGeneration[j1 + 2 + (k1 + 2) * 10];

                for (int l1 = -b0; l1 <= b0; ++l1)
                {
                    for (int i2 = -b0; i2 <= b0; ++i2)
                    {
                        BiomeGenBase biomegenbase1 = this.biomesForGeneration[j1 + l1 + 2 + (k1 + i2 + 2) * 10];
                        float f3 = biomegenbase1.rootHeight;
                        float f4 = biomegenbase1.heightVariation;

                        if (this.field_147435_p == WorldType.AMPLIFIED && f3 > 0.0F)
                        {
                            f3 = 1.0F + f3 * 2.0F;
                            f4 = 1.0F + f4 * 4.0F;
                        }

                        float f5 = this.parabolicField[l1 + 2 + (i2 + 2) * 5] / (f3 + 2.0F);

                        if (biomegenbase1.rootHeight > biomegenbase.rootHeight)
                        {
                            f5 /= 2.0F;
                        }

                        f += f4 * f5;
                        f1 += f3 * f5;
                        f2 += f5;
                    }
                }

                f /= f2;
                f1 /= f2;
                f = f * 0.9F + 0.1F;
                f1 = (f1 * 4.0F - 1.0F) / 8.0F;

Так вот, это и есть "Сглаживатель границ". Вычисляет среднюю высоту биомов в сетке 5 * 5 (за это отвечает переменная b0(он означает как-бы радиус))
 
4,045
63
645
Аааааааа! Годится ))) Пошёл рыть дальше )
Спасибо.
[merge_posts_bbcode]Добавлено: 30.05.2016 10:45:02[/merge_posts_bbcode]

А на счёт системы нойсов что-нибудь можешь сказать?
Они тоже наслаиваются друг на друга? Укажи хотя бы в каком месте это происходит? )

[merge_posts_bbcode]Добавлено: 30.05.2016 10:49:11[/merge_posts_bbcode]

О! По ходу в этом же методе всё это и твориться ))) Ладненько.
 
4,045
63
645
Ещё вопросик )))
Есть в чанк провайдере такой мудрёный метод:
Код:
public void func_147424_a(int p_147424_1_, int p_147424_2_, Block[] p_147424_3_)
    {
        byte b0 = 63;
        this.biomesForGeneration = this.worldObj.getWorldChunkManager().getBiomesForGeneration(this.biomesForGeneration, p_147424_1_ * 4 - 2, p_147424_2_ * 4 - 2, 10, 10);
        this.func_147423_a(p_147424_1_ * 4, 0, p_147424_2_ * 4);

        for (int k = 0; k < 4; ++k)
        {
            int l = k * 5;
            int i1 = (k + 1) * 5;

            for (int j1 = 0; j1 < 4; ++j1)
            {
                int k1 = (l + j1) * 33;
                int l1 = (l + j1 + 1) * 33;
                int i2 = (i1 + j1) * 33;
                int j2 = (i1 + j1 + 1) * 33;

                for (int k2 = 0; k2 < 32; ++k2)
                {
                    double d0 = 0.125D;
                    double d1 = this.field_147434_q[k1 + k2];
                    double d2 = this.field_147434_q[l1 + k2];
                    double d3 = this.field_147434_q[i2 + k2];
                    double d4 = this.field_147434_q[j2 + k2];
                    double d5 = (this.field_147434_q[k1 + k2 + 1] - d1) * d0;
                    double d6 = (this.field_147434_q[l1 + k2 + 1] - d2) * d0;
                    double d7 = (this.field_147434_q[i2 + k2 + 1] - d3) * d0;
                    double d8 = (this.field_147434_q[j2 + k2 + 1] - d4) * d0;

                    for (int l2 = 0; l2 < 8; ++l2)
                    {
                        double d9 = 0.25D;
                        double d10 = d1;
                        double d11 = d2;
                        double d12 = (d3 - d1) * d9;
                        double d13 = (d4 - d2) * d9;

                        for (int i3 = 0; i3 < 4; ++i3)
                        {
                            int j3 = i3 + k * 4 << 12 | 0 + j1 * 4 << 8 | k2 * 8 + l2;
                            short short1 = 256;
                            j3 -= short1;
                            double d14 = 0.25D;
                            double d16 = (d11 - d10) * d14;
                            double d15 = d10 - d16;

                            for (int k3 = 0; k3 < 4; ++k3)
                            {
                                if ((d15 += d16) > 0.0D)
                                {
                                    p_147424_3_[j3 += short1] = Blocks.stone;
                                }
                                else if (k2 * 8 + l2 < b0)
                                {
                                    p_147424_3_[j3 += short1] = Blocks.water;
                                }
                                else
                                {
                                    p_147424_3_[j3 += short1] = null;
                                }
                            }

                            d10 += d12;
                            d11 += d13;
                        }

                        d1 += d5;
                        d2 += d6;
                        d3 += d7;
                        d4 += d8;
                    }
                }
            }
        }
    }

Он собирает блоки в финальные чанки на основе сгенерированного в методе func_147423_a() ландшафта.
Вопрос: может кто-нибудь подсказать, в каком именно из приведённых циклов можно найти координаты блоков (x и z)? Моей головы не хватает, чтобы это постичь )))

Задача у меня следующая: хочу поднять уровень океана (параметр byte b0 = 63;) в конкретном биоме. А для этого нужно знать координаты блоков, чтобы по ним определить, что это тот самый биом.

Уверен, что они там есть, потому что, если сложить вариации всех приведённых выше циклов, как раз и получается тот самый заветный массив чанка из 65536 блоков.

Спасибо )
[merge_posts_bbcode]Добавлено: 31.05.2016 10:44:32[/merge_posts_bbcode]

Кстати, кому интересно, связь между методами func_147424_a() и func_147423_a() осуществляется через массив this.field_147434_q имеющий длину 825. Что это есть ума не приложу О_о
 
769
1
42
А разве высота (даже биомов и не только) не в generateTerrain? Этот метод похож на replaceBlocksForBiome и он (не весь) устанавливает блоки на поверхности в среднем и нижнем слоях мира.
 
4,045
63
645
Ну, в принципе, наверное, можно и там заменить...
Но в данном методе он устанавливается в ваниле... И, как я понял, замена происходит относительно криволинейной поверхности ландшафта. То есть, где впуклость - там вода, где выпуклость, там гора.

То есть, это гарантия того, что блоков воды не будет под океаном, в пещерах и т.д...

А через replaceBlocksForBiome придётся костылить, как выяснить - это действительно поверхность ландшафта или просто какая-то пещерка?
 
769
1
42
Не, это конкретно установка блоков в слои ландшафта. Ну вот пример
Код:
public void replaceBlocksForBiome(int par1, int par2, Block[] arrayOfIDs, byte[] arrayOfMeta, BiomeGenBase[] par4ArrayOfBiomeGenBase) { 
int var5 = 20; 
float var6 = 0.03125F; 
this.noiseGen4.setFrequency(var6 * 2); 
for (int var8 = 0; var8 < 16; ++var8) { 
for (int var9 = 0; var9 < 16; ++var9) { 
int var12 = (int) (this.noiseGen4.getNoise(par1 * 16 + var8, par2 * 16 + var9) / 3.0D + 3.0D + this.rand.nextDouble() * 0.25D); 
int var13 = -1; 
Block var14 = this.topBlockID; 
byte var14m = this.topBlockMeta; 
Block var15 = this.fillBlockID; 
byte var15m = this.fillBlockMeta; 

for (int var16 = ChunkProviderPluto.CHUNK_SIZE_Y - 1; var16 >= 0; --var16) { 
int index = this.getIndex(var8, var16, var9); 

if (var16 <= 0 + this.rand.nextInt(5)) { 
arrayOfIDs[index] = Blocks.bedrock; 
} else { 
Block var18 = arrayOfIDs[index]; 

if (Blocks.air == var18) { 
var13 = -1; 
} else if (var18 == this.lowerBlockID) { 
arrayOfMeta[index] = this.lowerBlockMeta; 

if (var13 == -1) { 
if (var12 <= 0) { 
var14 = Blocks.air; 
var14m = 0; 
var15 = this.lowerBlockID; 
var15m = this.lowerBlockMeta; 
} else if (var16 >= var5 - -16 && var16 <= var5 + 1) { 
var14 = this.topBlockID; 
var14m = this.topBlockMeta; 
var14 = this.fillBlockID; 
var14m = this.fillBlockMeta; 
} 

var13 = var12; 

if (var16 >= var5 - 1) { 
arrayOfIDs[index] = var14; 
arrayOfMeta[index] = var14m; 
} else { 
arrayOfIDs[index] = var15; 
arrayOfMeta[index] = var15m; 
} 
} else if (var13 > 0) { 
--var13; 
arrayOfIDs[index] = var15; 
arrayOfMeta[index] = var15m; 
} 
} 
} 
} 
} 
} 
}
Где topBlockID, fillerBlockID, lowerBlockID, эти блоки устанавливаются в слои ландшафта. Здесь эти блоки могут вызываться из биома, но тут он вызывается прям из этого класса(ChunkProviderPluto) таким кодом
Код:
Block topBlockID = PlutoBlocks.plutoBasicBlock;
byte topBlockMeta = 0; 
Block fillBlockID = PlutoBlocks.plutoBasicBlock; 
byte fillBlockMeta = 1;
Block lowerBlockID = PlutoBlocks.plutoBasicBlock;
byte lowerBlockMeta = 2;
 
167
3
23
Liahim написал(а):
Ещё вопросик )))
Есть в чанк провайдере такой мудрёный метод:
Код:
public void func_147424_a(int p_147424_1_, int p_147424_2_, Block[] p_147424_3_)
    {
        byte b0 = 63;
        this.biomesForGeneration = this.worldObj.getWorldChunkManager().getBiomesForGeneration(this.biomesForGeneration, p_147424_1_ * 4 - 2, p_147424_2_ * 4 - 2, 10, 10);
        this.func_147423_a(p_147424_1_ * 4, 0, p_147424_2_ * 4);

        for (int k = 0; k < 4; ++k)
        {
            int l = k * 5;
            int i1 = (k + 1) * 5;

            for (int j1 = 0; j1 < 4; ++j1)
            {
                int k1 = (l + j1) * 33;
                int l1 = (l + j1 + 1) * 33;
                int i2 = (i1 + j1) * 33;
                int j2 = (i1 + j1 + 1) * 33;

                for (int k2 = 0; k2 < 32; ++k2)
                {
                    double d0 = 0.125D;
                    double d1 = this.field_147434_q[k1 + k2];
                    double d2 = this.field_147434_q[l1 + k2];
                    double d3 = this.field_147434_q[i2 + k2];
                    double d4 = this.field_147434_q[j2 + k2];
                    double d5 = (this.field_147434_q[k1 + k2 + 1] - d1) * d0;
                    double d6 = (this.field_147434_q[l1 + k2 + 1] - d2) * d0;
                    double d7 = (this.field_147434_q[i2 + k2 + 1] - d3) * d0;
                    double d8 = (this.field_147434_q[j2 + k2 + 1] - d4) * d0;

                    for (int l2 = 0; l2 < 8; ++l2)
                    {
                        double d9 = 0.25D;
                        double d10 = d1;
                        double d11 = d2;
                        double d12 = (d3 - d1) * d9;
                        double d13 = (d4 - d2) * d9;

                        for (int i3 = 0; i3 < 4; ++i3)
                        {
                            int j3 = i3 + k * 4 << 12 | 0 + j1 * 4 << 8 | k2 * 8 + l2;
                            short short1 = 256;
                            j3 -= short1;
                            double d14 = 0.25D;
                            double d16 = (d11 - d10) * d14;
                            double d15 = d10 - d16;

                            for (int k3 = 0; k3 < 4; ++k3)
                            {
                                if ((d15 += d16) > 0.0D)
                                {
                                    p_147424_3_[j3 += short1] = Blocks.stone;
                                }
                                else if (k2 * 8 + l2 < b0)
                                {
                                    p_147424_3_[j3 += short1] = Blocks.water;
                                }
                                else
                                {
                                    p_147424_3_[j3 += short1] = null;
                                }
                            }

                            d10 += d12;
                            d11 += d13;
                        }

                        d1 += d5;
                        d2 += d6;
                        d3 += d7;
                        d4 += d8;
                    }
                }
            }
        }
    }

Он собирает блоки в финальные чанки на основе сгенерированного в методе func_147423_a() ландшафта.
Вопрос: может кто-нибудь подсказать, в каком именно из приведённых циклов можно найти координаты блоков (x и z)? Моей головы не хватает, чтобы это постичь )))

Задача у меня следующая: хочу поднять уровень океана (параметр byte b0 = 63;) в конкретном биоме. А для этого нужно знать координаты блоков, чтобы по ним определить, что это тот самый биом.

Уверен, что они там есть, потому что, если сложить вариации всех приведённых выше циклов, как раз и получается тот самый заветный массив чанка из 65536 блоков.

Спасибо )
[merge_posts_bbcode]Добавлено: 31.05.2016 10:44:32[/merge_posts_bbcode]

Кстати, кому интересно, связь между методами func_147424_a() и func_147423_a() осуществляется через массив this.field_147434_q имеющий длину 825. Что это есть ума не приложу О_о

Полазил, j3 - индекс блока в массиве, значит:
Код:
         |    z    |       |    x     |      |     y     |
         |||||||||||       ||||||||||||      |||||||||||||
int j3 = i3 + k * 4 << 12 | 0 + j1 * 4 << 8 | k2 * 8 + l2;

P.S. x и z мог местами перепутать xD
 
4,045
63
645
О! Фига се...
Ну, Y точно правильный, а вот X и Z нужно проверить...
Только вот непонятка, нафига тогда параметр k3 от 0 до 3-х, если координаты уже известны?
Не нужно ли это всё ещё умножать на 4?
 
167
3
23
Liahim написал(а):
О! Фига се...
Ну, Y точно правильный, а вот X и Z нужно проверить...
Только вот непонятка, нафига тогда параметр k3 от 0 до 3-х, если координаты уже известны?
Не нужно ли это всё ещё умножать на 4?
не, k3 - это x. Это просто небольшая оптимизация(чтобы индекс блока (j3) каждый раз не пересчитывать, ведь легче к индексу прибавлять 256 в цикле).
[merge_posts_bbcode]Добавлено: 31.05.2016 18:16:30[/merge_posts_bbcode]
Заметь, чтобы Z получить надо
i3 + k * 4 ( i3 и k инкрементится цикломи for 4 раза (0 - 3))
и может получится любое число от 0 до 16.

а X так:
0 + j1 * 4 (j1 инкрементится циклом for 4 раза (0 - 3))
А получится у нас могут только эти числа ------------------------------------> 0 4 6 8
k3 для того и нужен, чтобы достать все числа для X(0 - 16), а не только эти /\.
[merge_posts_bbcode]Добавлено: 31.05.2016 18:34:55[/merge_posts_bbcode]

/\
|
|
|
(Just for update. Pls, ignore this message)
 
Сверху