"Dym" po wystrzale z broni, pochodnie, piksele wydobywające się z różnych obrazków

0

Witam! W jaki sposób można by było zrobić wydobywające piksele z różnych reczy? Tak jak np. 0:22.
Jest jakaś metoda dostępna od SDL'a?

1

pytasz o technikalia czy o ogólny koncept?

bo ogólny koncept to są systemy cząsteczkowe (particle systems). Łatwo coś takiego zrobić samemu, np. robisz ileś obiektów, które są "wystrzeliwane" z jednego obszaru (niekoniecznie dokładnie z jednego punktu) a potem każdy obiekt porusza się własnym torem, ma swoją prędkość, działa na niego grawitacja itp. (można losować dla każdego punktu początkową szybkość i kierunek). Ma jak widać pewną półprzezroczystość, która może się zmieniać w trakcie lotu itp.

Jeszcze kwestia wyglądu - czy będą to piksele, albo jakieś figury generowane dynamicznie - czy może będą to np. obrazki zrobione wcześniej w programie graficznym - tu już różnie się robi.

no ale w kwestii technikaliów ci nie powiem już, bo nie znam SDL.

0

O, tego nie wiedziałem, że to taki system. Poszukam coś w necie o tej nazwie. A co do technikaliów, masz coś ciekawego?

0

Widać tutaj coś takiego:

SDL_Rect rect;
rect.x = position.getX();
rect.y = position.getY();
rect.w = size;
rect.h = size;

SDL_SetRenderDrawColor(renderer, color.r, color.g, color.b, color.a);
SDL_RenderFillRect(renderer, &rect);

Takich prostokątów można mieć dużo, wydobywają się z jakiegoś obszaru z jakąś częstotliwością, żyją sobie jakiś czas po czym znikają, w czasie zmieniają kolor lub rozmiar itp itd, można jeszcze uwzględnić czas życia, dzięki czemu można uzyskać ciekawe efekty.
Tu możesz coś poczytać o systemie cząstek: http://lazyfoo.net/SDL_tutorials/lesson28/index.php
A tutaj moja implementacja: https://pastebin.com/ZNCYiDZM
trochę roboty z tym jest.

0

Taak, już doszedłem do tego poradnika. Nie wiedziałem nawet jak to profesjonalnie nazwać. Dzięki wielkie. Mam jeszcze jedno pytanko, co do kodu. Napisałem sobie klasę

ParticleEngine

#include "ParticleEngine.h"
#include "System.h"

ParticleEngine::ParticleEngine(float x, float y, int moveDirection, SDL_Renderer *renderer)
{
	this->posX = (x  - 16 + rand()%15);
	this->posY =( y  - 8 + rand()%25);
	this->moveDirection = moveDirection;
	this->velY = 0;
	this->nextParticleFrameID = rand()%15;

	std::vector<std::string> tempS;
	std::vector<unsigned int> tempI;
	//------------ 0 --------------

	std::string spri = "";
	switch (rand()%4)
	{
		case 1: spri = "Particle/image1"; break;
		case 2: spri = "Particle/image2"; break;
		case 3: spri = "Particle/image3"; break;
		case 4: spri = "Particle/image4"; break;
	}
	tempS.push_back(spri);
	tempI.push_back(155);
	eSprite = new Sprite(tempS, tempI, renderer);
	tempS.clear();
	tempI.clear();
}

ParticleEngine::~ParticleEngine()
{
}

void ParticleEngine::Draw(SDL_Renderer * renderer)
{
	eSprite->getTexture()->Draw(posX, posY, renderer);
}

void ParticleEngine::Update(float velX)
{
	if (moveDirection)
	{
		posX += velX;
	}
	else
	{
		posX -= velX;
	}
        ++nextParticleFrameID;
}

bool ParticleEngine::deleteParticle()
{
	return nextParticleFrameID > 30;
}

I tą klasę dodaje do playerGun - klasa, która odpowiada za "broń" u naszego bohatera, można powiedzieć w taki sam sposób jak w poradniku.

I w entityManager

void EntityManager::Update()
{
	ePlayer->Update();

	for (int i = 0; i < ent.size(); i++)
	{
		for (int j = 0; j < ent[i].size(); j++)
		{
			ent[i][j]->Update();
		}
	}
	
        //update entity state
	for (int i = ent.size() - 1; i >= 0; --i)
	{
		for (int j = ent[i].size() - 1; j >= 0; --j)
		{
			if (ent[i][j]->spawnEntity)
			{
				if (ent[i][j]->entityState == ent[i][j]->eDead)
				{
					delete ent[i][j];
					ent[i].erase(ent[i].begin() + j);
					continue;
				}
			}
		}
	}
}

void EntityManager::AddPlayerGun(int x, int y, int moveDirection, SDL_Renderer *renderer)
{
	ent[getList(x)].push_back(new PlayerGun(x,y, moveDirection, renderer));
}

I tak, klasa broni dziedziczy z klasy Entity i kiedy usuwam w klasie broni aktualne Particle wyrzuca mi błąd, że wyszło poza wektor. Wydaje mi się, że to jest spowodowane tym, że player gun jest uznawane jako "entity" i usuwane podczas update.
Czy to może być dobry trop?

0

Ten zapis mi się nie podoba:

delete ent[i][j];
ent[i].erase(ent[i].begin() + j);

poza tym pogubić się można z tą podwójną tablicą. Zmienna ent to jest wektor wektorów? czy i indeksuje jednostki a j cząstki, jak tak to usuwanie elementu z wektora ent z indeksem i to nie najlepszy pomysł.

0

Pierwszy wektor oznacza sektor, A drugi wektor oznacza różne klasy.

0

Najlepiej użyj debugera, może pokaże miejsce w którym może wychodzić poza zakres, takie błędy to normalka.

1
std::vector<std::string> tempS;
    std::vector<unsigned int> tempI;
    //------------ 0 --------------

    std::string spri = "";
    switch (rand()%4)
    {
        case 1: spri = "Particle/image1"; break;
        case 2: spri = "Particle/image2"; break;
        case 3: spri = "Particle/image3"; break;
        case 4: spri = "Particle/image4"; break;
    }
    tempS.push_back(spri);
    tempI.push_back(155);
    eSprite = new Sprite(tempS, tempI, renderer);
    tempS.clear();
    tempI.clear();
  1. case'y w switchu powinny byc od 0 do 3
  2. Po co Ci te wektory? Jeśli Sprite ich wymaga, to lepiej konstruktor wywołać tak:
eSprite = new Sprite({spri}, {155}, renderer);

Dodatkowo niepotrzebnie je czyścisz na końcu. To są lokalne zmienne i same się wyczyszczą po wyjściu z konstruktora.
3. Nie zwalniasz w destruktorze Sprite'a (ani nigdzie indziej) - stąd pewnie zużycie pamięci
4. Gdzie jest eDead ustawiany?
5. deleteParticle sugeruje,
6. IMO tu też masz błąd:

        delete ent[i][j];
        ent[i].erase(ent[i].begin() + j);
        continue;

Najpierw wywalasz wszystko od 0 do j, a następnie, dając continue, przechodzisz do elementu ent[i][j-1] a powinienes do ent[i-1][j], gdzie nowe j to ent[i - 1].size() - 1
Abstrahując od tego, że ogólnie to jest brzydko i nieczytelnie napisane, to zamiast continue powinno tam byc break i przejście do kolejnego i.

0

Zapomniałem pkt. 5 uzupełnić
Ad. 5 deleteParticle sugeruje, że ma usuwać particle'a, a tak naprawdę zwraca tylko czy ma być usunięte - taka kosmetyka, ale sugeruję zmienić nazwę

0

1.Racja, głupi błąd

2.Też się zgadzam, to samo,
3.Destruktor wygląda tak:
Dla entityManager

EntityManager::~EntityManager()
{
for (int i = 0; i < ent.size(); i++)
{
    for (int j = 0; j < ent[i].size(); j++)
    {
        delete ent[i][j];
    }
    ent[i].clear();
}
ent.clear();
}

Dla playerGun

PlayerGun::~PlayerGun()
{
 Entity::~Entity(); // tutaj deletuje się sprite
 for(std::vector<ParticleEngine*>::iterator i = PE.begin(); i < PE.end(); ++i)
 {
     delete (*i);
 }
 PE.clear();
}

Nie mogę deletować sprite w destruktorze ParticleEngine, bo wyrzuca błąd.

  1. Nie rozumiem, możliwe, że też coś przegapiłem
  2. Rozumiem
  3. Z tym std::vector<std::vector<Entity*> ent chyba się źle zrozumieliśmy. Tutaj, nie pamiętam jak to się nazywa, nazwę to wymiarem, pierwszy wymiar jest miejscem na mapie, a drugi wymiar to naprawdę różne klasy tzn.
    screenshot-20171030164720.png
    Dzielę mapy na takie oto "sektory", "segmenty" - to jest pierwszy argument. Dlatego przechodzę do elementu[i][-1] zamiast [i-1][j];
    Jak w takim razie powinien wyglądać kod żeby był ładniejszy?

Jeszcze do dwójeczki się odwołam. Czyli w takim kodzie też niepotrzebnie na samym końcu czyszczę wektor na samym końcu:

void FileManager::LoadFromFile(std::string fileName, std::vector<std::vector<std::string>>& attributes, std::vector<std::vector<std::string>>& contents, std::string name)
{
	fileName = "Files/Config/" + fileName + ".cfg";
	std::ifstream openFile(fileName);

	attributes.clear();
	contents.clear();

	std::vector<std::string> tempAtt;
	std::vector<std::string> tempCont;

	if (openFile.is_open())
	{
		while (!openFile.eof())
		{
			std::string line;
			std::getline(openFile, line);
			
			line.erase(std::remove(line.begin(), line.end(), ' '), line.end());

			if (line.find(name + ":") != std::string::npos)
			{
				state = ATTRIBUTES;
				line.erase(0, line.find(':') + 1);
				tempAtt.clear();
			}
			else if (line.find(name + ";") != std::string::npos)
			{
				break;
				tempAtt.clear();
				tempCont.clear();
			}
			else
			{
				state = CONTENTS;
				tempCont.clear();
			}

			line.erase(std::remove(line.begin(), line.end(), '{'), line.end());
			line.erase(std::remove(line.begin(), line.end(), '}'), line.end());

			std::stringstream str(line);
			while (str)
			{
				std::getline(str, line, ',');

				if (line != "")
				{
					switch (state)
					{
						case ATTRIBUTES: tempAtt.push_back(line); break;
						case CONTENTS: tempCont.push_back(line); break;
					}
				//	std::cout << line << std::endl;
				}
			}

			if (state == CONTENTS && tempCont.size() > 0)
			{
				attributes.push_back(tempAtt);
				contents.push_back(tempCont);
			}
		}
		tempAtt.clear(), tempCont.clear();
	}

	openFile.clear();
	openFile.close();
}
0

Co do tego sprite'a, to chodziło mi o to, że nie widzę nigdzie abyś wywoływał: delete eSprite, natomiast widzę, jak wywołujesz eSprite = new Sprite(...)
Jeśli chodzi o tę pętlę to tam widzę taki błąd (patrz komentarze):

for (int i = ent.size() - 1; i >= 0; --i)
    {
        for (int j = ent[i].size() - 1; j >= 0; --j)
        {
            if (ent[i][j]->spawnEntity)
            {
                if (ent[i][j]->entityState == ent[i][j]->eDead)
                {
                    delete ent[i][j]; //usuwasz 1 element z pamięci
                    ent[i].erase(ent[i].begin() + j); //wywalasz z wektora j elementów (więc pozostałych nie usuniesz, i masz wyciek)
                    continue; //lecisz to kolejnej iteracji (--j)
//zatem pozniej, w kolejnej iteracji, probujesz wywołac:
//delete [i][j-1]; - ale tego juz w wektorze nie ma i tu ci sie zapewne wywala
//kolejny erase bedzie dla j-1 elementow, ale ich juz tam nie ma
//continue - i tak dalej
                }
            }
        }
    }

Wewnętrznych ifów i iteracji po j łatwo się pozbyć, używając std::remove_if (pamiętaj o erase, gdyż remove_if tylko odpowiednio sortuje elementy i zwraca iterator)

0

czyli coś takiego?

	for (int i = 0; i < ent.size(); i++)
	{
		ent[i].erase(
				std::remove_if(ent[i].begin(), ent[i].end(), [](const Entity* entity)
				{
					return entity->entityState == Entity::eDead;
				}
			),
				ent[i].end()
		);
	}

Edit:
Dodałem w particle engine entityState i usunąłem nextParticleFrameID, bo raczej na razie nie będzie mi potrzebne.

Wygląda to tak:

void Particle::MakeParticleDead()
{
	entityState = eDead;
}
//--------------------------------------------------------
PlayerGun::PlayerGun(int posX, int posY, int moveDirection, SDL_Renderer *renderer)
{
.../tekstura pocisku, pozycja początkowa i inne zmienne niemające znaczenia
this->sizePE = 1;
	for (int i = 0; i <= sizePE; i++)
	{
		PE.push_back(new ParticleEngine(posX,posY, !moveDirection, renderer));
	}
}

PlayerGun::~PlayerGun()
{
	Entity::~Entity();
	for(std::vector<ParticleEngine*>::iterator i = PE.begin(); i < PE.end(); ++i)
	{
		delete (*i);
	}
	PE.clear();
}

void PlayerGun::Draw(SDL_Renderer * renderer)
{
	if (!bDestroy)
	{
		DrawPE(renderer);
		eSprite->getTexture()->Draw(posX, posY, renderer, !moveDirection);
	}
	else
	{

	}
}

void PlayerGun::Update()
{
	if (bDestroy)
	{
		entityState = eDead;
		for (int i = 0; i <= sizePE; ++i)
		{
			PE[i]->MakeParticleDead();
		}
	}
	else
	{
		for (int i = 0; i <= sizePE; ++i)
		{
			PE[i]->Update(moveSpeed);
		}
		UpdatePosX();	
	}
}

void PlayerGun::DrawPE(SDL_Renderer * renderer)
{
	for (int i = 0; i <= sizePE; i++)
	{
		PE.push_back(new ParticleEngine(posX, posY, !moveDirection, renderer));
	}

	for (const auto &p : PE)
	{
		p->Draw(renderer);
	}
}

0

Teraz się taki błąd pojawił:
screenshot-20171030232137.png

Pojawia się kiedy chce w destruktorze w EntityManager zdeletować ent. Teraz pytanie, czy tą metodę z remove_if powinienem wstawić w destruktor i usunąć z update, czy destruktor pozostawić pusty, czy w jednym i w drugim dać to samo?

0

A możesz pokazać ten destruktor? Samo ent chcesz usunąć? Czy coś ze środka?
Ogólnie myślę, że twój problem rozwiązałyby smart pointery. Za bardzo się chyba zamotałeś z tą dynamiczną alokacją.

0

Ogólnie to teraz wygląda to tak

EntityManager::~EntityManager()
{
	for (int i = 0; i < ent.size(); i++)
	{
		ent[i].clear();
	}
	ent.clear();
}

void EntityManager::Update()
{
	ePlayer->Update();

	for (int i = 0; i < ent.size(); i++)
	{
		for (int j = 0; j < ent[i].size(); j++)
		{
			ent[i][j]->Update();
		}
	}
	
	for (int i = 0; i < ent.size(); i++)
	{
		ent[i].erase
		(
			std::remove_if(ent[i].begin(), ent[i].end(), [](const Entity* entity)
				{
					return entity->entityState == Entity::eDead;
				}
			),
			ent[i].end()
		);
	}
}

I działa. Tylko nie wiem czy w destruktorze nie powinienem jeszcze usuwać tego tego pointera, bo gdy usuwam to pokazuje się to co przed chwilą.
Może nie można tak usuwać skoro się używa remove_if. Może trochę wiecej o tym poczytam, bo tylko czytałem jak się tego używa żeby działało :d

Usunąć chciałbym ent[i][j]

Nie chce używać smart pointerów, bo jest spadek wydajności. U mnie przynajmniej tak się dzieje. Może mam spierdzielony kod.

Szczerze mówiąc to trudno jest zrozumieć co się dzieje z tą pamięcią. Jeszcze na samym początku jak pisałem menu itd. zabierało strasznie dużo, potem im więcej kodu było tym mniej zabierało...

0

Ale jak? Chcesz wywołać delete ent; ? ent nie jest pointerem,
Co do smart pointerów - unique pointer praktycznie nie rzutuje na wydajność ani na zużycie pamięci.
Co do wydajności - używanie vectorów w ten sposób nie jest zbyt wydajne. Zrób sobie memory pool (vector może przyjąć customowy allocator).

0

W taki sposób albo od tyłu, int i = ent.size() - 1, obojętnie, byle działało

	for (int i = 0; i < ent.size(); i++)
	{
		for (int j = 0; j < ent[i].size(); j++)
		{
			delete ent[i][j];
		}
		ent[i].clear();
	}
	ent.clear();
0

A nie doczytałem linijki, że chcesz usunąć ent[i][j]. Ja bym to usuwał w update. Możesz najpierw wywołać remove_if i zapisać zwracany iterator do zmiennej.
Później przeiteruj od tego iteratora do końca i wywołuj delete na każdym elemencie.
Następnie zrób erase(iterator, end())
To powinno działać

0

Spróbuje jeszcze raz może z tymi smart pointerami. Próbowałem już kiedyś, bo każdy je tak chwalił, polecał i w ogóle, i jedyne co zobaczyłem to z 5% użycia procesora to 15 :D. Jak nie wyjdzie, to zrobie tak jak mówiłeś z remove_if

0

Co do wydajności, to jeszcze jedna rzecz. Jeśli w ent[i][j] masz za każdym razem pojedynczy, dynamicznie zaalokowany element, to będziesz miał dużo cache missów, a to jest zabójcze dla wydajności. Dlatego, tak jak wspominałem albo zrób memory pool i trzymaj te dane blisko siebie, albo nie alokuj dynamicznie, tylko wrzucaj tam całe obiekty (najlepiej zdefiniuj konstruktor przenoszący dla particle'ów). Wtedy dane będą blisko siebie i iteracja po nich będzie szybsza. Możesz się też pokusić o implementację SOA (Structure Of Arrays). To może przyspieszyć także update'y.
Z dodatkowych tematów, to SSE/AVX.

0

To znaczy, żebyś zrobił coś takiego:

//zamiast
ent[i].push_back(new Particle());
//takie coś
ent[i].push_back(Particle()); //dla particle zdefiniuj konstruktor przenoszący.

I mam jeszcze jeden pomysł. Jak tworzysz particle'a to ładujesz dla każdego obrazek nawet jeśli obrazki się powtarzają. To jest idealna sytuacja na zastosowanie wzorca flywieght. Ogólnie polega to na tym, że wszystkie particle danego rodzaju współdzieliłby jedną teksturę. Poczytaj sobie o tym. Raz, że mniej pamięci zużyjesz, a dwa, że podczas tworzenia particle'a nie będziesz za każdym razem ładować tekstury.

0

Zmieniłem już z tą teksturą też wczoraj. Zrobiłem białą teksturą I randomwo zmieniam jej kolor.

0

Zmieniłem na smart pointery i teraz mam problem z implementacją tej "mapy" dla entity.
Przy zwykłym pointerze miałem:

void EntityManager::CreateEntity(std::vector<std::vector<std::string>> map)
{
	for (int y = 0; y < map.size(); y+=5)
	{
		std::vector<Entity*> tempEnt;
		for (int i = 0; i < map[y].size(); i++)
		{
			ent.push_back(tempEnt);
		}
	}

	entitySize = ent.size();
}

Tworzyło mi to mapę na której może być zespawnowany entity. Jest ona zależna od tego jaka jest mapa gry.

Zmieniając na smart pointer chciałem zrobić w taki sposób, analogicznie do starego systemu:


void EntityManager::CreateEntity(std::vector<std::vector<std::string>> map)
{
	for (int y = 0; y < map.size(); y+=5)
	{
		std::vector<std::unique_ptr<Entity>> tempEnt;
		for (int i = 0; i < map[y].size(); i++)
		{
			ent.push_back(tempEnt);
		}
	}

	entitySize = ent.size();
}

Przy kompilacji występuje błąd
screenshot-20171031143156.png

Edit. Użyłem shared_ptr, ale teraz rozumiem, że cała metoda ent.erase(std::remove_if(...)); idzie do wywalenia? Pointer rozumiem sam się usuwa, ale w takim razie dlaczego ilość "postaci" się nie zmienia. Wypisuje sobie na ekranie ilość Entity i za każdym raziem kiedy wywołuje np. 1 pocisk, ilość aktualnie zrenderowanych Entity zwiększa się o 1, a kiedy pocisk znika ilość aktualnie zrenderowanych Entity zmniejsza się o 1. Przy smart pointerach, liczba tlyko się zwiększa.
Edit 2.
Za każdym razem kiedy particle wchodzi w ruch obroty procka lecą strasznie w górę niezależnie od tego czy jest smart czy zwykły, to chyba zrozumiałe. Ale czy naprawdę takie coś potrzebuje tyle %%%%?

0

Błąd chyba związany jest z tym że unique_ptr to unikalny wskaźnik, tj może być tylko jeden element pokazujący na dany obszar pamięci, nie więcej, w wektorze pewnie dochodzi do jakiś przepisań elementów przy dokładaniu czy usuwaniu, stąd pewnie trzeba używać shared_ptr. (chciaż może to wynika z czego innego ale na shared_ptr działa).

Edit 1: wszystko powinno działać, masz wektor cząsteczek i go wyświetlasz, np tak:

for (const auto &p: particle)
{
    p->draw(renderer);
}

kiedy coś usuniesz z tego wektora particle, pętla nie uwzględni usuniętych elementów.

Edit 2: to zależy ile masz cząstek, weź sobie 1000 takich cząstek i dla każdej przeliczaj położenie, nakładaj teksturę i wykonuj inne operacje, trochę tego będzie, efekty cząsteczkowe są wymagające.

0

Ale że aż ponad 30% procesora kiedy mój loop wyglada tak?:

PlayerGun::PlayerGun (...)
{
 ...//gun stuff 
this->maxParticles = 1
for (int i = 0; i < maxParticles; ++i)
 PE.push_back (new ParticleEngine(...));
}

PlayerGun::DrawPE (...)
{
 for (int i = 0; i < maxParticles; ++i)
  PE.push_back (new ParticleEngine(...));

for  (const auto &p : PE)
 p->Draw(...)
}

Chyba trochę za dużo.

0

Odradzałbym używanie shared pointerów, gdyż ich najprawdopodobniej nie potrzebujesz. Co do unique pointerów, to źle ich używasz dlatego masz ten błąd:

void EntityManager::CreateEntity(std::vector<std::vector<std::string>> map)
{
    for (int y = 0; y < map.size(); y+=5)
    {
        std::vector<std::unique_ptr<Entity>> tempEnt; //tworzysz tymczasowy wektor na stosie
        for (int i = 0; i < map[y].size(); i++)
        {
            ent.push_back(tempEnt); //tutaj odpalany jest konstruktor kopiujący dla wektora unique pointerów
        }
    }

    entitySize = ent.size();
}

Natomiast unique pointery nie mają konstruktora kopiującego (= delete), zatem kompilator nie zgadza się na takie coś. Rozwiązaniem twojego problemu będzie zmuszenie kompilatora do użycia konstruktora przenoszącego w taki sposób:

void EntityManager::CreateEntity(std::vector<std::vector<std::string>> map)
{
    for (int y = 0; y < map.size(); y+=5)
    {
        for (int i = 0; i < map[y].size(); i++)
        {
            ent.push_back(std::vector<std::unique_ptr<Entity>>()); //tutaj odpalany jest konstruktor przenoszący dla wektora unique pointerów
        }
    }

    entitySize = ent.size();
}

Użyłem shared_ptr, ale teraz rozumiem, że cała metoda ent.erase(std::remove_if(...)); idzie do wywalenia?
Nie idzie. Erase usuwa elementy z wektora. Nie usuwając ich, nadal tam są. To co się zmienia, to nie musisz używać więcej operatora delete na wskaźnikach, gdyż, będąc unique pointerami, po wywaleniu ich z wektora, ich destruktor sam zwolni pamięć.

Co do wydajności:

  1. Używanie vector::push_back w ten sposób jest nieoptymalne. Skoro znasz ilość particle'i przed wejściem w pętlę, to użyj przed pętlą funkcji std::reserve. push_back za każdym razem, gdy w wektorze nie ma już miejsca (size == capacity), alokuje nową pamięć i kopiuje do niej istniejące elementy, następnie usuwa stary blok.
  2. Za każdym dodaniem nowego ParticleEngine ( push_back(new ParticleEngine()); ) alokujesz dynamicznie pamięć dla jednego particle'a w miejscu , gdzie system Ci przydzieli. Raz, że alokacja małych obiektów za pomocą new jest bardzo mało wydajna, a dwa, że pojedyncze particle mogą być od siebie bardzo oddalone w pamięci, co powoduje częste cache misses. Załadowanie danych z RAM do cache trwa dużo dłużej niż gdyby były one w cache - stąd kolejny powód spadku wydajności. Zatem jeśli znasz maksymalną ilość cząstek to zaalokuj na starcie potrzebną pamięć w postaci tablicy, następnie zamiast new, użyj placement new. Ewentualnie napisz sobie prosty memory pool do alokacji cząstek.
  3. Proponuję również zamiast vectora używać std::deque. Tutaj nie ma realokacji, gdy skończy się miejsce, tylko doalokowywany jest kolejny blok. A bloki są w postaci listy. Jest to szybsze, gdy masz dużo operacji dodawania/usuwania.
0

Zrobiłem tablicę niedynamiczną i teraz to w ogóle mi całą apke kraszuje. Coś takiego mam:

#pragma once
#ifndef PLAYERGUN_H
#define PLAYERGUN_H

#include "Entity.h"
#include "ParticleEngine.h"

class PlayerGun : public Entity
{
public:
	PlayerGun(int posX, int posY, int moveDirection, SDL_Renderer *renderer);
	~PlayerGun();

	void Draw(SDL_Renderer *renderer);
	void Update();
	void UpdatePosX();

	void DrawPE(SDL_Renderer *renderer);
private:
	static const int MAX_PARTICLES = 10000;
	ParticleEngine* pe[MAX_PARTICLES];
	int sizePE;
	int maxMoveSpeed;
};

#endif

//----------------------------------------------------------------------------
#include "PlayerGun.h"

PlayerGun::PlayerGun(int posX, int posY, int moveDirection, SDL_Renderer *renderer)
{
	this->posX = posX;
	this->posY = posY;
	this->moveDirection = moveDirection;

	this->bDestroy = false;

	this->passedTime = SDL_GetTicks();

	this->maxMoveSpeed = 15;
	this->moveSpeed = 5

	this->hitBoxX = 16;
	this->hitBoxY = 16;

	eSprite = new Texture("playerBullet", renderer);

	this->sizePE = 1;
	for (int i = 0; i < MAX_PARTICLES; ++i)
	{
		pe[i] = new ParticleEngine(posX, posY, !moveDirection, renderer);
	}
}

PlayerGun::~PlayerGun()
{
	Entity::~Entity();
	for(int i = 0; i < MAX_PARTICLES; ++i)
	{
		delete pe[i];
	}
}

void PlayerGun::Draw(SDL_Renderer * renderer)
{
	DrawPE(renderer);
	eSprite->Draw(posX + Config::getSM()->GetGame()->getPosX(), posY + Config::getSM()->GetGame()->getPosY(), renderer, !moveDirection);
}

void PlayerGun::Update()
{
	if (bDestroy)
	{
		entityState = eDead;
	}
	else
	{
		UpdatePosX();	
	}
}

void PlayerGun::DrawPE(SDL_Renderer * renderer)
{
	for (int i = 0; i < MAX_PARTICLES; i++)
	{
		//if (pe[i]->nextParticle())
		//{
			//delete pe[i];
			pe[i] = new ParticleEngine(posX, posY, !moveDirection, renderer);
		//}
	}

	for (int i = 0; i < MAX_PARTICLES; ++i)
	{
		pe[i]->Draw(renderer);
	}
}

Dodatkowo teraz to już w ogóle mi tyle pamięci dodaje, że to masakra jest. Wtedy mając 50MB po jedym strzale miałem moze 55?, teraz jak zrobi cały "przebieg" to mam 150MB.

0

Nie wiem jak to zrobiłeś, specjalnie nie dbając o optymalizacje mam zużycie proca ok 3-6%, pamięci 8000K przy 400 rysowanych cząstkach, używając smart pointerów i push_back() i innych mało optymalnych rozwiązań. Jednak nie używam tekstur.

Zrób nowy projekt ale tylko system cząstek, bez żadnych dodatków, zoptymalizuj, potem ten system doklej do tego projektu.

0

Może jednak nie będę używał tekstur. Ogólnie to właśnie zauważyłem, że przed SDL_RenderClear(); nie ustawiałem koloru, w każdych projektach ustawiałem, więc byłem pewny, że tutaj też tak zrobiłem, stąd ten błąd, że tło mi się malowało za każdym razem kiedy chciałem pokolorować recta :D
Jak będę miał trochę czasu, to zrobie tak jak mówisz, nowy projet.

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