返回博客
Tutorial2 分钟阅读

FiveM 餐饮与餐厅系统:厨房脚本编写

在 FiveM 中运营游戏内餐厅。制作配方、收银员界面、送餐任务以及顶级美食脚本,让您的城市美食场景栩栩如生。

Agency Scripts

Agency Scripts 创始人兼首席开发者

为什么餐厅系统提升角色扮演

食物和餐厅系统是你可以添加到FiveM角色扮演服务器中最具社交性的功能之一。与采矿或钓鱼等单人活动不同,餐厅创造了玩家一起烹饪、服务和用餐的有机聚集点。设计良好的餐厅系统为平民玩家提供了一个有意义的非犯罪职业,提供了角色定期访问地点的理由,并通过购买食材引入了经济消耗。餐厅与农业或狩猎脚本结合时还会形成供应链,意味着一个玩家种植番茄,另一个玩家配送,第三个玩家将其烹饪成汉堡。这种相互关联的游戏循环是活跃服务器与玩家反复刷同一路线服务器的区别。食物本身可以提供临时的生命值、耐力或护甲回复增益,使餐厅对服务器上所有玩家无论职业或阵营都具有意义。

餐厅配置与菜单设计

首先定义你的餐厅位置、菜单及每道菜所需的食材。数据驱动的配置让服务器拥有者无需修改核心逻辑即可添加新餐厅。每个餐厅应通过其菜单、内部道具和 NPC 员工制服拥有独特身份。结构化配置,使每个菜单项列出所需食材、制作时间、售价及对消费者的任何增益效果。这种方法意味着调整食物经济只需调整配置值,而非重写代码:

Config.Restaurants = {
    ['burgershot'] = {
        label = 'Burger Shot',
        blip = { sprite = 106, color = 1, scale = 0.8 },
        zones = {
            counter = vector3(-1196.42, -894.04, 13.98),
            kitchen = vector3(-1199.71, -897.12, 13.98),
            storage = vector3(-1202.84, -899.35, 13.98),
        },
        menu = {
            {
                item = 'bleeder_burger',
                label = 'Bleeder Burger',
                price = 25,
                ingredients = {
                    { item = 'raw_patty', amount = 2 },
                    { item = 'burger_bun', amount = 1 },
                    { item = 'lettuce', amount = 1 },
                    { item = 'cheese_slice', amount = 2 },
                },
                craftTime = 8000,
                buffs = {
                    hunger = 45,
                    thirst = 5,
                    armor_regen = { amount = 2, duration = 120 }
                }
            },
            {
                item = 'heartstopper',
                label = 'Heart Stopper',
                price = 40,
                ingredients = {
                    { item = 'raw_patty', amount = 3 },
                    { item = 'burger_bun', amount = 1 },
                    { item = 'bacon', amount = 4 },
                    { item = 'cheese_slice', amount = 3 },
                    { item = 'onion_ring', amount = 2 },
                },
                craftTime = 12000,
                buffs = {
                    hunger = 80,
                    thirst = -10,
                    stamina_boost = { amount = 15, duration = 180 }
                }
            },
        },
        requiredJob = 'burgershot',
    },
}

zones 该表将餐厅划分为功能区。柜台区是顾客下单和取餐的地方。厨房区仅限员工使用,用于烹饪。储藏区用于存放和管理食材。这种空间分离强制实现现实工作流程,厨师不能直接从炉灶为顾客服务,促进前厅与厨房员工的自然协作。

烹饪机制与制作流程

烹饪过程应感觉互动而非乏味。当玩家开始烹饪物品时,检查餐厅储藏库存中是否有所有必需原料,扣除后开始进度条或小游戏序列。简单进度条适用于基础菜品,添加技能检测小游戏则为高级菜肴带来掌控感,防止挂机烹饪作弊。烹饪动画应使用适当的GTA原生场景,如烧烤或厨房操作台,向附近观察者视觉传达玩家正在做什么:

function StartCooking(restaurantId, menuIndex)
    local restaurant = Config.Restaurants[restaurantId]
    local recipe = restaurant.menu[menuIndex]

    -- Check ingredients in storage
    local hasAll = lib.callback.await('restaurant:checkIngredients', false,
        restaurantId, recipe.ingredients)

    if not hasAll then
        lib.notify({ description = 'Missing ingredients!', type = 'error' })
        return
    end

    -- Lock player into cooking animation
    local playerPed = PlayerPedId()
    local animDict = 'mini@repair'
    lib.requestAnimDict(animDict)
    TaskPlayAnim(playerPed, animDict, 'fixing_a_player', 8.0, -8.0,
        -1, 1, 0, false, false, false)

    -- Cooking progress with skill check
    local success = lib.skillCheck(
        {'easy', 'easy', 'medium'},
        {'w', 'a', 's', 'd'}
    )

    ClearPedTasks(playerPed)

    if success then
        TriggerServerEvent('restaurant:finishCooking', restaurantId, menuIndex)
        lib.notify({ description = 'Cooked: ' .. recipe.label, type = 'success' })
    else
        -- Failed cooking wastes some ingredients
        TriggerServerEvent('restaurant:failedCooking', restaurantId, menuIndex)
        lib.notify({ description = 'Burnt the food! Some ingredients wasted.',
            type = 'error' })
    end
end

技能检测机制为烹饪增加了风险与回报元素。失败尝试应浪费部分材料而非全部,这样新玩家不会受到重罚,同时仍激励技能提升。您可以根据菜品价值调整难度,使廉价菜品易于烹饪,昂贵招牌菜需认真对待。这创造了自然进阶,新员工从简单菜品开始,逐步提升到高级菜单。

原料供应链

餐厅需要稳定的食材供应,玩家获取食材的方式定义了游戏循环的重要部分。有三种常见方法:从批发NPC供应商购买、从玩家经营的农场或狩猎中获取,以及混合模式。批发方式实现简单且保证供应,但缺乏玩家互动。从其他玩家处获取则创造丰富的经济关系,但供应商离线时餐厅可能停业。混合模式效果最佳:基础食材如面包、油和调味料来自NPC,而高级食材如新鲜肉类、有机蔬菜和特殊酱料必须通过玩家活动获得。这确保餐厅始终能以基本水平运营,同时当供应链完整时奖励全链条。为每个餐厅实现一个存储系统,跟踪食材数量并允许管理员下批发订单,订单费用从餐厅银行账户扣除:

RegisterNetEvent('restaurant:orderSupplies', function(restaurantId, order)
    local src = source
    local Player = QBCore.Functions.GetPlayer(src)

    -- Verify player is manager rank
    if Player.PlayerData.job.name ~= restaurantId then return end
    if Player.PlayerData.job.grade.level < 3 then
        TriggerClientEvent('QBCore:Notify', src,
            'Only managers can order supplies', 'error')
        return
    end

    -- Calculate total cost
    local totalCost = 0
    for _, item in ipairs(order) do
        local wholesalePrice = Config.WholesalePrices[item.name]
        if wholesalePrice then
            totalCost = totalCost + (wholesalePrice * item.amount)
        end
    end

    -- Deduct from restaurant bank account
    local balance = exports['qb-management']:GetAccount(restaurantId)
    if balance < totalCost then
        TriggerClientEvent('QBCore:Notify', src,
            'Insufficient restaurant funds', 'error')
        return
    end

    exports['qb-management']:RemoveMoney(restaurantId, totalCost)

    -- Add items to restaurant storage
    for _, item in ipairs(order) do
        AddToRestaurantStorage(restaurantId, item.name, item.amount)
    end

    TriggerClientEvent('QBCore:Notify', src,
        string.format('Order placed! $%d deducted', totalCost), 'success')
end)

客户订购与服务流程

无论客户是从 NPC 收银员还是玩家员工处点单,客户体验都应无缝衔接。当客户接近柜台区域时,显示菜单 UI,展示可用物品及其价格和任何有效的增益描述。对于玩家员工的餐厅,订单应传送到厨房显示屏,厨师实时看到新订单。实现订单队列系统,跟踪每个订单从下单、准备到交付的全过程。这营造了快餐模拟,厨房在高峰期会积压订单,给厨师角色带来真实压力。用简单指示器向客户显示订单状态:已下单、烹饪中、可取餐。食物准备好时通知客户,让他们在柜台取餐。为高端体验,允许玩家服务员直接将食物送到桌上就座的客户,根据服务速度获得小费。

食物增益与消费效果

食物应提供超越简单恢复饥饿的有意义游戏收益。实现一个增益系统,不同食物赋予临时属性修改,鼓励玩家在活动前策略性进食。追逐前的耐力提升餐点、抢劫前的护甲回复菜肴、战斗后的持续生命恢复餐点,都使食物在服务器经济中真正有价值。跟踪每个玩家的活跃增益并在HUD中显示,让玩家知道当前拥有的效果及其过期时间:

local activeBuffs = {}

function ApplyFoodBuff(buffType, amount, duration)
    -- Remove existing buff of same type
    if activeBuffs[buffType] then
        activeBuffs[buffType].active = false
    end

    activeBuffs[buffType] = {
        amount = amount,
        endTime = GetGameTimer() + (duration * 1000),
        active = true
    }

    -- Send buff data to HUD
    SendNUIMessage({
        action = 'addBuff',
        buffType = buffType,
        amount = amount,
        duration = duration
    })

    -- Create buff application thread
    CreateThread(function()
        local buff = activeBuffs[buffType]
        while buff.active and GetGameTimer() < buff.endTime do
            if buffType == 'armor_regen' then
                local current = GetPedArmour(PlayerPedId())
                if current < 100 then
                    SetPedArmour(PlayerPedId(), math.min(100, current + amount))
                end
            elseif buffType == 'stamina_boost' then
                RestorePlayerStamina(PlayerId(), amount * 0.1)
            elseif buffType == 'health_regen' then
                local current = GetEntityHealth(PlayerPedId())
                local max = GetEntityMaxHealth(PlayerPedId())
                if current < max then
                    SetEntityHealth(PlayerPedId(), math.min(max, current + amount))
                end
            end
            Wait(1000)
        end

        activeBuffs[buffType] = nil
        SendNUIMessage({ action = 'removeBuff', buffType = buffType })
    end)
end

平衡食物增益,确保它们增强游戏体验但不成为强制性。增益应提供明显优势,但不能强大到让玩家在每次活动前都必须进食。持续时间保持适中,在两到五分钟之间,以保持增益经济活跃。考虑在短时间内重复食用同种食物时添加递减效果,防止增益叠加漏洞。

员工管理与薪资

餐厅系统需要合理的层级结构和不同角色权限。至少定义三层:员工、主管和经理。员工负责烹饪和服务顾客。主管除了员工职责外,还管理原料储存和审批订单。经理拥有全面控制权,包括招聘解雇员工、定价和访问餐厅银行账户。将工资与餐厅收入挂钩,员工获得基础工资加其亲自烹饪或服务物品的提成。这激励积极参与而非挂机打卡。跟踪员工个人统计数据,如烹饪物品数、服务顾客数和产生收入,创建排行榜和绩效评估,丰富职位体验。

性能优化

餐厅系统涉及多个交互区域、NPC行人、道具对象以及实时订单跟踪,如果未经过优化,可能会影响客户端性能。使用目标系统交互替代基于标记的距离检测,以消除每帧坐标计算。仅当玩家进入建筑物内部时渲染餐厅内部道具,方法是使用 IsPlayerInScope 检查。批量处理您的 NUI 消息以用于订单队列显示,而不是为每个订单变更发送单独更新。在服务器端,将餐厅存储库存缓存在内存中,并按定时间隔写入数据库,而不是每次原料变更后写入。当多个餐厅同时运行时,错开它们的数据库同步周期以避免写入峰值。资源停止时清理所有地图标记、道具和 NPC 实体,防止资源重启时遗留孤立实体。负载测试您的系统,模拟多个餐厅同时运营,多名厨师同时工作,以及持续的客户订单流,以便在玩家发现之前识别瓶颈。

准备好开始了吗?

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