返回博客
Tutorial1 分钟阅读

FiveM 燃油系统脚本:车辆真实加油体验

为您的FiveM服务器添加工作燃料系统。加油站、油桶、电动车以及适用于QBCore、ESX和独立设置的最佳燃料脚本。

Agency Scripts

Agency Scripts 创始人兼首席开发者

燃油消耗机制

真实的燃油系统增加了必要的沉浸感层面,并创造了自然的资金消耗,有助于平衡服务器经济。核心机制是将每辆车的燃油量作为 0 到 100 的浮点值跟踪,根据发动机活动和驾驶行为减少。使用实体状态包存储燃油量, Entity(vehicle).state.fuel 以便该值在所有客户端间持久存在,并在网络所有权变更时保持。根据车辆当前通过获取的RPM计算每刻的消耗率 GetVehicleCurrentRpm,油门位置来自 GetControlNormal,以及车辆类别特定的消耗乘数。跑车和超级跑车的燃油消耗应明显快于经济型车辆,而卡车和SUV介于两者之间。对激烈驾驶行为如快速加速、高速持续行驶和发动机制动应用额外消耗修正。当燃油耗尽时,使用 SetVehicleMaxSpeed 模拟发动机熄火前的抖动,而非立即熄火,给予玩家几秒关键时间安全靠边停车。

-- Client-side fuel consumption thread
local fuelConsumptionRate = 0.0

CreateThread(function()
    while true do
        local ped = PlayerPedId()
        local vehicle = GetVehiclePedIsIn(ped, false)

        if vehicle ~= 0 and GetPedInVehicleSeat(vehicle, -1) == ped then
            local engineOn = GetIsVehicleEngineRunning(vehicle)

            if engineOn then
                local rpm = GetVehicleCurrentRpm(vehicle)
                local speed = GetEntitySpeed(vehicle) * 3.6 -- km/h
                local classMultiplier = Config.ClassConsumption[GetVehicleClass(vehicle)] or 1.0

                -- Base consumption + RPM factor + speed factor
                fuelConsumptionRate = (Config.BaseConsumption
                    + (rpm * Config.RpmFactor)
                    + (speed * Config.SpeedFactor)
                ) * classMultiplier

                local currentFuel = Entity(vehicle).state.fuel or 100.0
                local newFuel = math.max(0.0, currentFuel - fuelConsumptionRate)

                Entity(vehicle).state:set('fuel', newFuel, true)

                -- Engine stall when empty
                if newFuel <= 0 then
                    SetVehicleUndriveable(vehicle, true)
                    SetVehicleEngineOn(vehicle, false, true, true)
                elseif newFuel <= 5.0 then
                    -- Sputtering effect
                    SetVehicleMaxSpeed(vehicle, 30.0 * (newFuel / 5.0))
                end
            end
        end

        Wait(1000) -- Update every second
    end
end)

加油站UI和加油

加油站体验应通过设计良好的 NUI 界面显得精致且直观。当玩家停靠在定义半径内的加油泵附近时,显示交互提示。交互后打开加油界面,显示当前燃油水平(仪表或进度条)、车辆油箱容量、基于当前燃油类型的每升价格,以及完全加满或按指定金额加油的选项。加油动画应同时在玩家角色上播放,角色站在车辆旁边,手持使用的加油枪道具,使用 TaskPlayAnim,以及燃油泵本身,如果您使用自定义道具设置。燃油应实时逐渐加注,而非瞬间加满,制造短暂等待以增加真实感并防止玩家在追逐时快速加油。显示燃油泵运行成本总计的实时更新,仅在玩家停止加油或油箱加满时扣款。支持高级加油站的多种燃油类型:标准价格的普通无铅燃油、提供小幅性能提升且价格更高的高级燃油,以及用于卡车和工业车辆的柴油,使用独立油泵。

油桶和紧急加油

高速公路上燃油耗尽是一个现实场景,您的系统应优雅处理,而不强制玩家放弃车辆。实现一个可在加油站或五金店购买并可在任何油泵处加油的油桶道具。油桶应作为可用的库存物品,带有跟踪当前油量的元数据,通常可装 10-20 升。当玩家在空车附近使用油桶时,播放倒油动画并将燃油从油桶转移到车辆油箱。油桶在库存系统中应根据燃油量有相应重量,满桶比空桶重。添加一个机制,允许通过游戏内电话呼叫道路救援,派遣 NPC 或玩家操作的拖车提供燃油配送服务,需付费。为增强沉浸感,玩家应能使用软管道具从其他车辆抽油,在时间消耗和被抓风险下实现车辆间燃油转移。

-- Jerry can usage handler
RegisterNetEvent('fuel:useJerryCan')
AddEventHandler('fuel:useJerryCan', function(slot)
    local src = source
    local ped = GetPlayerPed(src)
    local coords = GetEntityCoords(ped)

    -- Find nearest vehicle
    local nearbyVehicle = GetClosestVehicle(coords, 3.0)
    if not nearbyVehicle or nearbyVehicle == 0 then
        return notify(src, 'No vehicle nearby.', 'error')
    end

    -- Get jerry can metadata
    local canData = exports.ox_inventory:GetSlot(src, slot)
    if not canData or not canData.metadata or not canData.metadata.fuel then
        return notify(src, 'Jerry can is empty.', 'error')
    end

    local canFuel = canData.metadata.fuel
    local vehicleFuel = Entity(nearbyVehicle).state.fuel or 0.0
    local maxTransfer = math.min(canFuel, 100.0 - vehicleFuel)

    if maxTransfer <= 0 then
        return notify(src, 'Vehicle tank is already full.', 'error')
    end

    -- Transfer fuel
    Entity(nearbyVehicle).state:set('fuel', vehicleFuel + maxTransfer, true)

    -- Update jerry can metadata
    local remainingFuel = canFuel - maxTransfer
    if remainingFuel <= 0 then
        exports.ox_inventory:SetMetadata(src, slot, { fuel = 0, label = 'Empty Jerry Can' })
    else
        exports.ox_inventory:SetMetadata(src, slot, { fuel = remainingFuel })
    end

    notify(src, ('Transferred %.1f liters to the vehicle.'):format(maxTransfer), 'success')
end)

电动车支持

现代角色扮演服务器越来越多配备电动车,需要配套充电基础设施。电动车不消耗汽油,而是消耗电池电量,电量显示与燃油表功能相同但消耗速率不同。电动车每公里消耗低于汽油车,反映其现实效率优势,但充电时间长于加油,形成不同的游戏节奏。在地图特定位置设置充电站,不必局限于传统加油站,创造电动车主独特的出行模式。实现充电界面,显示当前电量百分比、预计充满时间和每千瓦时费用。高级站点提供快速充电选项,充电时间减半但费用翻倍。车辆配置按模型哈希分类电动车,配置中存储车辆类型,系统自动识别车辆需汽油还是电力。支持混合动力车,可在低速切换电动模式,高速切换汽油模式,分别以较低速率消耗两种资源。

动态燃油定价经济

动态定价系统让燃油经济更具活力,为玩家创造有趣的经济决策。基于可配置费率,价格根据供需机制波动。跟踪所有加油站在滚动时间窗口内的总燃油购买量,消费高时价格逐步上涨,消费低时价格下降以吸引客户。实现站点特定定价,偏远的 Blaine County 站点因运输成本更高,城市站点则以较低利润竞争。允许玩家拥有加油站,业主可在服务器配置的最低和最高范围内设定燃油价格,形成真实价格竞争。业主以批发价从供应 NPC 购入燃油,再加价零售,利润即为收入。添加季节性或事件性价格飙升,如赛车或抢劫等高车辆活动服务器事件期间,燃油价格上涨,产生经济连锁反应,影响所有玩家,鼓励节约或提前战略性购买燃油。

加油站作为商业

将加油站转变为玩家运营的企业,为燃料系统添加完整经济循环。站点所有者通过从批发供应商订购燃料补货管理库存,支付泵维修和升级费用维护设备,并通过雇佣其他玩家担任加油员支付时薪管理员工。实现仅所有者可访问的站点管理面板,显示每日销售量、收入、利润率、地下油罐当前燃料库存及员工出勤记录。加油站应有实体地下油罐,容量有限,随着客户加油而减少,所有者需安排送货卡车访问,送货需时间且需预付费用。加油站内设商店,所有者可存货便利商品如零食、饮料和基本用品,客户可与燃料一同购买,创造额外收入。允许对站点本身进行升级,如增加更多加油泵位以同时服务更多客户,安装洗车设备收取额外服务费,或增设电动车充电站以捕捉电动车市场。根据定价公平性、供应可用性和客户满意度跟踪站点声誉,声誉较好的站点在地图上高亮显示,吸引更多流量。

HUD 集成与燃油表

燃油表是一个持久的 HUD 元素,玩家会持续看到它,因此必须视觉简洁且不干扰,同时一目了然地传达关键信息。设计一个极简的燃油指示器,放置在速度表附近或屏幕角落,以半圆形仪表、垂直条或简单百分比显示当前燃油水平。使用颜色编码,绿色表示燃油水平健康(高于50%),黄色表示中等燃油(20%-50%),红色警告燃油极低(低于20%)。当燃油低于15%时,触发周期性低油量警告音并闪烁仪表,确保玩家不会意外耗尽燃油。显示基于当前消耗率估算的剩余行驶距离,通过将剩余燃油除以过去几分钟的平均每公里消耗计算得出。玩家步行时完全隐藏燃油表以减少 HUD 杂乱,进入车辆时平滑淡入。对于偏好极简 HUD 的玩家,提供设置以完全隐藏燃油表,仅在燃油低于可配置阈值时作为临时通知显示。将燃油表与其他车辆 HUD 元素如速度表和引擎健康集成,打造统一的车辆仪表盘体验。

性能与同步

燃油系统在每个驾驶车辆的客户端上持续运行,因此性能优化至关重要。使用1000毫秒的消耗更新间隔,而非每帧更新,因为燃油变化足够缓慢,每秒更新在视觉上与每帧更新无异,但处理能力约减少60倍。将燃油值存储在实体状态包中,而非自定义网络事件,因为状态包针对频繁变化的值进行了优化,且仅在值实际变化时同步。为实现服务器重启间的持久化,将所有车辆燃油更新批量合并为每60秒执行一次的单条查询,而非每次燃油变化时单独写入。使用车辆的网络ID作为燃油跟踪的键,并清理不存在车辆的燃油数据以防止数据库膨胀。处理车辆在加油时网络所有权变更的边缘情况,通过在应用燃油变化前检查所有权,并对所有支付交易进行服务器端验证。对于同时有大量客户的加油站,实现排队系统,防止两个玩家同时使用同一加油泵,同时显示加油泵正在使用的视觉指示。

准备好开始了吗?

在我们的商店获取脚本,或加入 Discord 获取支持、更新以及新功能预告。