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

نظام الوشم والحلاقة في FiveM: سكريبتات التخصيص

إضافة صالونات الوشم والحلاقة إلى مدينتك في FiveM. زوايا الكاميرا، واجهات المستخدم، تدفق المواعيد وأفضل سكربتات التخصيص للعب الأدوار.

Agency Scripts

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

فهم مكونات مظهر الشخصيات

بناء نظام الوشم وصالون الحلاقة في FiveM يتطلب فهماً جيداً لكيفية تعامل GTA V مع مظهر الشخصية (ped). نظام المظهر مقسم إلى عدة فئات مكونات: طبقات الرأس لميزات مثل العيوب، شعر الوجه، الحواجب، والمكياج، مكونات قابلة للرسم للملابس وتسريحات الشعر، وزخارف الوشم التي توضع فوق نموذج الشخصية. يستخدم كل نوع مكون وظائف أصلية مختلفة ويتبع نظام فهرسة خاص به. تستخدم طبقات الرأس الفهارس 0-12 للتحكم في ميزات مثل العيوب (0)، شعر الوجه (1)، الحواجب (2)، الشيخوخة (3)، المكياج (4)، احمرار الخدود (5)، وأحمر الشفاه (8). يتم تعيين تسريحات الشعر من خلال نظام المكونات القابلة للرسم عند الفهرس 2 لشعر الرأس. تستخدم الوشوم نظام زخرفة منفصل تماماً مع أسماء مجموعات وقيم تجزئة. قبل بناء واجهة المستخدم، تحتاج إلى رسم خريطة لجميع الخيارات المتاحة لكل مكون، بما في ذلك عدد التنويعات المتوفرة لنموذج الشخصية الحالي، لأن الشخصيات الذكور والإناث لديها مجموعات مختلفة من الأنماط المتاحة. استخدم GetNumHeadOverlayValues(overlayIndex) للاستعلام عن عدد الخيارات الموجودة لكل نوع تراكب أثناء التشغيل.

نظام الكاميرا لمعاينة الشخصية

تجربة حلاقة وشم رائعة تعتمد على قدرة اللاعب على رؤية ما يختاره في الوقت الحقيقي. ابني نظام كاميرا مخصص يركز على الجزء المناسب من الجسم أثناء تصفح اللاعب للخيارات. عند تعديل الشعر، قرب الكاميرا إلى الرأس. عند اختيار الوشوم للجذع، اسحب الكاميرا للخلف لإظهار الجزء العلوي من الجسم. عند العمل على وشوم الساق، ضع الكاميرا لتأطير الجزء السفلي من الجسم. استخدم natives الكاميرا المبرمجة لإنشاء انتقالات سلسة بين وجهات النظر أثناء تبديل اللاعب بين الفئات. هذا نظام إدارة كاميرا يتعامل مع مستويات تكبير متعددة ومناطق تركيز الجسم:

local customCam = nil
local camActive = false

local CameraPresets = {
    head = {offset = vector3(0.0, 0.65, 0.65), fov = 35.0},
    face = {offset = vector3(0.0, 0.45, 0.62), fov = 25.0},
    torso = {offset = vector3(0.0, 1.2, 0.2), fov = 45.0},
    legs = {offset = vector3(0.0, 1.5, -0.5), fov = 50.0},
    full = {offset = vector3(0.0, 2.5, 0.2), fov = 50.0},
}

function SetupPreviewCamera(preset)
    local ped = PlayerPedId()
    local pedCoords = GetEntityCoords(ped)
    local pedHeading = GetEntityHeading(ped)
    local headingRad = math.rad(pedHeading)

    local cam = CameraPresets[preset] or CameraPresets.full
    local camX = pedCoords.x + cam.offset.y * math.sin(-headingRad)
    local camY = pedCoords.y + cam.offset.y * math.cos(-headingRad)
    local camZ = pedCoords.z + cam.offset.z

    if customCam then
        local newCam = CreateCam('DEFAULT_SCRIPTED_CAMERA', true)
        SetCamCoord(newCam, camX, camY, camZ)
        PointCamAtCoord(newCam, pedCoords.x, pedCoords.y, pedCoords.z + cam.offset.z)
        SetCamFov(newCam, cam.fov)
        SetCamActiveWithInterp(newCam, customCam, 800, 1, 1)

        Wait(800)
        DestroyCam(customCam, false)
        customCam = newCam
    else
        customCam = CreateCam('DEFAULT_SCRIPTED_CAMERA', true)
        SetCamCoord(customCam, camX, camY, camZ)
        PointCamAtCoord(customCam, pedCoords.x, pedCoords.y, pedCoords.z + cam.offset.z)
        SetCamFov(customCam, cam.fov)
        SetCamActive(customCam, true)
        RenderScriptCams(true, true, 500, true, false)
    end
    camActive = true
end

function DestroyPreviewCamera()
    if customCam then
        RenderScriptCams(false, true, 500, true, false)
        DestroyCam(customCam, false)
        customCam = nil
        camActive = false
    end
end

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

تخصيص الشعر واللحية

يستخدم تخصيص الشعر واللحية مكون drawable وأنظمة تراكب الرأس على التوالي. يتم تعيين تسريحات الشعر باستخدام SetPedComponentVariation باستخدام مؤشر المكون 2 (الشعر) والمؤشر 1 (التنوع في النسيج/اللون). يتم تطبيق لون الشعر بشكل منفصل باستخدام SetPedHairColor الذي يأخذ مؤشر لون أساسي ومؤشر لون تمييز. أنماط اللحية تستخدم نظام تراكب الرأس عند المؤشر 1 مع قيمة شفافية تتحكم في مدى كثافة أو رقة الشعر الوجهي. ابني NUI لعرض الصور المصغرة أو الأسماء لكل نمط متاح، مع تحديث الشخصية في الوقت الحقيقي أثناء تنقل اللاعب بين الخيارات. هذه هي منطق Lua لتطبيق تغييرات الشعر واللحية مع المعاينة الحية:

function ApplyHairStyle(ped, styleIndex, textureIndex, primaryColor, highlightColor)
    SetPedComponentVariation(ped, 2, styleIndex, textureIndex or 0, 0)
    SetPedHairColor(ped, primaryColor, highlightColor)
end

function ApplyBeardStyle(ped, styleIndex, opacity, color)
    SetPedHeadOverlay(ped, 1, styleIndex, opacity or 1.0)
    SetPedHeadOverlayColor(ped, 1, 1, color, color)
end

function ApplyEyebrows(ped, styleIndex, opacity, color)
    SetPedHeadOverlay(ped, 2, styleIndex, opacity or 1.0)
    SetPedHeadOverlayColor(ped, 2, 1, color, color)
end

function GetAvailableHairStyles(ped)
    local styles = {}
    local numStyles = GetNumberOfPedDrawableVariations(ped, 2)

    for i = 0, numStyles - 1 do
        local numTextures = GetNumberOfPedTextureVariations(ped, 2, i)
        table.insert(styles, {
            index = i,
            textures = numTextures,
            label = 'Style ' .. (i + 1)
        })
    end

    return styles
end

function GetAvailableBeardStyles(ped)
    local styles = {}
    local numStyles = GetNumHeadOverlayValues(1)

    for i = 0, numStyles - 1 do
        table.insert(styles, {
            index = i,
            label = 'Beard ' .. (i + 1)
        })
    end

    return styles
end

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

نظام تراكب الوشوم

الوشوم في GTA V تستخدم نظام الزينة، الذي يضع طبقات من التراكب على نموذج الشخصية باستخدام أزواج تجميع وتراكب الهاش. كل وشم معرف باسم تجميع (مثل "mpairraces_overlays") واسم تراكب (مثل "MP_Airraces_Tattoo_000_M") معًا لتحديد أصل الوشم المحدد. طبق الوشوم باستخدام AddPedDecorationFromHashes وأزل جميع الوشوم باستخدام ClearPedDecorations. الجزء الصعب هو أنه لا يوجد أمر أصلي لإزالة وشم واحد فقط، لذا عندما يريد اللاعب إزالة وشم واحد، يجب مسح كل الزينة وإعادة تطبيق كل شيء ما عدا الوشم الذي تمت إزالته. هذا يعني أنه يجب عليك الحفاظ على قائمة كاملة بالوشوم الحالية للاعب في نموذج بياناتك. إليك كيفية إدارة مجموعة الوشوم وتطبيقها:

-- Tattoo database structure
local TattooZones = {
    head = {label = 'Head', camera = 'face'},
    torso = {label = 'Torso', camera = 'torso'},
    left_arm = {label = 'Left Arm', camera = 'torso'},
    right_arm = {label = 'Right Arm', camera = 'torso'},
    left_leg = {label = 'Left Leg', camera = 'legs'},
    right_leg = {label = 'Right Leg', camera = 'legs'},
    back = {label = 'Back', camera = 'torso'}
}

-- Available tattoos per zone (abbreviated, real list has hundreds)
Config.Tattoos = {
    torso = {
        {collection = 'mpairraces_overlays', overlay = 'MP_Airraces_Tattoo_000_M',
         label = 'Wing Design', price = 500, zone = 'torso'},
        {collection = 'mpbiker_overlays', overlay = 'MP_MP_Biker_Tat_000_M',
         label = 'Flame Skull', price = 750, zone = 'torso'},
    },
    left_arm = {
        {collection = 'mpchristmas2_overlays', overlay = 'MP_Xmas2_M_Tat_000',
         label = 'Sleeve Art', price = 600, zone = 'left_arm'},
    }
}

function ApplyAllTattoos(ped, tattooList)
    ClearPedDecorations(ped)

    for _, tattoo in ipairs(tattooList) do
        local collectionHash = GetHashKey(tattoo.collection)
        local overlayHash = GetHashKey(tattoo.overlay)
        AddPedDecorationFromHashes(ped, collectionHash, overlayHash)
    end
end

function PreviewTattoo(ped, tattoo, currentTattoos)
    local previewList = {}
    for _, t in ipairs(currentTattoos) do
        table.insert(previewList, t)
    end
    table.insert(previewList, tattoo)
    ApplyAllTattoos(ped, previewList)
end

function RemoveTattoo(ped, tattooIndex, currentTattoos)
    local newList = {}
    for i, t in ipairs(currentTattoos) do
        if i ~= tattooIndex then
            table.insert(newList, t)
        end
    end
    ApplyAllTattoos(ped, newList)
    return newList
end

يحتوي كتالوج الوشوم في GTA V على مئات الإدخالات موزعة عبر مجموعات DLC متعددة. نظمها حسب منطقة الجسم في تكوينك حتى يتمكن NUI من التصفية حسب المنطقة. لكل وشم، تحقق مما إذا كان يجب استخدام النسخة الذكرية أو الأنثوية بناءً على نموذج شخصية اللاعب، حيث أن معظم الوشوم لها أسماء أصول منفصلة تنتهي بـ _M أو _F. اعرض معاينة عندما يحوم اللاعب فوق وشم في الكتالوج، ولا تفرض الرسوم إلا عند تأكيد الاختيار.

نظام المكياج والطلاء الوجهي

يستخدم المكياج نظام التراكب على الرأس عبر عدة مؤشرات: أحمر الخدود (5)، أحمر الشفاه (8)، وتراكبات شعر الصدر أو لون البشرة لتأثيرات إضافية. لكل نوع مكياج خيارات متعددة للأنماط وزر شفاف يتحكم في الشدة. أنشئ قسم المكياج في واجهة الحلاق التي تعرض كل فئة مع عينات بصرية تظهر الألوان المتاحة. يستخدم نظام الألوان للمكياج SetPedHeadOverlayColor مع معامل نوع اللون: النوع 0 للألوان الافتراضية، النوع 1 لألوان الشعر (تستخدم لللحى والحواجب)، والنوع 2 لألوان المكياج. يستخدم المكياج تحديدًا نوع اللون 2 الذي يوفر لوحة ألوان تجميلية تشمل درجات لون البشرة المختلفة، الأحمر، الوردي، والألوان الدراماتيكية. أضف منزلق شفافية لكل مكون مكياج ليتمكن اللاعبون من تحقيق مظهر طبيعي خفيف أو أنماط جريئة ودراماتيكية. خزّن جميع إعدادات المكياج جنبًا إلى جنب مع بيانات مظهر اللاعب لتستمر بين الجلسات، وقدم تفاعل مرآة في منزل اللاعب حيث يمكنهم تعديل المكياج دون زيارة المتجر.

نظام الدفع والتسعير

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

local sessionChanges = {}
local originalAppearance = nil

function StartCustomizationSession()
    local ped = PlayerPedId()
    originalAppearance = CaptureFullAppearance(ped)
    sessionChanges = {}
end

function TrackChange(category, item, price)
    -- Remove previous change in same category if exists
    for i = #sessionChanges, 1, -1 do
        if sessionChanges[i].category == category then
            table.remove(sessionChanges, i)
        end
    end

    table.insert(sessionChanges, {
        category = category,
        item = item,
        price = price
    })

    UpdateCartUI()
end

function UpdateCartUI()
    local total = 0
    local items = {}

    for _, change in ipairs(sessionChanges) do
        total = total + change.price
        table.insert(items, {
            label = change.category .. ': ' .. change.item,
            price = change.price
        })
    end

    SendNUIMessage({
        action = 'updateCart',
        items = items,
        total = total
    })
end

function ConfirmAndPay()
    local total = 0
    for _, change in ipairs(sessionChanges) do
        total = total + change.price
    end

    TriggerServerEvent('appearance:server:pay', total, sessionChanges)
end

function CancelSession()
    if originalAppearance then
        RestoreFullAppearance(PlayerPedId(), originalAppearance)
    end
    sessionChanges = {}
    DestroyPreviewCamera()
end

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

حفظ الملابس وتكامل خزانة الملابس

قم بتوسيع نظام الحلاق والوشم بميزة حفظ الملابس التي تتيح للاعبين تخزين تكوينات المظهر الكاملة كإعدادات مسبقة مسماة. كل إعداد مسبق يلتقط الحالة الكاملة لمظهر اللاعب بما في ذلك تسريحة الشعر ولونه، شعر الوجه، المكياج، وجميع الوشوم النشطة. يمكن للاعبين بعد ذلك التبديل بين المظاهر المحفوظة في خزانة ملابسهم المنزلية أو بزيارة المتجر مرة أخرى. خزّن الملابس في قاعدة البيانات مرتبطة بمعرف المواطن الخاص باللاعب مع كتلة JSON تحتوي على جميع بيانات المظهر. حدد عدد الملابس المحفوظة لكل لاعب أو قدم فتحات إضافية كميزة مميزة. يجب أن تعرض واجهة التفاعل مع خزانة الملابس في منزل اللاعب لوحة NUI بسيطة تسرد الملابس المحفوظة مع وظيفة معاينة تطبق كل مظهر مؤقتًا حتى يتمكن اللاعب من رؤيته قبل الالتزام. هذا يربط نظام المظهر الشخصي بنظام الإسكان، مما يجعل كلا الميزتين أكثر قيمة معًا مما هما عليه منفصلتين.

مواقع المتجر وتكامل الحلاق NPC

حدد مواقع متاجر متعددة عبر الخريطة، كل منها قد يقدم خدمات مختلفة ومضاعفات أسعار مختلفة. قد يقدم حلاق بميزانية منخفضة في الجانب الجنوبي قصات أساسية بأسعار منخفضة، بينما صالون فاخر في Vinewood يفرض أسعارًا مرتفعة لكنه يقدم أنماط وألوان حصرية. استخدم نظام الهدف أو علامات القرب لتشغيل قائمة المتجر عندما يقترب اللاعب من كرسي الحلاقة أو محطة الوشم. اجلس اللاعب على الكرسي باستخدام TaskStartScenarioInPlace مع سيناريو الجلوس لصالونات الحلاقة، أو جعلهم يقفون في مكان محدد لجلسات الوشم. استدعِ NPC الحلاقين أو فناني الوشم في كل موقع باستخدام نماذج شخصية مناسبة وعيّن لهم تحركات خمول تجعلهم يبدون كأنهم يعملون. عند تفاعل اللاعب مع المتجر، يمكن لـ NPC تشغيل تحريك عمل بالقرب من اللاعب لمحاكاة الخدمة المقدمة، مضيفًا غمرًا بصريًا للتجربة. بعد الجلسة، يعود NPC إلى وضع الخمول. تتبع إيرادات كل متجر في قاعدة البيانات لدعم لعب المتاجر المملوكة للاعبين حيث يشتري رواد الأعمال ويديرون صالونات الحلاقة أو محلات الوشم الخاصة بهم، يحددون الأسعار ويوظفون موظفي NPC أو حتى لاعبين آخرين كموظفين.

جاهز للبدء؟

احصل على السكربتات من متجرنا، أو انضم إلى Discord للدعم والتحديثات ونظرة على ما هو قادم.