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

Jak uniemożliwić ruch w przeciwną stronę w Snake'u?
Silv
Moderator Wiki
  • Rejestracja:ponad 10 lat
  • Ostatnio:prawie 3 lata
  • Lokalizacja:Warszawa
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):

Kopiuj
// 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
CR
  • Rejestracja:około 6 lat
  • Ostatnio:około 2 godziny
  • Postów:113
2

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
Silv
Moderator Wiki
  • Rejestracja:ponad 10 lat
  • Ostatnio:prawie 3 lata
  • Lokalizacja:Warszawa
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
BU
  • Rejestracja:około 10 lat
  • Ostatnio:24 dni
  • Postów:422
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
Silv
Moderator Wiki
  • Rejestracja:ponad 10 lat
  • Ostatnio:prawie 3 lata
  • Lokalizacja:Warszawa
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
Zobacz pozostałe 2 komentarze
Silv
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.
BU
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ć ;)
Silv
A graficznie to nie w JS, pewnie w C++?
BU
Turbo Pascal 7.0
fasadin
  • Rejestracja:prawie 14 lat
  • Ostatnio:prawie 3 lata
  • Postów:4882
1

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

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

Freja Draco
Freja Draco
  • Rejestracja:około 7 lat
  • Ostatnio:ponad 3 lata
  • Postów:3394
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
Zobacz pozostałe 2 komentarze
Maciej Cąderek
Maciej Cąderek
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
Maciej Cąderek
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.
Freja Draco
Freja Draco
@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?
Maciej Cąderek
Maciej Cąderek
Nie no jasne, pewnie w większości przypadków się "uda" - tylko po co ryzykować, jak poprawka jest w miarę prosta?
Silv
@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
Moderator Wiki
  • Rejestracja:ponad 10 lat
  • Ostatnio:prawie 3 lata
  • Lokalizacja:Warszawa
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
Freja Draco
Freja Draco
  • Rejestracja:około 7 lat
  • Ostatnio:ponad 3 lata
  • Postów:3394
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
Silv
Ciekawe. :) Szkoda, że nie mam czasu, to bym sobie poczytał o tym.
Maciej Cąderek
Maciej Cąderek
  • Rejestracja:ponad 9 lat
  • Ostatnio:ponad 3 lata
  • Lokalizacja:Warszawa
  • Postów:1264
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
Silv
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ą.
Maciej Cąderek
Maciej Cąderek
Btw też się ostatnio wziąłem za grę, tyle, że w WebGL, także akurat jestem w temacie ;)
Silv
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
Hm... Zrobiłem właśnie refaktoryzację. Pierwsze uruchomienie po niej wyświetliło napis Game over i nic więcej.
Silv
Ale, no. Zapomniałem, że dodałem jeszcze nową funkcjonalność...

Zarejestruj się i dołącz do największej społeczności programistów w Polsce.

Otrzymaj wsparcie, dziel się wiedzą i rozwijaj swoje umiejętności z najlepszymi.