Już szukałem odpowiedzi, ale nie byłem w stanie znaleźć najlepszego podejścia do obsługi drogich funkcji / obliczeń.
W mojej obecnej grze (budynek miasta oparty na kafelkach 2D) użytkownik może stawiać budynki, budować drogi itp. Wszystkie budynki wymagają połączenia ze skrzyżowaniem, które użytkownik musi umieścić na granicy mapy. Jeśli budynek nie jest podłączony do tego skrzyżowania, nad budynkiem dotkniętym katastrofą pojawi się znak „Niepołączony z drogą” (w przeciwnym razie należy go usunąć). Większość budynków ma promień i może być również ze sobą powiązana (np. Straż pożarna może pomóc wszystkim domom w promieniu 30 płytek). Właśnie to muszę zaktualizować / sprawdzić, gdy zmieni się połączenie drogowe.
Wczoraj napotkałem duży problem z wydajnością. Spójrzmy na następujący scenariusz: Użytkownik może oczywiście również usuwać budynki i drogi. Jeśli więc użytkownik przerywa połączenie zaraz po skrzyżowaniu , muszę zaktualizować wiele budynków jednocześnie . Myślę, że jedną z pierwszych rad byłoby unikanie zagnieżdżonych pętli (co jest zdecydowanie dużym powodem w tym scenariuszu), ale muszę sprawdzić ...
- jeśli budynek jest nadal podłączony do skrzyżowania w przypadku, gdy płytka drogi została usunięta (robię to tylko dla budynków dotkniętych tą drogą). (W tym scenariuszu może to być mniejszy problem)
lista płytek w promieniu i zdobądź budynki w promieniu (zagnieżdżone pętle - duży problem!) .
// Go through all buildings affected by erasing this road tile. foreach(var affectedBuilding in affectedBuildings) { // Get buildings within radius. foreach(var radiusTile in affectedBuilding.RadiusTiles) { // Get all buildings on Map within this radius (which is technially another foreach). var buildingsInRadius = TileMap.Buildings.Where(b => b.TileIndex == radiusTile.TileIndex); // Do stuff. } }
To wszystko rozkłada mój FPS z 60 do prawie 10 na sekundę.
Mógłbym to zrobić. Moje pomysły to:
- Nieużywanie głównego wątku (funkcja aktualizacji) dla tego, ale innego wątku. Mogę napotkać problemy z blokowaniem, gdy zacznę korzystać z wielowątkowości.
- Korzystanie z kolejki do obsługi wielu obliczeń (jakie byłoby najlepsze podejście w tym przypadku?)
- Zachowaj więcej informacji w moich obiektach (budynkach), aby uniknąć dalszych obliczeń (np. Budynki w promieniu).
Korzystając z ostatniego podejścia, mogę zamiast tego usunąć jedno zagnieżdżenie w tej foreach:
// Go through all buildings affected by erasing this road tile.
foreach(var affectedBuilding in affectedBuildings) {
// Go through buildings within radius.
foreach(var buildingInRadius in affectedBuilding.BuildingsInRadius) {
// Do stuff.
}
}
Ale nie wiem czy to wystarczy. Gry takie jak Cities Skylines muszą obsługiwać znacznie więcej budynków, jeśli gracz ma dużą mapę. Jak sobie z tym radzą ?! Może istnieć kolejka aktualizacji, ponieważ nie wszystkie budynki aktualizują się w tym samym czasie.
Czekam na Wasze pomysły i komentarze!
Wielkie dzięki!