Systemy zdarzeń są niesamowite, sprawiają, że kodowanie jest wyjątkowo niewygodne i naprawdę pozwala na dynamiczne tworzenie gier poprzez łatwą komunikację obiektów i pętli gry. Mam trudności z wydajnością mojego obecnego wdrożenia. Obecnie moja niewielka optymalizacja podziału list obiektów na zdarzenia, na które odpowiadają, dokonała cudów, ale mogę zrobić więcej.
Obecnie mam dwie metody:
Najprostsze: wszystkie obiekty są dodawane do wektora, gdy wysyłane jest zdarzenie, wszystkie obiekty są wysyłane zdarzeniem za pomocą metody handle_event ()
Bardziej skomplikowane: mam mapę z ciągiem znaków jako kluczem i liczbą całkowitą jako wartością. Gdy dodawany jest typ zdarzenia, jest on dodawany do tej mapy, przy czym int jest po prostu zwiększany (musi być lepszy sposób)
wektor wektorów obiektów, a następnie wypycha nowy wektor, aby obsłużyć tego typu zdarzenie.
Gdy wywoływane jest zdarzenie, po prostu wywołuje odpowiednią int w odwzorowaniu eventTypes do typu wewnątrz wektora wektorów obiektów i wysyła to zdarzenie do każdego obiektu obsługującego ten typ zdarzenia.
Ta pierwsza metoda jest dość wolna (oczywiście) dla wielu obiektów, ale dość szybka dla bardzo niewielu obiektów. Natomiast druga metoda jest dość szybka w przypadku dużych obiektów, które chciałyby obsługiwać różne typy zdarzeń, ale wolniej niż pierwsza metoda na obiekt z obiektami obsługującymi ten sam typ zdarzenia.
Czy istnieje szybszy (rozsądny w czasie) sposób? Czy istnieje szybszy sposób na wyszukanie int z typu ciągu? (Początkowo miałem wyliczenie, ale nie pozwalało na niestandardowe typy, które są konieczne ze względu na pożądany poziom dynamiki).
źródło
Odpowiedzi:
Wygląda na to, że mówisz, że dużym wąskim gardłem wydajności jest wyszukiwanie identyfikatorów zdarzeń (liczb całkowitych) na podstawie ich nazw ciągów. Możesz wstępnie przetworzyć swoje dane gry, aby przekonwertować wszystkie nazwy zdarzeń na liczby całkowite przed uruchomieniem gry lub ewentualnie podczas ładowania poziomu; wtedy nie będziesz musiał dokonywać żadnych konwersji podczas gry.
Jeśli obiekty są często tworzone i niszczone, wektory obiektów mogą być bardzo zmienne. W takim przypadku możesz skorzystać z połączonych list zamiast wektorów; są szybsze w wstawianiu i usuwaniu.
źródło
EventType takeDamageEvent = EventSystem::CacheEventType("takeDamageEvent");
. Ustaw go jako statycznego członka klas, a będziesz miał tylko jedną kopię pływającą w każdej klasie, która tego potrzebuje.Cóż, najpierw usuńmy proste rzeczy. Masz to
map
między ciągami znaków (prawdopodobnie nazwą zdarzenia) i liczbami całkowitymi (indeks zarejestrowanych detektorów zdarzeń).Czas wyszukiwania w
map
zależy od dwóch rzeczy: liczby elementów na mapie oraz czasu potrzebnego na porównanie dwóch kluczy (ignorowanie problemów z pamięcią podręczną). Jeśli czas wyszukiwania stanowi problem, jednym ze sposobów radzenia sobie z nim jest zmiana funkcji porównywania poprzez zmianę typu łańcucha.Załóżmy, że używasz
std::string
ioperator<
do porównania. Jest to wysoce nieefektywne; porównuje bajty. Nie obchodzi cię rzeczywisty ciąg mniej niż porównanie; potrzebujesz tylko porównania, które daje ściśle słabe uporządkowanie (bomap
inaczej nie działa).Dlatego powinieneś użyć 32-bajtowego ciągu o stałej długości zamiast
std::string
. Używam ich do identyfikacji. Testy porównawcze dla tych ustalonych ciągów nie przeprowadzają porównań bajtowych; zamiast tego wykonują porównania 32-bitowe (lub 64-bitowe). Pobiera co 4 bajty jako liczbę całkowitą bez znaku i porównuje je z odpowiednimi 4 bajtami drugiego łańcucha. W ten sposób porównanie zajmuje maksymalnie 8 porównań. Zapewnia bardzo słabe uporządkowanie, chociaż uporządkowanie nie ma nic wspólnego z danymi jako znakami.Przechowywanie ciągów dłuższych niż 31 bajtów (wymaga znaku NULL) obcina ciąg (ale od środka, a nie na końcach. Uważam, że entropia jest zwykle największa na początku i na końcu). I ciągi krótsze niż to wypierają pozostałe znaki za pomocą
\0
.Teraz możesz
map
całkowicie porzucić i użyć tabeli skrótów. Jeśli naprawdę masz ponad 100 000 różnych rodzajów zdarzeń, może to być dobry pomysł. Ale nie znam gry, w której byłoby to zdalnie rozsądne.źródło
Aby odpowiedzieć na ogólne pytanie:
Nie ma żadnego. Wszystko, co możesz zrobić, to określić specyficzne potrzeby swoich systemów wydarzeń (może być ich kilka), a następnie użyć odpowiedniego narzędzia do odpowiedniej pracy.
Wdrożyłem i próbowałem uogólnić mnóstwo systemów zdarzeń i doszedłem do tego wniosku. Ponieważ kompromisy są naprawdę różne, jeśli ustawisz czas kompilacji lub wykonania, jeśli użyjesz hierarchii obiektów lub tablic itp.
Najlepszym sposobem jest przestudiowanie różnych rodzajów systemów zdarzeń, a następnie dowiesz się, który z nich użyć w danym przypadku.
Teraz, jeśli chcesz najbardziej elastycznego systemu, zaimplementuj system czarnej tablicy (środowisko wykonawcze) z dynamicznymi właściwościami zdarzeń, a będziesz mieć coś bardzo elastycznego, ale może bardzo powolnego.
źródło