Optymalizacja gier w TGF i MMF2: Różnice pomiędzy wersjami

Klikipedia - klikowa encyklopedia
Skocz do: nawigacji, wyszukiwarki
m (lista, poprawki, wikizacja)
m (mniejsze nagłówki sekcji)
Linia 2: Linia 2:
 
Temat tego artykułu to odwieczna walka człowieka z ograniczeniami. Dokładniej chodzi o ograniczenia mocy obliczeniowej CPU w stosunku do tego, co chcielibyśmy powpychać do naszych gier. I nie chodzi o podkręcanie. Optymalizacja polega na upraszczaniu klikodu ([[Zdarzenie|zdarzeń]], akcji) i innych elementów gry tak, aby wynikowy EXE był jak najlżejszy dla procesora. Do dzieła!
 
Temat tego artykułu to odwieczna walka człowieka z ograniczeniami. Dokładniej chodzi o ograniczenia mocy obliczeniowej CPU w stosunku do tego, co chcielibyśmy powpychać do naszych gier. I nie chodzi o podkręcanie. Optymalizacja polega na upraszczaniu klikodu ([[Zdarzenie|zdarzeń]], akcji) i innych elementów gry tak, aby wynikowy EXE był jak najlżejszy dla procesora. Do dzieła!
 
__NOEDITSECTION__
 
__NOEDITSECTION__
=Rachunki=
+
==Rachunki==
 
Załóżmy, że w grze jest sporo obliczeń np. do fizyki, [[AI]] czy innych podobnych. Zazwyczaj trzeba takie obliczenia wykonywać w pętli, co oznacza jeszcze więcej liczenia. Najważniejszym zadaniem optymalizacyjnym jest wtedy ułatwienie ich wykonania.
 
Załóżmy, że w grze jest sporo obliczeń np. do fizyki, [[AI]] czy innych podobnych. Zazwyczaj trzeba takie obliczenia wykonywać w pętli, co oznacza jeszcze więcej liczenia. Najważniejszym zadaniem optymalizacyjnym jest wtedy ułatwienie ich wykonania.
  
==Dodatki zamiast popularnych równań==
+
===Dodatki zamiast popularnych równań===
 
Pierwszym krokiem do sukcesu jest sprawdzenie, czy przypadkiem żaden z posiadanych dodatków lub być może nawet któraś z wbudowanych funkcji edytora jest zdolna do wykonania zadania.
 
Pierwszym krokiem do sukcesu jest sprawdzenie, czy przypadkiem żaden z posiadanych dodatków lub być może nawet któraś z wbudowanych funkcji edytora jest zdolna do wykonania zadania.
  
Linia 16: Linia 16:
 
Jeśli więcej, niż jeden dodatek wykonuje jakieś zadanie, to warto pobieżnie sprawdzić, który jest w tym lepszy, a jeśli nie widać różnicy, to najlepiej wybrać najwygodniejszy.
 
Jeśli więcej, niż jeden dodatek wykonuje jakieś zadanie, to warto pobieżnie sprawdzić, który jest w tym lepszy, a jeśli nie widać różnicy, to najlepiej wybrać najwygodniejszy.
  
==Zmniejszanie ilości działań w równaniach==
+
===Zmniejszanie ilości działań w równaniach===
 
Jeśli z dodatkami nie przejdzie (na przykład ich po prostu nie ma), czas zająć się przeróbką równań. Kolejnym omawianym sposobem optymalizacji będzie więc zmniejszanie ilości działań w równaniach.
 
Jeśli z dodatkami nie przejdzie (na przykład ich po prostu nie ma), czas zająć się przeróbką równań. Kolejnym omawianym sposobem optymalizacji będzie więc zmniejszanie ilości działań w równaniach.
 
Na przykład, mając wyrażenie typu:
 
Na przykład, mając wyrażenie typu:
Linia 34: Linia 34:
 
W innym przypadku, gdy chcesz ustawić zmienną na np. szerokość planszy, to liczysz ''x=PlayfieldWidth'' lub np. ''x=640''. Oba rozwiązania działają tak samo szybko. Jednakże gdy już wcześniej ustawiłeś ''y=PlayfieldWidth'', to bezsensem jest wyrażenie ''x=y'' – wczytywanie zmiennej przez kliki jest o 20% wolniejsze, niż wczytywanie tego typu wbudowanej stałej, więc lepiej ustawić ponownie ''x=PlayfieldWidth''. Zachęcam do eksperymentów :)
 
W innym przypadku, gdy chcesz ustawić zmienną na np. szerokość planszy, to liczysz ''x=PlayfieldWidth'' lub np. ''x=640''. Oba rozwiązania działają tak samo szybko. Jednakże gdy już wcześniej ustawiłeś ''y=PlayfieldWidth'', to bezsensem jest wyrażenie ''x=y'' – wczytywanie zmiennej przez kliki jest o 20% wolniejsze, niż wczytywanie tego typu wbudowanej stałej, więc lepiej ustawić ponownie ''x=PlayfieldWidth''. Zachęcam do eksperymentów :)
  
==Rozdzielanie równań==
+
===Rozdzielanie równań===
 
Można również zabrać się za rozdzielenie np. 3 równań do 4, prostszych. Na przykład mając:
 
Można również zabrać się za rozdzielenie np. 3 równań do 4, prostszych. Na przykład mając:
 
  jeden=10*x*y/(z*z)
 
  jeden=10*x*y/(z*z)
Linia 69: Linia 69:
 
– czyli dodać ''30*j'' za pomocą akcji ''Add to value''.
 
– czyli dodać ''30*j'' za pomocą akcji ''Add to value''.
  
==Zmniejszanie ilości pętli==
+
===Zmniejszanie ilości pętli===
 
Teraz mamy całkiem ładne równania. Ale nadal uruchamiane są zawrotną ilość razy! (Każda ilość to za dużo.) Jeśli obliczenia wykonywane są w pętlach, należy się upewnić, czy pętla ma ustawioną rozsądną ilość powtórzeń. Na przykład: w TGF maksimum obiektów to 262. Klikowiec Gucio chce dla każdego z obiektów ''mjut'' obliczyć dystans do środka planszy. Zrobił wszystko, co trzeba i ustawił ilość powtórzeń na 261 – bo jednym obiektem jest dodatek ''Fast Loop'', więc to maksymalna liczba obiektów ''mjut''. Przyszła Maja i szlag ją trafił. Dlaczego? Zdarzenia w pętli mają warunki. Warunki są sprawdzane 261 razy, podczas gdy zwykle ''mjut'' występuje po kilkadziesiąt sztuk (np. 61). 200 pętli marnuje się na sprawdzanie warunków, które na pewno nie zostaną spełnione! Wyniki testu mówią wprost:
 
Teraz mamy całkiem ładne równania. Ale nadal uruchamiane są zawrotną ilość razy! (Każda ilość to za dużo.) Jeśli obliczenia wykonywane są w pętlach, należy się upewnić, czy pętla ma ustawioną rozsądną ilość powtórzeń. Na przykład: w TGF maksimum obiektów to 262. Klikowiec Gucio chce dla każdego z obiektów ''mjut'' obliczyć dystans do środka planszy. Zrobił wszystko, co trzeba i ustawił ilość powtórzeń na 261 – bo jednym obiektem jest dodatek ''Fast Loop'', więc to maksymalna liczba obiektów ''mjut''. Przyszła Maja i szlag ją trafił. Dlaczego? Zdarzenia w pętli mają warunki. Warunki są sprawdzane 261 razy, podczas gdy zwykle ''mjut'' występuje po kilkadziesiąt sztuk (np. 61). 200 pętli marnuje się na sprawdzanie warunków, które na pewno nie zostaną spełnione! Wyniki testu mówią wprost:
 
4 niemożliwe do spełnienia warunki i 100 tys. iteracji daje 109 FPS, podczas gdy 50 tys. iteracji to wynik rzędu 236 FPS, a przecież nic podczas tych pętli się nie działo.
 
4 niemożliwe do spełnienia warunki i 100 tys. iteracji daje 109 FPS, podczas gdy 50 tys. iteracji to wynik rzędu 236 FPS, a przecież nic podczas tych pętli się nie działo.
Linia 79: Linia 79:
 
Maja poradziła Guciowi, aby uruchomił pętlę tylko tyle razy, ile jest potrzebne – czyli odległość/8. Rozmowę usłyszał Filip i poradził, że najlepszym rozwiązaniem będzie uruchomienie pętli 125 razy, ale gdy ''ziu'' trafi w cel – zastopowanie jej. To rozwiązanie jest o tyle lepsze, że pozwala, aby pocisk nie leciał tylko do położenia kursora, ale do dowolnego położenia dalej też (do którego odległości się nie zna). Co najważniejsze, oba rozwiązania są podobnie szybkie.
 
Maja poradziła Guciowi, aby uruchomił pętlę tylko tyle razy, ile jest potrzebne – czyli odległość/8. Rozmowę usłyszał Filip i poradził, że najlepszym rozwiązaniem będzie uruchomienie pętli 125 razy, ale gdy ''ziu'' trafi w cel – zastopowanie jej. To rozwiązanie jest o tyle lepsze, że pozwala, aby pocisk nie leciał tylko do położenia kursora, ale do dowolnego położenia dalej też (do którego odległości się nie zna). Co najważniejsze, oba rozwiązania są podobnie szybkie.
  
=Grafika=
+
==Grafika==
 
Nie samą matematyką żyje człowiek. Bardzo duży wpływ na prędkość działania gry mają efekty graficzne w niej wykorzystywane, toteż teraz zajmiemy się właśnie nimi.
 
Nie samą matematyką żyje człowiek. Bardzo duży wpływ na prędkość działania gry mają efekty graficzne w niej wykorzystywane, toteż teraz zajmiemy się właśnie nimi.
  
==Dodatki graficzne==
+
===Dodatki graficzne===
 
Aby nie obciążać zbytnio zasobów komputera, należy unikać stosowania nadmiernej ilości obiektów takich jak:
 
Aby nie obciążać zbytnio zasobów komputera, należy unikać stosowania nadmiernej ilości obiektów takich jak:
 
* [[Flame Object]] - rozmiar pola działania dodatku jest kluczowy,
 
* [[Flame Object]] - rozmiar pola działania dodatku jest kluczowy,
Linia 92: Linia 92:
 
Często bardzo ciekawe efekty graficzne można uzyskać zwykłymi obiektami aktywnymi i nie trzeba stosować potworów takich jak Flame czy Bullet object. Lens często jest nieunikniony, warto pamiętać więc o tym, żeby nie miał zbyt dużego obrazka. Jedyny z wymienionych dodatków obecny w [[TGF]], Bullet object, może w dodatku powodować niestabilność. Gdy FPS spada poniżej normy, można niszczyć kolejne dodatki graficzne. W TGF najlepszym rozwiązaniem jest zapisanie informacji o spadku FPS i przy następnym uruchomieniu nie tworzenie w ogóle tych obiektów. (Można też dać odpowiednią opcję w menu.) Tak – należy ustawić akcje ich tworzenia, gdy informacji o spadku FPS brak. Wtedy, gdy taka informacja się pojawi, nie zostaną utworzone i nie spowolnią gry. Gdy zostaną zniszczone po utworzeniu, nadal mogą negatywnie wpływać na działanie.
 
Często bardzo ciekawe efekty graficzne można uzyskać zwykłymi obiektami aktywnymi i nie trzeba stosować potworów takich jak Flame czy Bullet object. Lens często jest nieunikniony, warto pamiętać więc o tym, żeby nie miał zbyt dużego obrazka. Jedyny z wymienionych dodatków obecny w [[TGF]], Bullet object, może w dodatku powodować niestabilność. Gdy FPS spada poniżej normy, można niszczyć kolejne dodatki graficzne. W TGF najlepszym rozwiązaniem jest zapisanie informacji o spadku FPS i przy następnym uruchomieniu nie tworzenie w ogóle tych obiektów. (Można też dać odpowiednią opcję w menu.) Tak – należy ustawić akcje ich tworzenia, gdy informacji o spadku FPS brak. Wtedy, gdy taka informacja się pojawi, nie zostaną utworzone i nie spowolnią gry. Gdy zostaną zniszczone po utworzeniu, nadal mogą negatywnie wpływać na działanie.
  
==Skalowanie, obrót, wygładzanie==
+
===Skalowanie, obrót, wygładzanie===
 
Wracając do MMF2: gdy obiektów jest dużo lub są duże, działania takie jak obrót i skalowanie (''Scale / Angle'') mogą bardzo spowolnić grę. Nie używaj obrotu, gdy obiekty są na tyle małe, że skutecznie można wykorzystać 32 kierunki animacji. Nie używaj skalowania, gdy robisz to skokowo (np. z 100% do 50%) – wtedy też możesz wykorzystać animacje, które są znacznie szybsze. Jeśli już używasz skalowania czy obrotu, nie stosuj wysokiej jakości (są dwie opcje: 0 – szybkość, 1 – jakość), chyba że obiekt naprawdę paskudnie wygląda bez tego. To bardzo zasobożerna opcja.
 
Wracając do MMF2: gdy obiektów jest dużo lub są duże, działania takie jak obrót i skalowanie (''Scale / Angle'') mogą bardzo spowolnić grę. Nie używaj obrotu, gdy obiekty są na tyle małe, że skutecznie można wykorzystać 32 kierunki animacji. Nie używaj skalowania, gdy robisz to skokowo (np. z 100% do 50%) – wtedy też możesz wykorzystać animacje, które są znacznie szybsze. Jeśli już używasz skalowania czy obrotu, nie stosuj wysokiej jakości (są dwie opcje: 0 – szybkość, 1 – jakość), chyba że obiekt naprawdę paskudnie wygląda bez tego. To bardzo zasobożerna opcja.
  
 
Jeszcze gorszy jest [[antyaliasing]] (we właściwościach obiektu: ''Properties -> Display options -> anti-aliasing''). Bardzo skutecznie zabija wydajność, szczególnie przy grach przewijanych, a efektów działania prawie nie widać.
 
Jeszcze gorszy jest [[antyaliasing]] (we właściwościach obiektu: ''Properties -> Display options -> anti-aliasing''). Bardzo skutecznie zabija wydajność, szczególnie przy grach przewijanych, a efektów działania prawie nie widać.
  
=Obiekty=
+
==Obiekty==
==Obiekty buntownicze==
+
===Obiekty buntownicze===
 
Czasem może się zdarzyć, że podczas działania gry nagromadzi się trochę „pomylonych” obiektów, które nie znikają (z winy twórcy :P ). Gdy są to np. obiekty dymu czy inne iskry, ich istnienie nie jest konieczne dla rozrywki, więc można stworzyć zdarzenie, które, gdy ilość obiektów danego typu jest niezerowa (lub większa od jakiegoś progu, np. 10), losuje obiekt i niszczy go. Można też wymyślić inne sposoby pozbywania się buntowników. Oczywiście nie ma lepszego, niż poprawianie błędu, który powoduje ich istnienie, jednak czasem błędy są nieuniknione (raczej w TGF) lub niewiarygodnie zakamuflowane.
 
Czasem może się zdarzyć, że podczas działania gry nagromadzi się trochę „pomylonych” obiektów, które nie znikają (z winy twórcy :P ). Gdy są to np. obiekty dymu czy inne iskry, ich istnienie nie jest konieczne dla rozrywki, więc można stworzyć zdarzenie, które, gdy ilość obiektów danego typu jest niezerowa (lub większa od jakiegoś progu, np. 10), losuje obiekt i niszczy go. Można też wymyślić inne sposoby pozbywania się buntowników. Oczywiście nie ma lepszego, niż poprawianie błędu, który powoduje ich istnienie, jednak czasem błędy są nieuniknione (raczej w TGF) lub niewiarygodnie zakamuflowane.
  
==Wstrzymywanie tworzenia obiektów==
+
===Wstrzymywanie tworzenia obiektów===
 
Niektórych obiektów nie powinno być za dużo w żadnym momencie. Wybuchy i pociski aktywnie wpływają na rozgrywkę, więc nie można regulować ich ilości w celu poprawienia wydajności. Można za to wstrzymywać tworzenie obiektów takich jak dym. Chodzi o to, żeby zdarzenie tworzące takie obiekty zachodziło jedynie wtedy, gdy warunek sprawdzający, czy ilość FPS jest w normie lub czy tych obiektów nie jest już za dużo. Można też w takim wypadku stworzyć obiekt, ale zaraz potem wylosować inny do zniszczenia. Drobny zabieg tego typu może znacząco zredukować lub przynajmniej ustabilizować na pewnym poziomie ilość [[Obiekt aktywny|obiektów aktywnych]], których często i tak z powodu ich ilości nie widać.
 
Niektórych obiektów nie powinno być za dużo w żadnym momencie. Wybuchy i pociski aktywnie wpływają na rozgrywkę, więc nie można regulować ich ilości w celu poprawienia wydajności. Można za to wstrzymywać tworzenie obiektów takich jak dym. Chodzi o to, żeby zdarzenie tworzące takie obiekty zachodziło jedynie wtedy, gdy warunek sprawdzający, czy ilość FPS jest w normie lub czy tych obiektów nie jest już za dużo. Można też w takim wypadku stworzyć obiekt, ale zaraz potem wylosować inny do zniszczenia. Drobny zabieg tego typu może znacząco zredukować lub przynajmniej ustabilizować na pewnym poziomie ilość [[Obiekt aktywny|obiektów aktywnych]], których często i tak z powodu ich ilości nie widać.
  
==Limit obiektów a błędy==
+
===Limit obiektów a błędy===
 
W MMF2 warto też znacząco zwiększyć limit obiektów (np. do 1000) – gdy nie będziemy go przekraczać ograniczając tworzenie i usuwając śmieci, to nie będzie przeszkadzał, ale nawet przy chwilowym zetknięciu się z tą barierą (standardowo 500) potrafi się zrobić naprawdę duży nieporządek. Gdy na przykład stosujemy akcję tworzenia obiektu i od razu ustawiania jego zmiennych, to gdy osiągniemy limit obiekt nie zostanie utworzony, ale... akcje ustawiania zostaną wykonane. Na wszystkich obiektach. Jak bardzo to niekorzystne, przekonywać chyba nie trzeba.
 
W MMF2 warto też znacząco zwiększyć limit obiektów (np. do 1000) – gdy nie będziemy go przekraczać ograniczając tworzenie i usuwając śmieci, to nie będzie przeszkadzał, ale nawet przy chwilowym zetknięciu się z tą barierą (standardowo 500) potrafi się zrobić naprawdę duży nieporządek. Gdy na przykład stosujemy akcję tworzenia obiektu i od razu ustawiania jego zmiennych, to gdy osiągniemy limit obiekt nie zostanie utworzony, ale... akcje ustawiania zostaną wykonane. Na wszystkich obiektach. Jak bardzo to niekorzystne, przekonywać chyba nie trzeba.
  
=Zdarzenia=
+
==Zdarzenia==
==Wyłączanie grup==
+
===Wyłączanie grup===
 
Bardzo pożyteczną strukturą w klikach jest [[grupa zdarzeń]]. Sama jej obecność nie ma wpływu na prędkość działania gry, czyli nawet przy obecności tysiąca grup w MMF2 spadek wydajności jest żaden, a w TGF przy 250 grupach (nigdy tyle nie jest potrzebne) spadek to ok. 5%, więc mniej niż 50 grup wywołuje w normalnych warunkach spadek bliski zeru. Dzięki temu można je wykorzystać do przyspieszania gry.
 
Bardzo pożyteczną strukturą w klikach jest [[grupa zdarzeń]]. Sama jej obecność nie ma wpływu na prędkość działania gry, czyli nawet przy obecności tysiąca grup w MMF2 spadek wydajności jest żaden, a w TGF przy 250 grupach (nigdy tyle nie jest potrzebne) spadek to ok. 5%, więc mniej niż 50 grup wywołuje w normalnych warunkach spadek bliski zeru. Dzięki temu można je wykorzystać do przyspieszania gry.
 
Grupy bowiem można wyłączać podczas rozgrywki. Zdarzenia w grupie wyłączonej mają co prawda wpływ na prędkość gry, ale jest on znacznie mniejszy. Wyłączając zbędne partie klikodu, na przykład fizykę beczek przy braku tychże lub AI, gdy wszyscy przeciwnicy nie żyją, albo zdarzenia wykonywane tylko na początku lub na końcu poziomu, oszczędzamy moc obliczeniową.
 
Grupy bowiem można wyłączać podczas rozgrywki. Zdarzenia w grupie wyłączonej mają co prawda wpływ na prędkość gry, ale jest on znacznie mniejszy. Wyłączając zbędne partie klikodu, na przykład fizykę beczek przy braku tychże lub AI, gdy wszyscy przeciwnicy nie żyją, albo zdarzenia wykonywane tylko na początku lub na końcu poziomu, oszczędzamy moc obliczeniową.
  
==x LUB y==
+
===x LUB y===
 
MMF2 wprowadziło nowe operatory przy warunkach w zdarzeniu: ''[[OR|OR (filtered)]]'' i ''OR (logical)''. Jak nazwa wskazuje, „''filtered''” coś filtruje, a „''logical''” działa czysto logicznie (a sam operator to po naszemu „''LUB''”). Nie będę tutaj omawiał [http://romanx.webd.pl/nb/viewtopic.php?t=2516 różnicy między efektem działania], ale jeśli już tego używasz, to chyba się w tym orientujesz. W każdym razie, filtrowanie, gdy nie jest potrzebne, lepiej jest zastąpić operatorem logicznym. Dla dwóch funkcji, każda uruchamiana po 15 tys. razy, które nie potrzebują filtrowania, ''OR (filtered)'' daje wynik 111 FPS, podczas gdy ''OR (logical)'' – 121 FPS.
 
MMF2 wprowadziło nowe operatory przy warunkach w zdarzeniu: ''[[OR|OR (filtered)]]'' i ''OR (logical)''. Jak nazwa wskazuje, „''filtered''” coś filtruje, a „''logical''” działa czysto logicznie (a sam operator to po naszemu „''LUB''”). Nie będę tutaj omawiał [http://romanx.webd.pl/nb/viewtopic.php?t=2516 różnicy między efektem działania], ale jeśli już tego używasz, to chyba się w tym orientujesz. W każdym razie, filtrowanie, gdy nie jest potrzebne, lepiej jest zastąpić operatorem logicznym. Dla dwóch funkcji, każda uruchamiana po 15 tys. razy, które nie potrzebują filtrowania, ''OR (filtered)'' daje wynik 111 FPS, podczas gdy ''OR (logical)'' – 121 FPS.
  
 
Ciut szybciej działają dwa osobne zdarzenia (125 FPS), ale dla różnych ilości funkcji (czyli różnych ilości operatorów ''OR'') przyrost jest raz mniejszy, raz większy. Można więc zignorować te różnice, jako że są bardzo małe. Poza tym, używanie ''OR'' (obu typów) bardzo ułatwia przyszłe modyfikacje klikodu.
 
Ciut szybciej działają dwa osobne zdarzenia (125 FPS), ale dla różnych ilości funkcji (czyli różnych ilości operatorów ''OR'') przyrost jest raz mniejszy, raz większy. Można więc zignorować te różnice, jako że są bardzo małe. Poza tym, używanie ''OR'' (obu typów) bardzo ułatwia przyszłe modyfikacje klikodu.
  
==komentowanie==
+
===komentowanie===
 
Niektórzy zastanawiają się, czy komentarze spowalniają działanie gry. Odpowiedź: oczywiście, że tak :) Jednak dopiero 1000 komentarzy powoduje spadek wydajności o ok. 10%. Normalna ilość, tj. poniżej 100 (do większości projektów wystarcza chyba 40-50) wywołuje spadek bliski zeru. Naprawdę, nie ma się co nimi przejmować. Ważne jest jedynie to, aby nie wstawiać do nich całego „Pana Tadeusza”, bo wpływa to później na rozmiar pliku EXE.
 
Niektórzy zastanawiają się, czy komentarze spowalniają działanie gry. Odpowiedź: oczywiście, że tak :) Jednak dopiero 1000 komentarzy powoduje spadek wydajności o ok. 10%. Normalna ilość, tj. poniżej 100 (do większości projektów wystarcza chyba 40-50) wywołuje spadek bliski zeru. Naprawdę, nie ma się co nimi przejmować. Ważne jest jedynie to, aby nie wstawiać do nich całego „Pana Tadeusza”, bo wpływa to później na rozmiar pliku EXE.
  
==„komentowanie”==
+
===„komentowanie”===
 
Czasem, na przykład aby coś przetestować, trzeba zablokować uruchamianie niektórych zdarzeń. Metod jest kilka – można na przykład wstawić dodatkowy warunek, który nigdy nie zachodzi. Jako, że nie ma dla klików różnicy pomiędzy porównywaniem liczb:
 
Czasem, na przykład aby coś przetestować, trzeba zablokować uruchamianie niektórych zdarzeń. Metod jest kilka – można na przykład wstawić dodatkowy warunek, który nigdy nie zachodzi. Jako, że nie ma dla klików różnicy pomiędzy porównywaniem liczb:
 
  1==2
 
  1==2
Linia 130: Linia 130:
 
Absolutnie najskuteczniejszą (najszybszą) metodą blokowania wykonywania danego zdarzenia jest dodanie nowego warunku, ''Never'' i ustawienie go na początku zdarzenia. To konieczne – pozostawienie go na końcu (podobnie jak innych warunków blokujących) co prawda zablokuje zdarzenie, ale edytor i tak najpierw sprawdzi najpierw resztę warunków, co niepotrzebnie obciąży zasoby.
 
Absolutnie najskuteczniejszą (najszybszą) metodą blokowania wykonywania danego zdarzenia jest dodanie nowego warunku, ''Never'' i ustawienie go na początku zdarzenia. To konieczne – pozostawienie go na końcu (podobnie jak innych warunków blokujących) co prawda zablokuje zdarzenie, ale edytor i tak najpierw sprawdzi najpierw resztę warunków, co niepotrzebnie obciąży zasoby.
  
=Testy=
+
==Testy==
 
Aby badać wpływ różnych kawałków klikodu na działanie gry najlepiej mierzyć FPS. W TGF jest to nieco utrudnione: aby dokładnie sprawdzić wydajność, należy użyć dodatku [http://romanx.webd.pl/down/ext/ext_autofps.zip AUTOFPS], który pozwala zwiększyć limit FPS do 999 w [http://romanx.webd.pl/down/autofps.zip ten sposób]. Tam też również jest metoda obliczania FPS.
 
Aby badać wpływ różnych kawałków klikodu na działanie gry najlepiej mierzyć FPS. W TGF jest to nieco utrudnione: aby dokładnie sprawdzić wydajność, należy użyć dodatku [http://romanx.webd.pl/down/ext/ext_autofps.zip AUTOFPS], który pozwala zwiększyć limit FPS do 999 w [http://romanx.webd.pl/down/autofps.zip ten sposób]. Tam też również jest metoda obliczania FPS.
  

Wersja z 15:17, 18 lis 2008

Autor.jpg
Autorem tego artykułu jest
RomanX

Temat tego artykułu to odwieczna walka człowieka z ograniczeniami. Dokładniej chodzi o ograniczenia mocy obliczeniowej CPU w stosunku do tego, co chcielibyśmy powpychać do naszych gier. I nie chodzi o podkręcanie. Optymalizacja polega na upraszczaniu klikodu (zdarzeń, akcji) i innych elementów gry tak, aby wynikowy EXE był jak najlżejszy dla procesora. Do dzieła!

Rachunki

Załóżmy, że w grze jest sporo obliczeń np. do fizyki, AI czy innych podobnych. Zazwyczaj trzeba takie obliczenia wykonywać w pętli, co oznacza jeszcze więcej liczenia. Najważniejszym zadaniem optymalizacyjnym jest wtedy ułatwienie ich wykonania.

Dodatki zamiast popularnych równań

Pierwszym krokiem do sukcesu jest sprawdzenie, czy przypadkiem żaden z posiadanych dodatków lub być może nawet któraś z wbudowanych funkcji edytora jest zdolna do wykonania zadania.

Na przykład, obecna w MMF2 wbudowana funkcja pobierania wartości kanału koloru z wartości RGB pozwala zrobić to znacznie szybciej, niż metodami pośrednimi.

Podobnie, zamiast liczyć pierwiastek z sumy kwadratów różnicy współrzędnych (z równania Pitagorasa) „ręcznie”, czyli za pomocą długaśnego wyrażenia typu:

( X( "Dwa" ) - X( "Jeden" ) ) * ( X( "Dwa" ) - X( "Jeden" ) ) + ( Y( "Dwa" ) - Y( "Jeden" ) ) * ( Y( "Dwa" ) - Y( "Jeden" ) )

potraktowanego pierwiastkiem (za pomocą dodatku w TGF czy wbudowaną funkcją Sqr() w MMF2), można użyć szybkiego dodatku, jak na przykład Advanced Math czy Advanced Direction (MMF2). Jak się okazuje, nawet uproszczone „ręczne” metody obliczania odległości, które dają wynik przybliżony, są znacznie wolniejsze, niż jakikolwiek dodatek. Można to łatwo sprawdzić nakazując edytorowi obliczanie odległości między dowolnymi punktami kilka tysięcy razy na pętlę i sprawdzając ilość klatek na sekundę (o tym później).

Jeśli więcej, niż jeden dodatek wykonuje jakieś zadanie, to warto pobieżnie sprawdzić, który jest w tym lepszy, a jeśli nie widać różnicy, to najlepiej wybrać najwygodniejszy.

Zmniejszanie ilości działań w równaniach

Jeśli z dodatkami nie przejdzie (na przykład ich po prostu nie ma), czas zająć się przeróbką równań. Kolejnym omawianym sposobem optymalizacji będzie więc zmniejszanie ilości działań w równaniach. Na przykład, mając wyrażenie typu:

jeden*dwa/(trzy/4.0)+jeden*dwa

można zastąpić je czymś typu:

jeden*dwa*(4.0/trzy+1)

– różnica jest niewielka, bo jest tylko o jedno działanie mniej, ale to aż 20% mniej! U mnie, licząc to 10 tys. razy na pętlę, pierwsze równanie dawało średnią 405 FPS, drugie – 446 FPS. Dodatkowym zyskiem jest mniejsza ilość wystąpień zmiennych jeden i dwa, więc łatwiej będzie modyfikować to równanie w przyszłości.

Tylko uwaga: przykład opierał się na [[liczbach zmiennoprzecinkowych (jak 3,14), a nie całkowitych (jak 3). Robi to różnicę w wyniku, gdy przestawi się działania kolejnością. Konkretnie chodzi o dzielenie:

4.0/10=0.4

ale:

4/10=0

– w podanym przykładzie, przyjmując liczby: jeden=10, dwa=20, trzy=30 i zamiast 4.0 dając 4, pierwszy wzór da wynik 228, drugi – 200, bo 4/30=0. Należy więc uważać przy takich przekształceniach.

Gdy potęgujesz w MMF2, to użyj zwykłego mnożenia (x*x), zamiast funkcji (x pow 2), o ile wykładnik potęgi jest stosunkowo mały. Funkcja jest bardziej obciążająca, niż kilka mnożeń, ale przy dużym wykładniku (dokładniej ponad 6) działa ciągle prawie tak samo szybko, niż gdy wykładnik to 2. Przykładowo x*x*x*x*x*x nadal działa szybciej, niż x pow 6, ale już „szlaczek” z 6 takich mnożeń wypada bardzo słabo przy x pow 7. I trudniej to modyfikować.

W innym przypadku, gdy chcesz ustawić zmienną na np. szerokość planszy, to liczysz x=PlayfieldWidth lub np. x=640. Oba rozwiązania działają tak samo szybko. Jednakże gdy już wcześniej ustawiłeś y=PlayfieldWidth, to bezsensem jest wyrażenie x=y – wczytywanie zmiennej przez kliki jest o 20% wolniejsze, niż wczytywanie tego typu wbudowanej stałej, więc lepiej ustawić ponownie x=PlayfieldWidth. Zachęcam do eksperymentów :)

Rozdzielanie równań

Można również zabrać się za rozdzielenie np. 3 równań do 4, prostszych. Na przykład mając:

jeden=10*x*y/(z*z)
dwa=20*x*y/(z*z)
trzy=30*x*y/(z*z)

– widać, iż wszystkie równania mają wspólny element. Można go wyciągnąć „przed równanie”, tj. najpierw obliczyć ten element, a później przemnożyć przez gotowca:

zero=x*y/(z*z)
jeden=10*zero
dwa=20*zero
trzy=30*zero

– jak widać nie dość, że ilość działań jest minimalna, to jeszcze bardzo łatwo modyfikować taki twór – w większości przypadków wystarczy zmienić jedną linijkę.

Ale takie rzeczy działają, gdy dostępna jest zmienna zero. W TGF jest z tym dosyć ciężko, ale w końcu można zrobić tak:

trzy=x*y/(z*z)
jeden=10*trzy
dwa=20*trzy
trzy=30*trzy

– zmienna trzy jest wykorzystywana zamiast zmiennej pomocniczej zero.

Gdyby z kolei równania były następujące:

jeden=10 + x*y/(z*z)
dwa=20*k + x*y/(z*z)
trzy=30*j + x*y/(z*z)

– element wspólny jest dodawany. Postępując analogicznie do wcześniejszej sytuacji:

trzy=x*y/(z*z)
jeden=10 + trzy
dwa=20*k + trzy
trzy=30*j + trzy

– ale czy na pewno? Łatwo sprawdzić, że działanie dodawania wartości do zmiennej (Add to value lub podobnie) jest o wiele szybsze, niż jej ustawianie (Set value lub podobnie) na sumę jej i wartości. Działanie dodawania uruchamiane 100 tys. razy na pętlę daje 118 FPS, a ustawiania – ok. 79, a to o jedną trzecią wolniej. Toteż warto spróbować:

(Set) trzy=x*y/(z*z)
(Set) jeden=10 + trzy
(Set) dwa=20*k + trzy
(Add) trzy+=30*j

– czyli dodać 30*j za pomocą akcji Add to value.

Zmniejszanie ilości pętli

Teraz mamy całkiem ładne równania. Ale nadal uruchamiane są zawrotną ilość razy! (Każda ilość to za dużo.) Jeśli obliczenia wykonywane są w pętlach, należy się upewnić, czy pętla ma ustawioną rozsądną ilość powtórzeń. Na przykład: w TGF maksimum obiektów to 262. Klikowiec Gucio chce dla każdego z obiektów mjut obliczyć dystans do środka planszy. Zrobił wszystko, co trzeba i ustawił ilość powtórzeń na 261 – bo jednym obiektem jest dodatek Fast Loop, więc to maksymalna liczba obiektów mjut. Przyszła Maja i szlag ją trafił. Dlaczego? Zdarzenia w pętli mają warunki. Warunki są sprawdzane 261 razy, podczas gdy zwykle mjut występuje po kilkadziesiąt sztuk (np. 61). 200 pętli marnuje się na sprawdzanie warunków, które na pewno nie zostaną spełnione! Wyniki testu mówią wprost: 4 niemożliwe do spełnienia warunki i 100 tys. iteracji daje 109 FPS, podczas gdy 50 tys. iteracji to wynik rzędu 236 FPS, a przecież nic podczas tych pętli się nie działo.

Maja po podaniu soli trzeźwiących zmieniła 261 wywołań na Number( "mjut" ), po czym poinstruowała Gucia, że gdyby pracował w MMF2 powinien użyć NObjects( "mjut" ).

Inny przykład to szybki strzał. Gucio stworzył klikod, który przesuwa obiekt ziu o np. 8 pikseli w kierunku kursora. Postanowił, że strzał będzie natychmiastowy, czyli już w następnej klatce animacji pocisk trafia w miejsce kursora i nie leci dalej. Gucio mądrze uznał, że skoro ekran ma przekątną 1000 px, to 125 pętli (8*125=1000) wystarczy na każdy strzał. Niestety, to nie jest rozwiązanie najlepsze.

Maja poradziła Guciowi, aby uruchomił pętlę tylko tyle razy, ile jest potrzebne – czyli odległość/8. Rozmowę usłyszał Filip i poradził, że najlepszym rozwiązaniem będzie uruchomienie pętli 125 razy, ale gdy ziu trafi w cel – zastopowanie jej. To rozwiązanie jest o tyle lepsze, że pozwala, aby pocisk nie leciał tylko do położenia kursora, ale do dowolnego położenia dalej też (do którego odległości się nie zna). Co najważniejsze, oba rozwiązania są podobnie szybkie.

Grafika

Nie samą matematyką żyje człowiek. Bardzo duży wpływ na prędkość działania gry mają efekty graficzne w niej wykorzystywane, toteż teraz zajmiemy się właśnie nimi.

Dodatki graficzne

Aby nie obciążać zbytnio zasobów komputera, należy unikać stosowania nadmiernej ilości obiektów takich jak:

  • Flame Object - rozmiar pola działania dodatku jest kluczowy,
  • Bullet (& Particle) object - ilość cząsteczek nie ma dużego znaczenia, bardziej istotne jest samo istnienie dodatku,
  • Lens - im większy obrazek soczewki, tym wolniej; obszary przezroczyste obrazka nie spowalniają działania,
  • Perspective – j.w.,
  • Line surface – ten dodatek jest ogólnie bez sensu i nie zalecam jego używania – są inne o podobnym działaniu.

Często bardzo ciekawe efekty graficzne można uzyskać zwykłymi obiektami aktywnymi i nie trzeba stosować potworów takich jak Flame czy Bullet object. Lens często jest nieunikniony, warto pamiętać więc o tym, żeby nie miał zbyt dużego obrazka. Jedyny z wymienionych dodatków obecny w TGF, Bullet object, może w dodatku powodować niestabilność. Gdy FPS spada poniżej normy, można niszczyć kolejne dodatki graficzne. W TGF najlepszym rozwiązaniem jest zapisanie informacji o spadku FPS i przy następnym uruchomieniu nie tworzenie w ogóle tych obiektów. (Można też dać odpowiednią opcję w menu.) Tak – należy ustawić akcje ich tworzenia, gdy informacji o spadku FPS brak. Wtedy, gdy taka informacja się pojawi, nie zostaną utworzone i nie spowolnią gry. Gdy zostaną zniszczone po utworzeniu, nadal mogą negatywnie wpływać na działanie.

Skalowanie, obrót, wygładzanie

Wracając do MMF2: gdy obiektów jest dużo lub są duże, działania takie jak obrót i skalowanie (Scale / Angle) mogą bardzo spowolnić grę. Nie używaj obrotu, gdy obiekty są na tyle małe, że skutecznie można wykorzystać 32 kierunki animacji. Nie używaj skalowania, gdy robisz to skokowo (np. z 100% do 50%) – wtedy też możesz wykorzystać animacje, które są znacznie szybsze. Jeśli już używasz skalowania czy obrotu, nie stosuj wysokiej jakości (są dwie opcje: 0 – szybkość, 1 – jakość), chyba że obiekt naprawdę paskudnie wygląda bez tego. To bardzo zasobożerna opcja.

Jeszcze gorszy jest antyaliasing (we właściwościach obiektu: Properties -> Display options -> anti-aliasing). Bardzo skutecznie zabija wydajność, szczególnie przy grach przewijanych, a efektów działania prawie nie widać.

Obiekty

Obiekty buntownicze

Czasem może się zdarzyć, że podczas działania gry nagromadzi się trochę „pomylonych” obiektów, które nie znikają (z winy twórcy :P ). Gdy są to np. obiekty dymu czy inne iskry, ich istnienie nie jest konieczne dla rozrywki, więc można stworzyć zdarzenie, które, gdy ilość obiektów danego typu jest niezerowa (lub większa od jakiegoś progu, np. 10), losuje obiekt i niszczy go. Można też wymyślić inne sposoby pozbywania się buntowników. Oczywiście nie ma lepszego, niż poprawianie błędu, który powoduje ich istnienie, jednak czasem błędy są nieuniknione (raczej w TGF) lub niewiarygodnie zakamuflowane.

Wstrzymywanie tworzenia obiektów

Niektórych obiektów nie powinno być za dużo w żadnym momencie. Wybuchy i pociski aktywnie wpływają na rozgrywkę, więc nie można regulować ich ilości w celu poprawienia wydajności. Można za to wstrzymywać tworzenie obiektów takich jak dym. Chodzi o to, żeby zdarzenie tworzące takie obiekty zachodziło jedynie wtedy, gdy warunek sprawdzający, czy ilość FPS jest w normie lub czy tych obiektów nie jest już za dużo. Można też w takim wypadku stworzyć obiekt, ale zaraz potem wylosować inny do zniszczenia. Drobny zabieg tego typu może znacząco zredukować lub przynajmniej ustabilizować na pewnym poziomie ilość obiektów aktywnych, których często i tak z powodu ich ilości nie widać.

Limit obiektów a błędy

W MMF2 warto też znacząco zwiększyć limit obiektów (np. do 1000) – gdy nie będziemy go przekraczać ograniczając tworzenie i usuwając śmieci, to nie będzie przeszkadzał, ale nawet przy chwilowym zetknięciu się z tą barierą (standardowo 500) potrafi się zrobić naprawdę duży nieporządek. Gdy na przykład stosujemy akcję tworzenia obiektu i od razu ustawiania jego zmiennych, to gdy osiągniemy limit obiekt nie zostanie utworzony, ale... akcje ustawiania zostaną wykonane. Na wszystkich obiektach. Jak bardzo to niekorzystne, przekonywać chyba nie trzeba.

Zdarzenia

Wyłączanie grup

Bardzo pożyteczną strukturą w klikach jest grupa zdarzeń. Sama jej obecność nie ma wpływu na prędkość działania gry, czyli nawet przy obecności tysiąca grup w MMF2 spadek wydajności jest żaden, a w TGF przy 250 grupach (nigdy tyle nie jest potrzebne) spadek to ok. 5%, więc mniej niż 50 grup wywołuje w normalnych warunkach spadek bliski zeru. Dzięki temu można je wykorzystać do przyspieszania gry. Grupy bowiem można wyłączać podczas rozgrywki. Zdarzenia w grupie wyłączonej mają co prawda wpływ na prędkość gry, ale jest on znacznie mniejszy. Wyłączając zbędne partie klikodu, na przykład fizykę beczek przy braku tychże lub AI, gdy wszyscy przeciwnicy nie żyją, albo zdarzenia wykonywane tylko na początku lub na końcu poziomu, oszczędzamy moc obliczeniową.

x LUB y

MMF2 wprowadziło nowe operatory przy warunkach w zdarzeniu: OR (filtered) i OR (logical). Jak nazwa wskazuje, „filtered” coś filtruje, a „logical” działa czysto logicznie (a sam operator to po naszemu „LUB”). Nie będę tutaj omawiał różnicy między efektem działania, ale jeśli już tego używasz, to chyba się w tym orientujesz. W każdym razie, filtrowanie, gdy nie jest potrzebne, lepiej jest zastąpić operatorem logicznym. Dla dwóch funkcji, każda uruchamiana po 15 tys. razy, które nie potrzebują filtrowania, OR (filtered) daje wynik 111 FPS, podczas gdy OR (logical) – 121 FPS.

Ciut szybciej działają dwa osobne zdarzenia (125 FPS), ale dla różnych ilości funkcji (czyli różnych ilości operatorów OR) przyrost jest raz mniejszy, raz większy. Można więc zignorować te różnice, jako że są bardzo małe. Poza tym, używanie OR (obu typów) bardzo ułatwia przyszłe modyfikacje klikodu.

komentowanie

Niektórzy zastanawiają się, czy komentarze spowalniają działanie gry. Odpowiedź: oczywiście, że tak :) Jednak dopiero 1000 komentarzy powoduje spadek wydajności o ok. 10%. Normalna ilość, tj. poniżej 100 (do większości projektów wystarcza chyba 40-50) wywołuje spadek bliski zeru. Naprawdę, nie ma się co nimi przejmować. Ważne jest jedynie to, aby nie wstawiać do nich całego „Pana Tadeusza”, bo wpływa to później na rozmiar pliku EXE.

„komentowanie”

Czasem, na przykład aby coś przetestować, trzeba zablokować uruchamianie niektórych zdarzeń. Metod jest kilka – można na przykład wstawić dodatkowy warunek, który nigdy nie zachodzi. Jako, że nie ma dla klików różnicy pomiędzy porównywaniem liczb:

1==2

i tekstów:

""=="mjut"

Warto stosować porównanie drugiego typu, ponieważ można wpisać tam jakiś tekst, np. „zdarzenie zepsute”. Co przy okazji prowadzi do dodatkowego wniosku – przy dobrym zdarzeniu można dodać zawsze spełniony warunek:

""<>"zdarzenie zastępcze"

Oczywiście takie porównanie obciąża nieco zasoby, ale w końcu takie formy są tymczasowe. Na dłuższą metę lepiej używać normalnych komentarzy. Absolutnie najskuteczniejszą (najszybszą) metodą blokowania wykonywania danego zdarzenia jest dodanie nowego warunku, Never i ustawienie go na początku zdarzenia. To konieczne – pozostawienie go na końcu (podobnie jak innych warunków blokujących) co prawda zablokuje zdarzenie, ale edytor i tak najpierw sprawdzi najpierw resztę warunków, co niepotrzebnie obciąży zasoby.

Testy

Aby badać wpływ różnych kawałków klikodu na działanie gry najlepiej mierzyć FPS. W TGF jest to nieco utrudnione: aby dokładnie sprawdzić wydajność, należy użyć dodatku AUTOFPS, który pozwala zwiększyć limit FPS do 999 w ten sposób. Tam też również jest metoda obliczania FPS.

W MMF2 wystarczy ustawić wartość licznika na FrameRate, a aby zmienić limit FPS należy przejść do właściwości aplikacji: Properties (lewy, dolny róg ekranu) -> Runtime options -> Frame Rate i wpisać 1000.

Moje testy były wykonywane na CPU z wynikiem ok. 1300 NCPU, więc przy średniej krajowej ok. 650 NCPU wyniki w FPS powinny być ok. 2 razy mniejsze.

Linki zewnętrzne