System wyścigów FiveM: Trasy, tabele wyników i zakłady
Dodaj wyścigi uliczne do swojego serwera FiveM. Kreatory tras, punkty kontrolne, zakłady, tabele wyników i najlepsze skrypty wyścigowe dla wymagających społeczności tunerów.
Agency Scripts
Założyciel i główny deweloper w Agency Scripts
Projektowanie systemu tworzenia wyścigów
Dobry system wyścigów ulicznych zaczyna się od umożliwienia graczom tworzenia i zarządzania własnymi trasami. Zamiast polegać wyłącznie na gotowych torach, najlepsze skrypty wyścigów pozwalają organizatorom dynamicznie stawiać punkty kontrolne poprzez jazdę trasą i oznaczanie pozycji. Zapisuj każdy wyścig jako strukturę danych zawierającą unikalny identyfikator, ID gracza tworzącego, nazwę, uporządkowaną listę współrzędnych punktów kontrolnych z kątem zwrotu oraz metadane, takie jak dystans i szacowany czas okrążenia. Proces tworzenia powinien być intuicyjny: gracz wchodzi w tryb edytora wyścigów, dojeżdża do każdego punktu kontrolnego, zatwierdza pozycję naciśnięciem klawisza i kończy, wracając na linię startu. Zapisuj ukończone wyścigi w bazie danych, aby zachowały się po restartach serwera i mogły być udostępniane społeczności. Wdróż system weryfikacji, w którym administratorzy mogą zatwierdzać trasy stworzone przez graczy przed ich pojawieniem się na publicznej liście wyścigów, zapobiegając griefingowi przez niemożliwe lub nieuczciwe układy tras.
System checkpointów i logika wyścigu
Checkpointy to kluczowa mechanika, która weryfikuje postęp w wyścigu i zapobiega skracaniu trasy. Każdy checkpoint powinien być zdefiniowany jako strefa 3D z konfigurowalnym promieniem, zwykle od 8 do 15 metrów w zależności od szerokości drogi. Użyj natywnych funkcji rysowania markerów z GTA, aby wizualnie wyświetlać checkpointy, z aktualnym checkpointem wyróżnionym jasnym kolorem i kolejnym widocznym jako przezroczysty podgląd. Śledź postęp każdego zawodnika na indeksowanej liście checkpointów, przesuwając go dalej tylko wtedy, gdy wejdzie w kolejną prawidłową strefę checkpointu w odpowiedniej kolejności. Wdróż zabezpieczenia anti-cheat poprzez śledzenie czasu między checkpointami i oznaczanie lub dyskwalifikowanie zawodników, którzy pokonują odcinki w niemożliwie szybkim czasie, co wskazuje na teleportację lub speed hacki.
-- Client-side checkpoint detection
local currentCheckpoint = 1
local raceCheckpoints = {} -- populated when race starts
CreateThread(function()
while isRacing do
local playerCoords = GetEntityCoords(PlayerPedId())
local checkpoint = raceCheckpoints[currentCheckpoint]
if checkpoint then
local dist = #(playerCoords - checkpoint.coords)
-- Draw current checkpoint marker
DrawMarker(1, checkpoint.coords.x, checkpoint.coords.y,
checkpoint.coords.z - 1.0, 0, 0, 0, 0, 0, 0,
checkpoint.radius * 2, checkpoint.radius * 2, 2.0,
45, 212, 191, 120, false, true, 2, false, nil, nil, false)
if dist < checkpoint.radius then
PlaySoundFrontend(-1, 'CHECKPOINT_NORMAL', 'HUD_MINI_GAME_SOUNDSET', true)
currentCheckpoint = currentCheckpoint + 1
if currentCheckpoint > #raceCheckpoints then
-- Race finished
local finishTime = GetGameTimer() - raceStartTime
TriggerServerEvent('racing:finished', raceId, finishTime)
isRacing = false
else
-- Notify server of checkpoint progress
TriggerServerEvent('racing:checkpoint', raceId, currentCheckpoint)
end
end
end
Wait(50)
end
end)
Tabela wyników i system rankingowy
Tabele wyników napędzają rywalizację i sprawiają, że gracze wracają, aby poprawiać swoje czasy. Zapisuj wyniki wyścigów w tabeli bazy danych, która rejestruje identyfikator gracza, ID wyścigu, czas ukończenia w milisekundach, użyty model pojazdu, datę oraz informację, czy przejazd był rekordem życiowym. Wyświetlaj tabele wyników przez interfejs NUI, który pokazuje najlepsze czasy dla każdego toru, z możliwością filtrowania według klasy pojazdu, aby gracze mogli sprawiedliwie porównywać się w kategoriach takich jak sports, super, muscle czy compact. Wdróż sezonowy system rankingowy, który resetuje się okresowo, aby utrzymać świeżość rywalizacji, zachowując jednocześnie tablicę rekordów wszech czasów dla prestiżu. Obliczaj ocenę wyścigową w stylu ELO na podstawie bezpośrednich pojedynków w wyścigach wieloosobowych, dając zręcznym kierowcom widoczną rangę, która rośnie po pokonaniu wyżej notowanych rywali i spada po przegranej ze słabszymi. Wyświetlaj tę ocenę w UI wyścigu i używaj jej do opcjonalnego dobierania graczy przy organizacji losowych wyścigów.
System zakładów i nagród
System zakładów dodaje stawkę finansową do wyścigów i integruje scenę wyścigową z szerszą gospodarką twojego serwera. Pozwól organizatorom wyścigów ustalać wpisowe, które muszą opłacić wszyscy uczestnicy, przy czym cała pula jest dzielona między najlepszych zawodników według konfigurowalnego podziału, na przykład 70-20-10 za pierwsze, drugie i trzecie miejsce. Wdróż system zakładów dla widzów, w którym gracze niebiorący udziału w wyścigu mogą obstawiać wyniki, wybierając kierowcę, na którego postawią pieniądze. Wykorzystuj wyłącznie logikę po stronie serwera dla wszystkich transakcji finansowych, aby zapobiec exploitom. Dodaj prowizję dla domu w wysokości 5-10% od wszystkich zakładów, która posłuży jako money sink w twojej ekonomii. W przypadku większych zorganizowanych wydarzeń pozwól administratorom dodawać bonusowe pule nagród finansowane ze skarbca serwera. Śledź statystyki zakładów każdego gracza, aby wykrywać i zapobiegać ustawianiu wyścigów, na przykład sytuacjom, gdy kierowca stale przegrywa wyścigi, w których postawiono przeciwko niemu duże kwoty.
-- Server-side betting handler
local raceBets = {}
RegisterNetEvent('racing:placeBet')
AddEventHandler('racing:placeBet', function(raceId, targetRacerId, amount)
local src = source
if not activeRaces[raceId] then return end
if activeRaces[raceId].state ~= 'lobby' then
return notify(src, 'Betting is closed once the race starts.', 'error')
end
-- Validate the target racer is in this race
local racerFound = false
for _, racer in ipairs(activeRaces[raceId].participants) do
if racer.src == targetRacerId then racerFound = true break end
end
if not racerFound then return end
-- Check and deduct funds
local playerMoney = GetPlayerMoney(src)
if playerMoney < amount or amount < Config.MinBet then
return notify(src, 'Insufficient funds or below minimum bet.', 'error')
end
RemoveMoney(src, amount)
if not raceBets[raceId] then raceBets[raceId] = {} end
table.insert(raceBets[raceId], {
bettor = src,
target = targetRacerId,
amount = amount,
})
notify(src, ('Bet $%s on %s'):format(amount, GetPlayerName(targetRacerId)), 'success')
end)
Tworzenie UI wyścigów z NUI
Interfejs wyścigowy musi przekazywać kluczowe informacje w czasie rzeczywistym bez zaśmiecania ekranu i rozpraszania kierowcy. Zaprojektuj minimalistyczny HUD pokazujący aktualną pozycję wśród zawodników, numer okrążenia (jeśli dotyczy), split timer porównujący bieżący przejazd z rekordem osobistym lub rekordem toru oraz nakładkę minimapy wskazującą kierunek kolejnego punktu kontrolnego. Użyj prędkościomierza dopasowanego do motywu wyścigu, wyświetlającego prędkość w mph lub km/h zależnie od preferencji gracza. Ekran lobby przed wyścigiem powinien wyświetlać wszystkich zarejestrowanych uczestników wraz z ich pojazdami, nazwę i długość trasy, podział puli nagród oraz licznik odliczający czas. Zbuduj UI za pomocą HTML, CSS i JavaScript przy użyciu systemu NUI oraz przesyłaj aktualizacje stanu wyścigu z klienckiego kodu Lua do ramki NUI za pośrednictwem SendNUIMessage. Zadbaj o responsywność UI, ograniczając aktualizacje pozycji do 4-5 razy na sekundę zamiast w każdej klatce, co znacznie zmniejsza narzut NUI bez widocznego wpływu na wyświetlane informacje. Rozważ dodanie ekranu wyników po wyścigu ze szczegółowymi statystykami, takimi jak osiągnięta prędkość maksymalna, średnia prędkość oraz porównanie czasów na poszczególnych odcinkach checkpointów.
Kategorie wyścigów i klasy pojazdów
Organizuj wyścigi w osobne kategorie, aby zapewnić uczciwą rywalizację i różnorodność. Zdefiniuj klasy pojazdów na podstawie wskaźników wydajności, takich jak prędkość maksymalna, przyspieszenie i oceny prowadzenia pobierane z natywnych danych handlingu gry. Twórz ograniczenia klasowe dla konkretnych wyścigów, aby gracz w Dominatorze nie mógł dołączyć do wyścigu ograniczonego do aut kompaktowych, i odwrotnie. Poza klasą pojazdu oferuj różne formaty wyścigów: sprinty z punktu A do B przez miasto, wyścigi torowe z wieloma okrążeniami na pętli, wyścigi drag na prostych drogach ze startem na czas reakcji oraz zawody driftu punktowane za kąt, prędkość i bliskość punktów clipping point. Każdy format wymaga nieco innej logiki punktacji i wykrywania ukończenia. W szczególności punktacja driftu wymaga niestandardowego algorytmu, który ocenia kąt poślizgu pojazdu względem wektora jego prędkości, prędkość utrzymywaną podczas driftu oraz mnożniki bonusowe za łączenie kolejnych driftów bez utraty kontroli. Pozwól organizatorom wyścigów przełączać opcje, takie jak mechanika catch-up, ghosting przez innych zawodników zapobiegający kolizjom typu griefing, a także ustawienia pogody lub pory dnia dla klimatu.
Integracja z policją i nielegalne wyścigi
Wyścigi uliczne powinny być nielegalną aktywnością, która generuje naturalne interakcje ze służbami porządkowymi. Gdy wyścig się rozpoczyna, generuj zgłoszenia o hałasie od NPC w okolicy, które pojawiają się w systemie dyspozytorskim policji po konfigurowalnym opóźnieniu, dając ścigającym się przewagę na starcie, ale sprawiając, że dłuższe wyścigi przyciągają uwagę. Policjanci reagujący na zgłoszenie mogą próbować zatrzymać wyścig, rozkładając kolczatki, stawiając blokady drogowe lub ścigając poszczególnych kierowców. Jeśli kierowca zostanie złapany przez policję, grożą mu mandaty, konfiskata pojazdu i potencjalnie kara więzienia, co dodaje realnych konsekwencji i sprawia, że scena wyścigowa staje się niebezpieczna oraz ekscytująca. Wdróż system poziomu poszukiwania dedykowany wyścigom, który eskaluje wraz z kolejnymi wykroczeniami, dzięki czemu notoryczni kierowcy z czasem ściągają na siebie bardziej agresywne reakcje policji. Pozwól policji na korzystanie z kamer monitoringu w znanych lokalizacjach wyścigowych w celu budowania spraw przeciwko organizatorom, tworząc warstwę śledczą, która nagradza cierpliwą pracę policji większymi akcjami rozbijającymi całe ekipy wyścigowe zamiast pojedynczych kierowców.
Optymalizacja wydajności dla wyścigów
Systemy wyścigów wymagają płynnego działania, ponieważ nawet niewielki lag może zepsuć rywalizację. Zminimalizuj komunikację klient-serwer podczas trwających wyścigów, obsługując wykrywanie checkpointów i śledzenie pozycji po stronie klienta, a aktualizacje wysyłaj na serwer tylko wtedy, gdy checkpoint zostanie osiągnięty lub wyścig się zakończy. Używaj entity state bags do rozgłaszania pozycji zamiast własnych zdarzeń sieciowych, ponieważ state bags są zoptymalizowane pod kątem częstych aktualizacji i efektywniej zarządzają przepustowością. Przy renderowaniu checkpointów rysuj markery tylko dla bieżącego i następnego punktu, a nie dla wszystkich jednocześnie, oraz stosuj logikę LOD (Level of Detail), aby zmniejszyć złożoność markerów na większych dystansach. Łącz zapytania o dane wyścigów, pobierając tabele wyników i listy wyścigów w operacjach wsadowych zamiast pojedynczych zapytań, oraz cachuj wyniki po stronie klienta z krótkim TTL, aby ograniczyć obciążenie bazy danych w godzinach szczytu. Przetestuj swój system przy maksymalnej przewidywanej liczbie jednoczesnych zawodników, zazwyczaj 8-16 na wyścig, aby upewnić się, że synchronizacja pozycji i walidacja checkpointów skalują się prawidłowo bez spadków klatek ani problemów z desynchronizacją.