FiveM-Benachrichtigungssystem: Hinweise, Warnungen und ox_lib
Erstelle ein elegantes Benachrichtigungssystem für FiveM mit Hinweisen, Fortschrittsbalken, Radialmenüs und UI-Rückmeldungen von ox_lib und Top-Scripts.
Agency Scripts
Gründer und leitender Entwickler bei Agency Scripts
Warum ein eigenes Benachrichtigungssystem erstellen?
Die Standardbenachrichtigungen von FiveM sind funktional, bieten aber wenig optische Qualität und Flexibilität. Die meisten Roleplay-Server setzen auf generische Text-Pop-ups, die alle gleich aussehen. Dadurch ist es für Spieler schwer, Fehler, Erfolgsbestätigungen und Informationsmeldungen auseinanderzuhalten. Ein eigenes Benachrichtigungssystem mit NUI gibt dir die volle Kontrolle über Gestaltung, Animationen, Positionierung, Soundeffekte und Warteschlangenverhalten. So wird aus einem einfachen UI-Element etwas, das zur Markenidentität deines Servers passt und das Spielerlebnis deutlich verbessert. In diesem Tutorial erstellen wir von Grund auf ein vollständiges Toast-Benachrichtigungssystem mit Lua auf Server- und Client-Seite sowie HTML, CSS und JavaScript für das NUI-Frontend.
NUI-Ebene einrichten
Die Grundlage jedes eigenen Benachrichtigungssystems in FiveM ist die NUI-Ebene (New UI). Mit NUI kannst du HTML-Inhalte als Overlay über dem Spiel rendern und hast dadurch alle Möglichkeiten moderner Webtechnologien zur Verfügung. Erstelle zuerst die Resource-Struktur mit einer fxmanifest.luaeinem clientseitigen Lua-Skript und einem html Ordner für deine UI-Dateien. In der Manifest-Datei musst du deine NUI-Seite deklarieren und Message-Handler registrieren, damit deine Lua-Skripte mit dem Frontend kommunizieren können.
-- fxmanifest.lua
fx_version 'cerulean'
game 'gta5'
ui_page 'html/index.html'
files {
'html/index.html',
'html/style.css',
'html/script.js',
'html/sounds/*.ogg'
}
client_script 'client.lua'
server_script 'server.lua'
Dein html/index.html Datei sollte möglichst schlank sein. Sie benötigt nur ein Container-div, in das JavaScript Benachrichtigungen dynamisch einfügt. Halte das HTML schlank, da jedes Benachrichtigungselement programmgesteuert erstellt und wieder entfernt wird. Binde dein Stylesheet und Skript ein und achte darauf, dass der Hintergrund des Bodys transparent ist, damit die Spielwelt hinter den Benachrichtigungen sichtbar bleibt.
<!-- html/index.html -->
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="style.css">
</head>
<body>
<div id="notification-container"></div>
<script src="script.js"></script>
</body>
</html>
Toast-Benachrichtigungen mit CSS gestalten
Toast-Benachrichtigungen sollten sich je nach Typ optisch unterscheiden. Definiere eigene Farbschemata für Erfolgs-, Fehler-, Info- , und Warn- Benachrichtigungen. Positioniere den Container mit einer festen Positionierung oben rechts auf dem Bildschirm und ordne die Benachrichtigungen mit einem kleinen Abstand vertikal an. Jeder Toast sollte einen dezenten Glasmorphismus-Effekt mit Hintergrundunschärfe, einen farbigen linken Rand zur Kennzeichnung des Typs sowie flüssige Ein- und Ausblendanimationen haben. Verwende CSS-Keyframes zum Einschieben von rechts und zum Ausblenden, wenn die Benachrichtigung abläuft.
/* html/style.css */
* { margin: 0; padding: 0; box-sizing: border-box; }
body { background: transparent; font-family: 'Segoe UI', sans-serif; overflow: hidden; }
#notification-container {
position: fixed;
top: 20px;
right: 20px;
display: flex;
flex-direction: column;
gap: 10px;
z-index: 9999;
max-width: 380px;
width: 100%;
}
.toast {
background: rgba(15, 15, 25, 0.85);
backdrop-filter: blur(12px);
border-radius: 10px;
padding: 14px 18px;
border-left: 4px solid #3b82f6;
color: #e2e8f0;
animation: slideIn 0.4s cubic-bezier(0.16, 1, 0.3, 1) forwards;
display: flex;
align-items: flex-start;
gap: 12px;
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.4);
}
.toast.success { border-left-color: #22c55e; }
.toast.error { border-left-color: #ef4444; }
.toast.warning { border-left-color: #f59e0b; }
.toast.info { border-left-color: #3b82f6; }
.toast-icon { font-size: 20px; flex-shrink: 0; margin-top: 2px; }
.toast-body { flex: 1; }
.toast-title { font-weight: 700; font-size: 14px; margin-bottom: 4px; }
.toast-msg { font-size: 13px; color: #94a3b8; line-height: 1.5; }
.toast-progress {
position: absolute;
bottom: 0; left: 0;
height: 3px;
background: currentColor;
border-radius: 0 0 0 10px;
animation: progress linear forwards;
}
@keyframes slideIn {
from { opacity: 0; transform: translateX(100px); }
to { opacity: 1; transform: translateX(0); }
}
@keyframes slideOut {
from { opacity: 1; transform: translateX(0); }
to { opacity: 0; transform: translateX(100px); }
}
@keyframes progress {
from { width: 100%; }
to { width: 0%; }
}
JavaScript-Warteschlange für Benachrichtigungen erstellen
Die JavaScript-Ebene verarbeitet eingehende Nachrichten von Lua, erstellt DOM-Elemente, verwaltet die Warteschlange und blendet Benachrichtigungen automatisch aus. Eine Warteschlange ist wichtig, denn du möchtest nicht, dass zehn Benachrichtigungen gleichzeitig auf dem Bildschirm stehen. Begrenze die Anzahl sichtbarer Benachrichtigungen auf maximal fünf und reiht weitere Benachrichtigungen in die Warteschlange ein, bis vorherige ausgeblendet werden. Für jede Benachrichtigung sollte eine Dauer eingestellt werden können. Ein Klick darauf sollte sie sofort ausblenden. Der Fortschrittsbalken unten in jedem Toast zeigt Spielern an, wie lange die Benachrichtigung noch auf dem Bildschirm bleibt.
// html/script.js
const container = document.getElementById('notification-container');
const MAX_VISIBLE = 5;
const queue = [];
let activeCount = 0;
const icons = {
success: '✔',
error: '✖',
warning: '⚠',
info: 'ℹ'
};
const sounds = {
success: new Audio('sounds/success.ogg'),
error: new Audio('sounds/error.ogg'),
warning: new Audio('sounds/warning.ogg'),
info: new Audio('sounds/info.ogg')
};
window.addEventListener('message', (event) => {
if (event.data.type === 'showNotification') {
addNotification(event.data);
}
});
function addNotification(data) {
if (activeCount >= MAX_VISIBLE) {
queue.push(data);
return;
}
createToast(data);
}
function createToast(data) {
activeCount++;
const duration = data.duration || 5000;
const toast = document.createElement('div');
toast.className = `toast ${data.style || 'info'}`;
toast.style.position = 'relative';
toast.innerHTML = `
<div class="toast-icon">${icons[data.style] || icons.info}</div>
<div class="toast-body">
<div class="toast-title">${data.title || ''}</div>
<div class="toast-msg">${data.message}</div>
</div>
<div class="toast-progress" style="animation-duration:${duration}ms"></div>
`;
toast.addEventListener('click', () => dismissToast(toast));
container.appendChild(toast);
if (data.sound !== false && sounds[data.style]) {
sounds[data.style].currentTime = 0;
sounds[data.style].play().catch(() => {});
}
setTimeout(() => dismissToast(toast), duration);
}
function dismissToast(toast) {
if (toast.dataset.dismissed) return;
toast.dataset.dismissed = 'true';
toast.style.animation = 'slideOut 0.3s ease forwards';
setTimeout(() => {
toast.remove();
activeCount--;
if (queue.length > 0) {
createToast(queue.shift());
}
}, 300);
}
Clientseitige Lua-Integration
Auf Client-Seite benötigst du eine Funktion, die NUI-Nachrichten an deine HTML-Ebene sendet, sowie einen Export, über den andere Resources Benachrichtigungen auslösen können, ohne direkt von den internen Funktionen deines Skripts abhängig zu sein. Die SendNUIMessage Native überträgt Daten an den Browser-Kontext, wo dein JavaScript sie verarbeitet. Wickle das in eine übersichtliche API-Funktion ein, die Titel, Nachricht, Typ und eine optionale Anzeigedauer entgegennimmt. Registriere sowohl ein Client-Event als auch einen Export, damit Benachrichtigungen von serverseitigen Skripten und anderen Client-Resources ausgelöst werden können. Dieser duale Ansatz sorgt für maximale Kompatibilität mit jedem Framework.
-- client.lua
local function ShowNotification(title, message, style, duration, sound)
SendNUIMessage({
type = 'showNotification',
title = title or '',
message = message or '',
style = style or 'info',
duration = duration or 5000,
sound = sound ~= false
})
end
-- Export for other resources
exports('ShowNotification', ShowNotification)
-- Event-based trigger from server
RegisterNetEvent('notifications:show', function(title, message, style, duration)
ShowNotification(title, message, style, duration)
end)
-- Convenience commands for testing
RegisterCommand('testnotify', function()
ShowNotification('Success', 'Your item has been saved.', 'success', 4000)
Wait(500)
ShowNotification('Error', 'Insufficient funds for this purchase.', 'error', 5000)
Wait(500)
ShowNotification('Warning', 'Your vehicle is low on fuel.', 'warning', 4000)
Wait(500)
ShowNotification('Info', 'Press E to interact with the NPC.', 'info', 3000)
end, false)
Serverseitige Event-Verteilung
Das serverseitige Skript stellt Hilfsfunktionen bereit, um Benachrichtigungen an bestimmte Spieler, alle Spieler oder Spielergruppen zu senden. Hier behandelst du Anwendungsfälle wie das Veröffentlichen von Ankündigungen, das Senden von Transaktionsbestätigungen oder das Benachrichtigen von Admins über verdächtige Aktivitäten. Wenn du die Verteilungslogik auf dem Server zentralisierst, hast du eine zentrale Stelle, über die du die Zustellung von Benachrichtigungen kontrollierst. Du kannst hier auch eine Ratenbegrenzung einbauen, um Benachrichtigungsspam durch böswillige oder fehlerhafte Client-Skripte zu verhindern. Der Server sollte die Benachrichtigungsparameter validieren, bevor er sie weiterleitet, um NUI-Injection durch manipulierte Nachrichten zu verhindern.
-- server.lua
local function NotifyPlayer(source, title, message, style, duration)
if not source or source <= 0 then return end
title = tostring(title or '')
message = tostring(message or '')
style = style or 'info'
TriggerClientEvent('notifications:show', source, title, message, style, duration)
end
local function NotifyAll(title, message, style, duration)
TriggerClientEvent('notifications:show', -1, title, message, style, duration)
end
exports('NotifyPlayer', NotifyPlayer)
exports('NotifyAll', NotifyAll)
-- Example: welcome notification
AddEventHandler('playerJoining', function()
local src = source
Wait(3000)
NotifyPlayer(src, 'Welcome', 'Welcome to the server! Have fun.', 'success', 6000)
end)
-- Admin broadcast command
RegisterCommand('broadcast', function(source, args)
if source > 0 and not IsPlayerAceAllowed(source, 'command.broadcast') then return end
local msg = table.concat(args, ' ')
NotifyAll('Announcement', msg, 'info', 8000)
end, true)
Soundeffekte und Feinschliff hinzufügen
Soundeffekte machen aus einer rein visuellen Benachrichtigung eine multisensorische Rückmeldung. Spieler lassen das Spiel oft im Hintergrund laufen oder konzentrieren sich aufs Fahren und übersehen möglicherweise einen lautlosen Toast. Ein kurzer, dezenter Klang bei Erfolgsbenachrichtigungen, ein tiefer Summton bei Fehlern und ein sanfter Ping bei Info-Meldungen sorgen dafür, dass Spieler wichtige Informationen wahrnehmen, auch wenn sie gerade nicht auf den Benachrichtigungsbereich schauen. Halte Audiodateien kurz, unter 500 Millisekunden, und verwende das OGG-Format, um die Browserkompatibilität sicherzustellen. Stelle die Lautstärke auf etwa 30 Prozent ein, damit sich der Sound in den Spielton einfügt, statt ihn zu übertönen. Speichere die Sounddateien in deinem html/sounds Verzeichnis ablegen und in JavaScript vorab laden, um Wiedergabeverzögerungen bei der ersten Benachrichtigung zu vermeiden.
Erweiterte Funktionen und Anpassungsmöglichkeiten
Sobald das Kernsystem funktioniert, solltest du erweiterte Funktionen hinzufügen, um deinen Server von anderen abzuheben. Dauerhafte Benachrichtigungen, die auf dem Bildschirm bleiben, bis der Spieler sie ausdrücklich anklickt, eignen sich für wichtige Hinweise wie ausstehende Anrufe oder Gefängnis-Timer. Aktionsschaltflächen in Benachrichtigungen ermöglichen es Spielern, direkt zu reagieren, zum Beispiel eine Handelsanfrage anzunehmen, ohne ein separates Menü zu öffnen. Du kannst Benachrichtigungen auch gruppieren, sodass wiederholte identische Meldungen zu einer einzigen Toast-Benachrichtigung mit einem Zähler zusammengefasst werden, der die Anzahl der Vorkommen anzeigt. Für die Framework-Integration kannst du Bridge-Dateien erstellen, die die Standard-Benachrichtigungsfunktionen in QBCore oder ESX überschreiben. So verwendet jede Ressource auf deinem Server automatisch dein eigenes System, ohne dass du Code ändern musst. Dieses Muster, Framework-Standards durch eine eigene Implementierung zu ersetzen, ist eine saubere Möglichkeit, die Benutzeroberfläche eines ganzen Servers auf einmal aufzurüsten.