Poprawa wydajności

*Ta zawartość została przetłumaczona przy użyciu narzędzi AI (w wersji beta) i może zawierać błędy. Aby wyświetlić tę stronę w języku angielskim, kliknij tutaj.

Ta strona opisuje powszechne problemy z wydajnością oraz najlepsze praktyki, aby je złagodzić.

Obliczenia skryptów

Kosztowne operacje w kodzie Luau zajmują więcej czasu na przetwarzanie i mogą wpłynąć na liczbę klatek na sekundę. O ile nie są wykonywane równolegle, kod Luau działa synchronicznie i blokuje główny wątek, aż napotka funkcję, która oddaje wątek.

Powszechne problemy

  • Intensywne operacje na strukturach tabel - Złożone operacje, takie jak serializacja, deserializacja i głębokie klonowanie, generują wysokie koszty wydajności, szczególnie w przypadku dużych struktur tabel. Jest to szczególnie prawdziwe, jeśli te operacje są rekurencyjne lub dotyczą bardzo dużych struktur danych.

  • Wydarzenia o wysokiej częstotliwości - Częste powiązanie kosztownych operacji z wydarzeniami oparte na klatkach z RunService bez ograniczania częstotliwości oznacza, że te operacje są powtarzane w każdej klatce, co często prowadzi do zbędnego zwiększenia czasu obliczeń. Wydarzenia te obejmują:

Złagodzenie

  • Zapewnij, aby kod był wywoływany podczas wydarzeń RunService oszczędnie, ograniczając jego użycie do przypadków, w których wysokoczęstotliwościowe wywołanie jest niezbędne (na przykład aktualizowanie kamery). Możesz wykonywać większość innych kodów w innych wydarzeniach lub rzadziej w pętli.
  • Rozłóż duże lub kosztowne zadania za pomocą task.wait(), aby rozłożyć prace na kilka klatek.
  • Zidentyfikuj i optymalizuj niepotrzebnie kosztowne operacje i używaj wielowątkowości do kosztownych zadań obliczeniowych, które nie muszą uzyskiwać dostępu do modelu danych.
  • Niektóre skrypty po stronie serwera mogą korzystać z generacji kodu natywnego, co jest prostym flagowaniem, które kompiluje skrypt do kodu maszynowego zamiast bajtowego.

Zakresy MicroProfilera

ZasięgPowiązane obliczenia
RunService.PreRenderKod wykonujący się podczas wydarzenia PreRender
RunService.PreSimulationKod wykonujący się podczas wydarzenia Stepped
RunService.PostSimulationKod wykonujący się podczas wydarzenia Heartbeat
RunService.HeartbeatKod wykonujący się podczas wydarzenia Heartbeat

Aby uzyskać więcej informacji na temat debugowania skryptów za pomocą MicroProfilera, zobacz bibliotekę debug, która zawiera funkcje do tagowania konkretnego 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ż własne powiązane tagi MicroProfilera, które mogą dostarczyć użytecznych sygnałów.

Użycie pamięci przez skrypty

Wycieki pamięci mogą występować, gdy piszesz skrypty, które konsumują pamięć, której garbage collector nie jest w stanie poprawnie zwolnić, gdy nie jest już w użyciu. Wycieki są szczególnie powszechne po stronie serwera, ponieważ mogą być nieprzerwanie online przez wiele dni, podczas gdy sesja klienta jest znacznie krótsza.

Poniższe wartości pamięci w Konsoli Dewelopera mogą wskazywać problemy wymagające dalszego dochodzenia:

  • LuaHeap - Wysoka lub rosnąca konsumpcja sugeruje wyciek pamięci.
  • InstanceCount - Ciągłe wzrastanie liczby instancji sugeruje, że odniesienia do niektórych instancji w twoim kodzie nie są zbierane przez garbage collector.
  • PlaceScriptMemory - Dostarcza szczegółowe informacje o użyciu pamięci z podziałem na skrypty.

Powszechne problemy

  • Pozostawianie połączeń aktywnych - Silnik nigdy nie zbiera pamięci dla zdarzeń podłączonych do instancji oraz jakichkolwiek wartości odwołanych wewnątrz podłączonego callbacka. W związku z tym, aktywne połączenia zdarzeń oraz kod wewnątrz podłączonych instancji, funkcji podłączonych i odwołanych wartości są poza zasięgiem dla garbage collectora pamięci, nawet po tym, jak zdarzenia są wywołane.

    Choć zdarzenia są odłączane, gdy instancja, do której należą, jest zniszczona, powszechnym błędem jest zakładanie, że dotyczy to obiektów Player. Po opuszczeniu gry przez użytkownika, silnik nie niszczy automatycznie ich reprezentacyjnego obiektu Player i modelu postaci, więc połączenia z obiektem Player oraz instancjami pod model postaci, takimi jak CharacterAdded, wciąż zużywają pamięć, jeśli nie odłączysz ich w swoich skryptach. Może to prowadzić do bardzo znacznych wycieków pamięci w czasie, gdy setki użytkowników dołączają i opuszczają grę.

  • Tabele - Wstawianie obiektów do tabel, ale nie usuwanie ich, gdy nie są już potrzebne, powoduje zbędne zużycie pamięci, szczególnie dla tabel, które śledzą dane użytkowników podczas dołączania. Na przykład, poniższy fragment kodu tworzy tabelę dodając informacje o użytkownikach za każdym razem, gdy użytkownik dołącza:

    Przykład
    local playerInfo = {}
    Players.PlayerAdded:Connect(function(player)
    playerInfo[player] = {} -- some info
    end)

    Jeśli nie usuniesz tych wpisów, gdy nie są już potrzebne, tabela nadal rośnie, powodując większe zużycie pamięci w miarę dołączania większej liczby użytkowników. Jakikolwiek kod, który iteruje przez tę tabelę, staje się również bardziej kosztowny obliczeniowo w miarę wzrostu rozmiaru tabeli.

Złagodzenie

Aby posprzątać wszystkie używane wartości w celu zapobiegania wyciekom pamięci:

  • Odłącz wszystkie połączenia - Przejdź przez swoją bazę kodu i upewnij się, że każde połączenie jest czyszczone przez jedną z następujących ścieżek:

    • Ręczne odłączenie za pomocą funkcji Disconnect().
    • Zniszczenie instancji, do której należy zdarzenie, za pomocą funkcji Destroy().
    • Zniszczenie obiektu skryptowego, do którego właściwość połączenia się odnosi.
  • Usuwaj obiekty graczy i postacie po opuszczeniu - Włącz Workspace.PlayerCharacterDestroyBehavior, aby automatycznie zniszczyć obiekty graczy i modele postaci po opuszczeniu przez użytkownika. Jeśli wolisz, możesz zamiast tego wyczyścić je ręcznie:

    Przykład czyszczenia graczy i postaci
    local 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 fizyczne

Nadmierna symulacja fizyczna może być kluczową przyczyną zwiększonego czasu obliczeń na klatkę zarówno po stronie serwera, jak i klienta.

Powszechne problemy

  • Nadmierna częstotliwość kroków fizycznych - Domyślnie, działanie kroków fizycznych jest w trybie adaptacyjnym, gdzie fizyka działa z częstotliwością 60 Hz, 120 Hz lub 240 Hz, w zależności od złożoności mechanizmu fizyki.

    Dostępny jest również sztywny tryb z lepszą precyzją fizyki, który wymusza, aby wszystkie zestawy fizyczne działały z częstotliwością 240 Hz (cztery razy na klatkę). To prowadzi do znacznie większych obliczeń w każdej klatce.

  • Nadmierna liczba i złożoność symulowanych obiektów - Im więcej 3D zespół jest symulowany, tym dłużej obliczenia fizyczne trwają w każdej klatce. Często w grach będą obiekty, które nie muszą być symulowane lub będą miały mechanizmy, które mają więcej ograniczeń i stawów niż potrzebują.

  • Zbyt precyzyjne wykrywanie kolizji - Części siatkowe mają właściwość CollisionFidelity do wykrywania kolizji, która oferuje różne tryby o różnych poziomach wpływu na wydajność. Tryb precyzyjnego wykrywania kolizji dla części siatkowych ma największy koszt wydajności i zajmuje silnikowi więcej czasu na obliczenia.

Złagodzenie

  • Kotwicz części, które nie wymagają symulacji - Kotwicz wszystkie części, które nie muszą być napędzane przez fizykę, takie jak statyczne NPC.

  • Użyj adaptacyjnego kroku fizycznego - Adaptacyjne kroki dynamicznie dostosowują częstość obliczeń fizycznych dla mechanizmów fizycznych, co pozwala na mniej częste aktualizacje fizyki w niektórych przypadkach.

  • Zredukuj złożoność mechanizmu

    • Tam, gdzie to możliwe, zminimalizuj liczbę ograniczeń fizycznych lub stawów w zespole.
    • Zmniejsz ilość samo-kolizji w mechanizmie, np. poprzez zastosowanie ograniczeń lub braku kolizji dla kończyn ragdoll, aby zapobiec ich kolizji ze sobą.
  • Zredukuj użycie precyzyjnej wierności kolizji dla siatek

    • Dla małych lub nieinteraktywnych obiektów, gdzie użytkownicy rzadko zauważyliby różnicę, używaj wierności pudełkowej.

    • Dla małych i średnich obiektów użyj wierności pudełkowej lub powłokowej, w zależności od kształtu.

    • Dla dużych i bardzo złożonych obiektów, twórz niestandardowe kolizje z niewidzialnych części, gdy to możliwe.

    • Dla obiektów, które nie wymagają kolizji, wyłącz kolizje i używaj wierności pudełkowej lub powłokowej, ponieważ geometria kolizji nadal jest przechowywana w pamięci.

    • Możesz renderować geometrię kolizji do celów debugowania w Studio, włączając Wierność kolizji w widżecie Opcje wizualizacji w prawym górnym rogu 3D viewport.

      Alternatywnie, możesz zastosować filtr CollisionFidelity=PreciseConvexDecomposition w Eksploratorze, który pokaże liczbę wszystkich części siatkowych z precyzyjną wiernością oraz umożliwi łatwy wybór.

    • Aby uzyskać dokładny przegląd, jak wybrać opcję wierności kolizji, która równoważy wymagania co do precyzji i wydajności, zobacz Ustaw parametry fizyki i renderowania.

Zakresy MicroProfilera

ZasięgPowiązane obliczenia
physicsSteppedOgólne obliczenia fizyczne
worldStepDyskretny krok fizyczny podejmowany w każdej klatce

Użycie pamięci przez fizykę

Ruch fizyczny i wykrywanie kolizji zużywają pamięć. Części siatkowe mają właściwość CollisionFidelity, która określa podejście używane do oceny granic kolizji siatki.

Powszechny problem

Domyślne i precyzyjne tryby wykrywania kolizji zużywają znacznie więcej pamięci niż dwa inne tryby z niższymi kształtami kolizji.

Jeśli widzisz wysokie poziomy zużycia pamięci w kategorii PhysicsParts, możesz potrzebować zmniejszyć wierność kolizji obiektów w swojej grze.

Jak złagodzić

Aby zmniejszyć pamięć używaną do wierności kolizji:

  • Dla części, które nie potrzebują kolizji, wyłącz ich kolizje, ustawiając BasePart.CanCollide, BasePart.CanTouch i BasePart.CanQuery na false.
  • Zmniejsz wierność kolizji, używając ustawienia CollisionFidelity. Box ma najniższe obciążenie pamięci, a Default i Precise są generalnie droższe.
    • Zazwyczaj bezpiecznie jest ustawić dowolny mały zakotwiczony obiekt na wierność kolizji Box.
    • Dla bardzo złożonych dużych siatek, możesz chcieć zbudować swoją własną siatkę kolizyjną z mniejszych obiektów z wiernością pudełkową.

Humanoidy

Humanoid to klasa, która zapewnia szeroki zakres funkcjonalności dla graczy i postaci nie-graczy (NPC). Choć potężna, klasa Humanoid wiąże się ze znacznymi kosztami obliczeniowymi.

Powszechne problemy

  • Pozostawienie wszystkich HumanoidStateTypes włączonych dla NPC - Istnieje koszt wydajności związany z pozostawieniem niektórych HumanoidStateTypes włączonych. Wyłącz te, które nie są potrzebne dla Twoich NPC. Na przykład, chyba że Twój NPC będzie wspinał się na drabiny, bezpiecznie jest wyłączyć stan Climbing.
  • Instancjonowanie, modyfikowanie i odradzanie modeli z Humanoids lub spakowanych MeshParts często
    • Może to być intensywne do przetworzenia przez silnik, szczególnie, jeśli te modele korzystają z warstw odzieżowych. Może to być szczególnie problematyczne w grach, w których awatary często się odradzają.
    • W MicroProfilerze długie tagi updateInvalidatedFastClusters (ponad 4 ms) często są sygnałem, że instancjonowanie/modyfikacja awatara wywołuje nadmierne unieważnienia.
  • Używanie Humanoids w przypadkach, gdy nie są wymagane - Statyczne NPC, które nie poruszają się, zazwyczaj 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ć zbędne obciążenie.
  • Wykonywanie zbędnych zmian rozmiaru i skali - Zmiany rozmiaru/skali powodują, że FastCluster jest przebudowywany. Staraj się ograniczać 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ż powodować przebudowę FastCluster, więc ogólnie staraj się ograniczać te zmiany tak bardzo, jak to możliwe.

Złagodzenie

  • Odtwarzaj animacje NPC po stronie klienta - W grach z dużą liczbą NPC, rozważ stworzenie Animator po stronie klienta i uruchamianie animacji lokalnie. Zmniejsza to obciążenie serwera i potrzebę niepotrzebnej replikacji. Umożliwia to również dodatkowe optymalizacje (takie jak odtwarzanie animacji tylko dla NPC, którzy są blisko postaci).
  • Używaj przyjaznych dla wydajności alternatyw do Humanoids - Modele NPC nie muszą koniecznie zawierać obiektu humanoida.
    • Dla statycznych NPC, użyj prostego AnimationController, ponieważ nie muszą się poruszać, a jedynie odtwarzać animacje.
    • Dla poruszających się NPC, rozważ implementację swojego własnego kontrolera ruchu i używanie AnimationController do animacji, w zależności od złożoności Twojego NPC.
  • Wyłącz nieużywane stany humanoida - Użyj Humanoid:SetStateEnabled(), aby włączyć tylko niezbędne stany dla każdego humanoida.
  • Pooling modeli NPC z częstym odradzaniem się - Zamiast całkowicie niszczyć NPC, wyślij NPC do puli nieaktywnych NPC. W ten sposób, gdy nowy NPC musi się odrodzić, możesz po prostu reaktywować jednego z NPC z puli. Proces ten nazywa się poolingiem, co minimalizuje liczbę koniecznych instancji postaci.
  • Spawnować NPC tylko wtedy, gdy użytkownicy są w pobliżu - Nie spawnuj NPC, gdy użytkownicy nie są w zasięgu, i wykluczaj ich, gdy użytkownicy opuszczają ich zasięg.
  • Unikaj wprowadzania zmian w hierarchii awatara po jego instancjonowaniu - Niektóre modyfikacje hierarchii awatara mają znaczące implikacje związane z wydajnością. Dostępne są niektóre optymalizacje:

Zakresy MicroProfilera

ZasięgPowiązane obliczenia
stepHumanoidKontrola humanoida i fizyka
stepAnimationAnimacja humanoida i animatora
updateInvalidatedFastClustersPowiązane z instancjonowaniem lub modyfikowaniem awatara

Rendering

Znacząca część czasu, który klient spędza w każdej klatce, jest poświęcona renderowaniu sceny w bieżącej klatce. Serwer nie wykonuje żadnych operacji renderujących, więc ta sekcja jest przeznaczona wyłącznie dla klienta.

Wywołania rysowania

Wywołanie rysowania to zestaw instrukcji od silnika do GPU w celu wyrenderowania czegoś. Wywołania rysowania mają znaczną ilość narzutów. Generalnie, im mniej wywołań rysowania na klatkę, tym mniej czasu obliczeniowego jest spędzanego na renderowaniu klatki.

Możesz sprawdzić, ile wywołań rysowania obecnie zachodzi, korzystając z elementu Statystyki renderowaniaCzas w Studio. Możesz wyświetlić 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 dokonanych do GPU. Jednak silnik Roblox wykorzystuje proces nazywany instancjonowaniem do łączenia identycznych siatek 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:

Inne powszechne problemy

  • Nadmierna gęstość obiektów - Jeśli duża liczba obiektów jest skoncentrowana z wysoką gęstością, to renderowanie tego obszaru sceny wymaga więcej wywołań rysowania. Jeśli zauważasz, że liczba klatek na sekundę spada, gdy patrzysz na określoną 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ąsteczki nie grupują się dobrze i wprowadzają dodatkowe wywołania rysowania. Zwróć szczególną uwagę na te rodzaje obiektów w scenie. W szczególności, zmiany właściwości ParticleEmitters mogą mieć dramatyczny wpływ na wydajność.

  • Przegapione możliwości instancjonowania - Często scena zawiera tę samą siatkę powieloną wielokrotnie, ale każda kopia siatki ma inne identyfikatory zasobów siatki lub textur. To uniemożliwia instancjonowanie i może prowadzić do zbędnych wywołań rysowania.

    Powszechną przyczyną tego problemu jest, gdy cały widok jest importowany naraz, zamiast importowaniu pojedynczych zasobów do Roblox, a następnie duplicowaniu ich po imporcie, aby zbudować scenę.

    Nawet prosty skrypt jak ten może pomóc Ci zidentyfikować części siatkowe z tym samym imieniem, które używają różnych identyfikatorów siatek:

    for _,descendant in workspace:GetDescendants() do
    if descendant:IsA("MeshPart") then
    print(descendant.Name .. ", " .. descendant.MeshId)
    end
    end

    Wyjście (z Stack Lines włączony) 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 nie są koniecznie złe, ale w zależności od schematu nazewnictwa, mogą wskazywać na duplikację siatek w Twojej grze:

    LargeRock, rbxassetid://106420009602747 (x144) -- good
    LargeRock, rbxassetid://120109824668127
    LargeRock, rbxassetid://134460273008628
    LargeRock, rbxassetid://139288987285823
    LargeRock, rbxassetid://71302144984955
    LargeRock, rbxassetid://90621205713698
    LargeRock, rbxassetid://113160939160788
    LargeRock, rbxassetid://135944592365226 -- all possible duplicates
  • Nadmierna złożoność obiektów - Chociaż nie jest to tak istotne jak liczba wywołań rysowania, liczba trójkątów w scenie wpływa na czas renderowania klatki. Sceny z bardzo dużą liczbą bardzo złożonych siatek są powszechnym problemem, podobnie jak sceny, w których właściwość MeshPart.RenderFidelity jest ustawiona na Precise na zbyt wielu siatkach.

  • 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 posiadają dużą liczbę i gęstość małych części wpływających na cienie) mogą napotkać problemy z wydajnością.

  • Wysoka przeźroczystość - Umieszczanie obiektów z częściową przeźroczystością w pobliżu siebie zmusza silnik do renderowania nakładających się pikseli wiele razy, co może zaszkodzić wydajności. Aby uzyskać więcej informacji na temat identyfikowania i naprawiania tego problemu, zobacz Usuń warstwowe przeźroczystości.

  • Niepotrzebny ruch siatkowanych MeshPartów - Siatkowane MeshPart, które są częścią modelu bez humanoida, są grupowane za pomocą szybko zorganizowanych FastClusters. Kiedy te MeshPart przesuwają się, muszą być ciągle dodawane i usuwane z tych przestrzennych klastrów, co wymusza ich przebudowę i wpływa na wydajność.

    • Skutecznym rozwiązaniem jest osadzenie humanoida w modelu. Obecność humanoida nadpisuje domyślne zachowanie w spatial clustering, co narzuca użycie jednego, zjednoczonego FastCluster dla całego modelu. W ten sposób aktualizacje pozycji nie wymagają ponownej budowy klastrów, co zatem łagodzi wąskie gardła wydajności. Ta technika powinna być reserva wyłącznie dla MeshPart z przewidywanym ruchem, ponieważ może wprowadzać narzut pamięci i negować korzyści z przestrzennej optymalizacji. Zawsze polecamy profilować swoją grę po wprowadzeniu tego rodzaju zmian. Zobacz Porady dotyczące wydajności humanoidów po więcej informacji.
  • Zbyt wiele części w Model - Zbyt wiele części w Modelu może powodować częstsze przebudowy z powodu możliwości zmiany właściwości części, co prowadzi do wymogu pełnej przebudowy. Znajdź właściwą równowagę części w Modelu, gdy jest używany FastCluster.

Złagodzenie

  • Instancjonowanie identycznych siatek i zmniejszenie liczby unikalnych siatek - Jeśli upewnisz się, że wszystkie identyczne siatki mają te same identyfikatory zasobów, silnik może rozpoznać i zrenderować je w jednym wywołaniu rysowania. Upewnij się, że ładujesz każdą siatkę w mapie raz i następnie duplikujesz je w Studio do ponownego wykorzystania, zamiast importować duże mapy jako całość, co może powodować, że identyczne siatki mają różne identyfikatory treści i są rozpoznawane przez silnik jako unikalne zasoby. 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 dla ostatniej renderowanej klatki. Domyślnie silnik pomija wywołania rysowania dla obiektów poza polem widzenia kamery (przestrzenne culling) oraz części, siatki i tereny zasłonięte przez inne obiekty (culling zasłaniania). W niektórych scenariuszach, takich jak środowiska wewnętrzne, możesz być w stanie wdrożyć system pokoi lub portali i ręcznie wykluczać obiekty, aby jeszcze bardziej zredukować wywołania rysowania lub całkowite obciążenie obliczeniowe.

  • Zmniejszanie poziomu szczegółowości dla modeli - Włącz streaming instancji i ustaw właściwość LevelOfDetail na SLIM, aby renderować optymalizowane, lekkie siatki SLIM dla modeli w miarę oddalania się od kamery.

  • Zmniejszanie poziomu szczegółowości dla awatarów - Włącz streaming instancji i ustaw Workspace.EnableSLIMAvatars, aby renderować platformowe awatary jako optymalizowane, lekkie reprezentacje SLIM z pełnym wsparciem animacji w miarę oddalania się od kamery.

  • Zmniejszanie wierności renderowania - Ustaw MeshPart.RenderFidelity na Automatic lub Performance. To pozwala na przejście siatek do mniej skomplikowanych alternatyw, 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 zmniejsza jakość cieni w miarę spadku jakości grafiki klienta, ostatecznie wyłączając cienie całkowicie na poziomach jakości poniżej 4. Jednak możesz selektywnie wyłączyć właściwości rzucania cieni dla obiektów świetlnych i części, aby poprawić wydajność, gdy cienie są włączone, i zwiększyć prawdopodobieństwo, że cienie pozostaną włączone. Oto niektóre przykłady optymalizacji, które można wykonać zarówno w czasie edycji, jak i dynamicznie w czasie uruchamiania:

    • Użyj właściwości BasePart.CastShadow, aby wyłączyć rzucanie cieni na małych częściach, gdzie cienie najprawdopodobniej nie będą widoczne. Ta strategia jest szczególnie skuteczna, gdy zastosowana jest do części, które są daleko od kamery użytkownika.

    • Wyłącz cienie na ruchomych obiektach, gdy to możliwe.

    • Wyłącz Light.Shadows na instancjach światła, gdzie obiekt nie musi rzucać cieni.

    • Ogranicz zakres i kąt instancji świetlnych.

    • Użyj mniejszych instancji świetlnych.

    • Rozważ wyłączenie świateł, które są poza określonym zasięgiem lub na podstawie pomieszczeń dla środowisk wewnętrznych.

Zakresy MicroProfilera

ZasięgPowiązane obliczenia
Prepare and PerformOgólne renderowanie
Perform/Scene/computeLightingPerformAktualizacje siatki oświetlenia i cieni
LightGridCPUAktualizacje siatki światła voxelowego
ShadowMapSystemMapowanie cieni
Perform/Scene/UpdateViewPrzygotowanie do renderowania i aktualizacje cząsteczek
Perform/Scene/RenderViewRenderowanie i przetwarzanie końcowe

Sieci i replikacja

Sieci i replikacja opisuje proces, w ramach którego dane są przesyłane pomiędzy serwerem a podłączonymi klientami. Informacje są przesyłane pomiędzy klientem a serwerem w każdej klatce, ale większe ilości informacji wymagają więcej czasu obliczeniowego.

Powszechne problemy

  • Nadmierny ruch zdalny - Wysyłanie dużej ilości danych przez obiekty RemoteEvent lub RemoteFunction lub wywoływanie ich bardzo często może prowadzić do dużej 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 do jego ograniczenia.
    • Dysponowanie większą ilością danych niż to konieczne. Na przykład, wysyłanie całej inwentarza gracza, gdy kupuje przedmiot, zamiast tylko szczegółów dotyczących zakupionego przedmiotu.
  • Tworzenie lub usuwanie złożonych drzew instancji - Kiedy w modelu danych na serwerze następuje zmiana, jest ona replikowana do podłączonych klientów. Oznacza to, że tworzenie i niszczenie dużych hierarchii instancji, takich jak mapy w runtime może być bardzo intensywne dla sieci.

    Powszechny winowajca jest złożone dane animacji zapisywane przez pluginy Animation Editor w rigach. Jeśli nie zostaną one usunięte przed opublikowaniem gry, a animowany model jest regularnie klonowany, duża ilość danych będzie replikowana niepotrzebnie.

  • Serwerowy TweenService - Jeśli TweenService jest używany do tweenowania obiektu po stronie serwera, tweenowane właściwości są replikowane do każdego klienta każdej klatki. To nie tylko skutkuje jitterem tween, gdy latencja klientów fluctuuje, ale powoduje także wiele niepotrzebnego ruchu sieciowego.

Złagodzenie

Możesz zastosować następujące taktyki, aby zredukować niepotrzebną replikację:

  • Unikaj wysyłania dużych ilości danych jednocześnie przez zdarzenia zdalne. Zamiast tego, wysyłaj tylko niezbędne dane przy 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 rozdzielić pracę replikacji tych danych na wiele klatek.
  • Wyczyść metadane animacji, szczególnie katalog animacji rigów, po zaimportowaniu.
  • Ogranicz niepotrzebną replikację instancji, szczególnie w przypadkach, w których serwer nie musi wiedzieć o instancjach tworzonych. To obejmuje:
    • Efekty wizualne, takie jak wybuch lub magia. Serwer musi wiedzieć tylko o lokalizacji, aby określić wynik, podczas gdy klienci mogą tworzyć wizualizacje lokalnie.
    • Modele widoków przedmiotów w pierwszej osobie.
    • Tween obiektów po stronie klienta zamiast serwera.

Zakresy MicroProfilera

ZasięgPowiązane obliczenia
ProcessPacketsPrzetwarzanie przychodzących pakietów sieciowych, takich jak wywołania eventów i zmiany właściwości
Allocate Bandwidth and Run SendersWychodzące zdarzenia istotne na serwerze

Użycie pamięci zasobów

Największy wpływ na zużycie pamięci przez klientów mają twórcy, aby poprawić zużycie pamięci, to włączyć streaming instancji.

Streaming instancji

Streaming instancji selektywnie ładuje części modelu danych, które nie są wymagane, co może prowadzić do znacznie krótszych czasów ładowania oraz zwiększenia zdolności klienta do zapobiegania awariom w przypadku presji pamięci.

Jeśli napotykasz problemy z pamięcią i masz wyłączony streaming instancji, rozważ zaktualizowanie swojej gry, aby go wspierała, 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 niego bardziej.

Jeśli streaming instancji jest włączony, możesz zwiększyć jego agresywność. Na przykład, rozważ:

  • Zmniejszenie użycia Enum.ModelStreamingMode.Persistent, gdzie to możliwe. Może być konieczne zaktualizowanie swoich skryptów, jeśli używasz tego jako środka kompatybilności.
  • Zmniejszenie Workspace.StreamingMinRadius i Workspace.StreamingTargetRadius.

Aby uzyskać więcej informacji na temat opcji streamingowych i ich zalet, zobacz właściwości streamingowe.

Inne powszechne problemy

  • Duplikacja zasobów - Powszechnym błędem jest ładowanie tego samego zasobu wiele razy, co prowadzi do różnych identyfikatorów zasobów. Może to prowadzić do ładowania tej samej treści w pamięci wiele razy.

  • Nadmierna objętość zasobów - Nawet gdy zasoby nie są identyczne, istnieją sytuacje, w których missed opportunities do ponownego wykorzystania tego samego zasobu i zaoszczędzenia pamięci są przegapione.

  • Pliki audio - Pliki audio mogą być zaskakującym współczynnikiem zużycia pamięci, szczególnie jeśli ładujesz je wszystkie do klienta jednocześnie, zamiast ładować tylko to, co potrzebujesz dla 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 określa zużycie pamięci. Na przykład, tekstura o wymiarach 1024x1024 pikseli zajmuje cztery razy więcej pamięci graficznej niż tekstura 512x512.

    Obrazy przesyłane do Roblox są transcoded do stałego formatu, więc nie ma korzyści związaną z pamięcią z przesyłania obrazów w modelu kolorów związanym z mniejszą liczbą bajtów na piksel. Podobnie, kompresowanie obrazów przed przesłaniem lub usunięcie kanału alfa z obrazów, które go nie potrzebują, może zmniejszyć rozmiar obrazu na dysku, ale nie poprawi zużycia pamięci.

    Gdy gra się ładuje, silnik zaczyna automatycznie z niższej jakości tekstur, a następnie zwiększa jakość na podstawie dostępnej pamięci urządzenia, odległości od kamery, ilości miejsca ekranowego, które tekstura zajmuje oraz innych czynników. Mimo to, strategiczne rozmiarowanie tekstur może poprawić zużycie pamięci w Twojej grze.

Złagodzenie

  • Ładuj zasoby tylko raz - Ponownie wykorzystuj ten sam identyfikator zasobu w obiektach i upewnij się, że te same zasoby, zwłaszcza siatki i obrazy, nie są przesyłane oddzielnie wielokrotnie.

  • Znajdź i napraw duplikaty zasobów - Szukaj identycznych części siatkowych i tekstur, które są przesyłane wielokrotnie z różnymi identyfikatorami.

    • Choć nie ma API do automatycznego wykrywania podobieństwa zasobów, możesz zgromadzić wszystkie identyfikatory zasobów obrazów w swoim miejscu (ręcznie lub za pomocą skryptu), pobrać je i porównać za pomocą zewnętrznych narzędzi porównawczych.
    • Dla części siatkowych, najlepszą strategią jest wzięcie unikalnych identyfikatorów meshowych i zorganizowanie ich według rozmiaru, aby ręcznie zidentyfikować duplikaty.
    • Zamiast używać oddzielnych tekstur dla różnych kolorów, załaduj jedną teksturę i użyj właściwości SurfaceAppearance.Color, aby zastosować różne odcienie.
  • Importuj zasoby w mapach oddzielnie - Zamiast importować całą mapę naraz, importuj i rekonstruuj zasoby w mapie indywidualnie. Importer nie wykonuje żadnej deduplikacji siatek, więc jeśli byłbyś w stanie zaimportować dużą mapę z wieloma oddzielnymi kafelkami podłogowymi, każda z tych płytek została by zaimportowana jako oddzielny zasób (nawet jeśli są duplikatami). Może to prowadzić do problemów z wydajnością i pamięcią, ponieważ każda siatka jest traktowana jako odrębna i zajmuje pamięć oraz wywołania rysowania.

  • Ogranicz liczby pikseli obrazów do nie więcej niż konieczna ilość. O ile obraz nie zajmuje dużej przestrzeni fizycznej na ekranie, zwykle potrzebuje maksymalnie 512x512 pikseli. Większość mniejszych obrazów powinna być mniejszych niż 256x256 pikseli.

  • Użyj arkuszy trim do zapewnienia maksimum ponownego wykorzystania tekstur w mapach 3D. Aby uzyskać kroki i przykłady, jak tworzyć arkusze trim, zobacz Utwórz arkusze trim.

    Możesz także rozważyć użycie arkuszy sprite'ów, aby załadować wiele mniejszych obrazów UI jako jeden obraz. Możesz wtedy użyć ImageLabel.ImageRectOffset i ImageLabel.ImageRectSize, aby wyświetlić części arkusza.

Czasy ładowania

Wiele gier implementuje niestandardowe ekrany ładowania i używa metody ContentProvider:PreloadAsync(), aby żądać zasobów, aby obrazy, dźwięki i siatki były pobierane w tle.

Zaletą tego podejścia jest to, że pozwala upewnić się, że ważne części Twojej gry są w pełni załadowane bez pojawiania się pop-in. Jednak powszechnym błędem jest nadmierna wykorzystywanie tej metody do wstępnego ładowania większej liczby zasobów, niż jest to naprawdę potrzebne.

Przykładem złej praktyki jest ładowanie całej Workspace. Chociaż może to zapobiec pojawianiu się tekstur, znacznie zwiększa czasy ładowania.

Inna podobna praktyka to wykorzystanie ContentProvider.RequestQueueSize, aby upewnić się, że wszystkie żądane zasoby zakończyły ładowanie. Jednak stwarza to ten sam problem znacznie zwiększających się czasów ładowania, a także jest to niepewna metoda ze względu na jej zmienność.

Zamiast tego, używaj ContentProvider:PreloadAsync() tylko w niezbędnych sytuacjach, które obejmują:

  • Obrazy w ekranie ładowania.
  • Ważne obrazy w menu gry, takie jak tła i ikony przycisków.
  • Ważne zasoby w obszarze startowym lub odrodzenia.

Jeśli musisz załadować dużą liczbę zasobów, zalecamy zapewnienie przycisku Pomiń ładowanie.

©2026 Roblox Corporation. Nazwa Roblox, logo Roblox oraz hasło „Powering Imagination” należą do naszych zarejestrowanych i niezarejestrowanych znaków towarowych na terenie Stanów Zjednoczonych oraz w innych krajach.