Tvoříme hru had v jazyce C - 2.Díl

C++ Pokročilé konstrukce Zdrojákoviště Tvoříme hru had v jazyce C - 2.Díl

Tak z minulého tutoriálu o vytvoření hada v céčku bychom měli mít soubory main.c, funkce.h a had.h. Ve funkcích by měly být barvičky, delay, MyGetch, Vymaz a funkce gotoxy. V souboru had.h mělo být následující.

#include "funkce.h"

#define START_DELKA_HADA 4      /* velikost hada pri vykresleni */
#define START_X 25              /* startovni souradnice hada na ose X */
#define START_Y 12              /* startovni souradnice hada na ose Y */
#define RADKY 25                /* pocet radku v hracim poli */
#define SLOUPECKY 80            /* pocet sloupecku v hracim poli */

void RAM_ZED();
void vykresli_hada_start(void);
void pohybuj_hada();
void vykresli_hlavicku(void);
void smaz_posledni(void);
void posun_hodnoty(void);
void bodiky();
void vypis_bodiky();
void novy_mysky(void);
void konec(int udalost);
void exit_fce(void);

Teď si v souboru had.h vytvoříme struktury :

typedef struct
{
  int x;
  int y;
} Souradnice_hada;
struct
{
  int x;
  int y;
} mysky;

Pro souřadnice hada a myšek, které budeme žrát.

Dále...

Souradnice_hada *alokuj_delku_hada(void);
Souradnice_hada *realokuj_delku_hada(void);

int hraci_pole[RADKY][SLOUPECKY],body,hadova_rychlost,i,j,mezernik = 0,start;
short n = START_DELKA_HADA; /* Delka hada pri vykresleni na startu */
short pauza,barva_hada;
char pocatecni_smer;
Souradnice_hada *sou_had;
konecprosdraca = 1;

To je alokace hada a nějaké globální proměnné, které by být v programu ideálně neměly, ale již jsem to nechtěl předělávat. :) (K funkci pro alokaci se dostaneme dále v kódu).

void RAM_ZED()
{
    /* Vyplni hraci pole v okrajich */
    for(i = 0; i < 80; i++)
    {
        hraci_pole[0][i] = 1;
        hraci_pole[24][i] = 1;
    }
    for(j = 0; j < 25; j++)
    {
        hraci_pole[j][0] = 1;
        hraci_pole[j][79] = 1;
    }
    /* Vykresli zed */

    for(i = 0; i < 25; i++)
    {
        barva(2);
        gotoxy(0,i);
        putc('1',stdout);
        gotoxy(79,i);
        putc('1',stdout);
    }
    for(i = 0; i < 80; i++)
    {
        barva(2);
        gotoxy(i,0);
        putc('1',stdout);
        gotoxy(i,24);
        putc('1',stdout);
    }

}

Vykreslíme si rámeček jak v dvourozměrném poli, tak jen tak "barvičkově" trošku drastickým způsobem. (Mám to takhle, protože když jsem nacpal víc cyklů dohromady, tak to bylo značně pomalejší).

Souradnice_hada *alokuj_delku_hada(void)
{
  Souradnice_hada *sou_had_start;
  sou_had_start = (Souradnice_hada  *) malloc((START_DELKA_HADA+1) * sizeof(Souradnice_hada));
  if (sou_had_start == NULL)
  {
    printf("Nelze provest alokaci hada!");
    exit(1);
  }
  return sou_had_start;
}
Souradnice_hada *realokuj_delku_hada(void)
{
  Souradnice_hada *sou_had_zvetsen;

  sou_had_zvetsen = (Souradnice_hada *) realloc(sou_had, (n+1) * (sizeof(Souradnice_hada)));

  if (sou_had_zvetsen == NULL)
  {
    printf("Nelze provest alokaci hada!");
    exit(1);
  }
  return sou_had_zvetsen;
}

Alokace a realokace paměti hada... není co dodat :) kdo se v tom nevyzná tak šup do mého tutoriálu o alikaci paměti.

void vykresli_hada_start(void)
{
  for(i = START_X; i < (START_DELKA_HADA+START_X); i++)
  {
    barva(barva_hada);
    sou_had[i-START_X].x = i;
    sou_had[i-START_X].y = START_Y;
    gotoxy(i,START_Y);
    putc('0', stdout);
  }
}

Funkce pro vykreslení hada v poli. Teď trošku delší funkce pro pohyb našeho hada.

void pohybuj_hada()
{

while (konecprosdraca > 0)

  {
    do
    {
      /* Nastavi hlavicku na smer XY */
      switch(pocatecni_smer)
      {
        case 'u' : sou_had[n].x = sou_had[n-1].x;
                   sou_had[n].y = sou_had[n-1].y-1;
                   break;
        case 'd' : sou_had[n].x = sou_had[n-1].x;
                   sou_had[n].y = sou_had[n-1].y+1;
                   break;
        case 'r' : sou_had[n].x = sou_had[n-1].x+1;
                   sou_had[n].y = sou_had[n-1].y;
                   break;
        case 'l' : sou_had[n].x = sou_had[n-1].x-1;
                   sou_had[n].y = sou_had[n-1].y;
                   break;
      }
      /* Kontrola narazu do zdi */
      if (hraci_pole[sou_had[n].y][sou_had[n].x])
        konec(2);

      /* kontrola kousnuti */
      for(i = 0; i < n-3; i++)
        if ((sou_had[n].x == sou_had[i].x) && (sou_had[n].y == sou_had[i].y))
          konec(1);


      vykresli_hlavicku();
      /* Kdyz had sezere mysku */
      if ((sou_had[n].x == mysky.x) && (sou_had[n].y == mysky.y))
        bodiky();
      else /* had nesezral mysku */
      {
        smaz_posledni();
        posun_hodnoty();
      }
      delay(hadova_rychlost);

    } while (!kbhit());

    int key;
    key = MyGetch();
    if (key) {
      switch (key) {
        case 27: // escape
        konecprosdraca = 0;
        break;
        case 328:/* sipka nahoru */
          if (pocatecni_smer != 'd') pocatecni_smer = 'u';
          break;
        case 336:/* sipka dolu */
          if (pocatecni_smer != 'u') pocatecni_smer = 'd';
          break;
        case 333: /* sipka vpravo*/
          if (pocatecni_smer != 'l') pocatecni_smer = 'r';
          break;
        case 331:/* sipka vlevo */
          if (pocatecni_smer != 'r') pocatecni_smer = 'l';
          break;
        case 32:
          if (!pauza)
          {
            pauza = 1;
            do{} while (!kbhit());
          }
          else
            pauza = 0;
          break;
        case 13:
             break;
        default:
          continue;
        }
      }
  }
}

Není co dodat, určuje směr pohybu hada a co se stane, když zmáčknu nějakou klávesu. Exkluzivně pro Sdraca jsem upravil nekonečný cyklus, za který by mě sežral za živa. :D a A proto :

while (konecprosdraca > 0)

No a nyní zbytek našich funkcí :

void smaz_posledni(void)
{
  gotoxy(sou_had[0].x,sou_had[0].y);
  barva(-1);
  putc(219, stdout);
}
void posun_hodnoty(void)
{
  for(i = 0; i < n; i++)
  {
    sou_had[i].x = sou_had[i+1].x;
    sou_had[i].y = sou_had[i+1].y;
  }
}
void vykresli_hlavicku(void)
{
  barva(barva_hada);
  gotoxy(sou_had[n].x,sou_had[n].y);
  putc('0', stdout);
}
void vypis_bodiky()
{
  barva(1);
  gotoxy(77,0);
  printf("%d", body);
}
void bodiky()
{
  barva(1);
  body++;
  gotoxy(77,0);
  printf("%d", body);
  n++; /* delka hada ++ */
  sou_had = realokuj_delku_hada();
  novy_mysky();
}
void novy_mysky(void)
{
  mysky.x = (rand() % 79) + 1;
  mysky.y = (rand() % 24) + 1;
  /* kdyz se mysky obevi v prekazce nebo v hadovi tak se vykesli nove */
  for(i = 0; i < (n-1); i++)
    if (((sou_had[i].x == mysky.x) && (sou_had[i].y == mysky.y)) || (hraci_pole[mysky.y][mysky.x]))
      novy_mysky();

  gotoxy(mysky.x,mysky.y);
  barva(0);
  putc('0', stdout);
}
void konec(int udalost)
{
if (udalost == 1)
    {
      barva(0);
      gotoxy(36,12);
      printf("Hryznul ses!");
      getchar();
    }
    else
    {
      barva(0);
      gotoxy(30,12);
      printf("Narazil si do zdi!");
      getchar();
    }
    body = 0;
    main();
  }
void exit_fce(void)
{
  free((void *) sou_had);
}

Smazat poslední hodnotu, posunout hada, vykreslit hlavičku a tak déle. ( nepopisuji to podrobně, protože to už má každý umět z tutoriálů (ne umět) ale vyznat se v tom co co dělá).

No a nyní už jen upravíme soubor main.c následovně.

#include "had.h"

int main(void)
{
    SetConsoleTitle( "Snake v1.0" );
    hadova_rychlost = 70;    /* 70 ms */
    barva_hada = 0;          /* barva hada je bila */
    n = START_DELKA_HADA;    /* Delka hada pri vykresleni */
    pocatecni_smer = 'r';    /* smer hada je vpravo */

    if (start == 1) /* Pokud zacne nova hra vynuluje se skore */
    {
        body = 0;
    }

    vymaz(); /* vymaze obraz */
    srand((unsigned int) time(NULL));
    RAM_ZED();
    sou_had = alokuj_delku_hada();
    vypis_bodiky();
    vykresli_hada_start();
    novy_mysky();
    pohybuj_hada();
}

A po kompilaci by měl výsledek vypadat nějak takto :

Hotová hra had v céčku

Tak doufám že se to všem povedlo! :) Pro případ, že ne, přidávám zdrojový kód k článku.


 

Stáhnout

Staženo 595x (178.48 kB)
Aplikace je včetně zdrojových kódů v jazyce C

 

  Aktivity (2)

Článek pro vás napsal Зайчик
Avatar
Коммунизм для нашего будущего!

Jak se ti líbí článek?
Celkem (4 hlasů) :
55555


 



 

 

Komentáře

Avatar
David Čápka
Tým ITnetwork
Avatar
David Čápka:

Ahoj, tutoriál se ti docela povedl, máš tu obrázky, zdrojáky, v kódu nějaké komentáře, používáš struktury a tak :)

Vytkl bych snad jen slučování hlaviček a těl metod do souborů .h a těch pár globálních proměnných, i když v céčku to neberu jako tak závažnou chybu a také zmiňuješ, že by tam být neměly.

Jsem rád, že jsi udělal délku hada dynamicky, většinou jsem to vídal pomocí pole, což nevadí, ale takhle se člověk více naučí. Vážím si toho, že jsem tě odnaučil používat nekonečný cyklys a dokonce tu mám i speciální věnování :D

Odpovědět  +1 29.10.2012 11:56
Miluji svou práci a zdejší komunitu, baví mě se rozvíjet, děkuji každému členovi za to, že zde působí.
Avatar
Зайчик
Člen
Avatar
Odpovídá na David Čápka
Зайчик:

Tak to jsem rád že se ti libí :) jojo já to musel upravit protože by si mě za ten cyklus sežral zaživa už :D každopádně ještě chci doplnit že to bude funkční jen pro platformu windows kuli knihovně windows :) takže linuxáci mají smůlu nebo až se k tomu dostanu udělám hada i pro linux :)

Odpovědět 29.10.2012 15:29
Коммунизм для нашего будущего!
Avatar
Libor Šimo (libcosenior):

Зайчик, мне очень нравитця твойе дело.
Mám však jednu pripomienku.
Vždy keď mi kompilátor vyhodí nejaký varning, nie som spokojný s programom.
Tu mi vyhodil hneď 3.

had.h|43|warning: data definition has no type or storage class|
had.h|43|warning: type defaults to 'int' in declaration of 'konecprosdraca'|
had.h|266|warning: implicit declaration of function 'main'|

Myslím, že v tutoriáloch by malo byť všetko bezchybné, tak by si to mal opraviť. ;)
Stačí deklarovať konecprosdraca ako premennú typu int a riadok 266 zmazať.

Editováno 26.6.2013 11:01
Odpovědět 26.6.2013 10:59
Aj tisícmíľová cesta musí začať jednoduchým krokom.
Avatar
Зайчик
Člen
Avatar
Odpovídá na Libor Šimo (libcosenior)
Зайчик:

Celý had je špatně navržený, .h soubory používám úplně jinak než se mají atd atd je toho spousta.

K C se vracet nebudu... ten kdo se dostal až k psaní hada tak by měl vědět jak si to opravit, že třeba do .h souborů se nepíšou definice funkcí.

Ale je tu spousta hadů co jsou napsané na styl "hlavně, že to funguje" ;) pokud to chceš opravit tak prosím pak se můj příspěvek smaže.

Odpovědět 26.6.2013 12:04
Коммунизм для нашего будущего!
Avatar
filip.proch
Člen
Avatar
filip.proch:

Zdarvim, teď sem si dělal na otestování pracování s MCU hada, a k čemu je dobré uchovávat pozici hada, když už máme definovaný pole obrazovky? Stačí uchovávat pozici a poslední pozici hada a pak pouze do mapy ukládat směr, který se had vydal v daném bodě. Jestli to je blbý řešení, tak se nechám opravit, ale přijde mi to vhodnější, než zde uvedené. Pokud se narazí na myš, tak se pouze přičte nejvyšší směr a mělo by to stačit. Funguje mi to správně. To že ten kód je naprasácky udělanej, by nemělo mít na řešení vliv, dělal sem to v rychlosti s tím, že to zítra smažu Zde je kód:

if (init == false) {
Lcd_Ini();
snakeScreen[star­t.x][start.y] = direct;
//end = start;
Draw_SnakePoin­t(start,BORDER_SNA­KE,FILL_SNAKE);
Draw_SnakePoin­t(mouse,BORDER_MO­USE,FILL_MOUSE);
for (i = 0; i < snakeScreenX; i++){
for (j = 0; j < snakeScreenY; j++){
snakeScreen[i][j] = 0;
}
}
for(i=0;i<BOR­DER;i++){
DrawRectangle(i, i, WIDTH-i-i, HEIGHT-i-i, YELLOW);
}
srand(time(NULL));
}

if(touch==1){
switch (tlacitko)
{
case 2:
direct++;
break;
case 3:
direct--;
break;
}
if(direct>Down)
direct=Right;
else if(direct<Right)
direct=Down;
//smazePunti

if (initEnd == 1){
if (snakeScreen[en­d.x][end.y] <= Down)
Draw_SnakePoin­t(end, 0x0, 0x0);
switch (snakeScreen[en­d.x][end.y]){
case Right:
snakeScreen[en­d.x][end.y] = 0;
end.x++;
break;
case Up:
snakeScreen[en­d.x][end.y] = 0;
end.y--;
break;
case Left:
snakeScreen[en­d.x][end.y] = 0;
end.x--;
break;
case Down:
snakeScreen[en­d.x][end.y] = 0;
end.y++;
break;
default:
snakeScreen[en­d.x][end.y] -= 4;
break;
}
}
if (initEnd == 0){
end = start;
initEnd = 1;
}
switch (direct){
case Right:
snakeScreen[star­t.x][start.y] += Right;
start.x++;
break;
case Up:
snakeScreen[star­t.x][start.y] += Up;
start.y--;
break;
case Left:
snakeScreen[star­t.x][start.y] += Left;
start.x--;
break;
case Down:
snakeScreen[star­t.x][start.y] += Down;
start.y++;
break;
}
if(snakeScreen[star­t.x][start.y]!=0){
while(1);
}
if(start.x==mouse.x && start.y==mouse.y){
snakeScreen[star­t.x][start.y]+=Dow­n;
do{
mouse.x=rand()%sna­keScreenX;
mouse.y=rand()%sna­keScreenY;
}while(snakeS­creen[mouse.x][mou­se.y]!=0);
Draw_SnakePoin­t(mouse,BORDER_MO­USE,FILL_MOUSE);
}
Draw_SnakePoin­t(start,BORDER_SNA­KE,FILL_SNAKE);
}

 
Odpovědět  -1 16.12.2014 11:20
Děláme co je v našich silách, aby byly zdejší diskuze co nejkvalitnější. Proto do nich také mohou přispívat pouze registrovaní členové. Pro zapojení do diskuze se přihlas. Pokud ještě nemáš účet, zaregistruj se, je to zdarma.

Zobrazeno 5 zpráv z 5.