ब्लॉग पर वापस
Guide10 मिनट पढ़ें

FiveM स्किल ट्री सिस्टम: सही तरीके से स्तर प्रगति

अपने FiveM सर्वर में एक कौशल वृक्ष जोड़ें। XP, अनलॉक, UI डिज़ाइन, नौकरी संबंध और खिलाड़ियों को सार्थक दीर्घकालिक लक्ष्य देने के लिए सर्वश्रेष्ठ कौशल स्क्रिप्ट।

Agency Scripts

Agency Scripts के संस्थापक और प्रमुख डेवलपर

क्यों स्किल ट्रीज़ रोलप्ले सर्वर्स को बदलते हैं

अधिकांश FiveM रोलप्ले सर्वर नौकरी-आधारित प्रगति पर निर्भर करते हैं जहाँ खिलाड़ी एक करियर चुनता है और तुरंत उसके सभी उपकरणों और क्षमताओं तक पहुँच प्राप्त करता है। जबकि यह कार्यात्मक है, यह विकास या दीर्घकालिक निवेश की भावना प्रदान नहीं करता। स्किल ट्री इस गतिशीलता को मौलिक रूप से बदल देते हैं। RPG-शैली की प्रगति पेश करके, खिलाड़ी अपने कार्यों के माध्यम से अनुभव कमाते हैं और शाखित प्रतिभा वृक्षों के भीतर क्षमताओं, लाभों, और निष्क्रिय बोनस को अनलॉक करने के लिए स्किल पॉइंट खर्च करते हैं। एक मैकेनिक जो सैकड़ों कारों की मरम्मत करता है, धीरे-धीरे तेज़ और अधिक कुशल बन जाता है। एक अपराधी जो बार-बार ताले खोलता है, उसमें सुधार होता है। यह समर्पण को पुरस्कृत करने वाला सार्थक चरित्र विकास बनाता है और हर चरित्र को अनूठा महसूस कराता है। कुंजी एक ऐसी प्रणाली डिजाइन करना है जो रोलप्ले को बढ़ावा दे बजाय आपके सर्वर को MMO ग्राइंड में बदलने के, बोनस इतने सूक्ष्म रखें कि स्किल अंतर RP परिदृश्यों को पूरक करें बजाय उन्हें प्रभुत्व देने के।

कौशल ट्री डेटा संरचना डिजाइन करना

एक अच्छी तरह से डिज़ाइन किया गया कौशल वृक्ष एक साफ डेटा संरचना से शुरू होता है। प्रत्येक कौशल वृक्ष एक श्रेणी से संबंधित होता है जैसे कि युद्ध, क्राफ्टिंग, ड्राइविंग, चिकित्सा, या आपराधिक। प्रत्येक वृक्ष के भीतर, व्यक्तिगत नोड कौशल का प्रतिनिधित्व करते हैं जिन्हें अनलॉक किया जा सकता है। प्रत्येक नोड का एक अद्वितीय पहचानकर्ता, एक प्रदर्शन नाम, एक विवरण, अधिकतम स्तर, प्रति स्तर पॉइंट लागत, पूर्वापेक्षित नोड्स जो पहले अनलॉक होने चाहिए, और वास्तविक गेमप्ले प्रभाव होता है जो यह लागू करता है। वृक्ष परिभाषाओं को एक साझा कॉन्फ़िगरेशन फ़ाइल में संग्रहित करें ताकि सर्वर और क्लाइंट दोनों उन्हें संदर्भित कर सकें। खिलाड़ी की प्रगति एक अलग डेटा संरचना है जो ट्रैक करती है कि उन्होंने कौन से नोड अनलॉक किए हैं और किस स्तर तक, साथ ही उनके उपलब्ध अप्रयुक्त कौशल अंक। वृक्ष परिभाषा को खिलाड़ी प्रगति से अलग रखना यह सुनिश्चित करता है कि आप बिना खिलाड़ी डेटा माइग्रेट किए वृक्ष को अपडेट या पुनर्संतुलित कर सकते हैं, जब तक कि नोड पहचानकर्ता स्थिर रहें।

-- shared/skill_trees.lua
SkillTrees = {
    mechanic = {
        label = 'Mechanic',
        icon  = 'fa-wrench',
        nodes = {
            quickFix = {
                label    = 'Quick Fix',
                desc     = 'Reduce vehicle repair time',
                maxLevel = 5,
                cost     = 1,
                prereqs  = {},
                effect   = {type = 'repair_speed', perLevel = 0.10},
            },
            engineExpert = {
                label    = 'Engine Expert',
                desc     = 'Repair engines to higher condition',
                maxLevel = 3,
                cost     = 2,
                prereqs  = {'quickFix'},
                effect   = {type = 'engine_quality', perLevel = 0.15},
            },
            bodyworkMaster = {
                label    = 'Bodywork Master',
                desc     = 'Repair body damage more effectively',
                maxLevel = 3,
                cost     = 2,
                prereqs  = {'quickFix'},
                effect   = {type = 'body_quality', perLevel = 0.15},
            },
            turboTuner = {
                label    = 'Turbo Tuner',
                desc     = 'Unlock performance tuning abilities',
                maxLevel = 1,
                cost     = 5,
                prereqs  = {'engineExpert'},
                effect   = {type = 'unlock_tuning', perLevel = 1},
            },
        },
    },
    criminal = {
        label = 'Street Smarts',
        icon  = 'fa-mask',
        nodes = {
            lockpicking = {
                label    = 'Lockpicking',
                desc     = 'Pick locks faster with fewer failures',
                maxLevel = 5,
                cost     = 1,
                prereqs  = {},
                effect   = {type = 'lockpick_speed', perLevel = 0.12},
            },
            silentStep = {
                label    = 'Silent Step',
                desc     = 'Reduce noise while crouching',
                maxLevel = 3,
                cost     = 2,
                prereqs  = {'lockpicking'},
                effect   = {type = 'noise_reduction', perLevel = 0.20},
            },
            safecracker = {
                label    = 'Safecracker',
                desc     = 'Attempt to crack safes',
                maxLevel = 1,
                cost     = 4,
                prereqs  = {'lockpicking'},
                effect   = {type = 'unlock_safecrack', perLevel = 1},
            },
        },
    },
}

अनुभव ट्रैकिंग और स्तर निर्धारण

अनुभव को गेमप्ले क्रियाओं के माध्यम से स्वाभाविक रूप से अर्जित किया जाना चाहिए, न कि बार-बार दोहराए जाने वाले ग्राइंडिंग लूप के माध्यम से। प्रत्येक कौशल वृक्ष श्रेणी के लिए अनुभव ट्रैक करें ताकि मैकेनिक कार्य करने पर मैकेनिक XP मिले, अपराध करने पर अपराधी XP मिले, आदि। प्रत्येक स्तर के लिए अनुभव सीमा निर्धारित करें, और जब खिलाड़ी सीमा पार करता है, तो उसे उस वृक्ष में खर्च करने के लिए एक या अधिक कौशल अंक मिलते हैं। शोषण लूप को रोकने के लिए समान क्रियाओं पर घटती वापसी का उपयोग करें। उदाहरण के लिए, 10 मिनट की विंडो में पहली वाहन मरम्मत 50 XP दे सकती है, लेकिन बाद की मरम्मतें क्रमशः 25, 10, फिर 5 XP देंगी। यह मनमाने दोहराव की तुलना में विविध गेमप्ले को पुरस्कृत करता है। सर्वर XP पुरस्कारों का एकमात्र प्राधिकारी होना चाहिए, कभी भी क्लाइंट पर अपने अनुभव मान रिपोर्ट करने पर भरोसा न करें। क्रिया की पुष्टि के बाद सर्वर-साइड लॉजिक से XP इवेंट ट्रिगर करें।

-- server/experience.lua
local XP_THRESHOLDS = {0, 100, 300, 600, 1000, 1500, 2200, 3000, 4000, 5500}

local recentActions = {}

function GrantXP(playerId, tree, amount, actionType)
    local identifier = GetPlayerIdentifier(playerId, 0)
    local now = os.time()

    -- Diminishing returns for repeated actions
    local key = identifier .. ':' .. tree .. ':' .. actionType
    if not recentActions[key] then
        recentActions[key] = {count = 0, resetAt = now + 600}
    end
    local ra = recentActions[key]
    if now > ra.resetAt then
        ra.count = 0
        ra.resetAt = now + 600
    end
    ra.count = ra.count + 1

    local multiplier = math.max(0.1, 1.0 - (ra.count - 1) * 0.25)
    local finalXP = math.floor(amount * multiplier)

    -- Update database
    MySQL.update([[
        UPDATE character_skills SET xp = xp + ?
        WHERE identifier = ? AND tree = ?
    ]], {finalXP, identifier, tree})

    -- Check for level up
    local data = MySQL.single.await([[
        SELECT xp, level, points FROM character_skills
        WHERE identifier = ? AND tree = ?
    ]], {identifier, tree})

    if data then
        local nextThreshold = XP_THRESHOLDS[data.level + 2]
        if nextThreshold and data.xp >= nextThreshold then
            local newLevel = data.level + 1
            MySQL.update([[
                UPDATE character_skills
                SET level = ?, points = points + 1
                WHERE identifier = ? AND tree = ?
            ]], {newLevel, identifier, tree})
            TriggerClientEvent('skills:levelUp', playerId, tree, newLevel)
        end
    end

    TriggerClientEvent('skills:xpGained', playerId, tree, finalXP)
end

खेल में कौशल प्रभाव लागू करना

स्किल प्रभाव वह जगह है जहाँ सिस्टम खिलाड़ियों के लिए मूर्त हो जाता है। प्रत्येक अनलॉक किया गया नोड एक विशिष्ट गेमप्ले मैकेनिक को मापनीय तरीके से संशोधित करता है। एक केंद्रीय प्रभाव रिज़ॉल्वर लागू करें जो एक खिलाड़ी पहचानकर्ता और प्रभाव प्रकार लेता है, फिर उस प्रभाव में योगदान देने वाले सभी अनलॉक किए गए नोड्स से कुल बोनस की गणना करता है। जब किसी अन्य स्क्रिप्ट को किसी खिलाड़ी की मरम्मत गति बोनस जाननी होती है, तो वह इस रिज़ॉल्वर को कॉल करता है और एक गुणक प्राप्त करता है। यह आपके स्किल सिस्टम को अन्य संसाधनों से अलग रखता है। अन्य स्क्रिप्ट्स को स्किल ट्री, नोड्स, या स्तरों के बारे में जानने की आवश्यकता नहीं होती; वे केवल प्रकार द्वारा बोनस मान पूछते हैं। सुरक्षित क्रैकिंग या टर्बो ट्यूनिंग जैसी अनलॉक करने योग्य क्षमताओं के लिए, रिज़ॉल्वर एक बूलियन लौटाता है जो बताता है कि खिलाड़ी के पास आवश्यक नोड है या नहीं। यह वास्तुकला मौजूदा स्क्रिप्ट्स में न्यूनतम कोड परिवर्तन के साथ स्किल्स को एकीकृत करना आसान बनाती है।

-- server/effects.lua
local playerSkillCache = {}

function GetSkillBonus(playerId, effectType)
    local identifier = GetPlayerIdentifier(playerId, 0)
    local cache = playerSkillCache[identifier]
    if not cache then return 0.0 end

    local total = 0.0
    for treeName, treeDef in pairs(SkillTrees) do
        for nodeId, nodeDef in pairs(treeDef.nodes) do
            if nodeDef.effect.type == effectType then
                local unlocked = cache[treeName] and cache[treeName][nodeId] or 0
                total = total + (nodeDef.effect.perLevel * unlocked)
            end
        end
    end
    return total
end

function HasSkillUnlock(playerId, effectType)
    return GetSkillBonus(playerId, effectType) > 0
end

-- Export for other resources
exports('GetSkillBonus', GetSkillBonus)
exports('HasSkillUnlock', HasSkillUnlock)

-- Example: another resource checking repair speed
-- local bonus = exports['skillsystem']:GetSkillBonus(source, 'repair_speed')
-- local repairTime = baseTime * (1.0 - bonus)

स्किल ट्री NUI बनाना

आपके स्किल ट्री की दृश्य प्रस्तुति सीधे तौर पर खिलाड़ियों को प्रगति प्रणाली के साथ जुड़ा हुआ महसूस कराती है। एक NUI पैनल बनाएं जो प्रत्येक ट्री को शाखाओं वाले ग्राफ के रूप में दिखाए जिसमें नोड्स प्रीरेक्विजिट्स दिखाने वाली लाइनों से जुड़े हों। अनलॉक किए गए नोड्स को रंग के साथ चमकना या पल्स करना चाहिए, लॉक लेकिन उपलब्ध नोड्स को थोड़ा धुंधला दिखाना चाहिए, और जिन नोड्स के प्रीरेक्विजिट्स पूरे नहीं हुए हैं उन्हें ग्रे किया जाना चाहिए। खिलाड़ी का वर्तमान स्तर, XP प्रगति पट्टी, और उपलब्ध स्किल पॉइंट्स शीर्ष पर प्रमुखता से दिखाएं। जब कोई खिलाड़ी नोड पर क्लिक करता है, तो उसका विवरण, अधिकतम स्तर में वर्तमान स्तर, अपग्रेड की लागत, और प्रति स्तर प्रभाव बोनस दिखाएं। पॉइंट्स खर्च करने से पहले पुष्टि चरण शामिल करें क्योंकि खिलाड़ी अपनी बिल्ड्स सावधानी से योजना बनाना चाहते हैं। स्थिति परिवर्तनों के लिए चिकनी CSS संक्रमणों का उपयोग करें ताकि नोड अपग्रेड करना एक छोटा फ्लैश या स्केल एनिमेशन के साथ पुरस्कृत अनुभव हो। एक रीसेट विकल्प जोड़ने पर विचार करें, या तो मुफ्त या इन-गेम मुद्रा लागत के साथ, ताकि खिलाड़ी अपने बिल्ड्स को फिर से सेट कर सकें यदि मेटा बदलता है या वे अलग खेल शैली आज़माना चाहते हैं।

संतुलन और एंटी-शोषण विचार

संतुलन किसी भी प्रगति प्रणाली का सबसे कठिन हिस्सा है। यदि बोनस बहुत मजबूत हैं, तो उच्च-स्तरीय खिलाड़ी अछूते हो जाते हैं और नए खिलाड़ी निराश महसूस करते हैं। यदि बोनस बहुत कमजोर हैं, तो कोई प्रगति की परवाह नहीं करता। अधिकतम स्तर पर प्रति कौशल 5 से 15 प्रतिशत बोनस के साथ रूढ़िवादी शुरुआत करें और खिलाड़ी प्रतिक्रिया की निगरानी करें। प्रत्येक कौशल बिंदु आवंटन के लिए सर्वर-साइड सत्यापन लागू करें। जब कोई खिलाड़ी नोड अपग्रेड करने का अनुरोध करता है, तो जांचें कि उनके पास पर्याप्त अंक हैं, सभी पूर्वापेक्षाएँ पूरी हैं, और नोड पहले से अधिकतम स्तर पर नहीं है। इन जांचों के लिए कभी भी क्लाइंट की स्थिति पर भरोसा न करें। सभी कौशल बिंदु लेनदेन के लिए लॉगिंग जोड़ें ताकि आप शोषण का पता लगा सकें और उसे वापस ले सकें। तेज़-तर्रार शोषण प्रयासों को रोकने के लिए कौशल आवंटन अनुरोधों की दर-सीमा निर्धारित करें। प्रतिस्पर्धी परिदृश्यों के लिए, कौशल कैप पर विचार करें जो सीमित करते हैं कि एक एकल चरित्र कितने पेड़ों में निवेश कर सकता है, जिससे सार्थक विकल्प मजबूर होते हैं बजाय इसके कि एक खिलाड़ी सब कुछ अधिकतम कर सके। इससे विविध चरित्र निर्माण होते हैं और पूरक कौशल सेट वाले खिलाड़ियों के बीच सहयोग को प्रोत्साहित करता है।

डेटाबेस स्कीमा और माइग्रेशन रणनीति

अपने डेटाबेस स्कीमा को इस तरह डिजाइन करें कि वर्तमान कौशल डेटा और भविष्य के ट्री विस्तार दोनों को सहजता से संभाल सके। खिलाड़ी कौशल आवंटन को प्रत्येक ट्री प्रति चरित्र के लिए व्यक्तिगत पंक्तियों के रूप में संग्रहित करें, न कि एक एकल JSON ब्लॉब के रूप में। इससे विशिष्ट कौशलों की क्वेरी करना कुशल होता है और विश्लेषण के लिए डेटाबेस-स्तरीय समेकन की अनुमति मिलती है। ट्री नाम, वर्तमान स्तर, कुल XP, उपलब्ध अंक, और उस ट्री के भीतर नोड आवंटन के लिए JSON कॉलम शामिल करें। जब आप भविष्य के अपडेट में नए कौशल ट्री या नोड जोड़ते हैं, तो मौजूदा खिलाड़ी डेटा सुरक्षित रहता है क्योंकि नए नोड केवल स्तर शून्य से शुरू होते हैं। यदि आप किसी नोड का नाम बदलते हैं या हटाते हैं, तो माइग्रेशन को इस तरह संभालें कि खर्च किए गए अंक खिलाड़ी के उपलब्ध पूल में वापस कर दिए जाएं। अपने ट्री परिभाषाओं का संस्करण बनाएं ताकि माइग्रेशन लॉजिक जान सके कि कौन से परिवर्तन लागू करने हैं। यह भविष्य-दृष्टि वाला दृष्टिकोण आपको महीनों के सर्वर संचालन के दौरान अपने कौशल प्रणाली के विकास के दौरान दर्दनाक डेटा माइग्रेशन से बचाता है।

-- SQL schema
CREATE TABLE IF NOT EXISTS character_skills (
    id          INT AUTO_INCREMENT PRIMARY KEY,
    identifier  VARCHAR(64) NOT NULL,
    char_slot   INT DEFAULT 1,
    tree        VARCHAR(32) NOT NULL,
    level       INT DEFAULT 0,
    xp          INT DEFAULT 0,
    points      INT DEFAULT 0,
    allocations JSON DEFAULT '{}',
    created_at  TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at  TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    UNIQUE KEY unique_char_tree (identifier, char_slot, tree),
    INDEX idx_identifier (identifier)
);

शुरू करने के लिए तैयार?

हमारी शॉप से स्क्रिप्ट लें, या सपोर्ट, अपडेट और आगे आने वाली चीज़ों की झलक के लिए Discord से जुड़ें।