Individální projekty MPOA

Mikroprocesory s architekturou ARM

Uživatelské nástroje

Nástroje pro tento web


2016:brick-game

Zadání

Realizujte hru Brick game racing na 8×8 RGB LED matici s použitím desky FRDM-KL25Z v online vývojovém prostředí mbed.com. Ovládání hry zajistěte pomocí akcelerometru. Při realizaci využijte barevnost matice.

Úvod

Cílem tohoto projektu bylo vytvořit hru podobnou „motokárám“, což je jedna z her z kdysi populárního herního zařízení Brick Game 9999 in 1, viz video níže.

Z videa je patrné, že motokára hráče může nabývat pouze dvou poloh, díky kterým má možnost vyhýbat se motokárám počítače, které jí stojí v cestě. Dále si lze povšimnout, že rychlost posouvání motokár počítače a rychlost posouvání svodidel na okrajích displaye je různá.
Pohyb motokáry hráče je v našem případě řešen pomocí akcelerometru. Polohy motokár protihráče jsou nastaveny pomocí pevného binárního pseudonáhodné sekvence.
Vzhledem k tomu, že použitá RG LED matice se svým rozlišením nemůže rovnat displayi původního zařízení, bylo nutné návrh trochu upravit, viz obrázek níže.

Po této úpravě se nám z „motokár“ staly „autíčka“. Dále zde byla snaha, o využití barevnosti matice, pro zlepšení celkového dojmu z hry.

Hardware

Kromě již zmíněné desky FRDM-KL25Z byla k realizaci ještě potřeba RG LED 8×8 matice. Ta je propojena s KL25Z pomocí redukce, kterou v předešlých letech vytvořil student Matěj Hojdík. Více informací o redukci naleznete (Redukce matice 8x8 pro KL25Z).

Software

Software je psaný v online vývojovém prostředí mbed.org. Celý zdrojový kód je k distpozici ZDE.
Vzhledem k poměrně podrobnému okomentování ve skriptu budu uvádět pouze stručný popis jednotlivých funkcí.
Volání funkcí, které zajišťují pohyb předem definovaných struktur jsem zvolil pomocí časovačů, protože ve while smyčce se mi to zdálo příliš komplikované. Časovač jsem si definoval do třech proměnných, pro lepší přehlednost

Main

Funkce main v podstatě řídí celý algoritmus. Nejprve se provede inicializace, počátečního polohy autíčka hráče a svodidel (autíčka počítače ještě nejsou v inicializačním obraze přítomna). Následně se nastaví časovače. Pomocí nich je realizováno nejen samotné zobrazování, ale také pohyb předem nastavených struktur – tj. konstantní pohyb svodidel a aut počítače a počáteční poloha autíčka hráče, definovaná vpravo.
Následuje nekonečná smyčka while, ve které je realizováno snímání akcelerometru, na jehož základě se pak mění poloha autíčka hráče. Dále je zde řešena i možnost kolize. V případě, že se tak stane, dojde k zmrazení matice, místo autíčka hráče se objeví jakýsi myšlený „vrak autíčka“ a celý program se resetuje.

int main() 
{  
    Inicializace();                         //iniacalizace počátečních podmínek
    disptick.attach(&Zobraz, 0.002);        //nastavení časovače pro rozsvicování displaye
    tick.attach(&SvodidlaPohyb, 1);         //nastavení časovače pro volání funkce zabezpečující pohyb svodidel
    tick2.attach(&AutaPohyb, 0.25);         //nastavení časovače pro volání funkce zabezpečující pohyb aut počítače
    while(1) 
    { 
        Pohyb();                                //ovládání pozice auta hráče pomocí akcelerometru
        Kolize();                               //detekce kolize auta hráče s auty počítače  
    }
}

Zozbraz

Tato funkce je jedna z nejdůležitějších, protože má na starost celé to kouzlo zobrazování. Pracuje s předem definovanými poli 8×8 pro zelené a červené LEDky, jejichž aktuální stav zobrazuje po řádcích. Zbylé funkce tedy již pracují pouze s definovanými poli, jejichž změnou vytvářejí výsledný obraz. Volání funkce je řešeno přes časovač každou 0,002 s.

Příklad funkce pro práci s polem zelených LED.

// pracovní pole pro matici zelených LED                   
int poleZeleny[8][8] = {                                       
                   {0, 0, 0, 0, 0, 0, 0, 0},             
                   {0, 0, 0, 0, 0, 0, 0, 0},    
                   {0, 0, 0, 0, 0, 0, 0, 0},    
                   {0, 0, 0, 0, 0, 0, 0, 0},    
                   {0, 0, 0, 0, 0, 0, 0, 0},    
                   {0, 0, 0, 0, 0, 0, 0, 0},    
                   {0, 0, 0, 0, 0, 0, 0, 0},    
                   {0, 0, 0, 0, 0, 0, 0, 0},  };
 
void Zobraz(void)
{
        static int line = 0;        //proměná inicializující prnví řádek
 
        AdresRadku(line);           //adresování řádků pomocí funkce AdresRadku() 
 
        //Rozsvicování LEDek je zajištěno jejich uzeměním - tedy, když je na řídící vstup přivedena 0.
        //Z důvodu lepší oirentace při práci bylo využito negace, aby v následujícím algoritmu rozsvícení LEDek 
        //způsobovalo číslo 1.
 
        //rozsvicování zelených LED
        zelena0 =! poleZeleny[line][0];      
        zelena1 =! poleZeleny[line][1];
        zelena2 =! poleZeleny[line][2];
        zelena3 =! poleZeleny[line][3];
        zelena4 =! poleZeleny[line][4];
        zelena5 =! poleZeleny[line][5];
        zelena6 =! poleZeleny[line][6];
        zelena7 =! poleZeleny[line][7];
 
        wait(0.001);                //zpožděni-aby se to stihlo rozsvitit
        line++;                     //přepis proměnné na další řádek
        if(line == 8)               //v případě,že dojde na konec,jede se od zacatku
        {              
                line = 0;
        }
}

Inicializace

Tato funkce proběhne pouze jednou na začátku programu nebo pokaždé při restartu zařízení. Má za úkol přenastavit pracovní pole tak, aby při každém startu (restartu) začínala hra za stejných počátečních podmínek (ze stejného počátečního obrazu). Nastaví tedy počáteční polohu oranžových svodidel a zeleného autíčka hráče.

void Inicializace(void){
    int col;
    int row;
/*------------inicializace svodidel--------------*/    
    for(col=0;col < 8; col = col+7)
    {    
        for(row = 0; row < 8; row++)
        {  
            if(row == 0 ||row == 4)
            {      
                poleCerveny[row][col]=0; 
                poleZeleny[row][col]=0;
            }                                                      
            else
            {
               poleCerveny[row][col]=1;
               poleZeleny[row][col]=1;
            } 
        }
    } 
/*----inicializace auta hráče - pozice napravo---*/
        poleZeleny[4][4] = 1;
        poleZeleny[5][4] = 1;
        poleZeleny[6][4] = 1;
        poleZeleny[4][5] = 1;
        poleZeleny[5][5] = 1;
        poleZeleny[6][5] = 1;   
}

SvodidlaPohyb

Úkolem této funkce je zajištění konstantního pohybu oranžových svodidel na okrajích matice. Pohyb je dán rychlostí 1 pixel / 1 sekundu (podle nastavení časovače, který tuto funkci volá).

void SvodidlaPohyb(void)
{
    int col;
    int row;
    int pom1; //pomocná proměnná    
 
    pom1 = poleCerveny[7][0]; //uloží hodnotu v poslendím 8. radku
 
    //posune svodidla o 1 pozici směrem dolu při každém zavolání funkce    
    for(col=0;col < 8; col = col+7)     //pracuje pouze v 1. a v posledním sloupci
    {
        for(row = 7; row > 0; row--)    //7. až 1. řádek přepíše na 8. až 2.
        {
          poleCerveny[row][col] = poleCerveny[row-1][col]; 
          poleZeleny[row][col] = poleZeleny[row-1][col];     
        }
    }   
    //poslední řádek přepíše jako 1. 
    poleCerveny[0][0] = pom1;
    poleCerveny[0][7] = pom1;
    poleZeleny[0][0] = pom1;
    poleZeleny[0][7] = pom1;
}

AutaPohyb

Tato funkce generuje auta počítače nalevo nebo napravo, v závislosti na hodnotě binární pseudonáhodné sekvence. Kromě rozsvicování LEDek v místech, kde se nachází auta počítače, však funkce musí dále zhasínat ty LEDky, kudy auto počítače prošlo. To řeší pomocí pomocných proměnných, které si pamatují předcházející polohy aut počítače, v závislosti na tom, zda byli vpravo či vlevo. Funkce je opět volána pomocí časovače každou 0,25 s.

Tato funkce je příliš dlouhá a komplexní na to, abych ji sem dával nebo jenom její část. Hlavní myšlenka je však stejná jako u předchozí funkce SvodidlaPohyb(), i když je tato funkce podstatně komplikovanější.

Pohyb

Tato funkce je volána v nekonečné smyčce a má na starost snímání Y-ové osy akcelerometru. Při překročení určitého prahu náklonu pak tato funkce rovnou zajistí změnu polohy autíčka počítače, ve kterém autíčko setrvá, dokud nedojde opět k překročení prahu náklonu (vlevo či vpravo).

Ukázka funkce pro náklon vpravo

void Pohyb(void)
{   
    prah = 0.3;          //nastavení prahu, vymezující úhel detekce
    ay = float(acc.getAccY());          //odečítání Y-ové osy akcelerometru
 
 
    if((ay > prah))     //náklon vpravo
    {       
        poloha =4;  //pravý (5.) sloupec
        //rožnout auto hráče napravo
        poleZeleny[4][poloha] = 1;
        poleZeleny[5][poloha] = 1;
        poleZeleny[6][poloha] = 1;
        poleZeleny[4][poloha+1] = 1;
        poleZeleny[5][poloha+1] = 1;
        poleZeleny[6][poloha+1] = 1;
        //zhasnout auto hráče vlevo
        poleZeleny[4][poloha-2] =! 1;
        poleZeleny[5][poloha-2] =! 1;
        poleZeleny[6][poloha-2] =! 1;
        poleZeleny[4][poloha-1] =! 1;
        poleZeleny[5][poloha-1] =! 1;
        poleZeleny[6][poloha-1] =! 1;
    } 
}

Kolize

Tato funkce hlídá, jestli nejsou hodnoty červeného a zeleného pole v určitých definovaných místech shodné. Pokud ano, tak se jedná o kolizi autíček. Následné pak funkce zmrazí všechny pohyby, zobrazí místo autíčka hráče „vrak autíčka“ a zavolá funkci Reset.

void Kolize(void)
{  
    if (poleZeleny[4][poloha] == poleCerveny[4][poloha] || poleZeleny[6][poloha] == poleCerveny[6][poloha])
    {
        //při detekci kolize dojde ke zmražení všech pohybů
        tick.detach(); 
        tick2.detach();
 
    //-----zobrazí se oranžový vrak auta-----//    
        poleZeleny[4][poloha-1] = 1;
        poleZeleny[5][poloha] = 1;
        poleZeleny[6][poloha-1] = 1;
        poleZeleny[4][poloha+2] = 1;
        poleZeleny[5][poloha+1] = 1;
        poleZeleny[6][poloha+2] = 1;
 
        poleCerveny[4][poloha-1] = 1;
        poleCerveny[5][poloha] = 1;
        poleCerveny[6][poloha-1] = 1;
        poleCerveny[4][poloha+2] = 1;
        poleCerveny[5][poloha+1] = 1;
        poleCerveny[6][poloha+2] = 1;
 
        wait(2); //zachování tohoto statického obrazu na dobu 2s
        Reset(); // zavolá funkci reset
    }
}

Reset

Z názvu funkce vyplývá, že tato funkce má na starost restart a reinicializaci počátečních podmínek a hodnot. Nejprve však celou RG LED matici vybarví na 2s do červena a následně provede reinicializaci a zruší zmražení všech pohybů.

void Reset(void)
{ 
//---na 2s se zobrazí pouze červené LED na celé ploše matice---//    
    for(col = 0; col < 8; col++)
    {
        for(row = 0; row < 8; row++)
        {
            poleZeleny[row][col]=0;
            poleCerveny[row][col]=1; 
        }
    } 
    wait(2);
 
//--------proběhne resetovací fáze--------//
//zhasnutí celé LED matice
    for(col = 0; col < 8; col++)
    {
        for(row = 0; row < 8; row++)
        {
            poleCerveny[row][col]=0; 
        }
    } 
//re-inicializace    
    Inicializace();
    pocitadlo = 0;
    rowAuto = 0;
    poloha =4;
 
//zrušení zmražení všech pohybů   
    tick.attach(&SvodidlaPohyb, 1);
    tick2.attach(&AutaPohyb, 0.25);
}

Video

Aby nedošlo k jisté mistifikaci, chtěl bych podotknout, že hra ve videu začíná od restartu (červená plocha na počátku), z čehož je patrná následná reinicializace a opětovné spuštění hry.

Závěr

Cílem tohoto projektu bylo zrealizovat hru z jednoho kdysi populárního herního zařízení pomocí RG LED 8×8 matice a vývojové desky FRDM KL25Z. Zadání se podařilo splnit v plném požadovaném rozsahu. Byla využita barevnost matice. Ovládání bylo zrealizované pomocí akcelerometru.
Hra je poměrně jednoduchá na ovládání a celkový chod programu byl vyladěn, aby nedošlo k nějakým zásadním chybám. Šlo by však uvažovat o následujících modifikacích. Hra je aktuálně jednoúrovňová, což znamená, že auta počítače se pohybují konstantní rychlostí. Bylo by však možné tuto rychlost měnit například pomocí slideru, který je zakomponován v desce KL25Z. Také by šlo nahradit psedonáhodnou binární sekvenci přímo generováním náhodných jedniček a nul pomocí funkce rand().

2016/brick-game.txt · Poslední úprava: 2017/01/13 17:49 autor: Viktor Typovský