Имеется двухмерный массив который объявляется и заполняется при объявлении. Тут все просто и понятно: Код (Text): byte masRG [8][8] = { {1, 1, 1, 1, 1, 1, 1, 1}, {1, 2, 2, 2, 2, 2, 2, 1}, {1, 2, 1, 1, 1, 1, 2, 1}, {1, 2, 1, 0, 3, 1, 2, 1}, {1, 2, 1, 3, 0, 1, 2, 1}, {1, 2, 1, 1, 1, 1, 2, 1}, {1, 2, 2, 2, 2, 2, 2, 1}, {1, 1, 1, 1, 1, 1, 1, 1} }; Но вот потребовалось иметь несколько таких массивов с разными данными, но с возможностью доступа к ним из цикла, то есть имя должно быть одно, индексы разные. Первое что пришло в голову - создать структуру с двумерным массивом внутри и создать массив из таких структур: Код (Text): struct spryteRG { byte mRG [8][8]; }; spryteRG spryte [2]; Все замечательно, казалось бы, но вот как заполнить из скетча элементы этого массива массивов? Интуитивно не получилось, гуглование пока что ответа не дало, но чувствую, что он где то в орфографии: Код (Text): spry[0] = { <-- не работает {1, 1, 1, 1, 1, 1, 1, 1}, {1, 2, 2, 2, 2, 2, 2, 1}, {1, 2, 1, 1, 1, 1, 2, 1}, {1, 2, 1, 0, 3, 1, 2, 1}, {1, 2, 1, 3, 0, 1, 2, 1}, {1, 2, 1, 1, 1, 1, 2, 1}, {1, 2, 2, 2, 2, 2, 2, 1}, {1, 1, 1, 1, 1, 1, 1, 1} }; Или я зверски путаю инициализацию массива с его заполнением?
Да и вообще n-мерный при необходимости. Можно работать с одномерным массивом как с n-мерным, пересчитывая вычисляя индекс в массиве по n "псевдоиндексов". В памяти они всё равно хранятся линейно. Можно пойти дальше и определять размерность массива во время выполнения. Решается или динамическим двусвязным (вообще даже n-связным) списком или динамическим буфером.
Синтаксис языка так не позволяет делать. При инициализации заполняйте весь массив полностью, а не только один элемент. После инициализации при объявлении больше такое присваивание не работает.
Проблема не в том, чтобы сделать 3х мерный массив, а в том, чтобы его наглядно заполнить. Сейчас вот картинку практически видно, а какая каша получится при трех измерениях? Я это уже понял, очень жаль. То есть заполнить массив в таком виде можно только один раз. (
Если я правильно понял задачу, то можно сделать примерно примерно так: Код (Text): #define SET_SPRITE(to,from) memcpy(to,from,sizeof(from)); typedef byte spryteRG [8][8]; spryteRG spryte1={ {1, 1, 1, 1, 1, 1, 1, 1}, {1, 2, 2, 2, 2, 2, 2, 1}, {1, 2, 1, 1, 1, 1, 2, 1}, {1, 2, 1, 0, 3, 1, 2, 1}, {1, 2, 1, 3, 0, 1, 2, 1}, {1, 2, 1, 1, 1, 1, 2, 1}, {1, 2, 2, 2, 2, 2, 2, 1}, {1, 1, 1, 1, 1, 1, 1, 1} }; spryteRG spryte2= { {5, 1, 1, 1, 1, 1, 1, 1}, {1, 2, 2, 2, 2, 2, 2, 1}, {1, 2, 1, 1, 1, 1, 2, 1}, {1, 2, 1, 0, 3, 1, 2, 1}, {1, 2, 1, 3, 0, 1, 2, 1}, {1, 2, 1, 1, 1, 1, 2, 1}, {1, 2, 2, 2, 2, 2, 2, 1}, {1, 1, 1, 1, 1, 1, 1, 1} }; spryteRG spryte[2]; А далее по месту, где надо задать значение элемента массива спрайтов, делать Код (Text): SET_SPRITE(spryte[0],spryte2); Естественно, в жизни вместо spryte1 и spryte2 надо использовать осмысленные имена, чтобы было наглядно видно, какой именно спрайт был присвоен элементу массива. И сами спрайты сделать разными.
То есть проинициализировать одноразовые двухмерные массивы, а потом переносить их в массивы с индексами для автоматизированного использования?
Может я чего-то недопонимаю в постановке задачи? Я полагал, что речь о динамическом присваивании спрайтов элементам массива. Например, формирование последовательности спрайтов на основании данных из последовательного порта. Если же надо просто изначально инициализировать массив спрайтов, а потом его пользовать, не изменяя, то все делается одним объявлением Код (Text): byte spryte [2][8][8]={ {{1, 1, 1, 1, 1, 1, 1, 1}, {1, 2, 2, 2, 2, 2, 2, 1}, {1, 2, 1, 1, 1, 1, 2, 1}, {1, 2, 1, 0, 3, 1, 2, 1}, {1, 2, 1, 3, 0, 1, 2, 1}, {1, 2, 1, 1, 1, 1, 2, 1}, {1, 2, 2, 2, 2, 2, 2, 1}, {1, 1, 1, 1, 1, 1, 1, 1}}, {{5, 1, 1, 1, 1, 1, 1, 1}, {1, 2, 2, 2, 2, 2, 2, 1}, {1, 2, 1, 1, 1, 1, 2, 1}, {1, 2, 1, 0, 3, 1, 2, 1}, {1, 2, 1, 3, 0, 1, 2, 1}, {1, 2, 1, 1, 1, 1, 2, 1}, {1, 2, 2, 2, 2, 2, 2, 1}, {1, 1, 1, 1, 1, 1, 1, 1}} };
Пятерка в середине имеет какой то смысл? Наверное, трехмерный массив действительно единственно правильное решение для удобного заполнения спрайтов изнутри скетча.
Код (Text): typedef uint8_t cubeblock_t[64]; cubeblock_t cubedata; void plotCube(cubeblock_t& data) { uint8_t ptr = 0; // layer 0 data[ptr++] = B11111111; data[ptr++] = B10000001; data[ptr++] = B10000001; data[ptr++] = B10000001; data[ptr++] = B10000001; data[ptr++] = B10000001; data[ptr++] = B10000001; data[ptr++] = B11111111; // layer 1-6 for (uint8_t lay=1; lay<=6; lay++) { data[ptr++] = B10000001; data[ptr++] = B00000000; data[ptr++] = B00000000; data[ptr++] = B00000000; data[ptr++] = B00000000; data[ptr++] = B00000000; data[ptr++] = B00000000; data[ptr++] = B10000001; } // layer 7 data[ptr++] = B11111111; data[ptr++] = B10000001; data[ptr++] = B10000001; data[ptr++] = B10000001; data[ptr++] = B10000001; data[ptr++] = B10000001; data[ptr++] = B10000001; data[ptr++] = B11111111; } void plotStar(cubeblock_t& data) { uint8_t ptr = 0; // layer 0 data[ptr++] = B10000001; data[ptr++] = B00000000; data[ptr++] = B00000000; data[ptr++] = B00011000; data[ptr++] = B00011000; data[ptr++] = B00000000; data[ptr++] = B00000000; data[ptr++] = B10000001; // layer 1 data[ptr++] = B00000000; data[ptr++] = B01000010; data[ptr++] = B00000000; data[ptr++] = B00011000; data[ptr++] = B00011000; data[ptr++] = B00000000; data[ptr++] = B01000010; data[ptr++] = B00000000; // layer 2 data[ptr++] = B00000000; data[ptr++] = B00000000; data[ptr++] = B00100100; data[ptr++] = B00011000; data[ptr++] = B00011000; data[ptr++] = B00100100; data[ptr++] = B00000000; data[ptr++] = B00000000; // layer 3 data[ptr++] = B00000000; data[ptr++] = B00000000; data[ptr++] = B00011000; data[ptr++] = B00111100; data[ptr++] = B00111100; data[ptr++] = B00011000; data[ptr++] = B00000000; data[ptr++] = B00000000; // layers 4-7 uint8_t sptr = ptr; for (uint8_t lay = 4; lay <= 7; lay++) { for (uint8_t row = 0; row < 8; row++) { data[ptr++] = data[--sptr]; } } }
Код (Text): void plotStar2(cubeblock_t& data) { uint8_t ptr = 0; // layer 0 data[ptr++] = B00010010; data[ptr++] = B10000000; data[ptr++] = B00000000; data[ptr++] = B00011001; data[ptr++] = B10011000; data[ptr++] = B00000000; data[ptr++] = B00000001; data[ptr++] = B01001000; // layer 1 data[ptr++] = B00000000; data[ptr++] = B01001010; data[ptr++] = B00000000; data[ptr++] = B01011000; data[ptr++] = B00011010; data[ptr++] = B00000000; data[ptr++] = B01010010; data[ptr++] = B00000000; // layer 2 data[ptr++] = B00000000; data[ptr++] = B00000000; data[ptr++] = B00101100; data[ptr++] = B00111000; data[ptr++] = B00011100; data[ptr++] = B00110100; data[ptr++] = B00000000; data[ptr++] = B00000000; // layer 3 data[ptr++] = B00000000; data[ptr++] = B00000000; data[ptr++] = B00011000; data[ptr++] = B00111100; data[ptr++] = B00111100; data[ptr++] = B00011000; data[ptr++] = B00000000; data[ptr++] = B00000000; // layers 4-7 uint8_t sptr = ptr; for (uint8_t lay = 4; lay <= 7; lay++) { for (uint8_t row = 0; row < 8; row++) { data[ptr++] = data[--sptr]; } } }
Код (Text): #define N_FRAMES 10 #define DELAY_FRAME 20 cubeblock_t animation[N_FRAMES]; void show(cubeblock_t& data) { uint8_t ptr = 0; uint8_t mask = 1; for (uint8_t lay = 0; lay < 8; lay++) { ShiftOut(PIN_LAYER_CLK, PIN_LAYER_DATA, mask); digitalWrite(PIN_LAYER_LATCH, LOW); // 595 digitalWrite(PIN_LAYER_LATCH, HIGH); // 595 mask <<= 1; for (uint8_t row = 0; row < 8; row++) { ShiftOut(PIN_ROW_CLK, PIN_ROW_DATA, data[ptr++]); } digitalWrite(PIN_ROW_LATCH, LOW); // 595 digitalWrite(PIN_ROW_LATCH, HIGH); // 595 } } void initAnimation(cubeblock_t *data, int frames) { for (int i = 0; i < frames; i++) { // plotFrame(i, data[i]); } } void playAnimation(cubeblock_t *data, int frames, int frame_delay) { for (int i = 0; i < frames; i++) { show(data[i]); delay(frame_delay); } } void test() { initAnimation(animation, N_FRAMES); playAnimation(animation, N_FRAMES, DELAY_FRAME); }
Это первый способ с цветоразделением, который я применил в первую очередь. Он неудобен тем, что картинку труднее составить, менее наглядно получается. Замеры показали, что единый массив обрабатывается медленнее всего на 8%, что не критично, 240 фпс против 260.
Никакого особенного смысла. Всего лишь одно из чисел, забиваемых в массив. Сам не знаю, как туда пятерка затесалась, заполнение спрайта просто копировалось одно и то же.
Пришел к выводу, что для данной задачи действительно оптимально использовать трехмерный массив. Вот например оформление двух кадров: Код (Text): byte masRG [2][8][8] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 1, 1, 2, 0, 0, 0, 0, 2, 1, 1, 2, 0, 2, 2, 0, 2, 1, 1, 2, 0, 2, 2, 0, 2, 1, 1, 2, 0, 2, 2, 0, 2, 1, 1, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 2, 2, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 2, 1, 2, 2, 2, 2, 2, 2, 1, 1, 2, 0, 0, 0, 0, 2, 1, 2, 2, 0, 3, 3, 0, 2, 2, 2, 2, 0, 3, 3, 0, 2, 2, 1, 2, 0, 3, 3, 0, 2, 1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 1, 1, 2, 2, 1, 1, 2 }; Работает довольно быстро, fps на UNO без дополнительной нагрузки порядка 260.
Пока что одну его плоскость разве что. ) Хотя, думаю, что принцип одинаков, только массив будет четырехмерный и не уверен, что Ардуино его потянет.
Пример готового скетча с 4 кадрами (спрайтами) и анимацией из серии "все любят гипножабу". Всем спасибо за советы! Код (Text): #define DATA_PIN 13 #define LATCH_PIN 12 #define CLOCK_PIN 11 #define kadrTime 100 // мс на кадр #define kadrov 4 // колво кадров byte masRG [4][8][8] = { 0, 0, 0, 0, 0, 0, 0, 0, // 1 0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 3, 3, 3, 3, 1, 0, 0, 1, 3, 2, 2, 3, 1, 0, 0, 1, 3, 2, 2, 3, 1, 0, 0, 1, 3, 3, 3, 3, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, //2 1, 3, 3, 3, 3, 3, 3, 1, 1, 3, 2, 2, 2, 2, 3, 1, 1, 3, 2, 0, 0, 2, 3, 1, 1, 3, 2, 0, 0, 2, 3, 1, 1, 3, 2, 2, 2, 2, 3, 1, 1, 3, 3, 3, 3, 3, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 3, 3, 3, 3, 3, 3, 3, // 3 3, 2, 2, 2, 2, 2, 2, 3, 3, 2, 0, 0, 0, 0, 2, 3, 3, 2, 0, 1, 1, 0, 2, 3, 3, 2, 0, 1, 1, 0, 2, 3, 3, 2, 0, 0, 0, 0, 2, 3, 3, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2, 2, 2, // 4 2, 0, 0, 0, 0, 0, 0, 2, 2, 0, 1, 1, 1, 1, 0, 2, 2, 0, 1, 3, 3, 1, 0, 2, 2, 0, 1, 3, 3, 1, 0, 2, 2, 0, 1, 1, 1, 1, 0, 2, 2, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2 }; long unsigned int ts; // таймер кадров byte kadrV; // видимый кадр // для подсчета fps //long unsigned int ttt; //unsigned int sss = 0; //unsigned int fps = 0; void setup() { pinMode(DATA_PIN, OUTPUT); pinMode(CLOCK_PIN, OUTPUT); pinMode(LATCH_PIN, OUTPUT); kadrV = 0; ts = millis(); // Serial.begin(9600); } void loop() { if ((millis() - ts) >= kadrTime) { // циклическое чередование кадров kadrV++; if (kadrV >= kadrov) kadrV = 0; ts = millis(); } setKadr_RG(masRG, kadrV); // отрисовка кадра // подсчет fps // fps++; // if ((millis() - ttt) > 2000) { // Serial.println(fps); // while (true); // } } void setKadr_RG (byte mRG[2][8][8], byte kadr) { // отрисовка кадра byte bytSTR; byte mR; byte mG; for (byte s = 0; s <= 7; s++) { bytSTR = 0; bitSet(bytSTR, s); for (byte i = 0; i <= 7; i++) { bitWrite(mR, i, !(bitRead(mRG[kadr][s][i], 0))); bitWrite(mG, i, !(bitRead(mRG[kadr][s][i], 1))); } digitalWrite(LATCH_PIN, LOW); writeByte(bytSTR); writeByte(mR); writeByte(mG); digitalWrite(LATCH_PIN, HIGH); } } byte writeByte(byte byteW) { // аналог shiftIn, работает чуть быстрее for (int i = 7; i >= 0; i--) { digitalWrite(DATA_PIN, (bitRead(byteW, i))); digitalWrite(CLOCK_PIN, HIGH); digitalWrite(CLOCK_PIN, LOW); } }