العودة إلى المدونة
Tutorial8 دقيقة قراءة

قوائم الإيماءات والرسوم المتحركة في FiveM: سكريبتات جاهزة للـ RP

إضافة تعبيرات غنية وحركات إلى خادم FiveM الخاص بك. RPEmotes، Dpemotes، أنماط المشي المخصصة وأفضل قوائم التعبيرات للانغماس في لعب الأدوار.

Agency Scripts

المؤسس والمطور الرئيسي في Agency Scripts

فهم قواميس الرسوم المتحركة في GTA V

الرسوم المتحركة في GTA V منظمة في قواميس رسوم متحركة، وهي مجموعات من مقاطع الرسوم المتحركة ذات الصلة. يتم تحديد كل رسوم متحركة بواسطة سلسلتين: اسم القاموس واسم الرسوم المتحركة داخل ذلك القاموس. على سبيل المثال، القاموس amb@world_human_hang_out_street@female_hold_arm@idle_a يحتوي على حركات وقوف خاملة للـ peds الإناث. قبل أن تتمكن من تشغيل أي حركة، يجب عليك طلب القاموس باستخدام 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)

تنظيم الإيماءات في فئات

قائمة تعبيرات منظمة جيدًا تجمع الرسوم المتحركة في فئات بديهية يمكن للاعبين تصفحها بسرعة. تشمل الفئات الشائعة الرقصات, تحيات, ردود الفعل, وضعيات, رياضة, السيناريوهات (مثل الجلوس، الاتكاء، أو الاستلقاء)، حركات Prop (تشمل أشياء مثل السجائر أو الهواتف)، أنماط المشي، و تعبيرات الوجه. يجب أن يخزن كل إدخال تعبير قاموسي، اسم الحركة، العلامات، ما إذا كان يستخدم أداة، هاش نموذج الأداة إذا كان ذلك مناسباً، مؤشر العظم لتثبيت الأداة، وقيم الإزاحة والدوران لوضع الأداة بشكل صحيح. تخزين كل هذا في جدول تكوين منظم يجعل من السهل إضافة تعبيرات جديدة بدون تعديل منطق التشغيل.

-- 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},
    },
}

حركات Prop وربط الكائنات

حركات prop تتضمن إنشاء كائن لعبة وربطه بعظمة محددة على شخصية اللاعب. يحتوي 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 للمشي بالكعب العالي. مثل قوام الرسوم المتحركة، يجب طلب وتحميل clipsets قبل التطبيق. قدم خيار إعادة تعيين حتى يتمكن اللاعبون من العودة إلى المشي الافتراضي. خزّن نمط المشي المفضل للاعب في بيانات شخصيته ليظل محفوظًا عبر الجلسات، مع إعادة تطبيقه تلقائيًا عند تسجيل الدخول.

-- 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 للدعم والتحديثات ونظرة على ما هو قادم.