Skocz do zawartości

Jak wyczyścić pamięć generowaną przez tworzenie spritów przez surface?


Rekomendowane odpowiedzi

Witam. Pracuję nad klonem BattleCity, do stworzenia cegieł zamiast pojedynczych obiektów użyłem sprite_create_from_surface, żeby gra nie lagowała przez tysiące obiektów. Wszystko spoko do momentu, kiedy zauważyłem, że po kilku minutach gra wykorzystuje już 3.7Gb i wtedy następuje crash. Zauważyłem też, że każda modyfikacja mapy (czyli zrobienie w niej dziury pociskiem gracza czy bota) dodaje kilka mega do wykorzystywanej pamięci. Według mnie wygląda to tak, jakby mapa przy każdym trafieniu w nią aktualizowaą się o nową dziurę, a stary sprite tej mapy jest zamieniany na nowy z dziurą, jednak gdzieś w pamięci pozostają poprzednie wersje tego sprite'a. Jak usunąć te zalegające wersje mapy? Mam nadzieję, że mówię dość jasno. Oto mój kod z Draw Event:

if(HIT)
{//make a new sprite code
	var _temp_surf = surface_create(sprite_width, sprite_height);
	
	surface_set_target(_temp_surf);
	
	draw_sprite(sprite_index, 0, 0, 0);
	
	gpu_set_blendmode(bm_subtract);
	
	draw_set_color(c_black);
	
	draw_rectangle(damage_x -8, damage_y -8, damage_x +7, damage_y +7, false);
	
	gpu_set_blendmode(bm_normal);
	
	sprite_index = sprite_create_from_surface(_temp_surf, 0, 0, sprite_width, sprite_height, false, false, 0, 0); <--- kiedy pamięć podręczna osiągnie 3.7Gb gra crashuje i pokazuje, że tu jest błąd
	
	sprite_collision_mask(sprite_index, false, 0, 0, 0, 0, 0, 0, 0);
	
	surface_reset_target();
	surface_free(_temp_surf);
	
	
	HIT = false;
}

Nie jestem zaawansowanym programistą, więc jeśli mógłby mnie pokierować zaawansowany programista GMS'a, jak to zrobić, to byłbym niezmiernie wdzięczny, bo od lat o tym projekcie myślę, a totalnie nie orientuję się w surface'ach. Chciałbym też uniknąć zapisywania czegokolwiek w plikach, żeby zachować prostotę kodu do dalszego szlifowania

Odnośnik do komentarza
Udostępnij na innych stronach

49 minut temu, MamPytanie napisał:

A ja zapytam jeśli to nie jest problem, dlaczego użycie surface ma pomóc? Chyba każdy z bloków cegieł i tak musi jakoś powodwoać kolizję? I jak to ma działać, że robisz surface jako cały room game i malujesz na nim bloki cegieł?

No tak. Nie, maska kolizji jest obliczana precyzyjnie i na nowo po każdym uszkodzeniu terenu. A "blokowość", czy inaczej wyrównanie do siatki jest zastosowane po stworzeniu wystrzelonego pocisku jak i po uderzeniu w teren. Czyli jeśli pocisk zostanie wystrzelony z pozycji x2 i y5, to ja to dopasowuję do siatki 4x4 piksele, czyli x0 i y4 i z tych nowych koordynatów dopiero leci pocisk. A do tego, jeśli miejscem jego kolizji z mapą/cegłami jest na przykład x42 i y85 to zmienia miejsce wybuchu, który uszkadza teren na x40 i y84. Mam nadzieję, że rozumiesz. Chociaż nie o tym chciałem rozmawiać, bo nie wiem, jak usunąć z pamięci poprzedni sprite mapy sprzed jej kolejnego uszkodzenia. A tak, jak pisałem, jeśli użyję ma przykład obiektów cegieł, to jeśli mapa będzie wypełniona cegłami na full, to gra będzie lagowała i to ostro nawet, jeśli cegły nie będą miały żadnych skryptów. A z moich obliczeń wyszło, że potrzebowałbym ponad 10.000 obiektów o ile nie więcej, bo już nie pamiętam. A jak niszczysz blok 16x16 pikseli, to powinien się podzielić na 4 bloki 8x8, a te z kolei na 16 bloczków 4x4, więc sam widzisz, jak liczba obiektów może się mnożyć podczas grania

 

Nie wiem, czy mogę, ale wrzucam filmik jak to wygląda za każdym razem [FILMIK]

Odnośnik do komentarza
Udostępnij na innych stronach

Swoją destrukcję ścian robię w bardzo podobny sposób i też zauważyłem, że zjada mi to ramu. Na początku się tym nie przejmowałem, bo to 1mb na odświeżenie ale w końcu musiałem to ogarnąć. Po mozolnym klepaniu doszedłem do ładu i robię to w następujący sposób. 

Create: 

sprite = sprite_duplicate(sprite_index); //duplikuje przypisany sprite, aby na nim przeprowadzać operacje. Ważne! (dlatego, że nie przypisuje ścieżki do pamięci, tylko tworzę osobny sprite, tak, żeby każdy obiekt miał swój własny, a przy restartowaniu gry, nie pokazuje błędu o nieistniejącym zasobie.

 

Draw: 

sprite_delete(sprite) //usuwam aktualny sprite z pamięci
sprite = sprite_create_from_surface(oGame.surface_wall, x, y, 32, 32, false, true, 0, 0) //przypisuje nowy sprite z surface
sprite_collision_mask(sprite, 1, 0, 0, 0, 0, 0, bboxkind_precise, 0) //tworzę do niego maskę
sprite_index = sprite; //przypisuje ścieżkę do sprite_index - nie muszę pamiętać, żeby pisać sprite zamiast sprite_index w reszcie kodu.


Bardzo długo próbowałem to skrócić, tak, żeby nie musieć tworzyć nowej zmiennej sprite, ale zawsze było jakieś "ale". 

(zwiększający się stopniowo RAM, są wywołane przez tworzenie pocisków a nie tą sytuacją z surface)
 

Odnośnik do komentarza
Udostępnij na innych stronach

  • Administratorzy

Dlaczego generujesz tyle nowych sprite w locie? Pamięć zapełni się szybko i jest to mega niewydajne, bowiem GM każdy sprite generuje jako osobną teksturę jeśli go tworzysz w grze, do tego trzyma dwie kopie do normalnego rysowania i blendingu, co daje WYSOKOŚĆ * SZEROKOŚĆ * 4 bajty. Obrazek 512x512px to jest 2 MB.

 

Z tego kodu widać też, że faktycznie, nie usuwasz starych sprite'ów, wiec one faktycznie zostają w pamięci - GM nie zwalnia automatycznie zasobów jeśli nie są używane, robi to tylko ze strukturami i z tablicami, resztę trzeba usuwać ręcznie.

Odnośnik do komentarza
Udostępnij na innych stronach

W moim przypadku to nie pociski, bo wszędzie dookoła tych czołgów są niezniszczalne szare ściany, przez które nic się nie przedostaje co oznacza, że pociski są przy takiej kolizji usuwane, żeby nie leciały w nieskończoność. Wrzuciłem Twój kod i przy uderzeniu w cegły, znikają wszystkie, a nie tylko fragment po uderzeniu/wybuchu. Jeszcze to próbuję ogarnąć, może jakoś to naprawię, bo serio głupi jestem ehh

Odnośnik do komentarza
Udostępnij na innych stronach

Ten kod musisz dopasować do swojego, podałem Ci tylko część odpowiedzialną za główną funkcję. Czyli jeszcze gdzieś musisz mieć choćby draw_self() oraz co ważniejsze dopasować tą linijkę

sprite = sprite_create_from_surface(oGame.surface_wall, x, y, 32, 32, false, true, 0, 0) //przypisuje nowy sprite z surface

Z tego co widzę, kolosalną różnicą jest to, że Ty robisz surface dopasowany do wysokości/szerokości sprite'a i tak dla każdego bloku jest osobny surface. Ja natomiast mam jeden o wielkości room'u, a wszystkie moje bloki ściany odwołują się do tego jednego surface i tylko wycinam odpowiednie fragmenty z których tworzę nowe sprite'y

Odnośnik do komentarza
Udostępnij na innych stronach

Ehh Panowie no to nie wiem. Teraz mam takie coś i to w dalszym ciągu po kolizji pocisku z cegłami usuwa wszystkie z mapy :(

 

if(HIT)
{
	sprite_delete(sprite)
	sprite = sprite_create_from_surface(sprite, 0, 0, room_width, room_height, false, true, 0, 0)
	sprite_collision_mask(sprite, 1, 0, 0, 0, 0, 0, bboxkind_precise, 0) //tworzę do niego maskę
	sprite_index = sprite; 
	HIT = false;
}
draw_sprite(sprite_index, 0, 0, 0);

 

Chciałbym, żeby po uderzeniu pocisku w cegły, te nie znikały. No i okej, po tej zmianie:

if(HIT)
{//make a new sprite code
	sprite_delete(sprite)
	sprite = sprite_create_from_surface(sprite, 0, 0, room_width, room_height, false, true, 0, 0)
	sprite_collision_mask(sprite, 1, 0, 0, 0, 0, 0, bboxkind_precise, 0)
	HIT = false;
}
draw_sprite(sprite, 0, 0, 0);

Faktycznie teren pozostaje nietknięty po uderzeniu pocisku. Dobra. Teraz muszę tylko ponownie zaaplikować robienie dziur

 

EDIT:

 

Teraz robienie dziur mi nie działa

if(HIT)
{
	sprite_delete(sprite)
	sprite = sprite_create_from_surface(sprite, 0, 0, room_width, room_height, false, true, 0, 0)
		gpu_set_blendmode(bm_subtract);
		draw_set_color(c_black);
		draw_rectangle(damage_x -8, damage_y -8, damage_x +7, damage_y +7, false);
		gpu_set_blendmode(bm_normal);
	sprite_collision_mask(sprite, 1, 0, 0, 0, 0, 0, bboxkind_precise, 0)
	
	HIT = false;
}
draw_sprite(sprite, 0, 0, 0);

 

Odnośnik do komentarza
Udostępnij na innych stronach

Cytat
sprite_create_from_surface(sprite, 0, 0, room_width, room_height, false, true, 0, 0)

nie nie nie, nie tak. Tutaj tworzysz spritea o wymiarach pokoju, a ja mówiłem o surface. 
Hymm... czyli coś takiego, nie pamiętam dokładnie

Osobny obiekt dla ścian, np. oWallController
Create: 
 

surface_wall = surface_create(room_width, room_height);


i teraz w bloku ściany, coś takiego:
Draw:

draw_self();

if(HIT)
{
	surface_set_target(oWallController.surface_wall); //Ustaw surface do edycji

	draw_sprite(sprite_index, image_index, x, y) //narysuj obiekt który oberwał od pocisku

	gpu_set_blendmode(bm_subtract) //zmień tryb na wycinanie
	draw_set_color(c_white) //c_white wycina sprite (u Ciebie to może być c_black, to chyba spowodowane niedawnym przejściem kolorów na rgb w gms)
	
	draw_rectangle(damage_x -8, damage_y -8, damage_x +7, damage_y +7, false); //wytnij kawałek bloku

	gpu_set_blendmode(bm_normal)// ustaw tryb na normalny
	surface_reset_target(); // wróć na domyślny surface
		
	//Odświerz sprite
    	sprite_delete(sprite)
    	sprite = sprite_create_from_surface(oWallController.surface_wall, x, y, 32, 32, false, true, 0, 0) //utwórz nowy sprite z pozycji x/y obiektu trafionego, o wymiarach 32x32
	sprite_collision_mask(sprite, 1, 0, 0, 0, 0, 0, bboxkind_precise, 0) //tu bboxkind_rectangle może być w twoim przypadku, tak myślę.
    	sprite_index = sprite; //podmień stary sprite na nowy

    HIT = false;
}

 

Mniej więcej coś takiego, choć może będziesz to nadal musiał dopasować do siebie. Pamiętaj, że jak robisz surface o małych wymiarach np. 32x32 i umieszczasz je w pokoju na współrzędnych x/y, to poruszając się po niej, zaczynasz od x: 0 y: 0 a nie po x/y pokoju. 

Odnośnik do komentarza
Udostępnij na innych stronach

Jeśli chcesz dodać odpowiedź, zaloguj się lub zarejestruj nowe konto

Jedynie zarejestrowani użytkownicy mogą komentować zawartość tej strony.

Zarejestruj nowe konto

Załóż nowe konto. To bardzo proste!

Zarejestruj się

Zaloguj się

Posiadasz już konto? Zaloguj się poniżej.

Zaloguj się
  • Ostatnio przeglądający   0 użytkowników

    • Brak zarejestrowanych użytkowników przeglądających tę stronę.
×
×
  • Dodaj nową pozycję...