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