返回博客
Guide4 分钟阅读

FiveM 农业系统:作物、动物与销售链

创建深度 FiveM 农业系统。作物、牲畜、加工、销售 NPC 及顶级农场脚本,打造沉浸式角色扮演经济。

Agency Scripts

Agency Scripts 创始人兼首席开发者

农场系统设计理念

农业系统为 FiveM 角色扮演服务器带来完全不同的节奏,为玩家提供高强度犯罪和执法活动之外的轻松选择。最佳农业系统创建自给自足的经济循环,玩家购买种子,在指定农田种植,经历真实时间生长周期,收获农作物并在市场出售获利。设计挑战在于让系统足够吸引玩家定期回归,同时不至于过于耗时变成负担。采用真实时间生长周期,即使玩家离线,农作物仍继续生长,农民登录时可见不同生长阶段。核心数据模型需跟踪每个农作物地块的位置、作物类型、当前生长阶段、种植时间戳、水分和质量评级。所有数据服务器端存储,确保农场状态跨服务器重启持久且防止客户端篡改。

作物种植与数据库架构

种植系统从玩家从其库存中选择种子并将其放置在有效的农田区域开始。将农田定义为地图上可配置的多边形区域,允许种植,防止玩家在人行道或建筑物内种植作物。每个农田地块应根据土地大小限制插槽数量,玩家可以通过NPC或房地产系统租赁或购买农田。当玩家种下种子时,创建一个数据库记录,跟踪作物的整个生命周期。以下是一个实用的数据库架构和服务器端种植处理程序:

CREATE TABLE IF NOT EXISTS farm_crops (
    id INT AUTO_INCREMENT PRIMARY KEY,
    owner VARCHAR(50) NOT NULL,
    farm_id VARCHAR(50) NOT NULL,
    crop_type VARCHAR(50) NOT NULL,
    position_x FLOAT NOT NULL,
    position_y FLOAT NOT NULL,
    position_z FLOAT NOT NULL,
    planted_at BIGINT NOT NULL,
    growth_stage INT DEFAULT 0,
    water_level FLOAT DEFAULT 100.0,
    quality FLOAT DEFAULT 1.0,
    is_harvestable TINYINT DEFAULT 0,
    is_dead TINYINT DEFAULT 0,
    last_watered BIGINT DEFAULT 0,
    INDEX idx_owner (owner),
    INDEX idx_farm (farm_id)
);

CREATE TABLE IF NOT EXISTS farm_plots (
    id INT AUTO_INCREMENT PRIMARY KEY,
    farm_id VARCHAR(50) NOT NULL UNIQUE,
    owner VARCHAR(50) DEFAULT NULL,
    max_slots INT DEFAULT 20,
    rent_price INT DEFAULT 500,
    coords_center_x FLOAT NOT NULL,
    coords_center_y FLOAT NOT NULL,
    coords_center_z FLOAT NOT NULL,
    radius FLOAT DEFAULT 30.0
);
RegisterNetEvent('farming:server:plantCrop', function(cropType, position)
    local src = source
    local Player = QBCore.Functions.GetPlayer(src)
    if not Player then return end

    local citizenid = Player.PlayerData.citizenid
    local seedItem = 'seed_' .. cropType

    if not Player.Functions.GetItemByName(seedItem) then
        return TriggerClientEvent('QBCore:Notify', src, 'You need seeds to plant', 'error')
    end

    local farmId = GetFarmAtPosition(position)
    if not farmId then
        return TriggerClientEvent('QBCore:Notify', src, 'You can only plant on farmland', 'error')
    end

    local currentCrops = MySQL.scalar.await(
        'SELECT COUNT(*) FROM farm_crops WHERE farm_id = ? AND is_dead = 0',
        {farmId}
    )
    local maxSlots = MySQL.scalar.await(
        'SELECT max_slots FROM farm_plots WHERE farm_id = ?', {farmId}
    )

    if currentCrops >= maxSlots then
        return TriggerClientEvent('QBCore:Notify', src, 'Farm is full', 'error')
    end

    Player.Functions.RemoveItem(seedItem, 1)

    MySQL.insert(
        'INSERT INTO farm_crops (owner, farm_id, crop_type, position_x, position_y, position_z, planted_at, last_watered) VALUES (?, ?, ?, ?, ?, ?, ?, ?)',
        {citizenid, farmId, cropType, position.x, position.y, position.z, os.time(), os.time()}
    )

    TriggerClientEvent('QBCore:Notify', src, 'Planted ' .. cropType, 'success')
    TriggerClientEvent('farming:client:spawnCropProp', -1, position, cropType, 0)
end)

每种作物类型应在配置表中定义不同的生长参数。小麦可能在现实30分钟内分4阶段生长,而大麻或葡萄可能需数小时分6阶段生长,耐心的农民可获得更高价值的产物。生长阶段决定世界中显示的道具模型,随着时间推移从小芽转变为成熟植物。

增长周期引擎与浇水

生长周期引擎是服务器端循环,定期检查所有种植作物,并根据经过时间及水位等条件推进生长阶段。作为定时服务器线程每隔几分钟执行一次,计算每株作物是否应根据其作物类型的生长周期和种植时间推进到下一阶段。水位是保持玩家参与的主要机制:作物随时间消耗水分,水位降至零时生长暂停且作物质量下降。玩家必须返回农场,使用水壶或灌溉系统补充水位。以下是服务器端生长引擎:

local CropConfig = {
    wheat = {
        stages = 4,
        growthTime = 1800,    -- 30 min total, ~7.5 min per stage
        waterConsumption = 5, -- per cycle
        baseYield = 5,
        sellPrice = 50
    },
    corn = {
        stages = 5,
        growthTime = 3600,
        waterConsumption = 7,
        baseYield = 4,
        sellPrice = 80
    },
    tomato = {
        stages = 4,
        growthTime = 2400,
        waterConsumption = 8,
        baseYield = 6,
        sellPrice = 65
    },
    grape = {
        stages = 6,
        growthTime = 7200,
        waterConsumption = 4,
        baseYield = 8,
        sellPrice = 120
    }
}

CreateThread(function()
    while true do
        Wait(60000 * 5) -- Run every 5 minutes

        local crops = MySQL.query.await(
            'SELECT * FROM farm_crops WHERE is_dead = 0 AND is_harvestable = 0'
        )

        for _, crop in ipairs(crops or {}) do
            local config = CropConfig[crop.crop_type]
            if not config then goto continue end

            local elapsed = os.time() - crop.planted_at
            local stageTime = config.growthTime / config.stages
            local expectedStage = math.min(math.floor(elapsed / stageTime), config.stages)

            -- Degrade water
            local newWater = math.max(0, crop.water_level - config.waterConsumption)
            local newQuality = crop.quality

            -- Quality degrades without water
            if newWater <= 0 then
                newQuality = math.max(0.3, crop.quality - 0.05)
                expectedStage = crop.growth_stage -- Pause growth
            end

            local isHarvestable = expectedStage >= config.stages and newWater > 0

            if expectedStage ~= crop.growth_stage or newWater ~= crop.water_level then
                MySQL.update(
                    'UPDATE farm_crops SET growth_stage = ?, water_level = ?, quality = ?, is_harvestable = ? WHERE id = ?',
                    {expectedStage, newWater, newQuality, isHarvestable and 1 or 0, crop.id}
                )

                if expectedStage ~= crop.growth_stage then
                    TriggerClientEvent('farming:client:updateCropStage', -1,
                        crop.id, expectedStage, crop.crop_type,
                        vector3(crop.position_x, crop.position_y, crop.position_z))
                end
            end

            :continue:
        end
    end
end)

浇水机制应感觉互动而非繁琐。给玩家一个浇水壶道具,靠近作物时触发动画,并一次性浇灌多个附近作物,而非逐个浇水。对于大型农场,引入灌溉系统升级,定期自动浇灌地块上的所有作物,减少手动劳动,但需前期材料投入。玩家观察作物时,通过3D文字或目标系统覆盖,在道具上方显示每株作物的水分和生长进度小指示器。

收获与产品质量

收获是整个农耕循环中令人满意的回报时刻。当作物达到最终生长期且充分浇水后,即可收获。玩家与作物互动,播放收获动画,并在库存中获得产品物品。产品的数量和质量取决于玩家在生长周期中对作物的维护程度:持续浇水会产生更高质量且售价更高的物品,而忽视的作物产量较少且质量较低。实现一个质量乘数,影响产量数量和销售价格,激励细心耕作。收获后,从数据库中移除作物并删除世界中的道具,释放地块槽位以便重新种植。考虑添加堆肥机制,将低质量收获转化为肥料,提高未来作物质量,形成零废弃循环,奖励持续耕作活动。

畜牧系统

动物为农业体验增加第二维度并创造额外收入来源。实现可购买的动物如鸡、牛和羊,放置在农田指定的动物围栏内。每种动物有自己的生产周期:鸡每隔数小时产蛋,牛每日产奶,羊可定期剪毛。动物需从玩家的农产品库存中喂养,将作物系统直接连接到动物系统,形成闭环经济。以下是农场动物的配置和喂养系统:

Config.Animals = {
    chicken = {
        model = 'a_c_hen',
        purchasePrice = 500,
        feedItem = 'wheat',
        feedInterval = 3600,       -- needs feeding every hour
        productionItem = 'eggs',
        productionAmount = 3,
        productionInterval = 7200, -- produces every 2 hours
        maxPerFarm = 10
    },
    cow = {
        model = 'a_c_cow',
        purchasePrice = 3000,
        feedItem = 'corn',
        feedInterval = 5400,
        productionItem = 'milk',
        productionAmount = 2,
        productionInterval = 14400,
        maxPerFarm = 5
    },
    sheep = {
        model = 'a_c_sheep',
        purchasePrice = 1500,
        feedItem = 'wheat',
        feedInterval = 4800,
        productionItem = 'wool',
        productionAmount = 1,
        productionInterval = 21600,
        maxPerFarm = 8
    }
}

RegisterNetEvent('farming:server:feedAnimal', function(animalId)
    local src = source
    local Player = QBCore.Functions.GetPlayer(src)
    if not Player then return end

    local animal = MySQL.query.await(
        'SELECT * FROM farm_animals WHERE id = ? AND owner = ?',
        {animalId, Player.PlayerData.citizenid}
    )

    if not animal or not animal[1] then return end
    animal = animal[1]

    local config = Config.Animals[animal.animal_type]
    if not Player.Functions.GetItemByName(config.feedItem) then
        return TriggerClientEvent('QBCore:Notify', src, 'Need ' .. config.feedItem .. ' to feed', 'error')
    end

    Player.Functions.RemoveItem(config.feedItem, 1)

    MySQL.update(
        'UPDATE farm_animals SET last_fed = ?, hunger = 100 WHERE id = ?',
        {os.time(), animalId}
    )

    TriggerClientEvent('QBCore:Notify', src, 'Animal fed successfully', 'success')
end)

未及时喂养的动物会变得不开心,降低产量。若长时间未喂养,则完全停止生产。通过目标交互显示动物状态,包括饥饿度、幸福度和下次产出时间。此机制促使农民每日检查,保持他们在作物生长周期间对服务器的参与。

市场系统与农产品销售

市场系统将农民与更广泛的服务器经济连接。地图上设立农贸市场、杂货店和餐馆的NPC摊位,玩家可出售农产品。实现动态定价系统,价格根据供需波动:若大量农民种植小麦,小麦价格下降,鼓励作物多样化。跟踪各作物类型在滚动时间窗口内的总销售量并相应调整价格。高品质作物的优质农产品以基础价的倍数出售,奖励精心耕作。考虑添加玩家间交易选项,农民可在指定区域设摊,直接以自定价格售卖给其他玩家。此举创造农民与买家间的有机RP互动,并让有创业精神的玩家通过批发低价购入、零售高价出售获利。

天气对农业的影响

天气整合为农业增加了不可预测性和策略性。连接到服务器的天气系统,为作物生长和用水量应用修正。雨水自然浇灌所有户外作物,减少湿润期间的手动浇水需求。极端高温增加用水量,迫使农民在炎热天气中更频繁浇水。风暴可能损坏作物,降低质量甚至摧毁长时间未收获的农产品。冬季霜冻事件可能完全杀死某些作物类型,要求农民选择耐寒品种或投资温室结构以保护作物免受天气影响。实现一个天气预报系统,通过游戏内手机应用或电视广播访问,让农民可以根据即将到来的天气条件规划种植时间表,增加策略规划层次,使农业体验超越简单的点击等待机制。

农场升级与进度

进阶系统为农民提供长期目标并奖励持续游戏。玩家从一小块可种植少量作物的地块开始,通过农业经验或直接购买获得扩展。每个升级等级解锁新功能:自动喷灌系统负责浇水、更大的动物围栏容纳更多牲畜、谷仓存储剩余农产品、加工站将原料转化为更高价值商品(如小麦制成面粉或葡萄制成葡萄酒),以及加快大面积种植和收割的拖拉机。农业经验独立于其他服务器进度跟踪,形成专门的农业技能,提升产量质量并缩短生长时间。加工系统对经济尤为重要,因为它在原料和成品之间增加增值步骤,创造供应链角色扮演机会,农民将原料卖给玩家拥有的面包店、酒庄或餐厅,后者将其转化为最终产品供消费者使用。

准备好开始了吗?

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