Pourquoi la radio personnalisée est importante dans le jeu de rôle
La musique et la radio sont des éléments atmosphériques essentiels qui donnent vie à un serveur de jeu de rôle FiveM. Alors que le GTA V est livré avec ses propres stations de radio, les systèmes de radio personnalisés permettent aux propriétaires de serveurs de créer des stations thématiques qui correspondent à l'identité de leur communauté, de diffuser des sets de DJ en direct, de lire des listes de lecture organisées par la communauté et même de diffuser des bulletins d'information en personne. Un système musical bien construit va au-delà de la simple lecture audio. Il offre des expériences d'écoute synchronisées pour les joueurs dans le même véhicule, un son spatial provenant de boombox et de haut-parleurs de club, des listes de lecture personnelles que les joueurs peuvent gérer via une application téléphonique et une intégration de cabine de DJ pour le jeu de rôle en boîte de nuit. La base technique repose sur les capacités audio du NUI, qui tu permettent d'exploiter toute la puissance de l'audio APIs basé sur un navigateur au sein du client FiveM.
Architecture audio et intégration du NUI
La couche NUI de FiveM exécute un navigateur basé sur Chromium intégré au client de jeu, ce qui signifie que tu as accès au Web Audio API, aux éléments audio HTML5 et même au MediaSource API pour le streaming adaptatif. L'architecture se divise en trois composants : le script client Lua gère les interactions avec le monde du jeu, comme l'entrée dans des véhicules ou l'approche de haut-parleurs, les couches NUI HTML et JavaScript gèrent la lecture et la visualisation audio réelles, et le serveur coordonne les états de lecture synchronisés sur plusieurs clients. Lorsqu'un joueur entre dans un véhicule, le script client envoie un message NUI pour commencer la lecture de la station actuelle. La couche NUI crée un élément audio, définit l'URL source et commence la lecture. Les contrôles de volume sont entièrement gérés dans la couche NUI à l'aide du GainNode de Web Audio API pour des transitions de volume fluides :
-- 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
Configuration du lecteur audio NUI
Le côté NUI nécessite un lecteur audio robuste qui gère les connexions de flux, la mise en mémoire tampon, la récupération des erreurs et la gestion du volume. Utilisez le Web Audio API plutôt que de simples éléments audio HTML5, car il tu permet de contrôler le routage audio, tu permet de créer des visualisations à partir de données de fréquence et prend en charge le traitement audio spatial. Créez un AudioContext avec un GainNode pour le contrôle du volume et un AnalyserNode pour les données de visualisation :
// 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();
Configuration des stations et diffusion en continu
Définissez tes stations de radio avec des URL de flux, des métadonnées et des catégories de genre. La plupart des systèmes radio FiveM personnalisés utilisent des flux radio Internet au format MP3 ou AAC car ils sont largement pris en charge et faciles à configurer. Tu peux héberger ton propre serveur Icecast ou Shoutcast pour un contrôle complet, ou utiliser des URL de flux radio publics. Pour chaque station, stockez l'URL du flux, un nom d'affichage, une balise de genre et éventuellement une image de logo pour l'interface utilisateur. Envisagez d'ajouter la prise en charge des URL YouTube et SoundCloud via des services proxy, mais tenez compte des conditions de service et des restrictions de droits d'auteur. Pour la fonctionnalité DJ en direct, intégrez des services qui fournissent une conversion de flux RTMP vers HTTP afin que les DJ puissent diffuser à l'aide d'OBS ou d'un logiciel similaire :
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,
},
}
Radio de véhicule synchronisée
Lorsque plusieurs joueurs voyagent dans le même véhicule, ils doivent tous entendre la même station radio. Cela nécessite une coordination côté serveur via des sacs d'état ou des événements en réseau. Lorsque le conducteur change de station, le serveur diffuse la nouvelle station à tous les occupants. Utilisez le système de sacs d'état du FiveM pour une synchronisation efficace, car il gère automatiquement la réplication réseau sans gestion manuelle des événements. Placez la station sur le sac d'état de l'entité du véhicule afin que tout joueur entrant dans le véhicule récupère automatiquement la station actuelle. Gérez les cas extrêmes tels que les passagers entrant dans un véhicule dans lequel une station est déjà en cours de lecture, le conducteur partant alors que les passagers restent et les changements de station conflictuels de la part des passagers si tu autorises le contrôle par un non-conducteur :
-- 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)
Systèmes audio spatiaux et boombox
L’audio spatial ajoute une couche d’immersion que la lecture audio plate ne peut égaler. Les boombox, les haut-parleurs de club et les autoradios doivent émettre un son qui s'estompe avec la distance et les panoramiques en fonction de la position de l'auditeur par rapport à la source. FiveM fournit des fonctions audio natives telles que PlaySoundFromCoord pour des sons spatiaux simples, mais pour le streaming audio, tu dois implémenter une mise à l'échelle du volume basée sur la distance dans la couche NUI. Calculez la distance entre le lecteur et la source audio à chaque image, puis mappez cette distance sur une courbe de volume. Utilisez une atténuation carrée inverse pour une atténuation sonore réaliste, avec des distances auditives minimales et maximales configurables. Pour les boombox que les joueurs peuvent placer dans le monde, créez un élément déployable qui génère un accessoire à l'emplacement du joueur et l'enregistre comme source audio. Les autres joueurs à portée devraient automatiquement entendre la musique, avec une mise à l'échelle du volume en fonction de leur distance par rapport à l'accessoire boombox.
Liste de lecture des joueurs et intégration du téléphone
Donnez aux joueurs un contrôle personnel sur leur expérience musicale grâce à un système de playlist intégré à la ressource téléphonique de ton serveur. Les joueurs devraient pouvoir créer des listes de lecture personnalisées en ajoutant des URL à des pistes individuelles, réorganiser les chansons par glisser-déposer, définir une liste de lecture à mélanger ou à répéter et partager des listes de lecture avec d'autres joueurs. Stockez les playlists dans la base de données liées à l'identifiant du joueur afin qu'elles persistent au fil des sessions. L'interface de l'application téléphonique doit afficher la piste en cours de lecture avec les métadonnées de l'artiste et du titre, les commandes de lecture pour la lecture, la pause, le saut et le précédent, un curseur de volume et une liste de listes de lecture enregistrées. Lorsque le lecteur se trouve dans un véhicule, le lecteur du téléphone doit automatiquement acheminer le son via le système radio du véhicule afin que les passagers puissent entendre la musique. Lorsqu'on est à pied avec des écouteurs équipés en accessoire, la musique joue en privé avec une petite icône d'écouteur visible par les autres joueurs, indiquant que le personnage écoute de la musique sans qu'elle soit audible par les autres.
Visualiseur et conception d'interface utilisateur
L’interface utilisateur d’un lecteur de musique doit être vivante et réactive à l’audio en cours de lecture. Utilisez l'AnalyserNode du Web Audio API pour extraire les données de fréquence en temps réel et restituer un visualiseur audio. Les visualiseurs de barres, les affichages de formes d'onde et les analyseurs de spectre circulaire fonctionnent tous bien en fonction de l'esthétique de ton interface utilisateur. Effectuez le rendu du visualiseur à l'aide d'un élément de canevas pour les performances, avec une mise à jour à 30 ou 60 images par seconde en fonction de la complexité de la visualisation. L'interface utilisateur de la radio elle-même doit être minimale et non intrusive, apparaissant sous la forme d'un petit widget dans le coin de l'écran affichant le nom de la station actuelle, le titre de la piste s'il est disponible à partir des métadonnées du flux et les commandes de lecture de base. Lorsque le lecteur ouvre le menu complet de la radio pour changer de station, affiche une grille ou une liste des stations disponibles avec leurs logos, genres et nombre d'auditeurs. Incluez une barre de recherche pour les serveurs avec de nombreuses stations et un système de favoris afin que les joueurs puissent accéder rapidement à leurs stations préférées sans faire défiler toute la liste.
Considérations relatives aux performances
Le streaming audio consomme de la bande passante et des ressources client, alors optimisez soigneusement ton implémentation. Limitez le nombre de flux audio simultanés par client pour éviter les fuites de mémoire et les pics de processeur. Lorsqu'un joueur n'est pas dans un véhicule et n'a pas de boombox à proximité, détruisez complètement le contexte audio plutôt que de simplement le mettre en pause. Pour l’audio spatial provenant de plusieurs boombox, créez uniquement des flux audio pour les sources à portée auditive et nettoyez les flux à mesure que les sources se déplacent hors de portée. Utilisez des flux à faible débit pour la musique de fond ambiante et réservez des flux de meilleure qualité pour l'expérience d'écoute principale. Côté serveur, évitez d'envoyer des URL de flux directement dans les événements car elles peuvent être interceptées et utilisées de manière abusive. Utilisez plutôt un système de rappel dans lequel le client demande l'URL de flux pour un ID de station spécifique et le serveur répond avec une URL signée à durée limitée. Surveillez les erreurs de lecture signalées par le client et reconnectez automatiquement les flux interrompus en raison de problèmes de réseau, avec une interruption exponentielle pour éviter de surcharger un serveur de flux en difficulté.
