Curseur FiveM coincé ou ne peut pas se déplacer après fermeture d'un menu : correction du focus NUI

Le curseur reste à l'écran ou ne peut pas se déplacer après fermeture d'un menu ? Comment SetNuiFocus fonctionne, fermeture avec un rappel NUI, gestion d'ESC en JavaScript et trouver le coupable.

Vous fermez un menu, et l'une de ces choses se produit :

text
the mouse cursor stays on screen
the character will not move or look around
the keyboard does nothing until you reconnect

Rien ne s'affiche dans la console, car rien n'est mal avec le code qui s'exécute : il n'a simplement jamais redonné le focus. Voici comment fonctionne le focus, comment fermer correctement un menu et comment trouver le coupable.

Comment fonctionne le focus NUI

Une NUI est une page web (HTML, CSS et JavaScript) que FiveM dessine sur le jeu. Pendant qu'elle a le focus, la souris et le clavier vont à la page au lieu du jeu. Un script donne et enlève le focus avec une native :

lua
SetNuiFocus(hasFocus, hasCursor)
  • SetNuiFocus(true, true) : la NUI reçoit le clavier et la souris, et le curseur s'affiche.
  • SetNuiFocus(true, false) : la NUI reçoit le clavier, pas de curseur.
  • SetNuiFocus(false, false) : le focus revient au jeu et le curseur se masque.

Si un menu s'ouvre avec true, true et aucun code n'appelle jamais SetNuiFocus(false, false), le jeu reste bloqué, même si la page est masquée.

La correction : toujours fermer avec SetNuiFocus(false, false)

Mettez la fermeture dans une seule fonction et utilisez-la pour chaque façon de fermer le menu :

lua
local open = false

local function openMenu()
    open = true
    SetNuiFocus(true, true)
    SendNUIMessage({ action = 'open' })
end

local function closeMenu()
    open = false
    SetNuiFocus(false, false)
    SendNUIMessage({ action = 'close' })
end

Faites de même si le menu peut être fermé par un autre code : lorsque le joueur meurt, entre dans un véhicule, ou lorsque la ressource s'arrête.

lua
AddEventHandler('onResourceStop', function(resource)
    if resource == GetCurrentResourceName() and open then
        SetNuiFocus(false, false)
    end
end)

C'est celui-ci qui importe. Si une ressource s'arrête alors que son menu est ouvert, le focus n'est pas retourné pour vous, et le joueur est bloqué jusqu'à ce qu'il se reconnecte.

Fermer depuis la page : RegisterNUICallback et fetch

Un bouton fermer, ou une pression de touche dans la page, s'exécute en JavaScript. JavaScript ne peut pas appeler une native, donc il envoie une demande au côté Lua, qui la traite :

lua
RegisterNUICallback('close', function(data, cb)
    closeMenu()
    cb('ok')
end)

Dans votre page, envoyez la demande avec fetch à https://<resource name>/close. GetParentResourceName() vous donne le nom de la ressource à l'intérieur d'une page NUI :

js
function closeMenu() {
  fetch(`https://${GetParentResourceName()}/close`, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json; charset=UTF-8' },
    body: JSON.stringify({}),
  })
}

document.getElementById('close-button').addEventListener('click', closeMenu)

Le rappel doit appeler cb. S'il ne répond jamais, la demande dans la page reste ouverte, et certaines bibliothèques arrêtent d'attendre. Terminez toujours un rappel avec cb('ok') ou une autre réponse.

ESC et d'autres touches en JavaScript

Pendant que la NUI a le focus, le jeu ne voit pas le clavier, donc la touche ESC n'atteint jamais votre code Lua. La page doit l'écouter :

js
window.addEventListener('keydown', (event) => {
  if (event.key === 'Escape') {
    closeMenu()
  }
})

Ajoutez la même vérification pour toute autre touche qui devrait fermer le menu. Si la page a une zone de texte, pensez à ce que la touche y fait : ESC dans un champ de texte devrait toujours fermer le menu, mais une lettre ne le doit pas.

Attention : ne vous fiez pas uniquement à un bouton fermer. Si un joueur appuie sur ESC et que la page n'a pas d'écouteur, le menu pause peut s'ouvrir, et le menu reste à l'écran sans aucun moyen de le fermer.

Marcher pendant qu'un menu est ouvert

Certaines interfaces, comme un HUD avec un petit panneau ou un téléphone, doivent laisser le joueur continuer à bouger. Pour cela, utilisez SetNuiFocusKeepInput :

lua
SetNuiFocus(true, true)
SetNuiFocusKeepInput(true)

Avec keep input, le jeu reçoit toujours le clavier pendant que la NUI a le focus. Si vous l'allumez, éteignez-le à la fermeture du menu, car il reste allumé jusqu'à ce que vous le fassiez :

lua
local function closeMenu()
    SetNuiFocusKeepInput(false)
    SetNuiFocus(false, false)
end

Un SetNuiFocusKeepInput(true) oublié donne des symptômes étranges : le curseur est parti mais certaines touches se comportent bizarrement ou ne font rien.

Trouver la ressource qui garde le focus

Souvent, le menu coincé n'est pas le vôtre. Quand vous ne savez pas quelle ressource a pris le focus, trouvez-le par élimination :

  1. Reproduisez le problème : ouvrez le menu, fermez-le, et voyez le curseur coincé.
  2. Dans la console du serveur (ou la console en direct txAdmin), tapez restart resource_name pour une ressource qui a un menu : un téléphone, inventaire, magasin, menu radial ou menu vêtements.
  3. Vérifiez si le curseur et les contrôles reviennent ou si le problème ne se produit plus.
  4. Répétez pour la ressource suivante jusqu'à ce que le comportement change.

Quand un redémarrage redonne le focus, ce script le garde. Cherchez dans son code un chemin de fermeture qui n'appelle pas SetNuiFocus(false, false) : une erreur avant la ligne de fermeture, une mort ou un changement de job qui ferme uniquement le HTML, ou un rappel qui ne répond pas. Une erreur dans la console au moment du bug est un indice fort : voir how to read a FiveM script error.

Si c'est un script que vous avez acheté, envoyez à son auteur les étapes exactes et le nom de la ressource que vous avez trouvé.

Liste de contrôle

Symptôme Correction
Le curseur reste après la fermeture Appeler SetNuiFocus(false, false) dans la fonction de fermeture
Le bouton fermer ne fait rien Ajouter RegisterNUICallback('close', ...) et une fetch à https://<resource>/close
ESC ne ferme pas le menu Écouter Escape en JavaScript et appeler le rappel de fermeture
Coincé après redémarrage d'une ressource Libérer le focus dans onResourceStop
Le personnage ne peut pas se déplacer après la fermeture Désactiver SetNuiFocusKeepInput(false) à la fermeture
Ne sais pas quel script le fait Redémarrer les ressources une par une jusqu'à ce que le curseur revienne
Le rappel se bloque Répondre toujours avec cb('ok')

Réponses rapides

Pourquoi mon curseur reste-t-il à l'écran après fermeture d'un menu ?

Le menu a masqué son HTML, mais rien n'a dit au jeu de rendre le focus. Le script doit appeler SetNuiFocus(false, false) à la fermeture du menu.

Que fait SetNuiFocusKeepInput ?

SetNuiFocusKeepInput(true) permet au jeu de recevoir l'entrée clavier pendant qu'une NUI a le focus, de sorte que le joueur peut toujours marcher. Il doit être désactivé à nouveau à la fermeture du menu.

Comment trouver quelle ressource garde le focus ?

Redémarrer vos ressources une par une et vérifier après chacune si le curseur et les contrôles reviennent. Celui dont le redémarrage le corrige est celui qui gardait le focus.

Des scripts sans ce problème

Mic PhoneUn téléphone pliable qui se déplie en tablette et se prolonge jusqu’au vrai téléphone du joueur.Voir le script →Arcade MachinesSept jeux d’arcade jouables dans de vraies bornes, avec classements et paris.Voir le script →Clothing DesignerCréez des vêtements dans FiveM — pinceau, calques, import d’images et IA — puis portez-les.Voir le script →

À lire aussi