Курсор FiveM зависает или не может двигаться после закрытия меню: исправление фокуса NUI
Курсор зависает на экране или не может двигаться после закрытия меню? Как работает SetNuiFocus, закрытие обратным вызовом NUI, обработка ESC в JavaScript и поиск причины.
Вы закрываете меню, и происходит одно из следующего:
the mouse cursor stays on screen
the character will not move or look around
the keyboard does nothing until you reconnectНичего не показано в консоли, потому что в коде, который работает, ничего не сломано: он просто никогда не вернул фокус. Вот как работает фокус, как правильно закрыть меню и как найти причину.
Как работает фокус NUI
NUI — это веб-страница (HTML, CSS и JavaScript), которую FiveM рисует поверх игры. Пока она имеет фокус, мышь и клавиатура идут на страницу вместо игры. Скрипт даёт и берёт фокус с одним native:
SetNuiFocus(hasFocus, hasCursor)SetNuiFocus(true, true): NUI получает клавиатуру и мышь, и курсор показывается.SetNuiFocus(true, false): NUI получает клавиатуру, нет курсора.SetNuiFocus(false, false): фокус возвращается к игре и курсор скрывается.
Если меню открывается с true, true и никогда не вызывает SetNuiFocus(false, false), игра остаётся заблокированной, даже если страница скрыта.
Решение: всегда закрывайте с SetNuiFocus(false, false)
Поместите закрытие в одну функцию и используйте её для каждого способа закрытия меню:
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Сделайте то же самое, если меню можно закрыть другим кодом: когда игрок умирает, входит в транспортное средство, или когда ресурс останавливается.
AddEventHandler('onResourceStop', function(resource)
if resource == GetCurrentResourceName() and open then
SetNuiFocus(false, false)
end
end)Это последнее очень важно. Если ресурс останавливается, пока его меню открыто, фокус не возвращается для вас, и игрок зависает, пока они не переподключатся.
Закрытие со страницы: RegisterNUICallback и fetch
Кнопка закрытия, или нажатие клавиши внутри страницы, запускается в JavaScript. JavaScript не может вызвать native, поэтому он отправляет запрос на сторону Lua, которая это обрабатывает:
RegisterNUICallback('close', function(data, cb)
closeMenu()
cb('ok')
end)На вашей странице отправьте запрос с fetch в https://<resource name>/close. GetParentResourceName() даёт вам имя ресурса внутри страницы NUI:
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)Обратный вызов должен вызвать cb. Если он никогда не ответит, запрос на странице остаётся открытым, и некоторые библиотеки перестают его ждать. Всегда заканчивайте обратный вызов с cb('ok') или другим ответом.
ESC и другие клавиши в JavaScript
Пока NUI имеет фокус, игра не видит клавиатуру, поэтому клавиша ESC никогда не достигает вашего кода Lua. Страница должна её слушать:
window.addEventListener('keydown', (event) => {
if (event.key === 'Escape') {
closeMenu()
}
})Добавьте ту же проверку для любой другой клавиши, которая должна закрыть меню. Если страница имеет поле ввода, подумайте о том, что клавиша там делает: ESC в текстовом поле должно всё ещё закрыть меню, но буква не должна.
Внимание: не полагайтесь только на кнопку закрытия. Если игрок нажимает ESC и на странице нет слушателя, меню паузы может открыться, и меню остаётся на экране без способа закрыть его.
Ходьба во время открытого меню
Некоторые интерфейсы, такие как HUD с небольшой панелью или телефон, должны позволить игроку продолжать двигаться. Для этого используйте SetNuiFocusKeepInput:
SetNuiFocus(true, true)
SetNuiFocusKeepInput(true)С сохранением ввода игра всё ещё получает клавиатуру, пока NUI имеет фокус. Если вы его включаете, выключите его, когда меню закроется, потому что оно остаётся включённым, пока вы это не сделаете:
local function closeMenu()
SetNuiFocusKeepInput(false)
SetNuiFocus(false, false)
endОставшееся SetNuiFocusKeepInput(true) даёт странные симптомы: курсор исчезает, но некоторые клавиши ведут себя странно или ничего не делают.
Найдите ресурс, который держит фокус
Часто застрявшее меню — это не ваше. Когда вы не знаете, какой ресурс взял фокус, найдите его путём исключения:
- Воспроизведите проблему: откройте меню, закройте его и посмотрите, как курсор зависает.
- В консоли сервера (или в Live Console txAdmin) введите
restart resource_nameдля одного ресурса, у которого есть меню: телефон, инвентарь, магазин, радиальное меню или меню одежды. - Проверьте, вернулись ли курсор и элементы управления, или проблема больше не происходит.
- Повторите для следующего ресурса, пока поведение не изменится.
Когда перезагрузка возвращает фокус, этот скрипт его держит. Посмотрите в его коде пути закрытия, которые не вызывают SetNuiFocus(false, false): ошибка перед строкой закрытия, смерть или изменение работы, которое закрывает только HTML, или обратный вызов, который не ответит. Ошибка в консоли в момент ошибки — сильный намёк: смотрите как читать ошибку скрипта FiveM.
Если это скрипт, который вы купили, отправьте его автору точные шаги и имя ресурса, который вы нашли.
Контрольный список
| Симптом | Решение |
|---|---|
| Курсор остаётся после закрытия | Вызовите SetNuiFocus(false, false) в функции закрытия |
| Кнопка закрытия ничего не делает | Добавьте RegisterNUICallback('close', ...) и fetch в https://<resource>/close |
| ESC не закрывает меню | Слушайте Escape в JavaScript и вызывайте обратный вызов закрытия |
| Зависает после перезагрузки ресурса | Освободите фокус в onResourceStop |
| Персонаж не может двигаться после закрытия | Выключите SetNuiFocusKeepInput(false) при закрытии |
| Не знаете, какой скрипт это делает | Перезагружайте ресурсы один за другим, пока курсор не вернётся |
| Обратный вызов зависает | Всегда ответьте с cb('ok') |
Короткие ответы
Почему мой курсор остаётся на экране после закрытия меню?
Меню скрыло свой HTML, но ничто не сказало игре вернуть фокус. Скрипт должен вызвать SetNuiFocus(false, false) при закрытии меню.
Что делает SetNuiFocusKeepInput?
SetNuiFocusKeepInput(true) позволяет игре получать вввод клавиатуры, пока NUI имеет фокус, поэтому игрок всё ещё может ходить. Это должно быть отключено, когда меню закроется.
Как найти, какой ресурс держит фокус?
Перезагрузите ваши ресурсы один за другим и проверьте после каждого, вернулись ли курсор и управление. Тот, чья перезагрузка исправляет это, — это тот, который держал фокус.
Скрипты без этой проблемы
Mic PhoneСкладной телефон, который раскладывается в планшет и работает и на настоящем телефоне игрока.Смотреть скрипт →
Arcade MachinesСемь аркадных игр в настоящих автоматах, с таблицами рекордов и ставками.Смотреть скрипт →
Clothing DesignerСоздавайте одежду прямо в FiveM — кисть, слои, импорт изображений и ИИ — и носите её.Смотреть скрипт →