Skrypty odtwarzacza muzyki i radia FiveM: xSound i więcej
Odtwarzaj muzykę w grze we właściwy sposób. xSound, aplikacje odtwarzacza radiowego, propy boomboxa i najlepsze skrypty audio FiveM na imprezy, do barów i aut.
Agency Scripts
Założyciel i główny deweloper w Agency Scripts
Dlaczego własne radio ma znaczenie w Roleplay
Muzyka i radio to kluczowe elementy klimatu, które ożywiają serwer roleplay FiveM. Chociaż GTA V zawiera własne stacje radiowe, własne systemy radiowe pozwalają właścicielom serwerów tworzyć stacje tematyczne dopasowane do tożsamości społeczności, transmitować sety DJ na żywo, odtwarzać playlisty tworzone przez graczy, a nawet nadawać wiadomości ze świata gry. Dobrze zbudowany system muzyczny to coś więcej niż zwykłe odtwarzanie dźwięku. Zapewnia zsynchronizowane wrażenia słuchowe dla graczy w tym samym pojeździe, dźwięk przestrzenny z boomboxów i głośników klubowych, osobiste playlisty zarządzane przez aplikację w telefonie oraz integrację ze stanowiskiem DJ do roleplayu w klubach nocnych. Techniczne fundamenty opierają się na możliwościach audio NUI, co pozwala wykorzystać pełną moc przeglądarkowych API audio bezpośrednio w kliencie FiveM.
Architektura audio i integracja NUI
Warstwa NUI w FiveM uruchamia przeglądarkę opartą na Chromium, wbudowaną w klienta gry, co oznacza, że masz dostęp do Web Audio API, elementów audio HTML5, a nawet MediaSource API do streamingu adaptacyjnego. Architektura dzieli się na trzy komponenty: skrypt klienta Lua obsługuje interakcje w świecie gry, takie jak wsiadanie do pojazdów lub podchodzenie do głośników, warstwa NUI HTML i JavaScript zarządza rzeczywistym odtwarzaniem dźwięku i wizualizacją, a serwer koordynuje zsynchronizowane stany odtwarzania między wieloma klientami. Gdy gracz wsiada do pojazdu, skrypt klienta wysyła komunikat NUI, aby rozpocząć odtwarzanie bieżącej stacji. Warstwa NUI tworzy element audio, ustawia źródłowy adres URL i rozpoczyna odtwarzanie. Sterowanie głośnością odbywa się całkowicie w warstwie NUI za pomocą węzła GainNode z Web Audio API, co zapewnia płynne przejścia głośności:
-- Client-side: Vehicle radio controller
local currentStation = nil
local radioActive = false
CreateThread(function()
while true do
Wait(500)
local ped = PlayerPedId()
if IsPedInAnyVehicle(ped, false) then
local vehicle = GetVehiclePedIsIn(ped, false)
if not radioActive then
radioActive = true
-- Disable default GTA radio
SetVehicleRadioEnabled(vehicle, false)
-- Load saved station preference
local savedStation = LocalPlayer.state.radioStation or 'station_1'
SetStation(savedStation)
end
elseif radioActive then
radioActive = false
SendNUIMessage({action = 'stopRadio'})
currentStation = nil
end
end
end)
function SetStation(stationId)
local station = Config.Stations[stationId]
if not station then return end
currentStation = stationId
SendNUIMessage({
action = 'playStation',
url = station.url,
name = station.name,
volume = LocalPlayer.state.radioVolume or 0.5,
})
end
Konfiguracja odtwarzacza audio NUI
Strona NUI wymaga niezawodnego odtwarzacza audio, który obsługuje połączenia strumieniowe, buforowanie, odzyskiwanie po błędach i zarządzanie głośnością. Użyj Web Audio API zamiast prostych elementów audio HTML5, ponieważ zapewnia ono kontrolę nad routingiem dźwięku, pozwala tworzyć wizualizacje na podstawie danych o częstotliwości oraz obsługuje przetwarzanie dźwięku przestrzennego. Utwórz AudioContext z GainNode do kontroli głośności oraz AnalyserNode dla danych wizualizacji:
// NUI JavaScript: Audio engine
class RadioPlayer {
constructor() {
this.audioContext = new (window.AudioContext || window.webkitAudioContext)();
this.gainNode = this.audioContext.createGain();
this.analyser = this.audioContext.createAnalyser();
this.analyser.fftSize = 256;
this.gainNode.connect(this.analyser);
this.analyser.connect(this.audioContext.destination);
this.audio = new Audio();
this.audio.crossOrigin = 'anonymous';
this.source = null;
this.isPlaying = false;
}
play(url, volume) {
this.stop();
this.audio = new Audio(url);
this.audio.crossOrigin = 'anonymous';
this.audio.volume = 1.0;
this.source = this.audioContext.createMediaElementSource(this.audio);
this.source.connect(this.gainNode);
this.gainNode.gain.value = volume;
this.audio.play().then(() => {
this.isPlaying = true;
}).catch(err => {
console.error('Radio playback failed:', err);
setTimeout(() => this.play(url, volume), 3000);
});
}
stop() {
if (this.audio) {
this.audio.pause();
this.audio.src = '';
this.isPlaying = false;
}
}
setVolume(vol) {
this.gainNode.gain.linearRampToValueAtTime(
vol, this.audioContext.currentTime + 0.1
);
}
getFrequencyData() {
const data = new Uint8Array(this.analyser.frequencyBinCount);
this.analyser.getByteFrequencyData(data);
return data;
}
}
const radio = new RadioPlayer();
Konfiguracja i streaming stacji
Zdefiniuj swoje stacje radiowe, podając adresy URL strumieni, metadane i kategorie gatunków. Większość niestandardowych systemów radiowych FiveM korzysta ze strumieni radia internetowego w formacie MP3 lub AAC, ponieważ są one powszechnie obsługiwane i łatwe w konfiguracji. Możesz hostować własny serwer Icecast lub Shoutcast, aby mieć pełną kontrolę, albo użyć publicznych adresów URL strumieni radiowych. Dla każdej stacji zapisz adres URL strumienia, nazwę wyświetlaną, tag gatunku oraz opcjonalnie logo dla UI. Rozważ dodanie obsługi adresów URL z YouTube i SoundCloud za pośrednictwem usług proxy, pamiętając jednak o regulaminach usług i ograniczeniach praw autorskich. W przypadku funkcji DJ-a na żywo zintegruj skrypt z usługami zapewniającymi konwersję strumienia RTMP do HTTP, aby DJ-e mogli nadawać przy użyciu OBS lub podobnego oprogramowania:
Config.Stations = {
['station_1'] = {
name = 'Agency Radio',
genre = 'Hip Hop',
url = 'https://stream.example.com/hiphop',
logo = 'station_hiphop.webp',
description = 'The freshest beats in Los Santos',
},
['station_2'] = {
name = 'LS Rock Radio',
genre = 'Rock',
url = 'https://stream.example.com/rock',
logo = 'station_rock.webp',
description = 'Classic and modern rock anthems',
},
['station_3'] = {
name = 'Vinewood Lounge',
genre = 'Jazz / Lofi',
url = 'https://stream.example.com/lofi',
logo = 'station_lofi.webp',
description = 'Smooth vibes for the night drive',
},
['station_live'] = {
name = 'Live DJ',
genre = 'Live',
url = 'https://stream.example.com/live',
logo = 'station_live.webp',
description = 'Live DJ sets from the community',
isLive = true,
},
}
Zsynchronizowane radio pojazdu
Gdy wielu graczy jedzie tym samym pojazdem, wszyscy powinni słyszeć tę samą stację radiową. Wymaga to koordynacji po stronie serwera za pomocą state bags lub zdarzeń sieciowych. Gdy kierowca zmienia stację, serwer rozsyła nową stację do wszystkich pasażerów. Użyj systemu state bags w FiveM do wydajnej synchronizacji, ponieważ automatycznie obsługuje replikację sieciową bez ręcznego zarządzania zdarzeniami. Ustaw stację w state bag encji pojazdu, aby każdy gracz wsiadający do pojazdu automatycznie odbierał aktualną stację. Obsłuż przypadki brzegowe, takie jak pasażerowie wsiadający do pojazdu, w którym stacja już gra, opuszczenie pojazdu przez kierowcę, gdy pasażerowie w nim pozostają, oraz sprzeczne zmiany stacji przez pasażerów, jeśli zezwalasz na sterowanie osobom niebędącym kierowcą:
-- Server-side: Sync radio state via state bags
RegisterNetEvent('radio:server:setStation', function(vehicleNet, stationId)
local src = source
local vehicle = NetworkGetEntityFromNetworkId(vehicleNet)
if not DoesEntityExist(vehicle) then return end
-- Verify player is the driver
local ped = GetPlayerPed(src)
if GetPedInVehicleSeat(vehicle, -1) ~= ped then return end
-- Set station on vehicle state bag
Entity(vehicle).state:set('radioStation', stationId, true)
end)
-- Client-side: Listen for state bag changes
AddStateBagChangeHandler('radioStation', nil, function(bagName, key, value)
if not value then return end
local entity = GetEntityFromStateBagName(bagName)
if not DoesEntityExist(entity) then return end
local ped = PlayerPedId()
if not IsPedInVehicle(ped, entity, false) then return end
SetStation(value)
end)
Systemy dźwięku przestrzennego i boomboxów
Dźwięk przestrzenny dodaje poziom immersji, któremu zwykłe odtwarzanie audio nie dorównuje. Boomboxy, głośniki klubowe i radia samochodowe powinny emitować dźwięk, który cichnie wraz z odległością i przesuwa się w panoramie w zależności od pozycji słuchacza względem źródła. FiveM oferuje natywne funkcje audio, takie jak PlaySoundFromCoord dla prostych dźwięków przestrzennych, ale w przypadku strumieniowania audio musisz zaimplementować skalowanie głośności oparte na odległości w warstwie NUI. Obliczaj odległość między graczem a źródłem dźwięku w każdej klatce, a następnie zmapuj tę odległość na krzywą głośności. Użyj zanikania według prawa odwrotnych kwadratów dla realistycznego tłumienia dźwięku, z konfigurowalną minimalną i maksymalną odległością słyszalności. W przypadku boomboxów, które gracze mogą stawiać w świecie gry, stwórz przedmiot możliwy do rozstawienia, który spawnuje prop w lokalizacji gracza i rejestruje go jako źródło audio. Inni gracze w zasięgu powinni automatycznie słyszeć muzykę, ze skalowaniem głośności na podstawie ich odległości od propa boomboxa.
Playlista gracza i integracja z telefonem
Daj graczom pełną kontrolę nad muzyką dzięki systemowi playlist zintegrowanemu z zasobem telefonu na Twoim serwerze. Gracze powinni mieć możliwość tworzenia własnych playlist poprzez dodawanie adresów URL do poszczególnych utworów, zmieniania kolejności utworów metodą przeciągnij i upuść, ustawiania odtwarzania losowego lub zapętlania oraz udostępniania playlist innym graczom. Zapisuj playlisty w bazie danych powiązanej z identyfikatorem gracza, aby były zachowywane między sesjami. Interfejs aplikacji w telefonie powinien wyświetlać aktualnie odtwarzany utwór z metadanymi wykonawcy i tytułu, przyciski sterowania odtwarzaniem (play, pause, skip, previous), suwak głośności oraz listę zapisanych playlist. Gdy gracz znajduje się w pojeździe, odtwarzacz w telefonie powinien automatycznie przekierowywać dźwięk przez radio pojazdu, aby pasażerowie mogli słyszeć muzykę. Podczas poruszania się pieszo z założonymi słuchawkami dousznymi jako przedmiotem, muzyka gra prywatnie z małą ikoną słuchawek widoczną dla innych graczy, sygnalizującą, że postać słucha muzyki, nie będąc słyszalną dla innych.
Wizualizator i projekt UI
Interfejs odtwarzacza muzyki powinien żyć i reagować na odtwarzany dźwięk. Użyj AnalyserNode z Web Audio API, aby wyodrębniać dane o częstotliwości w czasie rzeczywistym i renderować wizualizację audio. Paskowe wizualizatory, wykresy falowe i kołowe analizatory widma sprawdzają się świetnie w zależności od estetyki Twojego UI. Renderuj wizualizację za pomocą elementu canvas dla lepszej wydajności, odświeżając obraz z częstotliwością 30 lub 60 klatek na sekundę w zależności od złożoności animacji. Samo UI radia powinno być minimalistyczne i nienarzucające się, widoczne jako mały widget w rogu ekranu, pokazujący aktualną nazwę stacji, tytuł utworu (jeśli jest dostępny w metadanych strumienia) oraz podstawowe przyciski sterowania. Gdy gracz otwiera pełne menu radia, aby zmienić stację, wyświetl siatkę lub listę dostępnych stacji z ich logo, gatunkami i liczbą słuchaczy. Dodaj pasek wyszukiwania dla serwerów z dużą liczbą stacji oraz system ulubionych, aby gracze mogli szybko włączać preferowane stacje bez przewijania całej listy.
Kwestie wydajności
Streaming audio zużywa przepustowość oraz zasoby klienta, dlatego starannie zoptymalizuj swoją implementację. Ogranicz liczbę jednoczesnych strumieni audio na klienta, aby zapobiec wyciekom pamięci i skokom obciążenia procesora. Gdy gracz nie znajduje się w pojeździe i nie ma w pobliżu boomboxa, całkowicie zniszcz audio context, zamiast go tylko pauzować. W przypadku dźwięku przestrzennego z wielu boomboxów twórz strumienie audio tylko dla źródeł w zasięgu słuchu i usuwaj je, gdy źródła znajdą się poza zasięgiem. Używaj strumieni o niskim bitrate dla muzyki w tle, a wyższą jakość rezerwuj dla głównego odsłuchu. Po stronie serwera unikaj przesyłania adresów URL strumieni bezpośrednio w eventach, ponieważ mogą zostać przechwycone i nadużyte. Zamiast tego zastosuj system callback, w którym klient żąda adresu URL strumienia dla konkretnego station ID, a serwer odpowiada ograniczonym czasowo podpisanym adresem URL. Monitoruj błędy odtwarzania zgłaszane przez klienta i automatycznie wznawiaj zerwane połączenia z użyciem exponential backoff, aby uniknąć przeciążenia obciążonego serwera strumieniowego.