CreateThread i Wait(0) w FiveM wyjaśnione: dlaczego twój skrypt kosztuje ms
Jak CreateThread, Wait i SetTimeout działają w Lua FiveM, dlaczego Wait(0) w każdej pętli pojawia się w resmon i dlaczego pętla while bez Wait zamraża grę.
Piszesz pętlę, aby coś sprawdzić, a gra przestaje odpowiadać, lub resmon pokazuje twój zasób przy kilku ms, gdy stoiszesz w miejscu:
while true do
-- no Wait here: the game never gets control back
local ped = PlayerPedId()
endTen artykuł wyjaśnia, jak wątki i Wait działają w Lua FiveM, co Wait(0) naprawdę kosztuje i jak pisać pętle, które pozostają tanie.
Jak wątki działają w FiveM
Lua w FiveM jest kooperatywna. Twój skrypt nie działa równolegle z grą: uruchamia się, gdy runtime go wywołuje, i musi oddać kontrolę. Dwie rzeczy to umożliwiają:
- Handlery zdarzeń i callbacki uruchamiają się raz, od początku do końca, potem wracają.
- Wątki utworzone za pomocą
CreateThreadto małe korutyny, które scheduler wznawia znowu i znowu. Wątek oddaje kontrolę, gdy wywołujeWait.
CreateThread(function()
while true do
Wait(1000)
print('one second later')
end
end)CreateThread i Citizen.CreateThread to ta sama funkcja, i podobnie Wait i Citizen.Wait. Używaj któregokolwiek chcesz, ale bądź konsekwentny w jednym skrypcie.
Pętla while bez Wait zamraża grę
Jeśli wątek nigdy nie wywoła Wait, scheduler nigdy nie odzyskuje kontroli. Na kliencie gra przestaje rysować ramki, aż pętla się skończy, a na serwerze każdy inny skrypt czeka na twój.
CreateThread(function()
while true do
-- missing Wait: this blocks the whole client
end
end)To samo dzieje się z repeat … until lub długą pętlą for, która wykonuje ciężkie prace. Każda pętla, która może działać przez chwilę, potrzebuje Wait wewnątrz. Jeśli skrypt zamraża grę na starcie, najpierw poszukaj pętli jak ta.
Co kosztuje Wait(0)
Wait(0) oznacza: wstrzymaj się do następnej ramki (klient) lub następnego ticka (serwer), potem kontynuuj. Pętla uruchamia się znowu na każdej ramce, więc przy 60 fps jej główna część jest opłacana 60 razy na sekundę.
CreateThread(function()
while true do
Wait(0)
local coords = GetEntityCoords(PlayerPedId())
local dist = #(coords - vector3(215.0, -810.0, 30.7))
if dist < 3.0 then
-- draw a marker, show text...
end
end
end)Jedna pętla jak ta kosztuje mało. Dziesięć z nich, między trzydziestoma zasobami, sumuje się do kilku ms czasu klienta, co dokładnie to jest, co resmon pokazuje jako wysokie ms. Koszt to nie sam Wait(0), to wszystko, co powtarzasz każdą ramkę.
Wait tylko tak krótko, jak musisz
Większość testów nie musi uruchamiać się każdą ramkę. Zapytaj, do czego służy pętla:
| Co robi pętla | Rozsądne czekanie |
|---|---|
| Rysuj marker lub tekst, obsługuj naciśnięcie klawisza | 0 (tylko gdy gracz jest blisko) |
| Sprawdź odległość, aby zdecydować, czy coś jest blisko | 250 do 1000 |
| Sonduj stan, który rzadko się zmienia (praca, pojazd) | 1000 lub więcej, lub użyj zdarzenia |
Zwykła sztuczka to dwie prędkości: powolna pętla, która sprawdza odległość, i szybka tylko podczas gdy gracz jest w obszarze.
local zone = vector3(215.0, -810.0, 30.7)
CreateThread(function()
while true do
local sleep = 1000
local dist = #(GetEntityCoords(PlayerPedId()) - zone)
if dist < 10.0 then
sleep = 0
DrawMarker(1, zone.x, zone.y, zone.z - 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
1.5, 1.5, 0.5, 255, 255, 255, 120, false, false, 2, false, nil, nil, false)
end
Wait(sleep)
end
end)Daleko od strefy wątek budzi się raz na sekundę. Blisko niej rysuje każdą ramkę. Resmon spada na prawie nic dla większości graczy.
Porada: zanim sięgniesz po szybszą pętlę, zapytaj, czy zdarzenie lub torebka stanu może ci powiedzieć, kiedy wartość się zmienia. Brak pętli w ogóle jest tańszy niż jakikolwiek Wait.
SetTimeout: uruchom coś raz, później
Jeśli musisz coś zrobić tylko raz po opóźnieniu, nie potrzebujesz wątku z Wait:
SetTimeout(5000, function()
print('five seconds later')
end)SetTimeout(ms, callback) uruchamia callback raz po opóźnieniu i nic nie blokuje. Istnieje również na serwerze. Dla powtarzającego się zadania wątek z Wait to właściwe narzędzie, ponieważ SetTimeout nie powtarza się automatycznie.
Czekanie wewnątrz handlerów zdarzeń
Handler zdarzenia może również wywoływać Wait, ponieważ FiveM uruchamia go w swojej własnej korutynie. Bądź ostrożny na serwerze: po Wait, globalny source już nie wskazuje na gracza, który wyzwolił zdarzenie. Zapisz to najpierw (przeczytaj zdarzenia klient i serwer):
RegisterNetEvent('my_script:server:buy', function()
local src = source
Wait(500)
print(('player %s bought something'):format(src))
end)Zatrzymanie wątku
Wątek kończy się, gdy jego funkcja wraca. Aby zatrzymać pętlę z zewnątrz, użyj flagi, którą pętla sprawdza:
local running = true
CreateThread(function()
while running do
Wait(500)
-- work
end
end)
AddEventHandler('onResourceStop', function(resource)
if resource == GetCurrentResourceName() then
running = false
end
end)Wszystkie wątki zasobu zatrzymują się automatycznie, gdy zasób się zatrzyma, więc musisz to tylko, aby zatrzymać pętlę podczas gdy zasób nadal działa.
Lista kontrolna
| Objaw | Naprawa |
|---|---|
| Gra zamraża się, gdy skrypt się uruchamia | Pętla while true do nie ma Wait: dodaj jedną |
| Zasób pokazuje stałą ms w resmon w bezczynności | Zastąp Wait(0) sprawdzeniem odległości i dłuższym czekaniem |
| Potrzebuję uruchomić coś raz po opóźnieniu | SetTimeout(ms, fn) zamiast wątku |
source jest zły po Wait na serwerze |
Zapisz local src = source przed Wait |
| Sondowanie wartości, która rzadko się zmienia | Zamiast pętli użyj zdarzenia lub torebki stanu |
Citizen.Wait czy Wait? |
Ta sama funkcja, wybierz jeden styl |
Szybkie odpowiedzi
Co robi Wait(0) w FiveM?
Wstrzymuje bieżący wątek do następnej ramki na kliencie lub następnego ticka na serwerze. Pętla następnie uruchamia się ponownie na każdej ramce, więc wszystko w środku jest opłacane 60 lub więcej razy na sekundę.
Czy Citizen.Wait różni się od Wait?
Nie. Wait i Citizen.Wait to ta sama funkcja, i podobnie CreateThread i Citizen.CreateThread. Krótkie nazwy to tylko aliasy.
Czy Wait zatrzymuje całą grę?
Nie. Wstrzymuje tylko wątek, który go wywołuje, a inne wątki i gra działają dalej. Pętla, która nigdy nie wywołuje Wait, to to, co blokuje wszystko.
Skrypty bez tego problemu
Advanced BoostingBoosting pojazdów z tabletu: kontrakty od klasy D do S+, ekipy i kolejka na żywo.Zobacz skrypt →
CCTV Security CamerasKamery do rozstawienia, tablet z podglądem wielu kamer na żywo i drukowane zdjęcia jako dowody.Zobacz skrypt →
Mic PhoneSkładany telefon, który rozkłada się w tablet i działa też na prawdziwym telefonie gracza.Zobacz skrypt →