O que é um sistema de destino e por que usá-lo?
Um sistema de alvo, às vezes chamado de alvo ocular ou sistema de interação, substitui a abordagem tradicional de pressionar uma tecla enquanto estiver perto de algo. Em vez de caminhar até um NPC e pressionar E esperando estar perto o suficiente, os jogadores seguram uma tecla (geralmente alt esquerdo) para entrar no modo de mira, que exibe uma pequena cruz no centro da tela. Quando a mira passa sobre uma entidade, objeto ou zona segmentável, um menu de ações disponíveis é exibido. Essa abordagem é dramaticamente mais intuitiva porque dá aos jogadores controle preciso sobre aquilo com que interagem. Em uma cena lotada com vários NPCs, veículos e objetos, um sistema de alvo permite que você escolha exatamente aquele que deseja, sem lutar com prompts de interação sobrepostos. As duas implementações mais populares sãoboi_alvo(da equipe Overextended) ealvo qb(construído para QBCore). Este guia cobre ambos, com ênfase em ox_target, pois é independente de estrutura e desenvolvido de forma mais ativa.
ox_target vs qb-target: qual escolher
Ambos os sistemas atingem o mesmo objetivo, mas diferem no design e no desempenho da API.boi_alvoé independente, funciona com qualquer estrutura, usa raycasting para detecção de entidades e possui uma API mais limpa com melhor suporte a TypeScript.alvo qbestá totalmente integrado ao QBCore, usa uma abordagem raycast semelhante e já existe há mais tempo, portanto, mais scripts existentes o suportam imediatamente. Se você estiver iniciando um novo servidor ou usando ESX/autônomo, use ox_target. Se você estiver no QBCore e a maioria dos scripts adquiridos usar exportações qb-target, manter o qb-target evita dores de cabeça de compatibilidade. A boa notícia é que ox_target inclui uma camada de compatibilidade qb-target, então scripts que chamam exports['qb-target'] funcionarão com ox_target instalado. O inverso não é verdadeiro, portanto, scripts escritos para ox_target não funcionarão com qb-target sem modificação.
Adicionando Alvos a Entidades Específicas
Os alvos de entidade anexam opções de interação a uma entidade específica gerada, como um pedestre, veículo ou objeto. Isso é útil quando você cria um NPC para um trabalho e deseja que os jogadores interajam especificamente com esse NPC, em vez de com qualquer modelo de pedestre no mundo. O alvo segue a entidade, então se o NPC andar por aí, o ponto de interação se move com ele. Você fornece o identificador da entidade, a matriz de opções e uma distância opcional que controla a distância que a mira pode detectar a entidade. Sempre limpe os destinos da entidade quando a entidade for excluída ou o recurso for interrompido para evitar que os destinos órfãos permaneçam na memória.
-- client.lua: Entity targeting with ox_target
-- Create a shop NPC and add target
local model = joaat('a_m_m_business_01')
lib.requestModel(model)
local ped = CreatePed(0, model, 25.7, -1347.3, 29.5, 270.0, false, false)
FreezeEntityPosition(ped, true)
SetEntityInvincible(ped, true)
SetBlockingOfNonTemporaryEvents(ped, true)
-- Add target to this specific ped
exports.ox_target:addLocalEntity(ped, {
{
name = 'open_shop',
icon = 'fas fa-store',
label = 'Open General Store',
distance = 2.5,
onSelect = function(data)
-- data.entity contains the entity handle
TriggerEvent('shop:open', 'general')
end,
},
{
name = 'talk_to_clerk',
icon = 'fas fa-comment',
label = 'Talk to Clerk',
distance = 2.0,
canInteract = function(entity, distance, coords, name)
-- Only show if player has completed a quest
return PlayerData.questComplete == true
end,
onSelect = function()
TriggerEvent('dialogue:start', 'clerk_01')
end,
},
})
-- Clean up on resource stop
AddEventHandler('onResourceStop', function(resource)
if resource == GetCurrentResourceName() then
exports.ox_target:removeLocalEntity(ped)
DeleteEntity(ped)
end
end)Destinos baseados em modelo: visando todas as instâncias
Os alvos do modelo aplicam opções de interação a cada instância de um modelo específico no mundo do jogo. Isso é extremamente poderoso para coisas como caixas eletrônicos, máquinas de venda automática, lixeiras, latas de lixo ou qualquer acessório que exista em vários locais do mapa. Em vez de registrar alvos manualmente em centenas de coordenadas, você registra uma vez pelo nome do modelo e cada objeto correspondente se torna interativo. O impacto no desempenho é mínimo porque o sistema de destino verifica apenas os modelos dentro do alcance do raycast, e não todos os objetos no mundo inteiro. Porém, tenha cuidado com os modelos comuns. Mirar em cada prop_bin_01a no mapa dá aos jogadores centenas de pontos de interação, então certifique-se de que a ação faça sentido para todas as instâncias desse modelo.
-- client.lua: Model-based targeting
-- ox_target: Target all ATM models
exports.ox_target:addModel({'prop_atm_01', 'prop_atm_02', 'prop_atm_03', 'prop_fleeca_atm'}, {
{
name = 'use_atm',
icon = 'fas fa-credit-card',
label = 'Use ATM',
distance = 1.5,
onSelect = function(data)
TriggerEvent('banking:openATM')
end,
},
})
-- Target all vending machines
exports.ox_target:addModel({
'prop_vend_coffe_01',
'prop_vend_soda_01',
'prop_vend_soda_02',
'prop_vend_water_01',
}, {
{
name = 'buy_drink',
icon = 'fas fa-mug-hot',
label = 'Buy Drink ($5)',
distance = 1.5,
onSelect = function(data)
local model = GetEntityModel(data.entity)
TriggerServerEvent('vending:buy', model)
end,
},
})
-- qb-target equivalent for comparison
exports['qb-target']:AddTargetModel({'prop_atm_01', 'prop_atm_02'}, {
options = {
{
type = 'client',
event = 'banking:openATM',
icon = 'fas fa-credit-card',
label = 'Use ATM',
},
},
distance = 1.5,
})Alvos baseados em zonas: áreas de interação invisíveis
Os alvos de zona criam áreas de interação invisíveis em coordenadas específicas. Eles são ideais para locais onde não existe nenhum objeto físico, mas você ainda deseja que os jogadores interajam, como um balcão no interior de um edifício, um local específico no chão para um esconderijo ou uma área em frente a uma porta para uma opção de arrombamento. Você define zonas como caixas ou esferas com coordenadas, tamanho e rotação. A zona só fica ativa quando a mira aponta para a área da zona, tornando-a precisa e evitando interações acidentais. Os destinos de zona também são a solução ideal para adicionar interações a partes dos MLOs do mapa onde os adereços são incorporados ao mapa e não podem ser direcionados pelo modelo.
-- client.lua: Zone-based targeting
-- ox_target: Box zone for a reception desk
exports.ox_target:addBoxZone({
coords = vec3(441.8, -981.0, 30.7),
size = vec3(2.0, 1.0, 1.5),
rotation = 0,
debug = true, -- Shows the box in-game, set false for production
options = {
{
name = 'check_in',
icon = 'fas fa-clipboard-check',
label = 'Check In at Reception',
onSelect = function()
TriggerEvent('police:checkIn')
end,
canInteract = function()
return PlayerData.job == 'police'
end,
},
},
})
-- Sphere zone for a ground stash
local stashZone = exports.ox_target:addSphereZone({
coords = vec3(128.4, -1280.5, 29.0),
radius = 0.5,
debug = false,
options = {
{
name = 'open_stash',
icon = 'fas fa-box-open',
label = 'Open Stash',
distance = 1.5,
onSelect = function()
TriggerServerEvent('stash:open', 'ground_stash_01')
end,
},
},
})
-- Remove zone later if needed
exports.ox_target:removeZone(stashZone)Visibilidade Condicional com canInteract
A função canInteract é o que torna os sistemas alvo verdadeiramente poderosos. Ele é executado sempre que a mira passa sobre um alvo e determina se cada opção deve ser mostrada ou ocultada. Isso permite criar interações sensíveis ao contexto onde um policial vê opções diferentes de um civil, onde uma opção de arrombamento só aparece se o jogador tiver um item de arrombamento ou onde uma opção de reparo do veículo só aparece quando o veículo está danificado. A função recebe o identificador da entidade, a distância, as coordenadas e o nome da opção como parâmetros. Mantenha a lógica em canInteract leve porque ela é executada em cada quadro enquanto o jogador está pairando sobre o alvo. Evite consultas ao banco de dados ou cálculos pesados dentro dele. Em vez disso, armazene em cache os dados necessários em uma variável local que é atualizada por meio de eventos.
-- client.lua: Advanced canInteract examples
-- Vehicle interaction with multiple conditional options
exports.ox_target:addGlobalVehicle({
{
name = 'repair_vehicle',
icon = 'fas fa-wrench',
label = 'Repair Vehicle',
distance = 3.0,
bones = {'engine'}, -- Only when targeting the engine area
canInteract = function(entity)
local health = GetVehicleEngineHealth(entity)
return health < 900.0 -- Only show if damaged
end,
onSelect = function(data)
TriggerEvent('mechanic:repair', data.entity)
end,
},
{
name = 'lockpick_vehicle',
icon = 'fas fa-key',
label = 'Lockpick Door',
distance = 2.0,
bones = {'door_dside_f', 'door_pside_f'},
canInteract = function(entity)
local locked = GetVehicleDoorLockStatus(entity)
local hasItem = exports.ox_inventory:Search('count', 'lockpick') > 0
return locked == 2 and hasItem
end,
onSelect = function(data)
TriggerEvent('lockpick:start', data.entity)
end,
},
{
name = 'open_trunk',
icon = 'fas fa-box',
label = 'Open Trunk',
distance = 2.5,
bones = {'boot'},
canInteract = function(entity)
return not IsVehicleDoorFullyOpen(entity, 5)
end,
onSelect = function(data)
SetVehicleDoorOpen(data.entity, 5, false, false)
TriggerEvent('inventory:openTrunk', data.entity)
end,
},
})Alvos Globais: Interações entre Jogadores e Veículos
As metas globais se aplicam a todos os jogadores ou veículos do mundo, sem a necessidade de registrar cada um individualmente. addGlobalPlayer adiciona opções que aparecem ao mirar em qualquer outro jogador, o que é perfeito para interações como dar itens, verificar identidades, algemar suspeitos ou curar outros jogadores. addGlobalVehicle adiciona opções para cada veículo, úteis para mecânicos, buscas policiais ou sistemas de abastecimento. Use canInteract liberalmente com metas globais para evitar sobrecarga de opções. Um civil não deve ver a opção de manguito, e um mecânico só deve ver as opções de reparo quando estiver em serviço. As metas globais são a forma mais conveniente de adicionar interações universais, mas também a mais fácil de usar indevidamente, por isso pense cuidadosamente sobre quando cada opção deve realmente estar visível.
-- client.lua: Global player and vehicle targets
-- Target any player for EMS interactions
exports.ox_target:addGlobalPlayer({
{
name = 'revive_player',
icon = 'fas fa-heartbeat',
label = 'Revive Player',
distance = 2.0,
canInteract = function(entity)
-- Only show if player is EMS and target is downed
local isEms = PlayerData.job == 'ambulance'
local targetState = Entity(entity).state.isDead
return isEms and targetState == true
end,
onSelect = function(data)
local targetServerId = GetPlayerServerId(NetworkGetPlayerIndexFromPed(data.entity))
TriggerServerEvent('ems:revive', targetServerId)
end,
},
{
name = 'give_item',
icon = 'fas fa-hand-holding',
label = 'Give Item',
distance = 2.0,
onSelect = function(data)
local targetServerId = GetPlayerServerId(NetworkGetPlayerIndexFromPed(data.entity))
TriggerEvent('inventory:giveItem', targetServerId)
end,
},
})Dicas de desempenho e erros comuns
Os sistemas-alvo são bem otimizados, mas ainda podem afetar o desempenho se forem mal utilizados. O erro mais comum é registrar muitos destinos de zona com o modo de depuração habilitado na produção. A renderização de depuração desenha formas de wireframe para cada zona, o que é caro quando você tem dezenas delas. Sempre defina debug = false antes de implantar. Outro erro é colocar lógica cara dentro de funções canInteract. Como isso é executado em todos os quadros enquanto você passa o mouse, até mesmo uma única chamada exports.ox_inventory:Search pode ser adicionada se você tiver dez opções, todas executando verificações de inventário simultaneamente. Armazene esses valores em cache e atualize-os quando o inventário for alterado. Finalmente, sempre limpe os alvos quando o seu recurso parar. Use o manipulador de eventos onResourceStop para remover todos os destinos de zona, modelo e entidade. Os destinos vazados persistem na memória e podem causar erros ou opções duplicadas se o recurso for reiniciado. ox_target lida com algumas limpezas automaticamente para entidades locais, mas a limpeza explícita é sempre a abordagem mais segura.

