Pravouhlé 70s

V predošlom tutoriáli Lomená čiara sa lomená čiara šírila do okolia náhodne ľubovoľným smerom. Ak by sme chceli docieliť pravidelnejší vzhľad, obmedzíme smer pohybu len na 4 možnosti: doprava, doľava, nahor, nadol.

A aby sme to vizuálne ozvláštnili, budeme namiesto čiary kresliť farebné kruhy s estetikou 70-tych rokov:

Základný program

Základ je štandardný skeč so setup a draw. Budeme tiež potrebovať:

  • Súradnice x,y, ktoré hovoria, kde sa práve nachádzame.

  • Krok k, ktorý definuje o koľko pixlov sa v každom kroku pohneme

  • Veľkosť v, ktorá definuje veľkosť vykresľovaného krúžku.

float x,y;
float k = 40;
float v = 40;

void setup()
{
  size(800,400);
  background(#C1973B);
  noStroke();
  x = width/2;
  y = height/2;
}

void draw()
{
  circle(x,y,v);
}

Smer

V tutoriáli Lomená čiara bola zmena smeru úplne náhodná v celom ľubovoľnom okolí začiatočných súradníc čiary. Teoreticky to bolo nekonečne veľa možností. Ak sa chceme hýbať len v 4 smeroch, budeme náhodný smer generovať inak. Vyrobíme si globálnu premennú smer a tá bude mať hodnoty: 0 pre smer doprava, 1 pre smer nadol, 2 pre smer doľava, 3 pre smer nahor.

float x,y;
float k = 40;
float v = 40;
int smer = 0;

V závislosti od toho, akú hodnotu má smer, budeme posúvať súradnice x,y doprava, nadol, doľava alebo nahor. Ak je smer rovný 0, zväčšíme x o krok k. Ak je smer rovný 1, zväčšíme y o krok k. A tak ďalej.

Štandardne by sme toto vetvenie začali písať nejak takto:

if (smer == 0)
{
  x = x + d
}
else if (smer == 1)
.....

A postupne by sme takto rozvetvili všetky 4 možnosti smerovania. Ale museli by sme písať vela ifov a veľa elsov. Ukážeme si niečo lepšie.

Vetvenie cez switch

Ak chceme vetviť na základe niečoho, čo má len obmedzený počet hodnôt - ako napríklad naša int premenná s názvom smer - tak je praktické použiť takéto vetvenie:

void draw()
{
  circle(x,y,v);
  
  // Pohyb v smere
  switch(smer)
  {
    case 0:
      x = x + k;
    break;
    
    case 1:
      y = y + k;
    break;
    
    case 2:
      x = x - k;
    break;
    
    case 3:
      y = y - k;
    break;
  }

}

Ako parameter do príkazu switch dáme premennú, na základe ktorej sa chceme prepínať medzi rôznymi variantami. U nás je to smer.

Každú jednu variantu napíšeme ako jeden case. Naša varianty hodnôt premennej smer0,1,2,3, tak pre každú máme jeden case. Každý case je ukončený príkazom break. Na toto pozor, ak by ste zabudli break, program bude robiť hlúposti.

Všimnite si tiež, že všetky prípady case tvoria spolu jeden blok ohraničený zátvorkami {,}.

Krúžky sa teraz rozbehnú doprava, lebo máme smer nastavený na začiatku na hodnotu 0 a nijak ho zatiaľ nemeníme:

Môžete vyskúšať, či vám dobre fungujú aj iné smery, ak zmeníte default hodnotu premennej smer na 1 alebo 2 alebo 3. Tiež si všimnite, že budeme potrebovať dorobiť ešte loopovanie obrazovky, aby nám krúžky neutiekli úplne von.

Podprogramy

Celkovo nás ešte čakajú minimálne dva dlhšie kusy kódy: zmena smeru a loopovanie obrazovky. Aby sme nemali draw dlhočizný a neprehľadný, tak si upraceme kód pomocou podprogramov.

Začneme tým, že celý switch, ktorý nám robí posun premiestnime do podprogramu pohni:

void pohni()
{
  switch(smer)
  {
    case 0:
      x = x + k;
    break;
    
    case 1:
      y = y + k;
    break;
    
    case 2:
      x = x - k;
    break;
    
    case 3:
      y = y - k;
    break;
  } 
}

void draw()
{
  circle(x,y,v);
  
  // Pohyb v smere
  pohni();
}

A rovno si vyrobme aj (zatiaľ prázdne a ničnerobiace) podprogramy pre zaloopovanie obrazovky a zmenu smeru.

void zmen_smer()
{
  
}

void zaloopuj()
{
  
}

void draw()
{
  circle(x,y,v);
  
  // Pohyb v smere
  pohni();
  
  // Zaloopuj obrazovku
  zaloopuj();
  
  // Zmena smeru
  zmen_smer();  
}

Takto štruktúrovaný program sa ľahšie číta, náš draw má len pár riadkov a jednotlivé logické celky: hýbanie súradnicami, ich zacyklenie, keď vylezú z obrazovky, a zmena smeru sú navzájom oddelené, každý vo svojom vlastnom podprograme.

Takáto kozmetická úprava kódu nie je nevyhnutná. Pokojne by ste mohli mať veľký 100 riadkov dlhýdraw, ale je dôležité naučiť sa deliť program na logické celky.

Zmena smeru

Smer chceme meniť tak, aby zahýbal v pravom uhle. Takže ak je terajší smer doprava, tak nový smer bude buď nahor alebo nadol. Ak je terajší smer nadol, tak nový smer bude buď doľava alebo doprava. A tak podobne pre zvyšné dva smery.

Náhodný výber z dvoch možných smerov (napríklad výber z 1 a 3) vieme urobiť takto:

  if (random(1) > 0.5)
  {
    smer = 1;
  }
  else
  {
    smer = 3;
  }
// ^--- toto zatial do kodu nevkladajte, je to len na ukazku.

Príkaz random(1) vygeneruje náhodné desatinné číslo medzi 0 a 1. A keď sa v podmienke if spýtame, či je toto náhodné číslo väčšie ako 0.5, tak s pravdepodobnosťou 50:50 budeme náhodne priraďovať smeru raz hodnotu 1 a inokedy hodnotu 3.

Takto by sme vedeli napísať zmenu smeru pre všetky 4 varianty: Ak je terajší smer rovný 0, vyberáme z 1 a 3. Ak je terajší smer rovný 1, vyberáme z 0 a 2. Ak je terajší smer rovný 2, vyberáme z 1 a 3. Ak je terajší smer rovný 3, vyberáme z 0 a 2.

Použijeme teda opäť vetvenie cez switch na tieto 4 varianty náhodného výberu.

Ale s drobnou optimalizáciou. V prípade 0 a v prípade 2 vyberáme z tých istých nových smerov: 1 a 3. A analogicky v prípade 1 a v prípade 3 vyberáme z tých istých nových smerov: 0 a 2.

Toto vieme vo vetvení switch urobiť tak, že združíme rovnaké varianty case-y pod seba:

void zmen_smer()
{
  switch (smer)
  {
    case 0:
    case 2:
      if (random(1) > 0.5)
      {
        smer = 1;
      }
      else
      {
        smer = 3;
      }
    break;
    
    case 1:
    case 3:
      if (random(1) > 0.5)
      {
        smer = 0;
      }
      else
      {
        smer = 2;
      }
    break;    
  }
}

Smer sa už teraz mení a krúžky sa hýbu do rôznych strán:

Zaloopovanie

Loopovaniu obrazovky, aby súradnice vylezené na jednom okraji von z okna sa objavili na opačnom okraji okna, sme sa už venovali pri Lomenej čiare. Tu pre stručnosť len uvediem kód podprogramu zaloopuj:

void zaloopuj()
{
  if (x < 0)
  {
    x = x + width;
  }
  if (x > width)
  {
    x = x - width;
  }
  if (y < 0)
  {
    y = y + width;
  }
  if (y > height)
  {
    y = y - height;
  }
}

Zmena smeru len občas

V takejto podobe programu sa zmena smeru deje v každom jednom draw. Výsledkom je namiesto súvislejšieho pohybu jedným smerom len chaotický pohyb v najbližšom okolí.

Vyriešime to tak, že zmenu smeru nebudeme vykonávať v každom jednom draw, ale povedzme len každých 5 draw-ov.

Na to si budeme musieť v nejakom počítadle ukladať, koľko krát už prebehol draw.

Vytvorte globálnu premennú pocitadlo a nastavte ho na 0:

float x,y;
float k = 40;
float v = 40;
int smer = 0;
int pocitadlo = 0;

A potom v draw budeme zvyšovať počítadlo o 1 a zavrieme zmenu smeru do podmienky, ktorá nastane len ak je pocitadlo rovné 5:

void draw()
{
  pocitadlo++;
  
  if (pocitadlo == 5)
  {
    // Zmena smeru
    zmen_smer();     
  }
    
  circle(x,y,v);
  
  // Pohyb v smere
  pohni();
  
  // Zaloopuj obrazovku
  zaloopuj();
}

Zmena smeru naozaj nastala až po piatich vykresleniach:

Akurát zmena smeru nastala iba jeden raz a potom už nikdy. Prečo? Pretože pocitadlo na začiatku rástlo, dorástlo do 5, nastala zmena a potom rástlo ďalej na 6,7,8.... a už nikdy znova nenastala situácia, že by bolo rovné 5.

Preto ho musíme zresetovať naspäť na 0 vždy, keď dosiahne 5:

void draw()
{
  pocitadlo++;
  if (pocitadlo == 5)
  {
    // Zmena smeru
    zmen_smer();  
    
    // Zresetuj pocitadlo
    pocitadlo = 0;
  }
  
  circle(x,y,v);
  
  // Pohyb v smere
  pohni();
  
  // Zaloopuj obrazovku
  zaloopuj();  
}

Farby ako zo 70s

Aby sme mali farby pekné ako zo 70s, tak využijeme to, že červené, oranžové a hnedé farby sa vyskytujú na dolnom konci tónov HSB modeli. Preto prepneme skeč do HSB módu:

void setup()
{
  size(800,400);
  background(#C1973B);
  noStroke();
  x = width/2;
  y = height/2;
  colorMode(HSB);
}

A budeme pre každý krúžok generovať farbu, ktorá má H náhodné z rozsahu 0 až 40, sýtosť S má plných 255 a svetlosť B má náhodnú, aby nám vznikali aj tmavé odtiene.

  fill(random(40), 255, random(256));
  circle(x,y,v);

Náhodná dĺžka segmentov

Tým, že zmena smeru sa deje vždy po 5 vykresleniach, tak jednotlivé 5-dielne segmenty tvoria pravidelnú mriežku. Poďme to znáhodniť, aby sa zmena nediala vždy pravidelne po 5 opakovaniach, ale aby počet opakovaní bol nejaké náhodné číslo z rozsahu 5 až 10.

Teraz je 5ka natvrdo v kóde. Prvá zmena, čo musíme urobiť, je vytiahnuť ju do premennej. Nazvime si premennú dlzka:

float x,y;
float k = 40;
float v = 40;
int smer = 0;
int pocitadlo = 0;
int dlzka = 5;

A zmeniť aj podmienku v draw:

void draw()
{
  pocitadlo++;
  if (pocitadlo == dlzka)
  {
    // Zmena smeru
    zmen_smer();  
    
    // Zresetuj pocitadlo
    pocitadlo = 0;
  }
  
  fill(random(40), 255, random(256));
  circle(x,y,v);
  
  // Pohyb v smere
  pohni();
  
  // Zaloopuj obrazovku
  zaloopuj();  
}

A vždy, keď nastane zmena smeru, tak si náhodne určíme novú hodnotu dĺžky:

void draw()
{
  pocitadlo++;
  if (pocitadlo == dlzka)
  {
    // Zmena smeru
    zmen_smer();  
    
    // Zresetuj pocitadlo
    pocitadlo = 0;
    
    // Nova dlzka segmentu
    dlzka = int(random(5,10));
  }
  
  fill(random(40), 255, random(256));
  circle(x,y,v);
  
  // Pohyb v smere
  pohni();
  
  // Zaloopuj obrazovku
  zaloopuj();  
}

Náhodná veľkosť

Posledná úprava je, aby krúžky mali náhodnú veľkosť. Teraz máme veľkosť uloženú v premennej v, ktorá je inicializovaná na hodnotu 40, ale nikde ju už potom nemeníme.

Nastavme jej náhodnú hodnotu tiež pri každej zmene smeru:

void draw()
{
  pocitadlo++;
  if (pocitadlo == dlzka)
  {
    // Zmena smeru
    zmen_smer();  
    
    // Zresetuj pocitadlo
    pocitadlo = 0;
    
    // Nova dlzka segmentu
    dlzka = int(random(5,10));
    
    // Nova nahodna velkost
    v = random(5,40);
  }
  
  fill(random(40), 255, random(256));
  circle(x,y,v);
  
  // Pohyb v smere
  pohni();
  
  // Zaloopuj obrazovku
  zaloopuj();  
}

A to je hotovo:

Tu je finálny skeč:

Last updated