KeyListener w gierce i spowolnienie animacji

0

Witam.
Mam taki problem zacząłem programować prostą gierkę (w appleci i Swing'u) obecnie mam prosty kwadrat którym mogę poruszać za pomocą klawiszy (np. strzałki) i oddawać coś w stylu strzałów.
Niestety ale mam pewien problem a mianowicie im więcej klawiszy wcisną tym kwadracik wolniej się porusza np:

  • naciskam i trzymam strzałkę w prawo (obiekt porusza się płynnie w prawo) następnie wciskam klawisz do góry (obiekt zaczyna poruszać się po przekątnej) i tu zaczyna się spowolnienie.
    Jeżeli wcisnę jeszcze spację (strzał) dochodzi kolejne spowolnienie.
    A więc jak to jest z tym KeyListener'em i czy idzie jakoś to obejść.

Dodam tylko, że gierkę programuję pod kątem multiplayer'a i położenie zapamiętuje w kolekcji czyli wciśnięcie klawisza powoduje dorzucenie położenia do kolekcji z której to czyta specjalnie do tego stworzony wątek i aktualizuje położenie.
Dodam jeszcze, że tak samo działo się bez aktualizacja położenia za pomocą wątku czyli wciśnięcie klawisza bezpośrednio zmieniało położenie.
Liczyłem, że może wątek rozwiąże problem ale tak się nie stało.

0

wklej swój kod źródłowy (wystarczy tylko keylistenera)

0

W kodzie poniżej dodaję do kolekcji z której korzystna wątek kod wciśniętego klawisza.

 
@Override
	public void keyPressed(KeyEvent e) {
		player1.getKeyPressed().add(e.getKeyCode());		
	}

	@Override
	public void keyReleased(KeyEvent e) {
		player1.getKeyPressed().remove(e.getKeyCode());			
	}

player1 - obiekt reprezentujący kwadracik
keyPressed - kolekcja zawierająca wciśnięte klawisze

W wątku sprawdzam który klawisz znajduje się w kolekcji i dopóki jest w kolekcji następuje zwiększanie bądź zmniejszanie wartości xy (położenie kwadracika).

0

Według mnie, kożystanie z kolekcji przy eventach typu keyPressed (gdzie wciskasz klawisz i trzymając go zasypujesz JVM zdarzeniami które są obsługiwane) nie jest za mądre z racji powolności takiej operacji - kolekcje i wszystkie inne formy tablicowania dynamizujące swój rozmiar nie może być najszybszym rozwiązaniem. Czemu? Bo generalnie każda (bądź co któraś) operacja add, powoduje przepisywanie danych do nowo alokowanej tablicy o większym rozmiarze, a każda operacja remove (bądź jakiekolwiek odwołanie - szczególnie w mapach) skutkuje przeglądem zupełnym. Nie lepiej już korzystać z tablic ?? (zakładam, że i tak korzystasz zkilku klawiszy nie całej klawiatury).
Idąc dalej - tego typu rozwiązanie powinieneś zrealizować w osobnym wątku na następującej zasadzie (ja tak robię przynajmniej ;P:P - zysk wydajności rzędu 80% - testowane na aplikacji rysującej):

Wciśnięto klawisz (nie obchodzi mnie czy jest trzymany) -> znacznik wciśnięcia klawisza=true-> start wątku obsługującego

a w wątku -> zwiększasz tam te swoje wartości z racji wciśniętego klawisza WHILE(znacznik wciśnięcia klawisza==true) i ustawiasz przykładowego sleepa między inkrementami (50ms starczy ?)

Puszczono klawisz(osobny event idzie) -> znacznik wciśnięcia klawisza =false (wątek obsługi się zatrzyma)
btw zmienna znacznik wsciśnięcia klawisza powinna byc volatile - to tak na marginesie;

0

Obiekt jakiej klasy jest zwracany przez player1.getKeyPressed() ?
Powinna to być klasa, która spełnia 2 warunki:
-nie pozwala na duplikaty
-można z niej bezpiecznie korzystać w wielu wątkach
np. Collectionis.synchronizedSet(new HashSet<Integer>())

0

Generalna uwaga - po obsłużeniu ewenta należy go "zjeść" ;) metodą consume()
[code]
public void keyReleased(KeyEvent e) {
player1.getKeyPressed().remove(e.getKeyCode());
e.consume();
}
[/code]
Dzięki temu zdarzenie nie będzie dalej wędrowało po kolejnych listenerach, jego obsługa zakończy się.

Koledzy mają rację co do tego aby jak najbardziej ograniczyć ilość (a szczególnie CZAS !!!) operacji
wykonywanych w listenerach. Te kolekcje do których dodajesz/kasujesz, muszą być synchronizowane ,
najszybszy tu będzie HashSet

Ale jeszcz kliknij tutaj (tutriol):
http://docs.oracle.com/javase/tutorial/uiswing/events/keylistener.html

Na środku strony jest demo "Try this", kliknij w Launch, uruchom demo
i teraz

  • naciśnij na sekundę jeden klawisz i patrz co się dzieje
  • naciśnij na sekundę 2 (3, 4) klawisze na raz i patrz co się dzieje
0

Wielkie dzięki za waszą pomoc.

__krzysiek85 napisał(a)

Obiekt jakiej klasy jest zwracany przez player1.getKeyPressed() ?
Powinna to być klasa, która spełnia 2 warunki:
-nie pozwala na duplikaty
-można z niej bezpiecznie korzystać w wielu wątkach
np. Collectionis.synchronizedSet(new HashSet<Integer>())

Od samego początku używałem Collectionis.synchronizedSet(new HashSet<Integer>()) i niestety był wspomniany prze zemnie problem.

Antoniossss miałeś racje całe to spowolnienie było spowodowane operację na kolekcji (ciągłe dodawanie kodu wciśniętego klawisza, trochę to nierozsądne).
Dodałem do klasy player klasę z używanymi klawiszami którym to ustawiam true/false i muszę powiedzieć, że poprawa jest ogromna.

Mam zatem jeszcze jedno pytanie jak takie rozwiązanie sprawdzi się w multi. W sytuacji, gdy miałem kolekcje to była sprawa jasna wszystkie obiekty reprezentujące gracza miałyby kolekcje z ich położeniem (kolekcja byłaby aktualizowana przez wątki odbierające dane z serwera).
Oczywiście w przypadku rozwiązania, które teraz zastosowałem wysyłana byłaby informacja, że jakiś gracz zaczął lub przestał poruszać się w danym kierunku tylko, że jeżeli wystąpi jakiś lag to gracz wyląduje powiedzmy w ścianie (no chyba, że to już kwestia poprawnego oprogramowania).

0

Gra multi (nawet pong) nie jest taka oczywista jak prawdopodobnie może Tobie się wydawać chociażby właśnie ze względu na opóźnienia sieciowe.
Koncepcie które (które ja znam i jestem ich świadomy) są dwie:

  • Stawiamy serwer na którym odbywa się rozgrywka, klienci natomiast synchronizują się z serwerem. (Cała seria Call of Duty)
  • Na każdym z klientów odbywa się osobna rozgrywka, natomiast serwer służy jedynie jako pośrednik przy synchronizacji rozgrywki na wszystkich klientach. (Darmowy fps Alliance of Valiant Arms)

Jeżeli zagrasz w te obie gry, od razu zauważysz różnice.
Ja osobiście jestem za pierwszym rozwiązaniem, jednak wymaga to wywczas obciążenia serwera. Jeżeli jesteśmy ograniczeni sprzętowo to wykonujemy architekturę w drugiej opcji - serwer jest tylko mostem z ewentualną korekcją błędów i synchronizacją.

Jeżeli planujesz połączenie p2p, to dotyczy Ciebie też ta druga opcja (tylko bez serwera). Nie będzie łatwo, ale myślę, że powinieneś dać rade.
Ważne:
Dobrze zaplanuj własny protokół komunikacyjny - pisanie w locie tego typu spraw, to bardzo nierozsądna praktyka :)

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