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

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
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?

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.

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.

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ś.

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

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.

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.

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.

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.

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