Popraw wydajność

*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 ich łagodzenia.

Obliczenia skryptów

Kosztowne operacje w kodzie Luau zajmują więcej czasu na przetwarzanie i mogą wpływać na liczbę klatek na sekundę. O ile nie jest wykonywany równolegle, kod Luau działa synchronicznie i blokuje główny wątek, aż napotka 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, wiążą się z wysokim kosztem wydajności, szczególnie w przypadku dużych struktur tabel. Jest to szczególnie prawdziwe, jeśli te operacje są rekurencyjne lub obejmują iterację po bardzo dużych strukturach danych.
  • Wydarzenia o wysokiej częstotliwości - Powiązanie kosztownych operacji z wydarzeniami opartymi na klatkach z RunService bez ograniczenia częstotliwości oznacza, że te operacje są powtarzane w każdej klatce, co często prowadzi do niepotrzebnego wzrostu czasu obliczeń. Te wydarzenia obejmują:

Łagodzenie

  • Wywołuj kod na wydarzeniach RunService oszczędnie, ograniczając użycie do przypadków, w których wysokoczęstotliwościowe wywołanie jest niezbędne (na przykład aktualizacja kamery). Możesz wykonywać większość innych kodów w innych wydarzeniach lub rzadziej w pętli.
  • Podziel duże lub kosztowne zadania, używając task.wait(), aby rozłożyć pracę na wiele klatek.
  • Zidentyfikuj i zoptymalizuj niepotrzebnie kosztowne operacje i użyj wielowątkowości do kosztownych obliczeniowo zadań, które nie muszą uzyskiwać dostępu do modelu danych.
  • Niektóre skrypty po stronie serwera mogą korzystać z generacji kodu natywnego, prostego flagu, który kompiluje skrypt do kodu maszynowego zamiast bajtowego.
  • Jeśli Twoje serwery nadal mają problemy z CPU po optymalizacji, możesz zarezerwować więcej zasobów z Rozszerzonymi usługami obliczeniowymi.

Zakresy MicroProfilera

ZakresPowiązane obliczenia
RunService.PreRenderKod wykonujący się w zdarzeniu PreRender
RunService.PreSimulationKod wykonujący się w zdarzeniu Stepped
RunService.PostSimulationKod wykonujący się w zdarzeniu Heartbeat
RunService.HeartbeatKod wykonujący się w zdarzeniu Heartbeat

Aby uzyskać więcej informacji na temat debugowania skryptów za pomocą MicroProfilera, zapoznaj się z 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ż swoje 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ąpić, gdy piszesz skrypty, które konsumują pamięć, której 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ć ciągle 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, który wymaga dalszego zbadania:

  • LuaHeap - Wysokie lub rosnące zużycie sugeruje wyciek pamięci.
  • InstanceCount - Ciągłe rosnące liczby instancji sugerują, że odniesienia do niektórych instancji w Twoim kodzie nie są zbierane przez garbage collector.
  • PlaceScriptMemory - Zapewnia szczegółowy podział użycia pamięci na poziomie skryptu.

Powszechne problemy

  • Pozostawianie połączeń aktywnych - Silnik nigdy nie zbiera pamięci dla zdarzeń połączonych z instancją i wszelkich wartości odniesionych wewnątrz połączonego callbacka. Dlatego aktywne połączenia zdarzeń i kod wewnątrz połączonych instancji, połączonych funkcji i odniesionych wartości są poza zakresem dla garbage collectora, nawet po wywołaniu zdarzeń.

    Chociaż zdarzenia są odłączane, gdy instancja, do której należą, jest niszczona, 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 i instancjami pod modelem postaci, takimi jak CharacterAdded, nadal konsumują pamięć, jeśli nie odłączysz ich w swoich skryptach. Może to prowadzić do bardzo znaczących wycieków pamięci w czasie na serwerze, 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 niepotrzebne zużycie pamięci, szczególnie w przypadku tabel, które śledzą 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 dołącza:

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

    Jeśli nie usuniesz tych wpisów, gdy nie są już potrzebne, tabela nadal rośnie i zużywa więcej pamięci, gdy więcej użytkowników dołącza do sesji. Każdy kod, który iteruje po tej tabeli, staje się również bardziej kosztowny obliczeniowo, gdy tabela rośnie.

Łagodzenie

Aby oczyścić wszystkie używane wartości, aby zapobiec wyciekom pamięci:

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

    • Ręczne odłączanie za pomocą funkcji Disconnect().
    • Niszczenie instancji, do której należy zdarzenie, za pomocą funkcji Destroy().
    • Niszczenie obiektu skryptu, do którego odnosi się połączenie.
  • Usuwanie obiektów gracza i postaci po opuszczeniu - Włącz Workspace.PlayerCharacterDestroyBehavior, aby automatycznie niszczyć obiekty gracza i modele postaci po opuszczeniu przez użytkownika. Jeśli wolisz, możesz zamiast tego oczyścić je ręcznie:

    Przykład czyszczenia gracza 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 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ść kroków fizyki - Domyślnie zachowanie kroków 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ż tryb stały z poprawioną dokładnością fizyki, który wymusza, aby wszystkie zespoły fizyki działały z częstotliwością 240 Hz (cztery razy na klatkę). To skutkuje znacznym wzrostem obliczeń w każdej klatce.

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

  • Zbyt precyzyjne wykrywanie kolizji - Części siatki 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 siatki ma najwyż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ń fizyki dla mechanizmów fizycznych, co pozwala na rzadsze 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ść kolizji wewnętrznych w mechanizmie, na przykład stosując ograniczenia lub ograniczenia braku kolizji dla kończyn ragdolla, aby zapobiec ich kolizjom ze sobą.
  • Zredukuj użycie precyzyjnej dokładności kolizji dla siatek

    • Dla małych lub nieinteraktywnych obiektów, gdzie użytkownicy rzadko zauważą różnicę, użyj dokładności pudełkowej.

    • Dla małych i średnich obiektów użyj dokładności pudełkowej lub kadłubowej, w zależności od kształtu.

    • Dla dużych i bardzo złożonych obiektów, buduj niestandardowe kolizje za pomocą niewidocznych części, gdy to możliwe.

    • Dla obiektów, które nie wymagają kolizji, wyłącz kolizje i użyj dokładności pudełkowej lub kadłubowej, ponieważ geometria kolizji jest nadal przechowywana w pamięci.

    • Możesz renderować geometrię kolizji w celach debugowania w Studio, włączając Dokładność kolizji w widżecie Opcje wizualizacji w prawym górnym rogu widoku 3D.

      Alternatywnie, możesz zastosować filtr CollisionFidelity=PreciseConvexDecomposition do Eksploratora, który pokazuje liczbę wszystkich części siatki z precyzyjną dokładnością i pozwala łatwo je wybrać.

    • Aby uzyskać szczegółowy przewodnik po tym, jak wybrać opcję dokładności kolizji, która równoważy wymagania dotyczące precyzji i wydajności, zobacz Ustaw parametry fizyki i renderowania.

Zakresy MicroProfilera

ZakresPowiązane obliczenia
physicsSteppedOgólne obliczenia fizyki
worldStepDyskretny krok fizyki podejmowany w każdej klatce

Użycie pamięci przez fizykę

Ruch fizyczny i wykrywanie kolizji zużywają pamięć. Części siatki 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 sekcji PhysicsParts, możesz potrzebować zbadać zmniejszenie dokładności kolizji obiektów w swojej grze.

Jak złagodzić

Aby zmniejszyć pamięć używaną do dokładnoś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 dokładność kolizji, używając ustawienia CollisionFidelity. Box ma najniższe obciążenie pamięci, a Default i Precise są zazwyczaj droższe.
    • Zazwyczaj bezpiecznie jest ustawić dokładność kolizji dowolnej małej zablokowanej części na Box.
    • Dla bardzo złożonych dużych siatek, możesz chcieć zbudować własną siatkę kolizji z mniejszych obiektów o dokładności kolizji 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 znacznym kosztem obliczeniowym.

Powszechne problemy

  • Pozostawianie wszystkich HumanoidStateTypes włączonych dla NPC - Istnieje koszt wydajności związany z pozostawieniem niektórych HumanoidStateTypes włączonych. Wyłącz wszelkie, które nie są potrzebne dla Twoich NPC. Na przykład, chyba że Twój NPC ma wspinać się po drabinach, bezpiecznie jest wyłączyć stan Climbing.
  • Instancjonowanie, modyfikowanie i odradzanie modeli z Humanoids lub skórzonych MeshParts często
    • Może to być intensywne dla silnika do przetworzenia, szczególnie jeśli te modele używają warstwowej odzieży. 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) są często sygnałem, że instancjonowanie/modyfikacja awatara wywołuje nadmierne unieważnienia.
  • Używanie Humanoidów w przypadkach, gdy nie są wymagane - Statyczne NPC, które się nie poruszają, 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ć niepotrzebne obciążenie.
  • Wykonywanie niepotrzebnych zmian rozmiaru i skali - Zmiany rozmiaru/skali powodują przebudowę FastCluster. 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 ogranicz te zmiany tak bardzo, jak to możliwe.

Ł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 dla Humanoidów - Modele NPC nie muszą koniecznie zawierać obiektu humanoida.
    • Dla statycznych NPC użyj prostego AnimationController, ponieważ nie muszą się poruszać, ale muszą tylko 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 humanoidalne - Użyj Humanoid:SetStateEnabled(), aby włączyć tylko niezbędne stany dla każdego humanoida.
  • Pule modeli NPC z częstym odradzaniem - Zamiast całkowicie niszczyć NPC, wyślij NPC do puli nieaktywnych NPC. W ten sposób, gdy nowy NPC jest wymagany do odrodzenia, możesz po prostu reaktywować jednego z NPC z puli. Ten proces nazywa się poolingiem, co minimalizuje liczbę razy, kiedy postacie muszą być instancjonowane.
  • Spawnuj NPC tylko wtedy, gdy użytkownicy są w pobliżu - Nie spawnuj NPC, gdy użytkownicy nie są w zasięgu, i usuwaj 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 wydajnościowe. Dostępne są pewne optymalizacje:

Zakresy MicroProfilera

ZakresPowiązane obliczenia
stepHumanoidKontrola humanoida i fizyka
stepAnimationAnimacja humanoida i animatora
updateInvalidatedFastClustersPowiązane z instancjonowaniem lub modyfikowaniem awatara

Renderowanie

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 żadnego renderowania, 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ą znaczące obciążenie. Zazwyczaj im mniej wywołań rysowania na klatkę, tym mniej czasu obliczeniowego jest poświęcone na renderowanie klatki.

Możesz zobaczyć, ile wywołań rysowania aktualnie występuje, korzystając z elementu Statystyki renderowania ⟩ Czas 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 wysyłanych do GPU. Jednak silnik Roblox wykorzystuje proces zwany instancjonowaniem, aby zredukować identyczne siatki o tych samych cechach tekstury do jednego wywołania rysowania. Konkretnie, 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 w wysokiej gęstości, 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 typy obiektów w scenie. W szczególności zmiany właściwości w ParticleEmitters mogą mieć dramatyczny wpływ na wydajność.

  • Przegapione możliwości instancjonowania - Często scena będzie zawierać tę samą siatkę powieloną 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 jednocześnie, zamiast importowania poszczególnych zasobów do Roblox, a następnie powielania ich po imporcie, aby złożyć scenę.

    Nawet prosty skrypt, taki 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() do
    if descendant:IsA("MeshPart") then
    print(descendant.Name .. ", " .. descendant.MeshId)
    end
    end

    Wynik (z włączonym 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 Twojego schematu nazewnictwa mogą wskazywać na zduplikowane siatki w Twojej grze:

    LargeRock, rbxassetid://106420009602747 (x144) -- dobre
    LargeRock, rbxassetid://120109824668127
    LargeRock, rbxassetid://134460273008628
    LargeRock, rbxassetid://139288987285823
    LargeRock, rbxassetid://71302144984955
    LargeRock, rbxassetid://90621205713698
    LargeRock, rbxassetid://113160939160788
    LargeRock, rbxassetid://135944592365226 -- wszystkie możliwe duplikaty
  • Nadmierna złożoność obiektów - Chociaż nie jest to tak ważne jak liczba wywołań rysowania, liczba trójkątów w scenie wpływa na to, jak długo trwa renderowanie klatki. Sceny z bardzo dużą liczbą bardzo złożonych siatek są powszechnym problemem, podobnie jak sceny z właściwością MeshPart.RenderFidelity ustawioną na Precise na zbyt wielu siatkach.

  • Nadmierne rzucanie cieni - Obsługa cieni to kosztowny proces, 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 wpływających na cienie) mogą mieć problemy z wydajnością.

  • Wysoka przezroczystość nadmiaru - Umieszczanie obiektów z częściową przezroczystością blisko siebie zmusza silnik do renderowania nakładających się pikseli wielokrotnie, co może zaszkodzić wydajności. Aby uzyskać więcej informacji na temat identyfikacji i naprawy tego problemu, zobacz Usuń warstwowe przezroczystości.

  • Niepotrzebny ruch skórzonych MeshPartów - Skórzane MeshPart, które są częścią Modelu bez Humanoida, są grupowane za pomocą przestrzennie zorganizowanych FastClusters. Gdy te MeshParty się poruszają, muszą być ciągle dodawane i usuwane z tych klastrów przestrzennych, co zmusza klastry do przebudowy i wpływa na wydajność.

    • Bardzo skutecznym obejściem jest osadzenie Humanoida w Modelu. Obecność Humanoida nadpisuje domyślne zachowanie klastrowania przestrzennego, wymuszając użycie jednego, zjednoczonego FastCluster dla całego Modelu. W konsekwencji aktualizacje pozycji nie wymagają już przebudowy klastrów, co łagodzi wąskie gardło wydajności. Ta technika powinna być zarezerwowana wyłącznie dla MeshPartów z oczekiwanym ruchem, ponieważ może wprowadzać obciążenie pamięci i negować korzyści z optymalizacji przestrzennej. Zawsze zalecamy profilowanie swojej gry po wprowadzeniu tego typu zmian. Zobacz Wskazówki dotyczące wydajności Humanoida w celu uzyskania dodatkowych informacji.
  • Zbyt wiele części w Model - Zbyt wiele części w Modelu może powodować przebudowy częściej z powodu potencjalnej zmiany właściwości części, co prowadzi do konieczności pełnej przebudowy. Znajdź odpowiednią równowagę części w Modelu, gdy używa FastCluster.

Łagodzenie

  • Instancjonowanie identycznych siatek i zmniejszanie liczby unikalnych siatek - Jeśli upewnisz się, że wszystkie identyczne siatki mają te same identyfikatory zasobów, silnik może je rozpoznać i wyrenderować w jednym wywołaniu rysowania. Upewnij się, że przesyłasz każdą siatkę w mapie tylko raz, a następnie powielaj 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 zasoby przez silnik. Pakiety są pomocnym mechanizmem do ponownego użycia obiektów.
  • Culling - Culling opisuje proces eliminacji wywołań rysowania dla obiektów, które nie mają wpływu na ostateczną renderowaną klatkę. Domyślnie silnik pomija wywołania rysowania dla obiektów poza polem widzenia kamery (culling frustum) oraz części, siatki i teren zasłonięte przez inne obiekty (culling okluzji). W niektórych scenariuszach, takich jak środowiska wewnętrzne, możesz być w stanie wdrożyć system pokoi lub portali i ręcznie cullować obiekty, aby dalej zmniejszyć wywołania rysowania lub ogólne obciążenie obliczeniowe.
  • Zmniejszanie poziomu szczegółowości modeli - Włącz streaming instancji i ustaw właściwość LevelOfDetail swoich modeli świata na SLIM, aby renderować optymalizowane lekkie siatki SLIM w miarę oddalania się od kamery.
  • Zmniejszanie poziomu szczegółowości awatarów - Włącz streaming instancji i ustaw Workspace.EnableSLIMAvatars, aby renderować awatary platformy jako optymalizowane lekkie reprezentacje SLIM z pełnym wsparciem animacji w miarę oddalania się od kamery.
  • Zmniejszanie dokładności renderowania - Ustaw MeshPart.RenderFidelity na Automatic lub Performance. To pozwala siatkom przejść do mniej złożonych 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 obniża jakość cieni w miarę zmniejszania się poziomu jakości grafiki klienta, ostatecznie całkowicie wyłączając cienie na poziomach jakości poniżej 4. Możesz jednak selektywnie wyłączyć właściwości rzucania cieni na obiektach świetlnych i częściach, aby poprawić wydajność, 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 wprowadzić zarówno w czasie edycji, jak i dynamicznie w czasie wykonywania:
    • Użyj właściwości BasePart.CastShadow, aby wyłączyć rzucanie cieni na małych częściach, gdzie cienie prawdopodobnie nie będą widoczne. Ta strategia jest szczególnie skuteczna, gdy stosuje się ją do części, które są daleko od kamery użytkownika.

    • Wyłącz cienie na poruszających się obiektach, gdy to możliwe.

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

    • Ogranicz zasięg i kąt instancji światła.

    • Użyj mniejszej liczby instancji światła.

    • Rozważ wyłączenie świateł, które są poza określonym zasięgiem lub na zasadzie pokój po pokoju w przypadku środowisk wewnętrznych.

Zakresy MicroProfilera

ZakresPowiązane obliczenia
Prepare and PerformOgólne renderowanie
Perform/Scene/computeLightingPerformAktualizacje siatki świetlnej i cieni
LightGridCPUAktualizacje siatki świetlnej wokseli
ShadowMapSystemMapowanie cieni
Perform/Scene/UpdateViewPrzygotowanie do renderowania i aktualizacje cząsteczek
Perform/Scene/RenderViewRenderowanie i przetwarzanie post

Sieci i replikacja

Sieci i replikacja opisują proces, w którym dane są przesyłane między serwerem a podłączonymi klientami. Informacje są przesyłane mię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ą:

    • Replikowanie danych w każdej klatce, które nie muszą być replikowane.
    • Replikowanie danych na podstawie wejścia użytkownika bez żadnego mechanizmu ograniczającego.
    • Wysyłanie więcej danych, niż jest to wymagane. Na przykład, wysyłanie całego ekwipunku gracza, gdy kupuje przedmiot, zamiast tylko szczegółów dotyczących zakupionego przedmiotu.
  • Tworzenie lub usuwanie złożonych drzew instancji - Gdy zmiana jest dokonywana w modelu danych na serwerze, jest ona replikowana do podłączonych klientów. Oznacza to, że tworzenie i niszczenie dużych hierarchii instancji, takich jak mapy w czasie rzeczywistym, może być bardzo intensywne sieciowo.

    Powszechnym winowajcą są złożone dane animacji zapisywane przez wtyczki 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.

  • TweenService po stronie serwera - Jeśli TweenService jest używane do tweenowania obiektu po stronie serwera, tweenowana właściwość jest replikowana do każdego klienta w każdej klatce. Nie tylko prowadzi to do drgań tweena, gdy latencja klientów się zmienia, ale powoduje również wiele niepotrzebnego ruchu sieciowego.

Ł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 w 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 kawałkach, aby rozłożyć pracę replikacji na wiele klatek.
  • Oczyść metadane animacji, szczególnie katalog animacji rigów, po imporcie.
  • Ogranicz niepotrzebną replikację instancji, szczególnie w przypadkach, gdy serwer nie musi mieć wiedzy o tworzonych instancjach. Obejmuje to:
    • Efekty wizualne, takie jak eksplozja lub wybuch czarów. Serwer tylko musi znać lokalizację, aby określić wynik, podczas gdy klienci mogą tworzyć wizualizacje lokalnie.
    • Modele przedmiotów w widoku pierwszoosobowym.
    • Tweenuj obiekty po stronie klienta, a nie serwera.

Zakresy MicroProfilera

ZakresPowiązane obliczenia
ProcessPacketsPrzetwarzanie przychodzących pakietów sieciowych, takich jak wywołania zdarzeń i zmiany właściwości
Allocate Bandwidth and Run SendersWychodzące zdarzenia istotne na serwerach

Użycie pamięci przez zasoby

Najwyższy wpływ na mechanizm dostępny dla twórców w celu poprawy użycia pamięci przez klienta to włączenie streamingu instancji.

Streaming instancji

Streaming instancji selektywnie ładuje części modelu danych, które nie są wymagane, co może prowadzić do znacznego skrócenia czasu ładowania i zwiększenia zdolności klienta do zapobiegania awariom, gdy napotyka presję pamięci.

Jeśli napotykasz problemy z pamięcią i masz wyłączony streaming instancji, rozważ zaktualizowanie swojej gry, aby go obsługiwał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 go jako środka zgodnoś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 wiele razy, co skutkuje różnymi identyfikatorami zasobów. Może to prowadzić do tego, że ta sama treść jest ładowana do pamięci wiele razy.

  • Nadmierna objętość zasobów - Nawet gdy zasoby nie są identyczne, zdarzają się przypadki, gdy możliwości ponownego użycia tego samego zasobu i oszczędzenia pamięci są pomijane.

  • Pliki audio - Pliki audio mogą być zaskakującym źródłem zużycia pamięci, szczególnie jeśli ładujesz je wszystkie do klienta jednocześnie, zamiast ładować tylko to, co potrzebujesz na część 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 rozdzielczości 1024x1024 pikseli zużywa cztery razy więcej pamięci graficznej niż tekstura o rozdzielczości 512x512.

    Obrazy przesyłane do Roblox są transkodowane do stałego formatu, więc nie ma korzyści pamięciowych 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 usuwanie kanału alfa z obrazów, które go nie potrzebują, 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 zajmowanego przez teksturę 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 używaj tego samego identyfikatora 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 wiele razy z różnymi identyfikatorami.

    • Chociaż nie ma 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ą zewnętrznych narzędzi porównawczych.
    • 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ć oddzielnych tekstur dla różnych kolorów, przesyłaj jedną teksturę i użyj właściwości SurfaceAppearance.Color, aby zastosować różne odcienie.
  • Importuj zasoby w mapie osobno - Zamiast importować całą mapę jednocześnie, importuj i odbudowuj zasoby w mapie indywidualnie. Importer nie wykonuje żadnej de-duplication siatek, więc jeśli zaimportujesz dużą mapę z wieloma oddzielnymi kafelkami podłogowymi, każdy z tych kafelków zostanie zaimportowany jako oddzielny zasób (nawet jeśli są duplikatami). Może to prowadzić do problemów z wydajnością i pamięcią w przyszłości, ponieważ każda siatka jest traktowana jako indywidualna i zajmuje pamięć oraz wywołania rysowania.

  • Ogranicz liczbę pikseli obrazów do nie więcej niż niezbędna ilość. Chyba że obraz zajmuje dużą ilość fizycznej przestrzeni na ekranie, zazwyczaj potrzebuje maksymalnie 512x512 pikseli. Większość mniejszych obrazów powinna być mniejsza niż 256x256 pikseli.

  • Używaj arkuszy wykończeniowych w celu zapewnienia maksymalnego ponownego użycia tekstur w mapach 3D. Aby uzyskać kroki i przykłady dotyczące tworzenia arkuszy wykończeniowych, zobacz Tworzenie arkuszy wykończeniowych.

    Możesz również rozważyć użycie arkuszy sprite'ów do ładowania wielu mniejszych obrazów UI jako jednego obrazu. Możesz następnie użyć ImageLabel.ImageRectOffset i ImageLabel.ImageRectSize, aby wyświetlić fragmenty arkusza.

Czasy ładowania

Zanim gracz będzie mógł zacząć grać, klient musi otrzymać wszystko w ReplicatedFirst i ReplicatedStorage, początkowe zawartości Workspace oraz wszelkie zasoby, które Twoje skrypty wstępnie ładują. Im więcej treści klient musi pobrać i przetworzyć w tym czasie, tym dłużej czekają gracze. Aby uzyskać szczegółowy opis każdego kroku w procesie dołączania, zobacz Zidentyfikuj problemy z wydajnością.

Powszechne problemy

  • Wyłączony streaming instancji - Bez streamingu instancji, cały Workspace musi zostać zreplikowany do klienta, zanim gracz będzie mógł wejść, więc gry z wieloma częściami, modelami i zasobami mają proporcjonalnie dłuższe czasy ładowania.

  • Nadmierna zawartość w ReplicatedStorage - Wszystko w ReplicatedStorage replikuje się do każdego klienta, zanim gracz będzie mógł zobaczyć lub wchodzić w interakcje z czymkolwiek, niezależnie od tego, czy klient potrzebuje tego od razu. Darmowe modele i dziedziczone zasoby mogą cicho dodać znaczną objętość.

  • Ciężka zawartość ReplicatedFirst - ReplicatedFirst replikuje się przed wszystkim innym i istnieje głównie w celu przechowywania niestandardowego ekranu ładowania. Każdy dodatkowy zasób lub skrypt w nim opóźnia rozpoczęcie głównej fazy replikacji.

  • Nadmierne użycie PreloadAsync() - Wiele gier implementuje niestandardowe ekrany ładowania i używa ContentProvider:PreloadAsync(), aby żądać zasobów, aby obrazy, dźwięki i siatki były pobierane w tle. To podejście zapewnia, że ważne części Twojej gry są w pełni załadowane bez pojawiania się, ale powszechnym błędem jest wstępne ładowanie większej liczby zasobów, niż jest to naprawdę wymagane.

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

    Inną podobną praktyką jest wykorzystanie ContentProvider.RequestQueueSize, aby upewnić się, że wszystkie żądane zasoby zakończyły ładowanie. Jednak to stwarza ten sam problem znacznie zwiększonych czasów ładowania, a także jest zawodną metodą z powodu jej zmiennej natury.

  • Drogi skrypty startowe - Ciężkie wykonanie skryptów w LocalScripts, które działają podczas procesu dołączania, takie jak synchroniczne ładowanie dużych struktur danych, opóźnia przejście od replikacji do stanu grywalnego.

Łagodzenie

  • Włącz streaming instancji - Streaming wysyła tylko część Twojego świata w pobliżu punktu spawnu gracza przy początkowym dołączeniu, a następnie stopniowo przesyła resztę, gdy gracz porusza się po grze. Dla gier, które jeszcze go nie używają, streaming jest jednym z najskuteczniejszych sposobów na skrócenie czasów ładowania. Streaming jest domyślnie włączony dla nowych miejsc. Aby uzyskać kroki konfiguracyjne, odniesienia do właściwości i wskazówki dotyczące migracji, zobacz Streaming instancji.

  • Utrzymuj ReplicatedStorage w wąskim zakresie - Przechowuj tylko to, co klienci potrzebują w momencie dołączenia, takie jak wspólne ModuleScripts, które wymagają LocalScripts. Przenieś duże modele, dźwięki i inne zasoby, które nie są potrzebne od razu, do ServerStorage, a następnie przenieś je do klienta tylko wtedy, gdy gracz ich potrzebuje. Regularnie audytuj ReplicatedStorage pod kątem treści, których już nie używasz.

  • Utrzymuj ReplicatedFirst w wąskim zakresie - Ogranicz ReplicatedFirst do tego, co potrzebuje Twój ekran ładowania, takiego jak podstawowe elementy UI, a nie duże tekstury lub modele 3D, i unikaj ciężkich obliczeń w skryptach, które działają z niego. Aby uzyskać przewodnik po budowaniu niestandardowego ekranu ładowania, w tym kiedy wywołać ReplicatedFirst:RemoveDefaultLoadingScreen(), zobacz Ekrany ładowania.

  • Wstępnie ładuj tylko niezbędne zasoby - 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 przycisków i ikony.
    • Ważne zasoby w obszarze startowym lub spawnowym.

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

  • Odkładaj nieistotną pracę startową - Ładuj zawartość, do której gracz jeszcze nie dotarł, taką jak panele UI, które nie zostały jeszcze otwarte, tylko wtedy, gdy jest to po raz pierwszy potrzebne. W przypadku pracy inicjalizacyjnej, która musi odbyć się wcześnie, rozłóż ją na wiele klatek, zamiast uruchamiać wszystko naraz. Aby uzyskać techniki, zobacz Obliczenia skryptów.

©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.