[C\C++] Wątki

0

Witam, tworze coś co ciężko jest określić (nazwałbym to grą pisaną dla przećwiczenia pozanej wiedzy).
Mam postać towrzoną za zasadzie listy dwukierunkowej

typedef struct hero
{
   int x, y; //współżędne
   char *view; //wygląd w postaci literowej np. "A"
   hero *next, *prev;
};

No i jej inicjacja

hero *player = new hero;
hero *element2 = new hero;
player->x = 10;
player->y = 10;
player->view = "O";
player->next = NULL;
player->prev = elemnent2;

player->x = 10;
player->y = 9;
player->view = "o";
player->next = player;
player->prev = NULL;

Taki wężyk. Następnie pobieram od użytkownika klawisz w którym kierunku miałby się poruszyć ten wąż, poruszam go i wyświetlam funkcjami:

void printSnake(snake **tail, WINDOW *field) //WINDOW *field to wskaznik do okna w ktorym ma się owy wąż  wyświetlać
{
   snake *tmp = *tail;
   
   wclear(field);//funkcja z ncurses
   box(field, 0, 0);//funkcja z ncurses
   
   while(tmp != NULL)
   {
      mvwprintw(field, tmp->y, tmp->x, tmp->view); //funkcja z ncurses
      wrefresh(field); //funkcja z ncurses
      tmp = tmp->next;
   }
}

void moveSnake(snake **tail, snake **head, int way, WINDOW *field)
{
   switch(way)
   {
      case KEY_LEFT:
           if((*tail) != (*head))
           { 
              snake *newElement = new snake;
              newElement->x = (*head)->x--;
              newElement->y = (*head)->y;
              newElement->view = "o";
              newElement->next = (*head);
              newElement->prev = (*head)->prev;
              
              (*head)->prev->next = newElement;
              (*head)->prev = newElement;
              
              (*tail) = (*tail)->next;
              delete (*tail)->prev;
              (*tail)->prev = NULL;
           }
           else (*head)->x--;
           
           break;
				      
      case KEY_RIGHT:
           if((*tail) != (*head))
           {                
              snake *newElement = new snake;
              newElement->x = (*head)->x++;
              newElement->y = (*head)->y;
              newElement->view = "o";
              newElement->next = (*head);
              newElement->prev = (*head)->prev;
              
              (*head)->prev->next = newElement;
              (*head)->prev = newElement;
              
              *tail = (*tail)->next;
              delete (*tail)->prev;
              (*tail)->prev = NULL;
           }
           else (*head)->x++;
           break;
		 		      
     case KEY_UP:
           if((*tail) != (*head))
           { 
             snake *newElement = new snake;
              newElement->x = (*head)->x;
              newElement->y = (*head)->y--;
              newElement->view = "o";
              newElement->next = (*head);
              newElement->prev = (*head)->prev;
              
              (*head)->prev->next = newElement;
              (*head)->prev = newElement;
              
              *tail = (*tail)->next;
              delete (*tail)->prev;
              (*tail)->prev = NULL;
           }
           else (*head)->y--;
           break;
				      
       case KEY_DOWN:
           if((*tail) != (*head))
           { 
              snake *newElement = new snake;
              newElement->x = (*head)->x;
              newElement->y = (*head)->y++;
              newElement->view = "o";
              newElement->next = (*head);
              newElement->prev = (*head)->prev;
              
              (*head)->prev->next = newElement;
              (*head)->prev = newElement;            
             
              (*tail) = (*tail)->next;
              delete (*tail)->prev;
              (*tail)->prev = NULL;
           }
           else (*head)->y++;
           break;
   }
   printSnake(tail, field);
}

I jak wszystko sobie działa i chciałbym teraz dodać coś takiego aby po naciśnięciu np klawisza w lewo wąż poruszał się ciągle w lewo do momentu aż użytkownik nie klinknie kolejnego klawisza. Jestem pewnien że da się to zrobić tylko i wyłącznie za pomocą wątków. Chciałbym użyć tych z <process.h>. Tylko nie bardzo mi to wychodzi. Spłodziłem coś takiego.

typedef struct thread
{
   int speed, way;
   snake **tail, **head;
   WINDOW *field;
};

void moving(void *parametr) //moj watek
{
   thread *arg;
   arg = (thread*)parametr;
   while(1)
   {
     moveSnake (arg->tail, arg->head, arg->way);
     printSnake(arg->tail, (&arg)->field);
      _sleep(arg->speed);
   }
}

I miałby on być włączany na początku gry przerywany po naciśnięciu klawisza i znów uruchamiany z nowymi parametrami.

hero *head = player,
     *tail = element2;
...

thread a = new thread;
a->head = &head;
a->tail = &tail;
a->way = key;
a->speed = 100;
a->field = field;
            
_beginthread(moving, 0, &a);

Tylko nie bardzo wiem co robie źle ponieważ nic nie działa. Wątek jest uruchamiany ale albo sie wywala na printSnake(); albo (bez tego, powinno tez działać bo rysowanie jest także w samym moveSnake()) się nie porusza. Byłbym wdzięczny za wskazanie poprawnego rozwiązania. :/

0

Wcale wątków nie potrzeba. Rozumiem, że gra ma przypominać węża z Noki :) Utwórz timer, który co np. sekunde będzie poruszał wężem w odpowiednią strone. Niech gra opiera się na pętli, która czeka na wciśnięcie klawisza lub timer. Jeśli odbierze komunikat o wciśnięciu klawisza ruchu to zapisze w zmiennej ten klawisz. Jeśli odbierze komunikat od timera, to poruszy wężem w kierunko ostatnio zapisanego klawisza. Pomiędzy kolejnymi ruchami węża gracz może wielokrotnie naciskać klawisze ruchu, a gdy dotrze komunikat od timera wtedy ostatnio wciśnięty klawisz zadecyduje o kieunku ruchu węża.

Niestety nie wiem jak to zaimplementować pod Linuxem :/

Jeszcze w funkcji moveSnake powtarzasz 4x mnóstwo kodu, elemety wspólne wstaw przed i za switch'm.

0

Hehe tak chodzi mi o takiego snake'a, grierka ma byc w 100% pod Windows(pdcurses) poźniej wezme sie na przenoszenie na Linuksa. Dzięki za porade spróboje to jakoś napisać.

//EDIT: Po głębszej analizie twojego opmysłu doszedłem do wniosku że nie umiem sobie wyobrazić jakby to miało działać(bez wątków - kurde ciągle mi to po głowie lata) byłbym szczęśliwy jakbyś mi to jakoś obrazowo przedstawił.

0

Jeden wątek, który pobiera aktualny kierunek poruszania się przez węża.
Drugi wątek, który pobiera od użytkownika wciśnięte klawisze i zmienia kierunek węża.

0
Motoki napisał(a)

Po głębszej analizie twojego opmysłu doszedłem do wniosku że nie umiem sobie wyobrazić jakby to miało działać(bez wątków - kurde ciągle mi to po głowie lata) byłbym szczęśliwy jakbyś mi to jakoś obrazowo przedstawił.

Wydaję mi się, że adf88 dosyć obrazowo to przedstawił ;) Co do wątków to musisz mieć na uwadze ich poprawną synchronizację. W przeciwnym razie program będzie się sypał.

0

Ale adf88 twierdzi że da się to jakoś bez wątków zrobić, a ja sobie tego wyobrazić nie moge dlatego chciałem żeby to jakos inaczej przedstawił ^^ Ostatnio przymulony jestem. Co do mojego zamysłu to miałbybyć tylko jeden wątek(więc nie trzeba tu chyba synchronizacji bo z czym?) Ale i tak on sam w sobie nie działa i nie wiem czemu chyba złe wartości przekazuje do funkcji moveSnake();

0

Ale adf88 twierdzi że da się to jakoś bez wątków zrobić, a ja sobie tego wyobrazić nie moge dlatego chciałem żeby to jakos inaczej przedstawił ^^

Bo to zadanie da się zrobić BEZ wątków ;) Wiele lat temu pisałem tetrisa, który elegancko śmigał na timerze. Ogólnie idea jest banalna. Masz funkcję np. onTimer(int direction), która jest wywoływana co sekunde. Ze zmiennej direction odczytujesz kierunek węża, robisz odpowiednią akcję i wyrzucasz wszystko na ekran. Oczywiście zmienna direction musi przyjmować jedną z czterech wartości - KEY_LEFT, KEY_RIGHT, KEY_UP i KEY_DOWN nawet jeśli w danej chwili nic nie jest wciśnięte. W zasadzie działanie całej gry oparte jest na tej funkcji. To jak wywołasz funkcję timer'a zależy od tego w czym piszesz - tu już musisz pokombinować.

Co do mojego zamysłu to miałbybyć tylko jeden wątek(więc nie trzeba tu chyba synchronizacji bo z czym?)

Dziwne bo z pierwszego postu można co innego wywnioskować ;)

0

dlatego chciałem żeby to jakos inaczej przedstawił
Najlepiej wyjaśni to kod :-)

int kierunek = 0; //0 lewo, 1 gora, 2 prawo, 3 dol
HANDLE Handles[2];

Handles[0] = CreateWaitableTimer(0, false, 0); //uchwyt utworzonego timera
long long llDueTime = 0; //czas w 100 nanosekund po którym timer wystąpi po raz pierwszy
SetWaitableTimer(Handles[0], (PLARGE_INTEGER) &llDueTime, 500, 0, 0, false); //włącza timer z interwałem 500 milisekund

Handles[1] = GetStdHandle(STD_INPUT_HANDLE); //uchwyt wejścia konsoli

while(true)
{
   switch(WaitForMultipleObjects(2, Handles, false, INFINITE)) //czakaj na timer lub input konsoli
   {
      case WAIT_OBJECT_0: moveSnake(..., kierunek, ...); break; //wystąpił timer - rusz wężą
      case WAIT_OBJECT_0 + 1: //w konsoli czeka input
      {
         INPUT_RECORD InRec;
         ULONG EventsRead;
         UINT Key;

         ReadConsoleInput(Handles[0], &InRec, 1, &EventsRead); //pobiera pierwszy czekający input z konsoli
         //Jeśli w kosoli czeka więcej niż jedeno zdarzenie, zostanie ono pobrane przy następnym wykonaniu pętli

         if(EventsRead && (InRec.EventType == KEY_EVENT) && InRec.Event.KeyEvent.bKeyDown)
         //jeśli wczytany input to wciśnięcie klawisza
         {
            Key = InRec.Event.KeyEvent.wVirtualKeyCode - VK_LEFT;
            //Key to 0 dla VK_LEFT, 1 dla VK_UP, 2 dla VK_RIGHT, 3 dla VK_DOWN

            if((Key < 4) && ((kierunek + Key) & 1)) kierunek = Key;
            //(Key < 4) czyli wciśnięto klawisz VK_LEFT..VK_DOWN
            //((kierunek + Key) & 1) wąż może skręcać jedynie prostopadle, z kierunku parzystego w nieparzysty lub z nieparzystego w parzysty, czyli suma ostatniego i nowego kierunku musi być nieparzysta
         }
      }
   }
}

Aha, i jeszcze nie musisz rysować całego węża na nowo. Wystarczy:

  • na głowie namalować człon
  • namalować nową głowe
  • zmazać ogon
  • namalować nowy ogon na ostatnim członie

1 użytkowników online, w tym zalogowanych: 0, gości: 1