Jak uniemożliwić ruch w przeciwną stronę w Snake'u?

Odpowiedz Nowy wątek
2019-08-14 16:06
0

Krótko: mamy sobie grę Snake w JavaScript w Node.js, w konsoli. Klasycznie, poruszanie wężykiem za pomocą klawiszy strzałek. Plansza przeładowuje się cała za każdym posunięciem węża.

Założenie jest takie, że wąż nie może "zawracać w miejscu". I problem – przykładowo: jeśli wąż idzie w dół, a ja nacisnę szybko po sobie LEFT oraz UP, to program nie zdąży przeładować planszy i wyświetlić ruchu w lewo, tylko od razu wyświetla w górę.

Jak zrobić, żeby niezależnie od szybkości naciśnięcia po sobie tych klawiszy był wyświetlany zarówno ruch w lewo, jak i w górę?

To kod, który powinien się przydać (trochę go uprościłem, odrzucając abstrakcje):

// Plik "run.js"
// Tu początek pliku

... // tu cośtam

process.stdin.on("data", (keyPressed) => {
            settings.snakeSettings = handleKeypress(
                keyPressed,
                settings.snakeSettings,
                exitGame
            );
        }
    });

const intervalID = timers.setInterval(() => {
    // W funkcji "makeStep" wywoływana jest funkcja "printBoard"
    const newSnakeSettings = makeStep(
        settings.environmentSettings,
        settings.gameSettings,
        settings.boardSettings,
        settings.snakeSettings,
        exitGame,
        computeNewSnakePosition,
        clearOutput,
        printBoard
    );
    settings.snakeSettings = newSnakeSettings;
}, settings.gameSettings.level);

// Tu koniec pliku

edytowany 8x, ostatnio: Silv, 2019-08-14 16:12

Pozostało 580 znaków

2019-08-14 16:35

Np. dać flagę, że wąż zmienił już kierunek i jeżeli tak, to uniemożliwić kolejną zmianę kierunku, po czym czyścic flagę po przeładowaniu planszy?

edytowany 1x, ostatnio: Crazy_Rockman, 2019-08-14 16:38

Pozostało 580 znaków

2019-08-14 16:45
0

Wiesz co, próbowałem tego, ale wydawało mi się, że program zbyt wolno reaguje na kolejne klawisze. Ale to chyba musi tak już być – trzeba po prostu poczekać z wciśnięciem... Ostatecznie lepsze nieobsłużone wciśnięcie klawisza niż zawracanie w miejscu.


PS. Gdyby jednak udało się coś wymyślić, żeby oba wciśnięcia były obsługiwane, byłoby fajnie.


edytowany 3x, ostatnio: Silv, 2019-08-14 16:46

Pozostało 580 znaków

2019-08-14 16:48
1
Silv napisał(a):

Założenie jest takie, że wąż nie może "zawracać w miejscu". I problem – przykładowo: jeśli wąż idzie w dół, a ja nacisnę szybko po sobie LEFT oraz UP, to program nie zdąży przeładować planszy i wyświetlić ruchu w lewo, tylko od razu wyświetla w górę.

Po naciśnięciu LEFT zmienia się kierunek i wąż nie powinien reagować na żaden inny przycisk, dopóki nie poruszy się o jedno pole. Wydaje mi się, że w tym przypadku przycisk UP powinien zostać zignorowany. Co by było, gdybyś nacisnął jeszcze więcej różnych przycisków? Według mnie tylko pierwszy naciśnięty w danym ruchu powinien być brany pod uwagę. W takim przypadku szybkie naciśnięcie LEFT i UP zadziała dla szybkiego węża tak jak chcesz.

edytowany 1x, ostatnio: Burmistrz, 2019-08-14 16:50

Pozostało 580 znaków

2019-08-14 16:50
0

@Burmistrz: widzisz, ale to stwarza konieczność czekania, aż minie te 500 czy 400 ms i dopiero wciśnięcia klawisza. Taki jakby lag.


PS. Jak udostępnię gierkę na GitHubie, będzie można się przekonać. ;) Na razie jeszcze nie widać w tym gry.


UPDATE. Wyobraź sobie: wciskasz dwa klawisze, a tu działa tylko pierwszy. Wąż pełźnie z zawrotną prędkością, zaraz koniec planszy, a Ty kombinujesz, dlaczego nie zmienił kierunku tak, jak chciałeś.


edytowany 3x, ostatnio: Silv, 2019-08-14 16:54
Pokaż pozostałe 2 komentarze
PS. Ciekawe, ile uda Ci się napisać w jeden dzień... jak całość (z powiększaniem się węża, zawijaniem itp.), to będzie godne podziwu. - Silv 2019-08-14 16:58
Nie doczytałem, że w konsoli robisz. Ja robiłem graficznie w trybie 640x480, a jutro spróbuję w JS z użyciem canvas (nigdy z tego nie korzystałem, ale chciałem spróbować). Możliwe, że nie zrobię w jeden dzień, ale jak będę się nudził na kacu i nie trafię na jakiś poważny problem, to raczej jakąś działającą wersję uda mi się zrobić ;) - Burmistrz 2019-08-14 17:05
A graficznie to nie w JS, pewnie w C++? - Silv 2019-08-14 17:06
Turbo Pascal 7.0 - Burmistrz 2019-08-14 17:06

Pozostało 580 znaków

2019-08-14 18:08
1

@Silv: input wrzuc w kolejkę fifo. Naciśniesz kilka i wrzucisz do kolejki.

1 zbierasz input

  1. W pętli gdzieś masz tam rysowanie
    3 po rysowaniu przetwarzasz kolejkę inputow

Pozostało 580 znaków

2019-08-14 18:35
3

Che, che. To jest przykład programu, który łatwiej pisze się w oparciu o starą dobrą pętlę niż jakieś akcje/zdarzenia.

  • W keyPressed czytaj sobie naciśnięcie klawisza i ustaw flagę naciśniętego klawisza (licz tylko ostatnio wciśnięty, żadnych buforów).
  • W pętli setInterval sprawdzasz stan klawiszy (flagę) i rysujesz kolejną klatkę ruchu.

Jak wąż porusza się w poziomie, to reaguje tyko na naciśnięcia góra/dół, a jak porusza się w pionie to tylko na prawo/lewo.
Przy starcie odpalasz go zawsze w tym samym miejscu i kierunku.


edytowany 2x, ostatnio: Freja Draco, 2019-08-14 20:38
Pokaż pozostałe 2 komentarze
Opóźnienie jest jednorazowe, co nie znaczy, że musisz je wywoływać raz. Prosty przykład pętli gry z pseudorekurencją: https://codepen.io/caderek/pen/aboNZzO?editors=0010 (sama zawartość "gry", i to że jest to canvas jest nieistotne, zasada jest ta sama) - Maciej Cąderek 2019-08-14 23:45
Jeszcze chyba należy się wyjaśnienie czemu nie setInterval: w przypadku gdy gra nie zmieści się z renderowaniem klatki w tej 1/60 sekundy to problem zacznie się nawarstwiać - kolejna klatka zostanie wrzucona do kolejki zanim poprzednia zdąży sie wyrenderować, co prowadzi do wielu nieporządanych efektów - skoki tempa gry, dalsze zamulenie, itp. W przypadku setTimeout gra zawsze czeka aż poprzednia klatka będzie gotowa. - Maciej Cąderek 2019-08-14 23:54
@Maciej Cąderek: to zależy jak się ma czas wykonywania funkcji graficznych do długości interwału. Czy wąż potrzebuje 60 klatek na sekundę dla uzyskania sensownej animacji? - Freja Draco 2019-08-15 01:30
Nie no jasne, pewnie w większości przypadków się "uda" - tylko po co ryzykować, jak poprawka jest w miarę prosta? - Maciej Cąderek 2019-08-15 01:35
@Maciej Cąderek: dzięki! Wykorzystałbym to, bo ciekawe, gdyby nie to, że setInterval dobrze na razie działa. Gdyby były problemy z wydajnością, spróbuję. - Silv 2019-08-15 02:33

Pozostało 580 znaków

2019-08-14 22:36
0

@fasadin: próbowałem tego. W mojej ocenie jest zbyt nieintuicyjnym, gdy naciskając jedne klawisze, wykonują się akcje dla innych.

@Freja Draco: hm... bo ja wiem, czy lepiej w oparciu o pętlę? W jakim języku mogłabyś na coś innego zamienić zdarzenie naciśnięcia klawisza?

Twoje rozwiązanie to to, co @Crazy_Rockman napisał. I wybrałem je, bo jest najlepsze z dotychczasowych nienajlepszych.


PS. Najlepsze rozwiązanie byłoby chyba w oparciu o czas, który minął od ostatniego naciśnięcia konkretnego klawisza. Nie chcę jednak go wprowadzać, bo zaśmieciłbym sobie kod nie-aż-tak-potrzebnymi obecnie warunkami i zmiennymi.


edytowany 1x, ostatnio: Silv, 2019-08-14 22:38

Pozostało 580 znaków

2019-08-14 22:59
0
Silv napisał(a):

@Freja Draco: hm... bo ja wiem, czy lepiej w oparciu o pętlę? W jakim języku mogłabyś na coś innego zamienić zdarzenie naciśnięcia klawisza?

Napisałam kiedyś Węża w Basicu. W pętli głównej programu przy każdym kolejnym kroku węża wywoływana była funkcja inkey$ czyli "sprawdź jaki klawisz został ostatnio wciśnięty" i w zależności od jego stanu program podejmował kolejne reakcje. I paradoksalnie było to prostsze niż w języku mającym reagować na konkretne zdarzenia.


edytowany 2x, ostatnio: Freja Draco, 2019-08-14 23:02
Ciekawe. :) Szkoda, że nie mam czasu, to bym sobie poczytał o tym. - Silv 2019-08-14 23:00

Pozostało 580 znaków

2019-08-15 00:39
2
Silv napisał(a):

W jakim języku mogłabyś na coś innego zamienić zdarzenie naciśnięcia klawisza?

Np. w JavaScript ;)

Prymitywny przykład: https://codepen.io/caderek/pen/GRKZqBG?editors=0010

Nawet gdybyś w twoim przypdku chciał kolejkować klawisze (jak ktoś wcześniej napisał), to idea pozostaje taka sama - event handlery nie obsługują gry bezpośrednio - przekazują tylko informacje o klawiszach dalej, resztą zajmuje się game loop. Dzięki temu piszesz w zasadzie kod "synchroniczny" i łatwiej kontrolować co dokładnie dzieje się na ekranie.

edytowany 5x, ostatnio: Maciej Cąderek, 2019-08-15 01:18
Chodziło mi bardziej o niedziałanie na zdarzeniach w ogóle, ale OK. Lubię synchroniczność, pomyślę także nad tym, gdyby były problemy z wydajnością lub asynchronicznością. - Silv 2019-08-15 02:36
Btw też się ostatnio wziąłem za grę, tyle, że w WebGL, także akurat jestem w temacie ;) - Maciej Cąderek 2019-08-15 03:40
Ja swojej mam nadzieję nie ciągnąć... ot, coś innego niż zwykle. Skupię się po skończeniu na bracket-string-validator, Angularze (ech...) i CoyoteNET. - Silv 2019-08-15 03:53
Hm... Zrobiłem właśnie refaktoryzację. Pierwsze uruchomienie po niej wyświetliło napis Game over i nic więcej. - Silv 2019-08-15 05:16
Ale, no. Zapomniałem, że dodałem jeszcze nową funkcjonalność... - Silv 2019-08-15 05:19

Pozostało 580 znaków

Odpowiedz
Liczba odpowiedzi na stronę

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

Robot: Googlebot