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

FiveM इमोट और एनीमेशन मेनू: RP-रेडी स्क्रिप्ट्स

अपने FiveM सर्वर में समृद्ध इमोट्स और एनीमेशन जोड़ें। RPEmotes, Dpemotes, कस्टम वॉक स्टाइल और रोलप्ले में डूबने के लिए सर्वश्रेष्ठ इमोट मेनू।

Agency Scripts

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

GTA V एनीमेशन डिक्शनरीज को समझना

GTA V में एनिमेशन एनिमेशन डिक्शनरीज़ में व्यवस्थित होते हैं, जो संबंधित एनिमेशन क्लिप्स का संग्रह होती हैं। प्रत्येक एनिमेशन को दो स्ट्रिंग्स द्वारा पहचाना जाता है: डिक्शनरी का नाम और उस डिक्शनरी के भीतर एनिमेशन का नाम। उदाहरण के लिए, डिक्शनरी amb@world_human_hang_out_street@female_hold_arm@idle_a महिला पेड्स के लिए निष्क्रिय खड़े होने वाली एनिमेशन शामिल हैं। किसी भी एनिमेशन को चलाने से पहले, आपको शब्दकोश का अनुरोध करना होगा RequestAnimDict और लोडिंग समाप्त होने तक प्रतीक्षा करें HasAnimDictLoaded. शब्दकोश को पहले अनुरोध न करना नए डेवलपर्स द्वारा की जाने वाली सबसे आम गलतियों में से एक है, जिससे एनीमेशन चुपचाप विफल हो जाते हैं। GTA V हजारों एनीमेशन शब्दकोशों के साथ आता है जो लड़ाई की मुद्राओं से लेकर योग मुद्राओं तक सब कुछ कवर करते हैं, और उपयोगी खोजने में आधा चुनौती एक व्यापक इमोट मेनू बनाने की होती है।

एनिमेशन चलाना और प्रबंधित करना

एनिमेशन चलाने के लिए मुख्य फ़ंक्शन है TaskPlayAnim", जो प्लेयर पेड, डिक्शनरी, एनीमेशन नाम, ब्लेंड-इन स्पीड, ब्लेंड-आउट स्पीड, अवधि, फ्लैग्स, और प्लेबैक रेट स्वीकार करता है। एनीमेशन फ्लैग्स महत्वपूर्ण व्यवहार को नियंत्रित करते हैं: फ्लैग 1 एनीमेशन को लूप करता है, फ्लैग 2 अंतिम फ्रेम पर एनीमेशन को रोकता है, फ्लैग 16 केवल ऊपरी शरीर को चलाता है जिससे प्लेयर चल सकता है, और फ्लैग 32 एनीमेशन के दौरान रोटेशन की अनुमति देता है। बिटवाइज OR के साथ फ्लैग्स को मिलाकर आप सूक्ष्म व्यवहार बना सकते हैं। एक बैठने वाला इमोट फ्लैग 1 के लिए लूपिंग और फ्लैग 2 के लिए अंतिम फ्रेम को रोकने की जरूरत होती है यदि बीच में रोका जाए। एक हाथ हिलाने वाला इमोट फ्लैग 49 के साथ सबसे अच्छा काम करता है जो केवल ऊपरी शरीर, लूपिंग और रोटेशन को मिलाता है, जिससे प्लेयर चलते हुए हाथ हिला सकता है।

-- Core animation helper functions
local function LoadAnimDict(dict)
    if HasAnimDictLoaded(dict) then return true end
    RequestAnimDict(dict)
    local timeout = GetGameTimer() + 5000
    while not HasAnimDictLoaded(dict) do
        Wait(10)
        if GetGameTimer() > timeout then
            return false
        end
    end
    return true
end

local function PlayEmote(dict, anim, flags, duration)
    local ped = PlayerPedId()
    if IsPedInAnyVehicle(ped, false) then return end
    if not LoadAnimDict(dict) then return end

    flags = flags or 1      -- default: loop
    duration = duration or -1 -- -1 = indefinite

    TaskPlayAnim(ped, dict, anim, 2.0, 2.0, duration, flags, 0, false, false, false)
end

local function StopCurrentEmote()
    local ped = PlayerPedId()
    ClearPedTasks(ped)
    -- Also remove any attached props
    ClearPedSecondaryTask(ped)
end

-- Export for other resources
exports('PlayEmote', PlayEmote)
exports('StopEmote', StopCurrentEmote)

इमोट्स को श्रेणियों में व्यवस्थित करना

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

-- Emote configuration (config.lua)
Config = {}

Config.Emotes = {
    dances = {
        {label = 'Dance 1',    dict = 'anim@amb@nightclub@dancers@club_dance_bar_1',    anim = 'high_center',   flags = 1},
        {label = 'Dance 2',    dict = 'anim@amb@nightclub@mini@dance@dance_solo@female@var_a@',  anim = 'high_center',   flags = 1},
        {label = 'Dance 3',    dict = 'anim@amb@nightclub@dancers@crowddance_facedj@',  anim = 'hi_dance_facedj_09_v2_female^1', flags = 1},
        {label = 'Flamenco',   dict = 'special_ped@jessie@idle_latina',                 anim = 'idle_latina',   flags = 1},
    },
    greetings = {
        {label = 'Wave',       dict = 'friends@frj@ig_1',    anim = 'wave_A',         flags = 49, duration = 3000},
        {label = 'Salute',     dict = 'anim@mp_player_intuppersalute',  anim = 'idle_a',  flags = 49, duration = 3000},
        {label = 'Handshake',  dict = 'mp_ped_interaction',  anim = 'handshake_guy_a', flags = 49, duration = 4000},
        {label = 'Fist Bump',  dict = 'mp_ped_interaction',  anim = 'fist_bump',      flags = 49, duration = 3000},
    },
    props = {
        {label = 'Cigarette',  dict = 'amb@world_human_smoking@male@male_a@idle_a', anim = 'idle_c', flags = 49,
            prop = 'prop_cs_ciggy_01', bone = 28422, pos = {0.0, 0.0, 0.0}, rot = {0.0, 0.0, 0.0}},
        {label = 'Coffee',     dict = 'amb@world_human_drinking@coffee@male@idle_a', anim = 'idle_c', flags = 49,
            prop = 'p_amb_coffeecup_01', bone = 28422, pos = {0.0, 0.0, 0.0}, rot = {0.0, 0.0, 0.0}},
        {label = 'Clipboard',  dict = 'amb@world_human_clipboard@male@idle_a', anim = 'idle_c', flags = 49,
            prop = 'p_amb_clipboard_01', bone = 36029, pos = {0.16, 0.08, 0.03}, rot = {-100.0, 0.0, 0.0}},
    },
    scenarios = {
        {label = 'Sit Chair',  scenario = 'PROP_HUMAN_SEAT_CHAIR', isScenario = true},
        {label = 'Lean Wall',  scenario = 'WORLD_HUMAN_LEANING',   isScenario = true},
        {label = 'Guard',      scenario = 'WORLD_HUMAN_GUARD_STAND', isScenario = true},
        {label = 'Jog',        scenario = 'WORLD_HUMAN_JOG_STANDING', isScenario = true},
    },
}

प्रॉप इमोट्स और ऑब्जेक्ट अटैचमेंट

प्रॉप इमोट्स में एक गेम ऑब्जेक्ट स्पॉन करना और उसे खिलाड़ी के पेड की एक विशिष्ट हड्डी से जोड़ना शामिल है। GTA V में एक कंकाल हड्डी प्रणाली है जहाँ प्रत्येक हड्डी की एक अद्वितीय इंडेक्स होती है। सामान्य अटैचमेंट हड्डियों में शामिल हैं 57005 दाएं हाथ के लिए, 18905 बाएं हाथ के लिए, 28422 दाएं उंगली क्षेत्र के लिए (सिगरेट के लिए आदर्श), और 31086 सिर के लिए। प्रॉप को स्पॉन करने के बाद CreateObjectइसे संलग्न करें उपयोग करके AttachEntityToEntity जिसके लिए प्रॉप इकाई, पेड इकाई, बोन इंडेक्स, स्थिति ऑफसेट, रोटेशन ऑफसेट, और टकराव व भौतिकी व्यवहार के लिए कई बूलियन फ्लैग्स की आवश्यकता होती है। स्थिति और रोटेशन ऑफसेट सही पाने के लिए परीक्षण और त्रुटि की जरूरत होती है, और विभिन्न पेड मॉडल में थोड़े समायोजन की आवश्यकता हो सकती है। जब इमोट रद्द किया जाता है तो स्पॉन की गई इकाई हैंडल को ट्रैक करके और उसे हटाकर प्रॉप्स को हमेशा साफ करें।

-- Prop emote system
local currentProp = nil

local function PlayPropEmote(emoteData)
    local ped = PlayerPedId()
    if IsPedInAnyVehicle(ped, false) then return end

    -- Clean up any existing prop
    if currentProp and DoesEntityExist(currentProp) then
        DeleteEntity(currentProp)
        currentProp = nil
    end

    if not LoadAnimDict(emoteData.dict) then return end

    -- Spawn and attach prop
    local model = GetHashKey(emoteData.prop)
    RequestModel(model)
    while not HasModelLoaded(model) do Wait(10) end

    local pos = GetEntityCoords(ped)
    currentProp = CreateObject(model, pos.x, pos.y, pos.z, true, true, false)
    SetModelAsNoLongerNeeded(model)

    local boneIndex = GetPedBoneIndex(ped, emoteData.bone)
    AttachEntityToEntity(
        currentProp, ped, boneIndex,
        emoteData.pos[1], emoteData.pos[2], emoteData.pos[3],
        emoteData.rot[1], emoteData.rot[2], emoteData.rot[3],
        true, true, false, true, 1, true
    )

    -- Play animation
    TaskPlayAnim(ped, emoteData.dict, emoteData.anim,
        2.0, 2.0, -1, emoteData.flags or 49, 0, false, false, false)
end

-- Cleanup on emote cancel
local function CleanupProps()
    if currentProp and DoesEntityExist(currentProp) then
        DeleteEntity(currentProp)
        currentProp = nil
    end
end

चलने की शैलियाँ और मूवमेंट क्लिप्स

चलने की शैलियाँ खिलाड़ी पात्र के चलने के तरीके को बदलती हैं और लागू की जाती हैं SetPedMovementClipsetसामान्य एनिमेशन के विपरीत जो क्रियाओं को ओवरराइड करते हैं, मूवमेंट क्लिपसेट सभी लोकमोशन स्टेट्स में बने रहते हैं जिनमें चलना, दौड़ना, और स्प्रिंटिंग शामिल हैं। GTA V में दर्जनों बिल्ट-इन मूवमेंट क्लिपसेट शामिल हैं जैसे MOVE_M@DRUNK@MODERATEDRUNK एक नशेड़ी लड़खड़ाहट के लिए, MOVE_M@GANGSTER@NG एक गैंगस्टर स्वैगर के लिए, MOVE_M@CONFIDENT एक आत्मविश्वासी चाल के लिए, और MOVE_F@HEELS@C उच्च हील्स में चलने के लिए। एनिमेशन डिक्शनरी की तरह, क्लिपसेट्स को अनुरोध और लोड किया जाना चाहिए इससे पहले कि वे लागू हों। एक रीसेट विकल्प प्रदान करें ताकि खिलाड़ी डिफ़ॉल्ट वॉक पर वापस आ सकें। खिलाड़ी की पसंदीदा चलने की शैली को उनके चरित्र डेटा में स्टोर करें ताकि यह सत्रों के बीच बनी रहे, और जब वे लॉग इन करें तो इसे स्वचालित रूप से पुनः लागू करें।

-- Walking style system
local currentWalkStyle = nil

Config.WalkStyles = {
    {label = 'Default',     clipset = nil},
    {label = 'Drunk',       clipset = 'MOVE_M@DRUNK@MODERATEDRUNK'},
    {label = 'Gangster',    clipset = 'MOVE_M@GANGSTER@NG'},
    {label = 'Confident',   clipset = 'MOVE_M@CONFIDENT'},
    {label = 'Tough',       clipset = 'MOVE_M@TOUGH_GUY@'},
    {label = 'Femme',       clipset = 'MOVE_F@FEMME@'},
    {label = 'Heels',       clipset = 'MOVE_F@HEELS@C'},
    {label = 'Injured',     clipset = 'move_m@injured'},
    {label = 'Hobo',        clipset = 'MOVE_M@HOBO@A'},
    {label = 'Brave',       clipset = 'MOVE_M@BRAVE@A'},
}

local function SetWalkStyle(clipset)
    local ped = PlayerPedId()
    ResetPedMovementClipset(ped, 0.2)

    if not clipset then
        currentWalkStyle = nil
        return
    end

    RequestClipSet(clipset)
    local timeout = GetGameTimer() + 3000
    while not HasClipSetLoaded(clipset) do
        Wait(10)
        if GetGameTimer() > timeout then return end
    end

    SetPedMovementClipset(ped, clipset, 0.2)
    currentWalkStyle = clipset
end

exports('SetWalkStyle', SetWalkStyle)
exports('GetWalkStyle', function() return currentWalkStyle end)

चेहरे के भाव

चेहरे के भाव रोलप्ले इंटरैक्शन में सूक्ष्म भावनात्मक गहराई जोड़ते हैं। GTA V चेहरे की एनीमेशन का समर्थन करता है SetFacialIdleAnimOverride", जो पेड के आरामदायक चेहरे के भाव को बदलता है। उपलब्ध भावों में शामिल हैं mood_normal_1, mood_happy_1, mood_angry_1, mood_injured_1, mood_stressed_1, mood_smug_1, और mood_sulk_1 अन्य के बीच। ये अभिव्यक्तियाँ तब तक बनी रहती हैं जब तक कि साफ़ न कर दी जाएं ClearFacialIdleAnimOverride. चेहरे के भाव को शरीर की एनीमेशन और चलने की शैली के साथ मिलाकर खिलाड़ी अपने चरित्र के प्रस्तुतीकरण पर पूर्ण नियंत्रण प्राप्त करते हैं। दीवार के सहारे झुका हुआ एक चरित्र जो घमंडी भाव और आत्मविश्वासी चाल के साथ चलता है, एक पूरी तरह अलग कहानी बताता है बनाम एक घायल लंगड़ाहट और तनावग्रस्त चेहरे वाला। ये छोटे विवरण रोलप्ले को बुनियादी इंटरैक्शन से समृद्ध चरित्र प्रस्तुति तक ले जाते हैं।

-- Facial expression system
local currentExpression = nil

Config.Expressions = {
    {label = 'Normal',    expression = 'mood_Normal_1'},
    {label = 'Happy',     expression = 'mood_Happy_1'},
    {label = 'Angry',     expression = 'mood_Angry_1'},
    {label = 'Injured',   expression = 'mood_Injured_1'},
    {label = 'Stressed',  expression = 'mood_Stressed_1'},
    {label = 'Smug',      expression = 'mood_smug_1'},
    {label = 'Sulking',   expression = 'mood_sulk_1'},
    {label = 'Sleeping',  expression = 'pose_injured_1'},
    {label = 'Scared',    expression = 'mood_scared_1'},
}

local function SetExpression(expression)
    local ped = PlayerPedId()
    ClearFacialIdleAnimOverride(ped)
    if expression then
        SetFacialIdleAnimOverride(ped, expression, nil)
        currentExpression = expression
    else
        currentExpression = nil
    end
end

exports('SetExpression', SetExpression)

-- Shared emotes: sync with nearby players
RegisterNetEvent('emote:syncShared', function(senderId, dict, anim)
    local senderPed = GetPlayerPed(GetPlayerFromServerId(senderId))
    if not DoesEntityExist(senderPed) then return end
    local myPed = PlayerPedId()
    local dist = #(GetEntityCoords(myPed) - GetEntityCoords(senderPed))
    if dist > 3.0 then return end
    if not LoadAnimDict(dict) then return end
    TaskPlayAnim(myPed, dict, anim, 2.0, 2.0, -1, 1, 0, false, false, false)
end)

इमोट मेनू इंटरफ़ेस बनाना

इमोट मेनू को या तो NUI-आधारित रेडियल मेनू के रूप में या ox_lib या qb-menu जैसी लाइब्रेरी का उपयोग करके इन-गेम मेनू के रूप में लागू किया जा सकता है। रेडियल मेनू इमोट्स के लिए स्वाभाविक लगता है क्योंकि खिलाड़ी जल्दी से श्रेणियों के माध्यम से नेविगेट कर सकते हैं और बिना लंबे टेक्स्ट सूचियों को पढ़े एक इमोट चुन सकते हैं। मेनू को किसी कुंजी से बाइंड करें जैसे F4 या एक कॉन्फ़िगर योग्य कीबाइंड, और कमांड फॉलबैक प्रदान करें जैसे /emote [name] प्रत्यक्ष पहुँच के लिए। मेनू में इमोट का नाम, एक वैकल्पिक पूर्वावलोकन थंबनेल दिखाना चाहिए, और यह संकेत देना चाहिए कि कौन से इमोट्स प्रॉप्स का उपयोग करते हैं। एक पसंदीदा प्रणाली शामिल करें जो खिलाड़ियों को उनके सबसे अधिक उपयोग किए जाने वाले इमोट्स को त्वरित पहुँच रिंग में पिन करने देती है, जिससे उन्हें सामान्य क्रिया करने के लिए हर बार श्रेणियों को ब्राउज़ करने से बचाया जा सके। पसंदीदा को खिलाड़ी के KVP स्टोरेज में SetResourceKvp ताकि वे सर्वर रिस्टार्ट के बाद भी बिना डेटाबेस लिखने की जरूरत के जीवित रहें। एक अच्छी तरह से निर्मित इमोट मेनू किसी भी रोलप्ले सर्वर पर सबसे अधिक उपयोग की जाने वाली विशेषताओं में से एक बन जाता है, इसलिए इंटरफ़ेस में समय निवेश करना खिलाड़ी संतुष्टि में लाभ देता है।

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

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