Właściwy sposób radzenia sobie z niszczeniem bytów gry

10

Wyobraź sobie świat gry, w którym ładunki i ładunki bytów cały czas są dynamicznie ładowane. Być może przedstawiłbym to jako listę bytów, ale co z ich usunięciem?

Podczas dodawania mogłem odpychać nowy byt, ale mogłem mieć potrzebę usunięcia go z dowolnego miejsca w kontenerze. Jakie opcje mam, aby uniknąć przeszukiwania elementu w celu znalezienia jego pozycji do usunięcia?

Myślałem, że mogę zapisać identyfikator jednostki jako jego pozycję w kontenerze, tworząc ścieżkę do bezpośredniego usunięcia, ale czy nie wygenerowałoby to pewnego rodzaju „zaburzenia” wzajemnej zależności?

Wiem, że właściwym sposobem byłoby coś takiego jak List.RemoveAt (whereToRemove); ale co jeśli tylko istota wie, kiedy powinna umrzeć?

A może po prostu czegoś brakuje, a kontener listy wiedziałby, kiedy obiekt jest zniszczony i zmniejsza swój własny rozmiar?

Grimshaw
źródło

Odpowiedzi:

10

Oto twoje warunki:

  • Inne obiekty mogą nadal zależeć od usuniętej istoty po jej usunięciu.

  • Chcesz, aby tylko jednostka określiła własne usunięcie.

Nie możesz mieć obu. Dlaczego? Ponieważ kod na wyższym poziomie niż sama encja (patrz przykłady poniżej) decyduje, kiedy encja musi zostać użyta. W związku z tym tylko kod na tym samym poziomie może określić, czy dany podmiot jest odpowiedni do usunięcia, czy nie.

Jednak to, co może się zdarzyć, że jednostka może zażądać swój własny usunięcie, przez wypalanie się zdarzenia, którego kod wyższy poziom nasłuchuje. Ten wyższy poziom następnie przechowuje to żądanie usunięcia na liście.


Przykład 1: bez zdarzeń

Sprawdzasz kolizje między bytami w twoim świecie. Jest to obsługiwane wyżej, zwykle w głównej pętli gry, która porównuje każdą jednostkę ze sobą. W tym przykładzie konkretnie, gdy jednostka zderza się z inną, tylko jej wewnętrzna logika może określić, ile szkód odniosła i czy „wygasła”. Podążajmy więc logiką w przypadku kolizji, w których masz cztery byty w swoim świecie: A, B, C i D.

Sprawdzamy A pod kątem kolizji z B. Jest kolizja. A otrzymuje obrażenia w 50%.

Sprawdzamy A pod kątem kolizji z C. Występuje kolizja. A otrzymuje obrażenia w 50%. Ponieważ obrażenia osiągają 0, A określa, że ​​„umarł”. Usuwa się z listy.

Sprawdzamy A pod kątem kolizji z D. Nie byłoby kolizji, ale nigdy nie dojdziesz tak daleko: otrzymujesz wyjątek czasu wykonywania, ponieważ lista twoich encji została zmodyfikowana w trakcie operacji trawersowania.

Przykład 2: ze zdarzeniami

Taka sama konfiguracja jak poprzednio.

Sprawdzamy A pod kątem kolizji z B. Jest kolizja. A otrzymuje obrażenia w 50%.

Sprawdzamy A pod kątem kolizji z C. Występuje kolizja. A otrzymuje obrażenia w 50%. Ponieważ obrażenia osiągają 0, A określa, że ​​„umarł”. Wystrzeliwuje zdarzenie do kodu zarządzania jednostką, mówiąc: „Usuń mnie jak najszybciej”. Kod zarządzania jednostką sprawdza odniesienie do podmiotu wysłane jako część zdarzenia i zapisuje to odniesienie na liście podmiotów do usunięcia.

Sprawdzamy A pod kątem kolizji z D. Nie ma kolizji, a sprawdzenie działa dobrze.

Teraz, na samym końcu bieżącej iteracji pętli gry , przejrzyj listę podmiotów do usunięcia i usuń każdy z nich z listy głównych podmiotów.


Możesz zobaczyć, jak to całkowicie uniknąć problemu. Nie musisz używać zdarzeń, możesz używać sygnałów lub czegoś innego, ale zasada jest taka sama - nie usuwaj bytów, dopóki nie będziesz mógł tego bezpiecznie zrobić. Drugą stroną tego podejścia, aby utrzymać porządek i porządek, jest to samo z dodawaniem bytów - pamiętaj, aby zachować do nich odniesienia i dodawać je tylko na początku kolejnej pętli gry.

Na koniec nie zapomnij opróżnić zarówno list do usunięcia, jak i list do dodania, za każdym razem, gdy używasz ich do dodawania / usuwania na głównej liście jednostek.

PS. Nie bój się przeszukiwać swojej głównej listy w celu indywidualnego usunięcia. Jest to nieodłączna część zarządzania jednostkami, a nawet ogromne listy przeszukują bardzo szybko - w końcu po to są zaprojektowane.

Inżynier
źródło
0

Na pewno szukasz HashMap / HashTable . Tablica skrótów to mapa, która dopasowuje klucz do określonej wartości. Klucz może być dowolny (na przykład identyfikator jednostki).

Setheron
źródło
0

Myślę, że możesz użyć pomysłu smartpointer do obsługi zwolnień dla ciebie, w takim przypadku nie będzie potrzeby przechowywania listy wszystkich encji w kodzie.

w niektórych przypadkach potrzebujesz listy, aby iterować wszystkie obiekty w grze. ta lista może być po prostu listą linków, w której wstawianie i usuwanie obiektów z niej zajmie dokładnie O (1) czas.

nawet aby zwiększyć prędkość bardziej, możesz użyć jakiegoś statycznego układu (być może wektora). w takim przypadku musisz śledzić 2 połączone listy w tym samym wektorze, jedna będzie iterować po prawidłowych obiektach, a druga na wolnych obiektach. ilekroć smartpointer oznacza miejsce do usunięcia, wystarczy usunąć ten wskaźnik i dodać jego miejsce do listy wolnych miejsc. i za każdym razem, gdy dodajesz jakiś byt, musisz tylko usunąć najpierw wolną przestrzeń, wypełnij go wskaźnikiem bytu, a następnie dodaj do listy prawidłowych obiektów.

Ali1S232
źródło