ox_target addBoxZone Örneği: FiveM Raycast Targeting Lua
FiveM raycast targeting için kopyala, yapıştır Lua kodu içeren ox_target addBoxZone örneği. Ayrıca addSpherezone ve qb-target konularını da kapsar. QBCore, ESX ve Standalone.
Agency Scripts
Agency Scripts Kurucusu ve Baş Geliştiricisi
FiveM'de Raycasting nedir?
Raycast, 3D uzayda bir noktadan diğerine görünmez bir çizgi (ışın) gönderme ve yol boyunca neye çarptığını kontrol etme tekniğidir. FiveM içinde raycast: hedefleme sistemleri, nesne etkileşimi, görüş hattı kontrolleri, yüzey tespiti ve özel nişan mekanikleri için kullanılır. Oyun motoru: entity'leri, yüzeyleri ve dünya geometrisini hassasiyetle tespit etmenizi sağlayan çeşitli raycast native'leri sunar. Raycast mantığını kavramak: özel hedefleme, akıllı yerleştirme sistemleri, lazer işaretçileri ve oyuncunun baktığı şeye tepki veren duruma duyarlı etkileşimler gibi gelişmiş oynanış mekaniklerinin kapısını açar.
StartShapeTestRay ile Temel Raycasting
Raycasting'in en basit biçimi şunu kullanır: StartShapeTestRay iki koordinat arasında bir ışın gönderen ve çarptığı ilk nesne hakkında bilgi döndüren işlev. Sonuç, çarpma durumunu, çarpma noktasının tam koordinatlarını, yüzey normal vektörünü ve bir entity vurulduysa entity handle değerini içerir. Şunu çağırmalısınız: GetShapeTestResult sonuçları almak için bir sonraki frame'de.
-- client/raycast_basic.lua
local function Raycast(origin, direction, maxDistance, flags, ignoreEntity)
local destination = origin + direction * maxDistance
local shapeTest = StartShapeTestRay(
origin.x, origin.y, origin.z,
destination.x, destination.y, destination.z,
flags or -1, -- flags: -1 = everything
ignoreEntity or 0, -- entity to ignore
0
)
local _, hit, endCoords, surfaceNormal, entityHit = GetShapeTestResult(shapeTest)
return {
hit = hit == 1,
coords = endCoords,
normal = surfaceNormal,
entity = entityHit,
}
end
-- Raycast from camera to world (what the player is looking at)
local function GetPlayerAimTarget(maxDist)
local camCoords = GetGameplayCamCoord()
local camRot = GetGameplayCamRot(2)
-- Convert rotation to direction vector
local radX = math.rad(camRot.x)
local radZ = math.rad(camRot.z)
local direction = vector3(
-math.sin(radZ) * math.abs(math.cos(radX)),
math.cos(radZ) * math.abs(math.cos(radX)),
math.sin(radX)
)
local playerPed = PlayerPedId()
return Raycast(camCoords, direction, maxDist or 50.0, -1, playerPed)
end
Raycast Bayrakları ve Filtreleme
Şuradaki flags parametresi: StartShapeTestRay ışının ne tür nesnelere çarpabileceğini denetler. Doğru flag'leri kullanmak performans ve doğruluk açısından kritik öneme sahiptir. Her şeye karşı kontrol etmek (-1) maliyetlidir ve genellikle görünmez çarpışma sınırları gibi istenmeyen sonuçlar döndürür. Yalnızca ihtiyacınız olanları hedeflemek için belirli bayraklar kullanın.
-- client/raycast_flags.lua
-- Common flag values for StartShapeTestRay
local RayFlags = {
WORLD = 1, -- Static world geometry (buildings, terrain)
VEHICLES = 2, -- Vehicles
PEDS = 4, -- Pedestrians and players (on foot)
OBJECTS = 16, -- Props and objects
WATER = 32, -- Water surfaces
VEGETATION = 256, -- Trees and bushes
-- Common combinations
WORLD_AND_VEHICLES = 3,
WORLD_AND_OBJECTS = 17,
ENTITIES_ONLY = 22, -- Vehicles + Peds + Objects
ALL = -1, -- Everything
}
-- Example: Only detect vehicles (for a speed camera script)
local function DetectVehicleAhead(origin, direction)
return Raycast(origin, direction, 100.0, RayFlags.VEHICLES, PlayerPedId())
end
-- Example: Detect ground position (for object placement)
local function GetGroundPosition(x, y, z)
local result = Raycast(
vector3(x, y, z + 50.0),
vector3(0.0, 0.0, -1.0),
100.0,
RayFlags.WORLD
)
if result.hit then
return result.coords
end
return nil
end
Hedefleme Sistemi Geliştirme
Bir hedefleme sistemi, oyuncuların oyun dünyasındaki belirli entity'leri seçmesine ve bunlarla etkileşime girmesine olanak tanımak için raycasting ile entity filtrelemeyi birleştirir. Bu sistem ox_target ve qb-target gibi etkileşim sistemlerinin temelidir. Sistem her karede kameradan bir ray gönderir, isabet eden entity'nin kayıtlı hedeflerden herhangi biriyle eşleşip eşleşmediğini kontrol eder ve etkileşim yönlendirmelerini görüntüler.
-- client/targeting.lua
local TargetSystem = {
targets = {},
currentTarget = nil,
enabled = true,
}
function TargetSystem.AddTarget(entity, options)
TargetSystem.targets[entity] = {
label = options.label or 'Interact',
icon = options.icon or 'fas fa-hand',
distance = options.distance or 3.0,
canInteract = options.canInteract,
onSelect = options.onSelect,
}
end
function TargetSystem.RemoveTarget(entity)
TargetSystem.targets[entity] = nil
end
-- Main targeting loop
CreateThread(function()
while true do
if TargetSystem.enabled then
local aimResult = GetPlayerAimTarget(10.0)
if aimResult.hit and aimResult.entity ~= 0 then
local entity = aimResult.entity
local target = TargetSystem.targets[entity]
if target then
local playerCoords = GetEntityCoords(PlayerPedId())
local entityCoords = GetEntityCoords(entity)
local dist = #(playerCoords - entityCoords)
if dist <= target.distance then
local canInteract = true
if target.canInteract then
canInteract = target.canInteract(entity)
end
if canInteract then
TargetSystem.currentTarget = entity
-- Draw interaction prompt
DrawText3D(entityCoords.x, entityCoords.y, entityCoords.z + 1.0,
target.label)
-- Handle interaction key press
if IsControlJustPressed(0, 38) then -- E key
target.onSelect(entity)
end
end
end
end
else
TargetSystem.currentTarget = nil
end
end
Wait(0)
end
end)
-- Helper: Draw 3D text at world coordinates
function DrawText3D(x, y, z, text)
SetTextScale(0.35, 0.35)
SetTextFont(4)
SetTextProportional(true)
SetTextColour(255, 255, 255, 215)
SetTextEntry('STRING')
SetTextCentre(true)
AddTextComponentString(text)
SetDrawOrigin(x, y, z, 0)
DrawText(0.0, 0.0)
ClearDrawOrigin()
end
Yüzey Algılama ve Nesne Yerleştirme
Raycast, oyuncunun bir yüzeye nişan aldığı ve bir nesne önizlemesinin nişanı takip ettiği nesne yerleştirme sistemleri için gereklidir. Raycast tarafından döndürülen yüzey normali, yüzeyin yönünü size bildirerek yerleştirilen nesneleri yamaçlarda, duvarlarda ve tavanlarda doğru şekilde hizalamanıza olanak tanır.
-- client/placement.lua
local Placement = {
active = false,
previewEntity = nil,
modelName = nil,
}
function Placement.Start(modelName)
Placement.modelName = modelName
Placement.active = true
-- Create preview prop
local model = joaat(modelName)
RequestModel(model)
while not HasModelLoaded(model) do Wait(10) end
Placement.previewEntity = CreateObject(model, 0.0, 0.0, 0.0, false, true, false)
SetEntityAlpha(Placement.previewEntity, 150, false)
SetEntityCollision(Placement.previewEntity, false, false)
FreezeEntityPosition(Placement.previewEntity, true)
SetModelAsNoLongerNeeded(model)
CreateThread(function()
while Placement.active do
local result = GetPlayerAimTarget(15.0)
if result.hit then
local coords = result.coords
SetEntityCoords(Placement.previewEntity,
coords.x, coords.y, coords.z, false, false, false, false)
-- Align to surface normal
local normal = result.normal
local pitch = math.deg(math.asin(normal.z)) - 90.0
SetEntityRotation(Placement.previewEntity, pitch, 0.0,
GetEntityHeading(PlayerPedId()), 2, true)
-- Color: green if valid, red if not
local valid = IsPlacementValid(coords)
if valid then
SetEntityDrawOutline(Placement.previewEntity, true)
end
-- Confirm placement
if IsControlJustPressed(0, 38) and valid then
Placement.Confirm(coords)
end
end
-- Cancel placement
if IsControlJustPressed(0, 73) then -- X key
Placement.Cancel()
end
Wait(0)
end
end)
end
function Placement.Confirm(coords)
Placement.active = false
if Placement.previewEntity then
DeleteEntity(Placement.previewEntity)
Placement.previewEntity = nil
end
TriggerServerEvent('myresource:placeObject', Placement.modelName, coords)
end
function Placement.Cancel()
Placement.active = false
if Placement.previewEntity then
DeleteEntity(Placement.previewEntity)
Placement.previewEntity = nil
end
end
function IsPlacementValid(coords)
local playerCoords = GetEntityCoords(PlayerPedId())
local dist = #(playerCoords - coords)
return dist >= 1.0 and dist <= 10.0
end
Görüş Hattı Kontrolleri
Raycast, iki noktanın engelsiz bir şekilde birbirini görüp göremediğini kontrol etmenin standart yoludur. Bu: gizlilik sistemlerinde, yapay zeka farkındalığında, keskin nişancı mekaniklerinde ve görüş açısına dayalı her türlü oynanışta kullanılır. Bir noktadan diğerine bir ışın gönderin ve hedefe ulaşmadan önce bir şeye çarpıp çarpmadığını kontrol edin.
-- shared/los.lua (can run on both client and server)
local function HasLineOfSight(from, to, ignoreEntity)
local direction = to - from
local distance = #direction
direction = direction / distance -- normalize
local result = Raycast(from, direction, distance, RayFlags.WORLD, ignoreEntity)
if not result.hit then
return true -- nothing blocking the path
end
-- Check if the hit point is past the target
local hitDist = #(from - result.coords)
return hitDist >= distance * 0.95
end
-- Usage: Check if NPC can see the player
local function CanNPCSeePlayer(npcPed, playerPed)
local npcCoords = GetEntityCoords(npcPed) + vector3(0, 0, 0.7)
local playerCoords = GetEntityCoords(playerPed) + vector3(0, 0, 0.7)
return HasLineOfSight(npcCoords, playerCoords, npcPed)
end
Raycast'ler İçin Performans Optimizasyonu
- Raycast sıklığını sınırlandırın. Kesinlikle gerekli olmadıkça her karede raycast yapmayın. Etkileşim kontrolleri için genellikle her 100 ms'de bir kontrol yeterlidir. Kare başına raycast işlemini yalnızca aktif nişan alma ve yerleştirme sistemleri için kullanın.
- Belirli bayraklar kullanın. Şuna dönüştürme
-1(her şey), belirli varlık türlerini hedeflemekten önemli ölçüde daha yavaştır. Yalnızca ihtiyacınız olanları filtreleyin. - Sonuçları önbelleğe alın. Oyuncu hareket etmediyse veya farklı bir yöne bakmadıysa raycast sonucu aynı olacaktır. Kamera konumunu ve rotasyonunu karşılaştırarak gereksiz cast işlemlerini atlayın.
- Önce mesafe kontrollerini kullanın. Entity etkileşimini kontrol etmek için raycasting yapmadan önce, oyuncunun makul bir yarıçap içinde olduğunu doğrulayın. Mesafe kontrolleri raycast işlemlerine göre katbekat daha tasarrufludur.
- LOS için StartShapeTestLosProbe fonksiyonunu tercih edin. Bilinen iki nokta arasındaki basit görüş hattı kontrolleri için,
StartShapeTestLosProbeşundan daha hafiftir:StartShapeTestRayçünkü yalnızca isabet/isabetsizlik belirten bir boolean döndürür. - Tüm harita boyunca raycast yapmaktan kaçının. Maksimum mesafeleri makul tutun. 50 birimlik bir raycast, 1000 birimlik olandan çok daha az maliyetlidir.