Bloga Geri Dön
Tutorial9 dk. okuma

FiveM Taksi Sistemi: NPC ve Oyuncu Taksicileri Kodlama

FiveM'e çalışan bir taksi sistemi ekleyin. Canlı rol yapma şehirleri için NPC taksileri, oyuncu sürücüler, ücret mantığı, taksimetreler ve en iyi taksi scriptleri.

Agency Scripts

Agency Scripts Kurucusu ve Baş Geliştiricisi

Taksi Mesleği Sistem Mimarisi

Bir taksicilik mesleği sistemi, oyunculara gerçek oyuncu etkileşimi içeren erişilebilir bir başlangıç seviyesi meslek sunmak isteyen her roleplay sunucusu için temel bir özelliktir. Tekrarlayıcı ve yorucu gelebilecek diğer birçok mesleğin aksine, iyi tasarlanmış bir taksi sistemi; sürücüler yolcu aldıkça, ücret pazarlığı yaptıkça ve zaman baskısı altında şehirde yol aldıkça organik roleplay karşılaşmaları yaratır. Mimari iki farklı moda ayrılır: gerçek yolcuların yolculuk talep ettiği oyuncudan oyuncuya ulaşım ve yoğun olmayan saatlerde tek başına oynanış için sistemin alma ve bırakma konumları ürettiği NPC görev modu. Her iki mod da aynı ücret hesaplama motorunu, taksimetre UI arayüzünü ve araç yönetim sistemini paylaşır. Temel tasarım kararı, yolculuk taleplerinin görevdeki tüm sürücüler tarafından görülebilen paylaşımlı bir kuyruğa girdiği merkezi bir dispatch sisteminin mi yoksa en yakın sürücünün öncelik kazandığı yakınlık tabanlı bir sistemin mi kullanılacağıdır. En başarılı uygulamalar, bekleyen tüm talepleri gösteren ancak yakındakileri vurgulayan bir dispatch panosu ile hibrit bir yaklaşım kullanır; böylece sürücülere işlerini seçme özgürlüğü tanırken yolcuların da hızlıca alınmasını sağlar.

Taksimetre Entegrasyonu

Taksimetre, ücret sisteminin kalbidir; hem sürücü hem de yolcu için şeffaf kalırken gerçekçi hissettirmelidir. Düzgün bir taksimetre iki bileşeni izler: her yolculuğun başında alınan bir taban ücret ve araç hareket ettikçe artan mesafe başına ücret. Bazı sunucular, trafikte geçen süre için ücret alma yönündeki gerçek dünya uygulamasını simüle ederek, taksi dururken saniye başına daha düşük bir ücret alan bekleme süresi ücreti de ekler. Taksimetre, ekranın köşesine yerleştirilmiş kalıcı bir NUI öğesi aracılığıyla hem sürücü hem de araçtaki tüm yolcular tarafından görülebilmelidir. Kat edilen mesafeye göre ücreti hesaplayan istemci tarafı bir taksimetre uygulaması şöyledir:

local meterActive = false
local currentFare = 0.0
local lastPosition = nil
local baseFare = 50
local ratePerMile = 15
local waitRatePerSecond = 0.5
local totalDistance = 0.0

function StartMeter()
    meterActive = true
    currentFare = baseFare
    totalDistance = 0.0
    lastPosition = GetEntityCoords(PlayerPedId())

    SendNUIMessage({action = 'showMeter', fare = currentFare, distance = 0.0})

    CreateThread(function()
        while meterActive do
            Wait(500)
            local currentPos = GetEntityCoords(PlayerPedId())
            local speed = GetEntitySpeed(PlayerPedId())

            if speed > 1.0 then
                local dist = #(currentPos - lastPosition)
                totalDistance = totalDistance + dist
                local miles = totalDistance / 1609.34
                currentFare = baseFare + (miles * ratePerMile)
            else
                currentFare = currentFare + (waitRatePerSecond * 0.5)
            end

            lastPosition = currentPos

            SendNUIMessage({
                action = 'updateMeter',
                fare = math.floor(currentFare),
                distance = math.floor(totalDistance),
                speed = math.floor(speed * 2.236936)
            })
        end
    end)
end

function StopMeter()
    meterActive = false
    local finalFare = math.floor(currentFare)
    SendNUIMessage({action = 'stopMeter', fare = finalFare})
    return finalFare
end

Taksimetreye ait NUI bileşeni kompakt ancak okunabilir olmalı, kat edilen mesafe ve uygulanan geçerli tarife ile birlikte mevcut ücreti belirgin şekilde göstermelidir. Gerçek bir taksimetre görünümünü yansıtmak için ücret göstergesinde eş aralıklı bir yazı tipi kullanın ve taksimetrenin çalıştığına dair görsel geri bildirim sağlamak için ücret her arttığında hafif bir tıklama animasyonu ekleyin. Yolcuların devam etmek isteyip istemediklerine karar verebilmeleri adına, yolculuk başlamadan önce varış noktasına olan kuş uçuşu mesafeye göre yaklaşık maliyeti hesaplayan bir ücret tahmini özelliği eklemeyi değerlendirin.

NPC Yolcu Alma Görev Sistemi

NPC görevleri, sunucuda yeterince gerçek yolcu bulunmadığında taksi şoförlerini meşgul tutar. Sistem, harita etrafındaki özenle seçilmiş ilgi çekici noktalar listesinden rastgele alma ve bırakma konumları oluşturur, alma konumunda bir NPC yaratır ve sürücüyü tüm ücret döngüsü boyunca yönlendirir. NPC görevi deneyiminin kalitesi büyük ölçüde konum verilerine bağlıdır: Yalnızca rastgele koordinatlar kullanmak ıssız yerlerde doğal olmayan yolcu alımlarına yol açar, bu nedenle hastaneler, restoranlar, oteller ve alışveriş merkezleri gibi gerçekçi konumlardan oluşan elle düzenlenmiş bir liste tutmak görevlerin gerçekçi hissettirmesini sağlar. İşte NPC görevleri oluşturma ve dağıtmaya yönelik sunucu tarafı mantığı:

local MissionLocations = {
    {coords = vector3(-240.49, -987.78, 29.29), label = 'Pillbox Medical Center'},
    {coords = vector3(228.28, -790.70, 30.58), label = 'Legion Square'},
    {coords = vector3(-1044.57, -2739.76, 13.85), label = 'Los Santos Airport'},
    {coords = vector3(126.19, -1282.72, 29.27), label = 'Strawberry Avenue'},
    {coords = vector3(-556.18, -196.17, 38.22), label = 'Rockford Hills'},
    {coords = vector3(1159.56, -314.45, 69.21), label = 'Vinewood Hills'},
    {coords = vector3(-1222.08, -906.82, 12.32), label = 'Del Perro Pier'},
    {coords = vector3(373.58, 325.71, 103.57), label = 'Downtown Vinewood'},
}

RegisterNetEvent('taxi:server:requestNPCMission', function()
    local src = source
    local Player = QBCore.Functions.GetPlayer(src)
    if not Player or Player.PlayerData.job.name ~= 'taxi' then return end

    local pickup = MissionLocations[math.random(#MissionLocations)]
    local dropoff = MissionLocations[math.random(#MissionLocations)]

    while pickup.label == dropoff.label do
        dropoff = MissionLocations[math.random(#MissionLocations)]
    end

    local estimatedDistance = #(pickup.coords - dropoff.coords)
    local basePay = 50
    local distancePay = math.floor(estimatedDistance / 1609.34 * 15)
    local totalPay = basePay + distancePay

    TriggerClientEvent('taxi:client:startNPCMission', src, {
        pickup = pickup,
        dropoff = dropoff,
        estimatedPay = totalPay,
        npcModel = Config.NPCModels[math.random(#Config.NPCModels)],
        missionId = 'TAXI-' .. os.time() .. '-' .. math.random(1000, 9999)
    })
end)

Client tarafında, NPC'yi alma noktasında bir bekleme animasyonuyla spawn edin, sürücüye yol göstermesi için bir blip ekleyin ve sürücü geldiğinde araca binme animasyonu oynatın. Yolculuk sırasında NPC etrafa bakınma ve sohbet balonları aracılığıyla sürüş hakkında ara sıra yorum yapma gibi doğal davranışlar sergilemelidir. Araç varış noktasına ulaştığında NPC iner ve ücret bir server event'i üzerinden otomatik olarak ödenir. Havalimanı transferleri veya hiç çarpışma olmadan dikkatli sürüş talep eden VIP müşteriler gibi daha yüksek kazançlı özel görevleri ara sıra oluşturarak çeşitlilik katın.

Ücret Hesaplama ve Dinamik Fiyatlandırma

Temel mesafe tabanlı hesaplamanın ötesinde, gelişmiş bir ücret sistemi günün saatini, trafik koşullarını ve araç türünü hesaba katar. Sunucuda birçok oyuncunun çevrim içi olduğu ve taksi talebinin arzı aştığı yoğun saatlerde dinamik fiyatlandırma uygulayınız. Sürpriz yaşanmaması için çarpan katsayısı, yolcular yolculuğu onaylamadan önce şeffaf şekilde iletilmelidir. Bekleyen yolculuk taleplerinin müsait sürücülere oranını takip ederek talebi dinamik olarak hesaplayabilirsiniz. İşte bu değişkenleri içeren bir ücret hesaplama motoru:

function CalculateFare(pickupCoords, dropoffCoords, vehicleClass)
    local distance = #(pickupCoords - dropoffCoords)
    local miles = distance / 1609.34

    local rates = Config.FareRates
    local baseFare = rates.base
    local perMile = rates.perMile

    -- Vehicle class multiplier
    local classMultiplier = {
        [0] = 0.8,   -- Compacts (budget)
        [1] = 1.0,   -- Sedans (standard)
        [2] = 1.0,   -- SUVs
        [3] = 1.2,   -- Coupes (comfort)
        [18] = 1.5,  -- Super (luxury)
    }
    local vehMultiplier = classMultiplier[vehicleClass] or 1.0

    -- Time-of-day multiplier (night premium)
    local hour = GetClockHours()
    local timeMultiplier = 1.0
    if hour >= 22 or hour <= 5 then
        timeMultiplier = 1.3  -- 30% night surcharge
    end

    -- Surge pricing based on demand
    local surgeMultiplier = GetCurrentSurgeMultiplier()

    local totalFare = (baseFare + (miles * perMile)) * vehMultiplier * timeMultiplier * surgeMultiplier

    return {
        total = math.floor(totalFare),
        breakdown = {
            base = baseFare,
            distance = math.floor(miles * perMile),
            vehicleClass = vehMultiplier,
            nightSurcharge = timeMultiplier > 1.0,
            surgeActive = surgeMultiplier > 1.0,
            surgeRate = surgeMultiplier
        }
    }
end

Yolcular bir sürüş talep ettiğinde, ne için ödeme yaptıklarını anlamaları adına ücret dökümünü gösterin. Dinamik fiyatlandırma aktifse, çarpanı arayüzde farklı bir renk göstergesiyle net bir şekilde belirtin. Bu şeffaflık, sürücüler ve yolcular arasında güven oluşturur ve ücret tutarları konusundaki anlaşmazlıkları azaltır. Yolcuların rezervasyon sırasında tercih ettikleri araç sınıfını seçmelerine izin verin: bütçe dostu seçeneklerde kompakt araçlar, lüks seçeneklerde ise premium tarifelerle üst segment sedan veya SUV araçlar kullanılsın.

Rota Gösterimi ve GPS Navigasyonu

Görsel rota rehberliği sağlamak, hem yeni hem de deneyimli sürücüler için taksi deneyimini daha sorunsuz hale getirir. FiveM şunu sağlar: SetNewWaypoint temel GPS rotası için native, ancak daha incelikli bir yaklaşım rotayı doğrudan mini haritada çizer ve isteğe bağlı olarak dünyada 3B bir yol oluşturur. Şunu kullanın: SetBlipRoute Dahili GPS rotasını etkinleştirmek için hedef blip'lerinde function kullanın ve güzergah üzerindeki önemli kontrol noktalarında özel blip'lerle bunu geliştirin. Yolcunun faydalanması için taksimetre UI'sinde tahmini varış süresini ve kalan mesafeyi gösterin, araç ilerledikçe gerçek zamanlı olarak güncelleyin. Ara nokta takibiyle rota görüntüleme sistemini şu şekilde kurabilirsiniz:

local routeBlip = nil
local checkpointBlips = {}

function SetTaxiRoute(destination, checkpoints)
    ClearTaxiRoute()

    routeBlip = AddBlipForCoord(destination.x, destination.y, destination.z)
    SetBlipSprite(routeBlip, 198)
    SetBlipColour(routeBlip, 5)
    SetBlipScale(routeBlip, 1.0)
    SetBlipRoute(routeBlip, true)
    SetBlipRouteColour(routeBlip, 5)
    BeginTextCommandSetBlipName('STRING')
    AddTextComponentSubstringPlayerName('Destination')
    EndTextCommandSetBlipName(routeBlip)

    if checkpoints then
        for i, cp in ipairs(checkpoints) do
            local cpBlip = AddBlipForCoord(cp.x, cp.y, cp.z)
            SetBlipSprite(cpBlip, 1)
            SetBlipColour(cpBlip, 3)
            SetBlipScale(cpBlip, 0.6)
            table.insert(checkpointBlips, cpBlip)
        end
    end
end

function ClearTaxiRoute()
    if routeBlip then
        SetBlipRoute(routeBlip, false)
        RemoveBlip(routeBlip)
        routeBlip = nil
    end
    for _, blip in ipairs(checkpointBlips) do
        RemoveBlip(blip)
    end
    checkpointBlips = {}
end

function UpdateETADisplay(destination)
    CreateThread(function()
        while routeBlip do
            Wait(2000)
            local pos = GetEntityCoords(PlayerPedId())
            local remaining = #(pos - destination)
            local speed = GetEntitySpeed(PlayerPedId())

            local etaSeconds = speed > 2.0 and (remaining / speed) or 0
            local etaMinutes = math.floor(etaSeconds / 60)

            SendNUIMessage({
                action = 'updateETA',
                distance = math.floor(remaining),
                eta = etaMinutes > 0 and (etaMinutes .. ' min') or 'Arriving'
            })
        end
    end)
end

Ekstra bir gerçekçilik katmanı için yolcuların yolculuklarını tahmini süreye kıyasla seyahat süresi, yolculuk sırasındaki çarpışma sayısı ve sürücünün trafik kurallarına uyup uymadığı gibi faktörlere göre puanladığı bir derecelendirme sistemi uygulayın. Sürücünün ortalama puanını taksi profilinde gösterin; böylece yolcular yolculuğu kabul etmeden önce bunu görebilir ve bu da sürücüler için iyi hizmet sağlama teşviki yaratır.

Bahşiş Sistemi ve Sürücü Puanları

Bir bahşiş sistemi, sürücüleri mükemmel hizmet sunmaya teşvik eden ödüllendirici bir sosyal mekanizma ekler. Ücret ödendikten ve yolcu araçtan indikten sonra, yolcuya %10, %15, %20 gibi önceden belirlenmiş seçenekler veya özel bir miktar içeren bir bahşiş istemi gösterin. Bahşiş, taban ücretin ve taksi şirketinden alınan herhangi bir komisyonun üzerine doğrudan sürücüye gider. Liderlik tabloları oluşturmak ve en yüksek puanlı sürücüler için bonusların kilidini açmak üzere veritabanında sürücü başına birikmiş bahşişleri takip edin. Tutarlılığı sağlamak ve mükerrer kayıtları önlemek için sunucu tarafında bahşişleri ücretlerle aynı işlem sistemi üzerinden işleyin. Zaman içinde yüksek bir puan ve bahşiş ortalaması yakalayan sürücüler; premium araçlara erişim, indirimli şirket komisyon oranları veya dispatch kuyruğunda öncelik gibi avantajların kilidini açabilir. Bahşiş sistemini, yolcuların sürücü tarafından görülebilen kısa bir yazılı değerlendirme bıraktığı bir inceleme sistemiyle birleştirerek sunucudaki genel taksi hizmeti kalitesini artıran bir geri bildirim döngüsü oluşturun.

Dispatch Panosu ve Yolculuk Talepleri

Dispatch panosu, taksi şoförlerinin bir sonraki işlerini buldukları merkezi noktadır. Bunu taksi şirketi merkezinden veya görevdeyken bir kısayol tuşuyla erişilebilen bir NUI paneli olarak oluşturun. Pano, her girişte yolcu adı, alış konumu, varış noktası, tahmini ücret ve tahmini mesafe gösterilecek şekilde, yakınlığa göre sıralanmış tüm bekleyen yolculuk taleplerini göstermelidir. Bir sürücü işi kabul ettiğinde, bunu diğer tüm sürücüler için panodan kaldırın ve bir geri sayım sayacı başlatın; sürücü süre sınırı içinde alış noktasına ulaşamazsa iş panoya geri döner ve sürücü bir güvenilirlik cezası alır. Yolcu tarafı için, mevcut konumlarını ve varış noktalarını belirterek yolculuk talep etmelerini sağlayan basit bir telefon uygulaması veya komut oluşturun. Talep dispatch kuyruğuna girer ve yolcu, bir sürücü kabul edip yaklaştıkça güncellemeler alır. Sürücülerin zamanını boşa harcayan ve sinir bozucu bir deneyim yaratan sürekli yolculuk talep edip iptal etme durumunu önlemek için, geç iptallerde küçük bir ücret içeren bir iptal sistemi uygulayın.

Araç Yönetimi ve Filo Sistemi

Eksiksiz bir taksi sistemi, sürücülerin uygun araçları kullanması ve şirketin filosunu koruması için düzgün bir araç yönetimine ihtiyaç duyar. Taksi merkezinde sürücülerin mevcut şirket araçları arasından seçim yaptığı bir araç teslim alma sistemi oluşturun. Her aracın takip edilen bir kilometre sayacı, yakıt seviyesi ve durum bilgisi olmalıdır; sürücüler araçları kabul edilebilir durumda iade etmekle yükümlüdür, aksi takdirde kazançlarından onarım kesintisi yapılır. Merkezde farklı araç sınıfları bulunmalıdır: Standart ücretler için ekonomik sedanlar, premium hizmet için konforlu SUV'lar ve VIP rezervasyonlar için lüks araçlar. Tamamlanan toplam sürüşler, elde edilen gelir ve bakım maliyetleri dahil olmak üzere her aracın kullanım istatistiklerini takip edin. Sürücülerin vardiyaları sırasında benzin istasyonlarında yakıt ikmali yapmasını gerektiren bir yakıt tüketim sistemi uygulayın; bu, deneyimli sürücülerin verimli bir şekilde yönetmeyi öğrendiği gerçekçi bir operasyonel maliyet ekler. Vardiya sonunda sürücü aracı merkeze teslim eder, şirket komisyonu ve olası araç hasar maliyetleri düşülerek toplam kazancı hesaplanır ve net ödeme banka hesabına yatırılır. Bu eksiksiz döngü, gerçek taksi şirketi operasyonlarını yansıtan gerçekçi bir meslek deneyimi sunar.

Başlamaya Hazır mısınız?

Scriptleri mağazamızdan edinin veya destek, güncellemeler ve gelecek yeniliklere göz atmak için Discord'a katılın.