Optymalizacja Canvas'a

0

Hej, jestem w trakcie pisania projektu na zaliczenie i trafiłem na pewien problem.
Otóż piszę w JS prostą grę RTS, cały canvas to mapa świata po której można się poruszać. Niestety, ale przy zbyt dużych rozmiarach płótna (10000x10000) pojawiają się dziwne przycinki. Przy rozmiarach 5000x5000 taki problem nie występuje. Rysowane od samego początku są 3 proste figury i text służący do debugowania. Na przeglądarkowym profilerze widać, że co jakiś czas funkcja requestAnimationFrame potrafi wykonywać się kilkadziesiąt razy dłużej niż powinna.
title
Zakładam, że to przez tak duże rozmiary płótna, ale wciąż nie rozumiem czemu tak się dzieje.
Jakieś pomysły jak to zoptymalizować?
P.S.:Posiłkuje się p5 i jQuery.

1

Otóż piszę w JS prostą grę RTS, cały canvas to mapa świata po której można się poruszać.

Takie podejście miało by sens, gdybyś robił grę na divach, albo na SVG, czyli na rzeczach, które działają w sposób obiektowy (gdzie przesuwasz konkretne obiekty) i gdzie pustego miejsca nie wyświetla.

Canvas jednak działa w ten sposób, że działasz na pikselach i komputer nie wie, czy tam coś jest, czy nie. Jak czyścisz taki duży ekran (np. clearRect) to czyści cały canvas. A potem nic dziwnego, że muli (i pewnie zabiera pamięć RAM jeszcze).

Jak się pracuje na canvasie, to się robi inaczej. Nie robi się wymiarów płótna takiego jak świat, a jedynie tak duży jest canvas, jak "okienko" przez które widzisz świat (czyli np. pełnoekranowa gra nie potrzebuje mieć canvasa większego niż ekran). To okienko czasem się określa mianem "viewport" czy "camera" (ale to jest abstrakcja, nie ma czegoś takiego na Canvas, musisz to ręcznie zrobić. Po prostu wyobraź sobie, że świat to jest jeden wielki prostokąt, a ekran komputera to jest mały prostokącik ,który się przesuwa po tym dużym. I to co widzi gracz, jest w tym małym prostokąciku, taki "viewport", czy "camera")

Po prostu przy rysowaniu się przelicza współrzędne - ze współrzędnych świata na współrzędne ekranu. Albo przeliczając ręcznie współrzędne, albo np. dodając translację do macierzy przekształceń https://developer.mozilla.org[...]sRenderingContext2D/translate )

Zresztą tu masz jakiś wątek na SO https://stackoverflow.com/que[...]viewport-how-to-actally-do-it

0

Zgadzam się że jest to głupie i powinno się to zrobić dokładnie tak jak to opisałeś:

Nie robi się wymiarów płótna takiego jak świat, a jedynie tak duży jest canvas, jak "okienko" przez które widzisz świat ...

Ale teraz jeśli będę miał 1000 obiektów rozmieszczonych po całej mapie (przy płótnie będącym moim "oknem" na świat gry) to w momencie "poruszania kamerą" przemieszczać muszę też wszystkie obiekty, które w nim są (ich pozycje x i y) - tak by w momencie gdy ich pozycja znajdzie się pomiędzy rozmiarami mojej "kamery" mojego "view'a" dopiero się renderowały. Tylko przy tak dużej ilości obiektów lagi za to zaczną pojawiać się przez to iż skrypt będzie musiał przelecieć przez nie wszystkie odpowiednio zmieniając ich pozycje, gdy są poza widokiem.

2

No nie. Obiektów nie musisz przesuwać, a jedynie współrzędne, w których je rysujesz.
Aczkolwiek - musisz wiedzieć, które elementy rysujesz, które są poza ekranem, więc też - w najprostszym przypadku przechodzisz przez te 1000 obiektów i sprawdzasz, czy obiekt leży na ekranie, czy nie. Jak nie leży, to nie rysujesz, jak leży, to rysujesz (przeliczając pozycję/ustawiając macierz).

Tylko przy tak dużej ilości obiektów lagi za to zaczną pojawiać się przez to iż skrypt będzie musiał przelecieć przez nie

to już potem się będziesz martwić. Wiem, że na większą skalę robi się coś takiego, że się dzieli listę obiektów (jest coś takiego jak BSP https://en.wikipedia.org/wiki/Binary_space_partitioning albo drzewa czwórkowe https://en.wikipedia.org/wiki/Quadtree ), ale nie stosowałem tego akurat.

Są też inne sposoby optymalizacji, ale i tak najpierw zacząłbym od zmniejszenia rozmiaru płótna.

0

Hmm... to co opisałeś bardzo przypomina mi metodę wykorzystywaną w grach proceduralnie renderowanych gdzie świat dzieli się na tak zwane chunk'i, które są dużymi obiektami na które nakłada się w odpowiednich miejscach tekstury tak by imitowały np.: teren złożony z mniejszych części.
Zaraz zmodyfikuję swój kod i zrobię mały benchmark.
Wciąż jednak wydaje mi się że niezależnie od tego:

  • czy będę sprawdzał czy pozycja "kamery" znajduję się w polu widzenia jakiś obiektów (będzie trzeba sprawdzać czy to "kamera" najechała na obiekt),
  • czy będę przesuwał wszystkie obiekty (ich pozycje) poza jak i w polu widzenia (będzie trzeba modyfikować i sprawdzać zmienne obiektów względem "kamery")

to i tak wystąpią lag spike'i.

0

wow...
Nie wierze w to co widze...
title

13,5k obiektów, a JS śmiga - a myślałem, że java już mnie nie zaskoczy.
LukeJL mistrzu mój, działa.
Thx za pomoc ;3

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