Cursore FiveM bloccato o impossibile muoversi dopo la chiusura di un menu: correzione del focus NUI

Cursore bloccato sullo schermo o impossibile muoversi dopo la chiusura di un menu? Come funziona SetNuiFocus, chiusura con un callback NUI, gestione di ESC in JavaScript e ricerca del colpevole.

Chiudi un menu, e succede una di queste cose:

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

Nulla appare nella console, perché nulla è sbagliato nel codice che si esegue: semplicemente non ha mai restituito il focus. Ecco come funziona il focus, come chiudere correttamente un menu e come trovare il colpevole.

Come funziona il focus NUI

Un NUI è una pagina web (HTML, CSS e JavaScript) che FiveM disegna sopra il gioco. Mentre ha il focus, il mouse e la tastiera vanno alla pagina invece che al gioco. Uno script dà e prende il focus con un nativo:

lua
SetNuiFocus(hasFocus, hasCursor)
  • SetNuiFocus(true, true): il NUI ottiene la tastiera e il mouse, e il cursore mostra.
  • SetNuiFocus(true, false): il NUI ottiene la tastiera, nessun cursore.
  • SetNuiFocus(false, false): il focus torna al gioco e il cursore si nasconde.

Se un menu si apre con true, true e nessun codice chiama mai SetNuiFocus(false, false), il gioco rimane bloccato, anche se la pagina è nascosta.

La soluzione: chiudi sempre con SetNuiFocus(false, false)

Metti la chiusura in una funzione e usala per ogni modo di chiudere il 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

Fai lo stesso se il menu può essere chiuso da altro codice: quando il giocatore muore, entra in un veicolo, o quando la risorsa si ferma.

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

Questo ultimo è importante. Se una risorsa si ferma mentre il suo menu è aperto, il focus non viene restituito per te, e il giocatore rimane bloccato finché non si riconnetta.

Chiudi dalla pagina: RegisterNUICallback e fetch

Un pulsante di chiusura, o una pressione di tasto all'interno della pagina, si esegue in JavaScript. JavaScript non può chiamare un nativo, quindi invia una richiesta al lato Lua, che la gestisce:

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

Nella tua pagina, invia la richiesta con fetch a https://<resource name>/close. GetParentResourceName() ti dà il nome della risorsa all'interno di una pagina 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)

Il callback deve chiamare cb. Se non risponde mai, la richiesta nella pagina rimane aperta, e alcune librerie smettono di aspettarla. Termina sempre un callback con cb('ok') o un'altra risposta.

ESC e altri tasti in JavaScript

Mentre il NUI ha il focus, il gioco non vede la tastiera, quindi il tasto ESC non raggiunge mai il tuo codice Lua. La pagina deve ascoltarlo:

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

Aggiungi lo stesso controllo per qualsiasi altro tasto che dovrebbe chiudere il menu. Se la pagina ha una casella di input, pensa a cosa fa il tasto lì: ESC in un campo di testo dovrebbe comunque chiudere il menu, ma una lettera no.

Attenzione: non fare affidamento solo su un pulsante di chiusura. Se un giocatore preme ESC e la pagina non ha un listener, il menu di pausa può aprirsi, e il menu rimane sullo schermo senza nessun modo di chiuderlo.

Camminare mentre un menu è aperto

Alcune interfacce, come un HUD con un piccolo pannello o un telefono, dovrebbero permettere al giocatore di continuare a muoversi. Per questo usa SetNuiFocusKeepInput:

lua
SetNuiFocus(true, true)
SetNuiFocusKeepInput(true)

Con il keep input, il gioco riceve ancora la tastiera mentre il NUI ha il focus. Se lo attivi, disattivalo quando il menu si chiude, perché rimane attivo finché non lo fai:

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

Un residuo SetNuiFocusKeepInput(true) dà sintomi strani: il cursore è sparito ma alcuni tasti si comportano in modo strano o non fanno nulla.

Trova la risorsa che tiene il focus

Spesso il menu bloccato non è il tuo. Quando non sai quale risorsa ha preso il focus, trovalo per eliminazione:

  1. Riproduci il problema: apri il menu, chiudilo, e vedi il cursore bloccato.
  2. Nella console del server (o la Live Console di txAdmin), digita restart resource_name per una risorsa che ha un menu: un telefono, inventario, negozio, menu radiale o menu di abbigliamento.
  3. Controlla se il cursore e i controlli ritornano o se il problema non si verifica più.
  4. Ripeti per la risorsa successiva finché il comportamento non cambia.

Quando un riavvio restituisce il focus, quello script lo mantiene. Cerca nel suo codice per un percorso di chiusura che non chiama SetNuiFocus(false, false): un errore prima della riga di chiusura, una morte o un cambio di lavoro che chiude solo l'HTML, o un callback che non risponde. Un errore nella console al momento del bug è un forte indizio: vedi come leggere un errore di script FiveM.

Se è uno script che hai acquistato, invia al suo autore i passi esatti e il nome della risorsa che hai trovato.

Checklist

Sintomo Soluzione
Il cursore rimane dopo la chiusura Chiama SetNuiFocus(false, false) nella funzione di chiusura
Il pulsante di chiusura non fa nulla Aggiungi RegisterNUICallback('close', ...) e un fetch a https://<resource>/close
ESC non chiude il menu Ascolta Escape in JavaScript e chiama il callback di chiusura
Bloccato dopo il riavvio di una risorsa Rilascia il focus in onResourceStop
Il personaggio non può muoversi dopo la chiusura Disattiva SetNuiFocusKeepInput(false) alla chiusura
Non so quale script lo fa Riavvia le risorse una per una finché il cursore non ritorna
Il callback si blocca Rispondi sempre con cb('ok')

Risposte rapide

Perché il mio cursore rimane sullo schermo dopo aver chiuso un menu?

Il menu ha nascosto il suo HTML, ma nulla ha detto al gioco di restituire il focus. Lo script deve chiamare SetNuiFocus(false, false) quando il menu si chiude.

Cosa fa SetNuiFocusKeepInput?

SetNuiFocusKeepInput(true) permette al gioco di ricevere input da tastiera mentre un NUI ha focus, in modo che il giocatore possa ancora camminare. Deve essere disattivato di nuovo quando il menu si chiude.

Come trovo quale risorsa mantiene il focus?

Riavvia le tue risorse una per una e controlla dopo ognuna se il cursore e i controlli tornano. Quella il cui riavvio lo corregge è quella che ha mantenuto il focus.

Script senza questo problema

Mic PhoneUn telefono pieghevole che si apre in un tablet e arriva sul telefono vero del giocatore.Vedi script →Arcade MachinesSette giochi arcade giocabili in cabinati veri, con classifiche e scommesse.Vedi script →Clothing DesignerDisegna vestiti dentro FiveM — pennello, livelli, import di immagini e IA — e poi indossali.Vedi script →

Continua a leggere