Ta strona opisuje powszechne problemy z wydajnością i najlepsze praktyki w zakresie ich łagodzenia.
Obliczenia skryptów
Kosztowne operacje w kodzie Luau trwają dłużej do przetworzenia i mogą wpłynąć na liczbę klatek na sekundę. Chyba że jest wykonywany równolegle, kod Luau działa synchronicznie i blokuje główny wątek, aż natrafi na funkcję, która wstrzymuje wątek.
Powszechne problemy
Intensywne operacje na strukturach tabel - Złożone operacje, takie jak serializacja, deserializacja i głębokie klonowanie, niosą ze sobą duży koszt wydajności, szczególnie w przypadku dużych struktur tabel. Jest to szczególnie prawdziwe, gdy te operacje są rekursywne lub obejmują iterację po bardzo dużych strukturach danych.
Szukane zdarzenia o wysokiej częstotliwości - Wiązanie kosztownych operacji z zdarzeniami opartymi na klatkach z RunService bez ograniczenia częstotliwości oznacza, że operacje te są powtarzane w każdej klatce, co często skutkuje niepotrzebnym zwiększeniem czasu obliczeń. Te wydarzenia obejmują:
Łagodzenie
- Wywołuj kod w zdarzeniach RunService oszczędnie, ograniczając użycie do przypadków, gdy niezbędne jest wywoływanie o wysokiej częstotliwości (na przykład aktualizując kamerę). Możesz wykonać większość innych kodów w innych wydarzeniach lub rzadziej w pętli.
- Podziel duże lub kosztowne zadania przy użyciu task.wait(), aby rozłożyć pracę na wiele klatek.
- Zidentyfikuj i zoptymalizuj niepotrzebnie kosztowne operacje i użyj wielowątkowości dla obliczeniowo kosztownych zadań, które nie muszą uzyskiwać dostępu do modelu danych.
- Niektóre skrypty po stronie serwera mogą korzystać z generowania kodu natywnego, prostego flagi, która kompiluje skrypt do kodu maszynowego zamiast bajtkodów.
Zakresy MicroProfilera
| Zakres | Powiązane obliczenia |
| RunService.PreRender | Kod wykonujący się podczas zdarzenia PreRender |
| RunService.PreSimulation | Kod wykonujący się podczas zdarzenia Stepped |
| RunService.PostSimulation | Kod wykonujący się podczas zdarzenia Heartbeat |
| RunService.Heartbeat | Kod wykonujący się podczas zdarzenia Heartbeat |
Aby uzyskać więcej informacji na temat debugowania skryptów za pomocą MicroProfilera, zobacz bibliotekę debug, która zawiera funkcje do tagowania określonego kodu i dalszego zwiększania szczegółowości, takie jak debug.profilebegin i debug.profileend. Wiele metod API Roblox wywoływanych przez skrypty ma również swoje własne powiązane tagi MicroProfilera, które mogą dostarczyć przydatnych sygnałów.
Użycie pamięci przez skrypty
Wycieki pamięci mogą występować, gdy piszesz skrypty, które konsumują pamięć, którą garbage collector nie może prawidłowo zwolnić, gdy nie jest już używana. Wycieki są szczególnie powszechne po stronie serwera, ponieważ mogą być online przez wiele dni, podczas gdy sesja klienta jest znacznie krótsza.
Następujące wartości pamięci w Konsoli Dewelopera mogą wskazywać na problem wymagający dalszego zbadania:
- LuaHeap - Wysoka lub rosnąca konsumpcja sugeruje wyciek pamięci.
- InstanceCount - Ciągle rosnąca liczba instancji sugeruje, że odwołania do niektórych instancji w Twoim kodzie nie są zbierane przez garbage collector.
- PlaceScriptMemory - Dostarcza szczegółowe informacje o wykorzystaniu pamięci dla każdego skryptu.
Powszechne problemy
Pozostawianie połączeń aktywnych - Silnik nigdy nie zbiera zdarzeń połączonych z instancją ani żadnych wartości odwołanych wewnątrz połączonego callbacku. Dlatego aktywne połączenia zdarzeń oraz kod wewnątrz połączonych instancji, połączonych funkcji i odwołanych wartości są poza zakresem dla pamięci garbage collectora, nawet po wystąpieniu zdarzeń.
Chociaż zdarzenia są rozłączane, gdy instancja, do której należą, jest niszczona, powszechnym błędem jest zakładanie, że to dotyczy obiektów Player. Po odejściu użytkownika z gry silnik nie niszczy automatycznie ich reprezentatywnego obiektu Player i modelu postaci, więc połączenia do obiektu Player oraz instancji pod modelem postaci, takich jak CharacterAdded, nadal konsumują pamięć, jeśli ich nie rozłączysz w swoich skryptach. Może to skutkować bardzo znacznymi wyciekami pamięci w czasie na serwerze, gdy setki użytkowników wchodzi do gry i wychodzi z niej.
Tabele - Wstawianie obiektów do tabel, ale nie usuwanie ich, gdy nie są już potrzebne, powoduje niepotrzebne zużycie pamięci, szczególnie w tabelach śledzących dane użytkowników, gdy dołączają. Na przykład, poniższy przykład kodu tworzy tabelę dodającą informacje o użytkownikach za każdym razem, gdy użytkownik się dołącza:
Przykładlocal playerInfo = {}Players.PlayerAdded:Connect(function(player)playerInfo[player] = {} -- jakieś informacjeend)Jeśli nie usuniesz tych wpisów, gdy nie są już potrzebne, tabela wciąż rośnie i konsumuje więcej pamięci w miarę dołączania kolejnych użytkowników. Jakikolwiek kod, który iteruje po tej tabeli, staje się również coraz bardziej obliczeniowo kosztowny w miarę wzrostu rozmiaru tabeli.
Łagodzenie
Aby posprzątać wszystkie używane wartości w celu zapobiegania wyciekom pamięci:
Rozłącz wszystkie połączenia - Przejdź przez swoją bazę kodu i upewnij się, że każde połączenie zostało sprzątnięte za pomocą jednego z następujących sposobów:
- Rozłączanie ręczne przy użyciu funkcji Disconnect().
- Niszczenie instancji, do której należy zdarzenie, za pomocą funkcji Destroy().
- Niszczenie obiektu skryptu, z którym połączenie zostało jakieś powiązane.
Usuwaj obiekty gracza i postacie po opuszczeniu - Włącz Workspace.PlayerCharacterDestroyBehavior, aby automatycznie niszczyć obiekty gracza i modele postaci po odejściu użytkownika. Jeśli wolisz, możesz je posprzątać ręcznie:
Przykład sprzątania gracza i postacilocal Players = game:GetService("Players")Players.PlayerAdded:Connect(function(player)player.CharacterRemoving:Connect(function(character)task.defer(character.Destroy, character)end)end)Players.PlayerRemoving:Connect(function(player)task.defer(player.Destroy, player)end)
Obliczenia fizyki
Nadmierna symulacja fizyki może być kluczową przyczyną zwiększonego czasu obliczeń na klatkę zarówno po stronie serwera, jak i klienta.
Powszechne problemy
Nadmierna częstotliwość kroku fizyki - Domyślnie zachowanie kroku jest w trybie adaptacyjnym, gdzie fizyka działa przy 60 Hz, 120 Hz lub 240 Hz, w zależności od złożoności mechanizmu fizyki.
Dostępny jest również tryb stały z lepszą dokładnością fizyki, który zmusza wszystkie zbiory fizyki do działania przy 240 Hz (cztery razy na klatkę). To skutkuje znacznie większymi obliczeniami każdej klatki.
Nadmierna liczba złożonych obiektów symulowanych - Im więcej złożeń 3D jest symulowanych, tym dłużej trwają obliczenia fizyczne w każdej klatce. Często w grach symuluje się obiekty, które nie muszą być symulowane, lub mają mechanizmy, które mają więcej ograniczeń i stawów, niż są potrzebne.
Nadmiernie precyzyjna detekcja kolizji - Części siatki mają właściwość CollisionFidelity do wykrywania kolizji, która oferuje różne tryby o różnym poziomie wpływu na wydajność. Tryb precyzyjnej detekcji kolizji dla części siatki ma najdroższy koszt wydajności i zajmuje silnikowi więcej czasu na obliczenia.
Łagodzenie
Zablokuj części, które nie wymagają symulacji - Zablokuj wszystkie części, które nie muszą być napędzane przez fizykę, takie jak statyczne NPC.
Użyj adaptacyjnego kroku fizyki - Adaptacyjne kroki dynamicznie dostosowują częstotliwość obliczeń fizycznych dla mechanizmów fizycznych, pozwala to na rzadsze wykonywanie aktualizacji fizyki w niektórych przypadkach.
Zredukuj złożoność mechanizmów
- W miarę możliwości minimalizuj liczbę ograniczeń fizycznych lub stawów w złożeniu.
- Zmniejsz ilość kolizji wewnętrznych w mechanizmie, na przykład poprzez zastosowanie ograniczeń braku kolizji na kończynach ragdoll, aby zapobiec ich kolizjom ze sobą.
Zmniejsz użycie precyzyjnej kolizji dla siatek
W przypadku małych lub nieinteraktywnych obiektów, gdzie użytkownicy rzadko zauważają różnicę, używaj precyzyjności pudełkowej.
Dla małych i średnich obiektów, używaj precyzyjności pudełkowej lub kształtowej, w zależności od kształtu.
W przypadku dużych i bardzo złożonych obiektów stwórz niestandardowe kolizje używając niewidocznych części, gdy to możliwe.
Dla obiektów, które nie wymagają kolizji, wyłącz kolizje i używaj precyzyjności pudełkowej lub kształtowej, ponieważ geometria kolizji nadal jest przechowywana w pamięci.
Możesz renderować geometrię kolizji do celów debugowania w Studio, włączając Precyzyjność kolizji w widżecie Opcje wizualizacji w prawym górnym rogu widoku 3D.
Alternatywnie możesz zastosować filtr CollisionFidelity=PreciseConvexDecomposition do Eksploratora, który wyświetla liczbę wszystkich części siatki z precyzyjną precyzją i pozwala łatwo je wybierać.
Aby uzyskać szczegółowy przewodnik po wyborze opcji precyzyjnej kolizji, która równoważy Twoje wymagania dotyczące precyzji i wydajności, zobacz Ustaw parametry fizyki i renderowania.
Zakresy MicroProfilera
| Zakres | Powiązane obliczenia |
| physicsStepped | Ogólne obliczenia fizyczne |
| worldStep | Dyskretny krok fizyki podejmowany w każdej klatce |
Użycie pamięci w fizyce
Ruch fizyczny i detekcja kolizji zużywają pamięć. Części siatki mają właściwość CollisionFidelity, która określa podejście stosowane do oceny granic kolizji siatki.
Powszechny problem
Domyślne i precyzyjne tryby detekcji kolizji zużywają znacznie więcej pamięci niż dwa inne tryby z kształtami kolizji o niższej precyzyjności.
Jeśli widzisz wysoki poziom użycia pamięci pod PhysicsParts, może być konieczne rozważenie zmniejszenia precyzji kolizji obiektów w Twojej grze.
Jak łagodzić
Aby zmniejszyć pamięć używaną do precyzji kolizji:
- W przypadku części, które nie potrzebują kolizji, wyłącz ich kolizje, ustawiając BasePart.CanCollide, BasePart.CanTouch i BasePart.CanQuery na false.
- Zmniejsz precyzję kolizji, używając ustawienia CollisionFidelity. Box ma najniższe zużycie pamięci, a Default i Precise są zazwyczaj droższe.
- Generalnie jest bezpiecznie ustawić precyzję kolizji dowolnej małej zablokowanej części na Box.
- W przypadku bardzo złożonych dużych siatek warto stworzyć własną siatkę kolizji z mniejszych obiektów o precyzyjności pudełkowej.
Humanoidy
Humanoid to klasa, która zapewnia szeroki zakres funkcjonalności dla graczy i postaci niegraczy (NPC). Chociaż potężna, Humanoid wiąże się z istotnym kosztem obliczeniowym.
Powszechne problemy
- Pozostawianie wszystkich HumanoidStateTypes włączonych dla NPC - Istnieje koszt wydajności związany z pozostawieniem włączonymi niektórych HumanoidStateTypes. Wyłącz wszelkie, które nie są potrzebne dla Twoich NPC. Na przykład, chyba że Twoje NPC będą wspinać się po drabinach, bezpiecznie jest wyłączyć stan Climbing.
- Instancjonowanie, modyfikowanie i ponowne odradzanie modeli z Humanoids lub skórzonymi MeshParts zbyt często
- Może to być intensywne dla silnika do przetworzenia, szczególnie jeśli te modele używają ubrań warstwowych. Może to również być szczególnie problematyczne w grach, w których awatary często odradzają się.
- W MicroProfilerze, długie tagi updateInvalidatedFastClusters (ponad 4 ms) często są sygnałem, że instancjonowanie/modyfikacja awatara wyzwala nadmierne unieważnienia.
- Używanie humanoidów w przypadkach, gdy nie są wymagane - Statyczne NPC, które się nie poruszają, generalnie nie potrzebują klasy Humanoid.
- Odtwarzanie animacji na dużej liczbie NPC z serwera - Animacje NPC, które działają na serwerze, muszą być symulowane na serwerze i replikowane do klienta. Może to być niepotrzebne obciążenie.
- Wprowadzanie niepotrzebnych zmian w rozmiarze i skali - Zmiany rozmiaru/skalowania powodują, że FastCluster jest odbudowywane. Staraj się zmniejszyć to podczas rozgrywki, jeśli zauważasz problemy z wydajnością związane z FastCluster. Podobnie, inne zmiany właściwości mogą również prowadzić do odbudowy FastCluster, dlatego ogólnie ogranicz te zmiany tak bardzo, jak to możliwe.
Łagodzenie
- Odtwarzaj animacje NPC na kliencie - W grach z dużą liczbą NPC, rozważ utworzenie Animator na kliencie i uruchamianie animacji lokalnie. Zmniejsza to obciążenie na serwerze i potrzebę niepotrzebnej replikacji. Umożliwia to także dodatkowe optymalizacje (na przykład granie animacji tylko dla NPC, którzy są blisko postaci).
- Używaj przyjaznych dla wydajności alternatyw dla humanoidów - Modele NPC nie muszą koncentrować się na obiekcie humanoida.
- Dla statycznych NPC używaj prostego AnimationController, ponieważ nie muszą się poruszać, a tylko potrzebują odtwarzać animacje.
- Dla poruszających się NPC, rozważ wdrożenie własnego kontrolera ruchu i użycie AnimationController do animacji, w zależności od złożoności Twoich NPC.
- Wyłącz nieużywane stany humanoida - Używaj Humanoid:SetStateEnabled(), aby włączać tylko niezbędne stany dla każdego humanoida.
- Korzystaj z puli modeli NPC z częstym odradzaniem - Zamiast całkowicie niszczyć NPC, wysyłaj NPC do puli nieaktywnych NPC. W ten sposób, gdy nowy NPC musi zostać odrodzony, możesz po prostu ponownie aktywować jeden z NPC z puli. Proces ten nazywa się pulowaniem, co minimalizuje liczbę razy, kiedy postacie muszą być instancjonowane.
- Spawnować NPC tylko wtedy, gdy użytkownicy są w pobliżu - Nie spawnuj NPC, gdy użytkownicy nie są w zasięgu, i usuwaj je, gdy użytkownicy opuszczają ich zasięg.
- Unikaj wprowadzania zmian w hierarchii awatara po jego instancjonowaniu - Pewne modyfikacje w hierarchii awatara mają znaczne konsekwencje dla wydajności. Dostępne są niektóre optymalizacje:
- Dla własnych animacji proceduralnych, nie aktualizuj właściwości JointInstance.C0 i JointInstance.C1. Zamiast tego, aktualizuj właściwość Motor6D.Transform.
Zakresy MicroProfilera
| Zakres | Powiązane obliczenia |
| stepHumanoid | Kontrola humanoida i fizyka |
| stepAnimation | Animacja humanoida i animatora |
| updateInvalidatedFastClusters | Powiązane z instancjonowaniem lub modyfikowaniem awatara |
Renderowanie
Znaczna część czasu, jaki klient spędza każdą klatkę, to renderowanie sceny w bieżącej klatce. Serwer nie zajmuje się żadnym renderowaniem, więc ta sekcja dotyczy wyłącznie klienta.
Wywołania rysowania
Wywołanie rysowania to zestaw instrukcji od silnika do GPU, aby coś wyrenderować. Wywołania rysowania mają znaczny narzut. Generalnie, im mniej wywołań rysowania na klatkę, tym mniej czasu obliczeniowego spędza się na renderowaniu klatki.
Możesz sprawdzić, ile wywołań rysowania obecnie występuje w pozycji Statystyki Renderowania ⟩ Czas w Studio. Możesz zobaczyć Statystyki Renderowania w kliencie, naciskając ShiftF2.
Im więcej obiektów musi być narysowanych w Twojej scenie w danej klatce, tym więcej wywołań rysowania jest kierowanych do GPU. Jednak silnik Roblox wykorzystuje proces nazywany instancjonowaniem, aby połączyć identyczne siatki o tych samych cechach tekstury w jedno wywołanie rysowania. W szczególności wiele siatek o tym samym MeshContent jest obsługiwanych w jednym wywołaniu rysowania, gdy:
- SurfaceAppearances są identyczne, jeśli są obecne, w przeciwnym razie, gdy TextureContents są identyczne.
- Materiały są identyczne, gdy zarówno SurfaceAppearance, jak i MeshPart.TextureID nie istnieją.
Inne powszechne problemy
Nadmierna gęstość obiektów - Jeśli duża liczba obiektów jest skoncentrowana w wysokiej gęstości, to renderowanie tego obszaru sceny wymaga większej liczby wywołań rysowania. Jeśli zauważasz spadki klatek w momencie patrzenia na pewną część mapy, może to być dobry sygnał, że gęstość obiektów w tym obszarze jest zbyt wysoka.
Obiekty takie jak naklejki, tekstury i cząstki nie grupują się dobrze i wprowadzają dodatkowe wywołania rysowania. Zwróć szczególną uwagę na te typy obiektów w scenie. W szczególności, zmiany właściwości w ParticleEmitters mogą mieć znaczący wpływ na wydajność.
Zgubione okazje instancjonowania - Często w scenie znajduje się ta sama siatka powielona wiele razy, ale każda kopia siatki ma różne identyfikatory zasobów siatki lub tekstury. To uniemożliwia instancjonowanie i może prowadzić do niepotrzebnych wywołań rysowania.
Powszechną przyczyną tego problemu jest importowanie całej sceny naraz, zamiast importowania pojedynczych zasobów do Roblox, a następnie powielania ich po imporcie, aby skonstruować scenę.
Nawet prosty skrypt, jak ten, może pomóc Ci zidentyfikować części siatki o tej samej nazwie, które używają różnych identyfikatorów siatki:
for _,descendant in workspace:GetDescendants() doif descendant:IsA("MeshPart") thenprint(descendant.Name .. ", " .. descendant.MeshId)endendWynik (z włączoną Stack Lines) może wyglądać mniej więcej tak. Powtarzające się linie wskazują na ponowne użycie tej samej siatki, co jest dobre. Unikalne linie niekoniecznie są złe, ale w zależności od schematu nazewnictwa, mogą wskazywać na duplikaty siatek w Twojej grze:
LargeRock, rbxassetid://106420009602747 (x144) -- dobreLargeRock, rbxassetid://120109824668127LargeRock, rbxassetid://134460273008628LargeRock, rbxassetid://139288987285823LargeRock, rbxassetid://71302144984955LargeRock, rbxassetid://90621205713698LargeRock, rbxassetid://113160939160788LargeRock, rbxassetid://135944592365226 -- wszystkie potencjalne duplikatyNadmierna złożoność obiektów - Chociaż nie jest to tak ważne jak liczba wywołań rysowania, liczba trójkątów w scenie ma wpływ na to, jak długo trwa renderowanie klatki. Sceny z bardzo dużą liczbą bardzo złożonych siatek są powszechnym problemem, podobnie jak scenerie, w których właściwość MeshPart.RenderFidelity ustawiona jest na Precise w nadmiernych ilościach.
Nadmierne rzucanie cieni - Obsługa cieni jest kosztownym procesem, a mapy, które zawierają dużą liczbę i gęstość obiektów świetlnych rzucających cienie (lub dużą liczbę i gęstość małych części odpowiadających za cienie) mogą mieć problemy z wydajnością.
Wysoka przeźroczystość - Umieszczanie obiektów z częściową przeźroczystością blisko siebie zmusza silnik do renderowania nakładających się pikseli wielokrotnie, co może wpłynąć na wydajność. Aby uzyskać więcej informacji na temat identyfikacji i naprawiania tego problemu, zobacz Usuń warstwowe przezroczystości.
Niepotrzebny ruch skórzonych MeshPartów - Skórzane MeshPartsy, które są częścią modelu bez Humanoida, są grupowane przy użyciu przestrzennie zorganizowanych FastClusters. Kiedy te MeshPartsy się poruszają, muszą być nieustannie dodawane do i usuwane z tych przestrzennych klastrów, zmuszając klastry do odbudowy i wpływając na wydajność.
- Bardzo skutecznym obejściem jest osadzenie Humanoida wewnątrz modelu. Obecność Humanoida zastępuje domyślne zachowanie klasyfikacji przestrzennej, zmuszając do użycia jednego, zunifikowanego FastCluster dla całego modelu. W rezultacie aktualizacje pozycji nie wymagają już odbudowy klastrów, co łagodzi wąskie gardło wydajności. Ta technika powinna być zarezerwowana wyłącznie dla MeshPartów, które będą się poruszać, ponieważ może wprowadzać dodatkowe narzuty pamięci i negować korzyści płynące z optymalizacji przestrzennej. Zaleca się zawsze profilować swoją grę po wprowadzeniu takich zmian. Zobacz Porady dotyczące wydajności humanoidów w celu uzyskania dodatkowych informacji.
Zbyt wiele części w Model - Zbyt wiele części w modelu może prowadzić do częstszych odbudów z powodu potencjału zmiany właściwości części, co wymaga pełnej odbudowy. Znajdź odpowiednią równowagę części w modelu, gdy korzysta z FastCluster.
Łagodzenie
Instancjonowanie identycznych siatek i zmniejszanie liczby unikalnych siatek - Jeśli zapewnisz wszystkie identyczne siatki z tymi samymi podstawowymi identyfikatorami zasobów, silnik może je rozpoznać i wyrenderować w jednym wywołaniu rysowania. Upewnij się, że przesyłasz każdą siatkę na mapie tylko raz, a następnie powielasz je w Studio do ponownego użycia zamiast importować duże mapy jako całość, co może spowodować, że identyczne siatki będą miały oddzielne identyfikatory treści i będą rozpoznawane jako unikalne przez silnik. Pakiety są pomocnym mechanizmem do ponownego wykorzystywania obiektów.
Culling - Culling opisuje proces eliminacji wywołań rysowania dla obiektów, które nie mają znaczenia w ostatecznej renderowanej klatce. Domyślnie silnik pomija wywołania rysowania dla obiektów poza polem widzenia kamery (culling frustrum) oraz części, siatek i terenu zasłoniętych przez inne obiekty (culling zasłonięcia). W niektórych scenariuszach, takich jak środowiska wewnętrzne, możesz być w stanie wdrożyć system pomieszczeń lub portali i ręcznie ukrywać obiekty, aby dalej zmniejszyć wywołania rysowania lub ogólne obciążenie obliczeniowe.
Zmniejszenie poziomu szczegółowości modeli - Włącz streaming instancji i ustaw właściwość LevelOfDetail Twoich modeli świata na SLIM, aby renderować optymalizowane lekkie siatki SLIM w miarę oddalania się od kamery.
Zmniejszenie poziomu szczegółowości awatarów - Włącz streaming instancji i ustaw właściwość Workspace.EnableSLIMAvatars, aby renderować modele awatarów platform z dowolną liczbą akcesoriów lub warstw odzieżowych jako optymalizowane lekkie reprezentacje awatarów z pełnym wsparciem dla animacji w miarę oddalania się od kamery.
Zmniejszenie precyzji renderowania - Ustaw MeshPart.RenderFidelity na Automatic lub Performance. To pozwala na zastąpienie siatek mniej złożonymi alternatywami, co może zmniejszyć liczbę wielokątów, które muszą być rysowane.
Wyłączanie rzucania cieni na odpowiednich częściach i obiektach świetlnych - Silnik Roblox automatycznie degradują jakość cieni w miarę spadku poziomu jakości grafiki klienta, ostatecznie całkowicie wyłączając cienie na poziomach jakości poniżej 4. Możesz jednak selektywnie wyłączać właściwości rzucania cieni na obiektach świetlnych i częściach, aby poprawić wydajność, podczas gdy cienie są włączone i zwiększyć prawdopodobieństwo, że cienie pozostaną włączone. Oto kilka przykładów optymalizacji, które możesz dokonać w czasie edycji lub dynamicznie w czasie rzeczywistym:
Użyj właściwości BasePart.CastShadow, aby wyłączyć rzucanie cieni na małych częściach, gdzie cienie są mało widoczne. Ta strategia jest szczególnie skuteczna, gdy stosuje się ją do części, które są daleko od kamery użytkownika.
Wyłączaj cienie na obiektach poruszających się, gdy to możliwe.
Wyłącz Light.Shadows na instancjach świateł, gdy obiekt nie musi rzucać cieni.
Ogranicz zasięg i kąt instancji świetlnych.
Używaj mniej instancji świetlnych.
Rozważ wyłączenie świateł, które są poza określonym zasięgiem lub na zasadzie pokój po pokoju w środowiskach wewnętrznych.
Zakresy MicroProfilera
| Zakres | Powiązane obliczenia |
| Przygotowanie i wykonanie | Ogólne renderowanie |
| Wykonaj/Scena/computeLightingPerform | Aktualizacje siatki oświetlenia i cieni |
| LightGridCPU | Aktualizacje siatki światła voxel |
| ShadowMapSystem | Mapowanie cieni |
| Wykonaj/Scena/UpdateView | Przygotowanie do renderowania i aktualizacje cząstek |
| Wykonaj/Scena/RenderView | Renderowanie i przetwarzanie post |
Sieci i replikacja
Sieciowanie i replikacja opisują proces, w ramach którego dane są wysyłane między serwerem a podłączonymi klientami. Informacje są wysyłane między klientem a serwerem w każdej klatce, ale większe ilości informacji wymagają większej ilości czasu obliczeniowego.
Powszechne problemy
Nadmierny ruch zdalny - Wysyłanie dużej ilości danych za pomocą obiektów RemoteEvent lub RemoteFunction lub wywoływanie ich bardzo często może prowadzić do dużych ilości czasu CPU spędzonego na przetwarzaniu przychodzących pakietów w każdej klatce. Powszechne błędy obejmują:
- Replikacja danych w każdej klatce, które nie muszą być replikowane.
- Replikacja danych na podstawie wejścia użytkownika bez żadnego mechanizmu ograniczającego.
- Wysyłanie większej ilości danych, niż jest to wymagane. Na przykład, przesyłanie całego ekwipunku gracza, gdy kupuje przedmiot zamiast tylko szczegółów zakupionego przedmiotu.
Tworzenie lub usuwanie złożonych drzew instancji - Gdy na serwerze zostaje dokonana zmiana w modelu danych, jest ona replikowana do podłączonych klientów. Oznacza to, że tworzenie i niszczenie dużych hierarchii instancji, takich jak mapy w czasie działania, może być bardzo intensywne sieciowo.
Powszechnym winowajcą w tym przypadku są złożone dane animacji zapisywane przez wtyczki Animation Editor w riggach. Jeśli nie zostaną one usunięte przed opublikowaniem gry i animowany model jest regularnie klonowany, duża ilość danych będzie replikowana niepotrzebnie.
TweenService po stronie serwera - Jeśli TweenService jest używane do tweenowania obiektu po stronie serwera, tweenowana właściwość jest replikowana dla każdego klienta w każdej klatce. Nie tylko powoduje to, że tween jest drgający w miarę wahań latencji klientów, ale powoduje także wiele niepotrzebnych ruchów w sieci.
Łagodzenie
Możesz zastosować następujące taktyki, aby zmniejszyć niepotrzebną replikację:
- Unikaj wysyłania dużych ilości danych jednocześnie przez zdalne zdarzenia. Zamiast tego wysyłaj tylko niezbędne dane o niższej częstotliwości. Na przykład dla stanu postaci replikuj go, gdy się zmienia, a nie w każdej klatce.
- Podziel złożone drzewa instancji, takie jak mapy, i ładuj je w częściach, aby rozłożyć pracę replikacji na wiele klatek.
- Oczyść metadane animacji, szczególnie katalog animacji riggów, po zaimportowaniu.
- Ogranicz niepotrzebną replikację instancji, szczególnie w przypadkach, gdy serwer nie musi wiedzieć o stworzonych instancjach. Obejmuje to:
- Efekty wizualne, takie jak wybuch lub czar magiczny. Serwer tylko musi znać lokalizację, aby określić wynik, podczas gdy klienci mogą tworzyć efekty wizualne lokalnie.
- Modele wyświetlania przedmiotów w pierwszej osobie.
- Tween obiektów po stronie klienta, a nie serwera.
Zakresy MicroProfilera
| Zakres | Powiązane obliczenia |
| ProcessPackets | Przetwarzanie przychodzących pakietów sieciowych, takich jak wywołania zdarzeń i zmiany właściwości |
| Allocate Bandwidth and Run Senders | Wychodzące zdarzenia istotne na serwerach |
Użycie pamięci przez zasoby
Największym mechanism, jaki twórcy mają do dyspozycji, aby poprawić zużycie pamięci przez klienta, jest włączenie streamingu instancji.
Streaming instancji
Streaming instancji selektywnie ładowuje części modelu danych, które nie są wymagane, co może prowadzić do znacznej redukcji czasów ładowania i zwiększenia zdolności klienta do zapobiegania awariom pod wpływem presji pamięci.
Jeśli napotykasz problemy z pamięcią i masz wyłączony streaming instancji, rozważ aktualizację swojej gry, aby go obsłużyć, szczególnie jeśli Twój świat 3D jest duży. Streaming instancji opiera się na odległości w przestrzeni 3D, więc większe światy naturalnie korzystają z tego bardziej.
Jeśli streaming instancji jest włączony, możesz zwiększyć jego agresywność. Na przykład rozważ:
- Zmniejszenie użycia Enum.ModelStreamingMode.Persistent tam, gdzie to możliwe. Możesz potrzebować zaktualizować swoje skrypty, jeśli używasz go jako miary kompatybilności.
- Zmniejszenie Workspace.StreamingMinRadius i Workspace.StreamingTargetRadius.
Aby uzyskać więcej informacji na temat opcji streamingu i ich korzyści, zobacz właściwości streamingu.
Inne powszechne problemy
Duplikacja zasobów - Powszechnym błędem jest przesyłanie tego samego zasobu wielokrotnie, co skutkuje różnymi identyfikatorami zasobów. Może to prowadzić do ładowania tej samej treści do pamięci wiele razy.
Nadmierna ilość zasobów - Nawet gdy zasoby nie są identyczne, zdarzają się przypadki, gdy okazje do ponownego wykorzystania tego samego zasobu i zaoszczędzenia pamięci są marnowane.
Pliki audio - Pliki audio mogą być niespodziewanym wkładem w zużycie pamięci, szczególnie jeśli ładujesz je wszystkie do klienta jednocześnie, zamiast ładować tylko to, co potrzebujesz na części gry. Aby uzyskać strategie, zobacz Czasy ładowania.
Tekstury o wysokiej rozdzielczości - Zużycie pamięci graficznej dla tekstury nie jest związane z rozmiarem tekstury na dysku; liczba pikseli w teksturze decyduje o zużyciu pamięci. Na przykład tekstura o rozmiarze 1024x1024 pikseli zużywa cztery razy więcej pamięci graficznej niż tekstura o rozmiarze 512x512 pikseli.
Obrazy przesyłane do Roblox są transkodowane do ustalonego formatu, więc nie ma korzyści pamięciowych z przesyłania obrazów w modelu kolorowym związanym z mniejszą liczbą bajtów na piksel. Podobnie, kompresowanie obrazów przed przesłaniem lub usuwanie kanału alfa z obrazów, które nie potrzebują go, może zmniejszyć rozmiar obrazu na dysku, ale nie poprawia zużycia pamięci.
Gdy gra się ładuje, silnik automatycznie zaczyna od tekstur o niższej jakości, a następnie zwiększa jakość w zależności od dostępnej pamięci urządzenia, odległości od kamery, ilości miejsca na ekranie, które zajmuje tekstura, i innych czynników. Mimo to, strategiczne dostosowanie rozmiarów tekstur może poprawić zużycie pamięci w Twojej grze.
Łagodzenie
Przesyłaj zasoby tylko raz - Ponownie wykorzystuj ten sam identyfikator zasobu w obiektach i upewnij się, że te same zasoby, szczególnie siatki i obrazy, nie są przesyłane osobno wiele razy.
Znajdź i napraw duplikaty zasobów - Szukaj identycznych części siatki i tekstur, które są przesyłane wielokrotnie z różnymi identyfikatorami.
- Chociaż nie istnieje API do automatycznego wykrywania podobieństwa zasobów, możesz zebrać wszystkie identyfikatory zasobów obrazów w swoim miejscu (ręcznie lub za pomocą skryptu), pobrać je i porównać za pomocą narzędzi porównawczych zewnętrznych.
- Dla części siatki najlepszą strategią jest wzięcie unikalnych identyfikatorów siatki i uporządkowanie ich według rozmiaru, aby ręcznie zidentyfikować duplikaty.
- Zamiast używać osobnych tekstur dla różnych kolorów, przesyłaj jedną teksturę i używaj właściwości SurfaceAppearance.Color, aby zastosować różne tony do niej.
Importuj zasoby w mapie osobno - Zamiast importować całą mapę naraz, importuj i rekonstrukcjonuj zasoby w mapie osobno i zestawiaj je. Importer nie wykonuje żadnej de-dupikacji siatek, więc jeśli zaimportujesz dużą mapę z dużą ilością oddzielnych płytek podłogowych, każda z tych płytek zostanie zaimportowana jako osobny zasób (nawet jeśli są to duplikaty). Może to prowadzić do problemów z wydajnością i pamięcią w przyszłości, ponieważ każda siatka traktowana jest jako indywidualna i zajmuje pamięć oraz wywołania rysowania.
Ogranicz piksele obrazów do nie więcej niż konieczna ilość. Chyba że obraz zajmuje dużą ilość fizycznej przestrzeni na ekranie, zazwyczaj potrzebuje maksymalnie 512x512 pikseli. Większość mniejszych obrazów powinna mieć mniej niż 256x256 pikseli.
Używaj arkuszy tnących w celu zapewnienia maksymalnego ponownego wykorzystania tekstur w mapach 3D. Aby uzyskać kroki i przykłady, jak stworzyć arkusze tnące, zobacz Utwórz arkusze tnące.
Możesz również rozważyć użycie arkuszy sprite’ów, aby załadować wiele mniejszych obrazów interfejsu użytkownika jako jeden obrazek. Następnie możesz użyć ImageLabel.ImageRectOffset i ImageLabel.ImageRectSize, aby wyświetlić fragmenty arkusza.
Czasy ładowania
Wiele gier implementuje niestandardowe ekrany ładowania i używa metody ContentProvider:PreloadAsync(), aby żądać zasobów, tak aby obrazy, dźwięki i siatki były pobierane w tle.
Zaletą tego podejścia jest to, że pozwala Ci upewnić się, że ważne części gry są w pełni załadowane bez połączeń. Jednak powszechnym błędem jest nadmierne wykorzystywanie tej metody do wstępnego załadowania większej ilości zasobów, niż jest to rzeczywiście wymagane.
Przykładem złej praktyki jest ładowanie całego Workspace. Chociaż zapobiega to włączeniu tekstur, znacznie zwiększa czasy ładowania.
Inną podobną praktyką jest korzystanie z ContentProvider.RequestQueueSize, aby upewnić się, że wszystkie żądane zasoby zakończyły ładowanie. Jednak to też rodzi ten sam problem znacznie zwiększonych czasów ładowania, a także jest niestabilną metodą ze względu na jej zmienny charakter.
Zamiast tego korzystaj z ContentProvider:PreloadAsync() tylko w sytuacjach niezbędnych, które obejmują:
- Obrazy w ekranie ładowania.
- Ważne obrazy w menu gry, takie jak tła przycisków i ikony.
- Ważne zasoby w obszarze startowym lub spawnowania.
Jeśli musisz załadować dużą ilość zasobów, zaleca się, abyś dostarczył przycisk Pomiń ładowanie.