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

إدارة الكيانات في FiveM: التعامل مع الشخصيات، المركبات، الأجسام

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

Agency Scripts

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

فهم الكيانات في FiveM

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

ظهور الأجسام والدعائم

الأمر الأصلي الأساسي لإنشاء العناصر هو CreateObject (أو CreateObjectNoOffset ). قبل إنشاء أي كائن، يجب عليك طلب النموذج والانتظار حتى يتم تحميله. عدم طلب النموذج أولاً سيؤدي إلى كيان غير مرئي أو مفقود. قم دائمًا بإطلاق النموذج بعد إنشاء الكائن لتحرير الذاكرة.

-- client/entity_spawner.lua
local function SpawnProp(modelName, coords, rotation, isNetwork)
    local model = joaat(modelName)

    -- Request the model and wait for it to load
    RequestModel(model)
    local timeout = 0
    while not HasModelLoaded(model) do
        Wait(10)
        timeout = timeout + 10
        if timeout > 5000 then
            print(('[ERROR] Model %s failed to load after 5s'):format(modelName))
            return nil
        end
    end

    local obj = CreateObject(model, coords.x, coords.y, coords.z, isNetwork, true, false)

    if rotation then
        SetEntityRotation(obj, rotation.x, rotation.y, rotation.z, 2, true)
    end

    -- Freeze the object so it does not fall through the ground
    FreezeEntityPosition(obj, true)

    -- Release the model from memory
    SetModelAsNoLongerNeeded(model)

    return obj
end

-- Usage
local chair = SpawnProp('prop_chair_01a', vector3(200.0, -800.0, 31.0), nil, false)

ظهور المركبات

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

-- client/vehicle_spawner.lua
local function SpawnVehicle(modelName, coords, heading)
    local model = joaat(modelName)

    RequestModel(model)
    while not HasModelLoaded(model) do
        Wait(10)
    end

    local vehicle = CreateVehicle(model, coords.x, coords.y, coords.z, heading, true, false)

    -- Basic vehicle setup
    SetVehicleOnGroundProperly(vehicle)
    SetEntityAsMissionEntity(vehicle, true, true)
    SetVehicleHasBeenOwnedByPlayer(vehicle, true)
    SetVehicleNeedsToBeHotwired(vehicle, false)
    SetVehRadioStation(vehicle, 'OFF')

    -- Set fuel if using a fuel system
    Entity(vehicle).state:set('fuel', 100.0, true)

    SetModelAsNoLongerNeeded(model)

    return vehicle
end

تتبع وتنظيف الكيانات

أهم جانب في إدارة الكيانات هو تتبع كل كيان ينشئه سكربتك وتنظيفهم عند توقف المورد. بدون تنظيف مناسب، تستمر الكيانات في عالم اللعبة كيتامى يستهلكون الذاكرة ويعيقون الموارد. استخدم جدول تتبع لتخزين كل مقابض الكيانات وسجل onResourceStop المعالج الذي يزيلها.

-- client/entity_manager.lua
local ManagedEntities = {}

function RegisterEntity(entity, category)
    if not DoesEntityExist(entity) then return end

    ManagedEntities[entity] = {
        category = category or 'default',
        created = GetGameTimer(),
        model = GetEntityModel(entity),
    }
end

function UnregisterEntity(entity)
    if ManagedEntities[entity] then
        if DoesEntityExist(entity) then
            SetEntityAsMissionEntity(entity, false, true)
            DeleteEntity(entity)
        end
        ManagedEntities[entity] = nil
    end
end

function CleanupCategory(category)
    for entity, data in pairs(ManagedEntities) do
        if data.category == category then
            UnregisterEntity(entity)
        end
    end
end

function CleanupAllEntities()
    for entity, _ in pairs(ManagedEntities) do
        UnregisterEntity(entity)
    end
    ManagedEntities = {}
end

-- Critical: Clean up when resource stops
AddEventHandler('onResourceStop', function(resourceName)
    if GetCurrentResourceName() ~= resourceName then return end
    CleanupAllEntities()
end)

ملكية كيان الشبكة

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

-- server/ownership.lua
-- Request control of a networked entity
local function RequestEntityOwnership(src, netId)
    local entity = NetworkGetEntityFromNetworkId(netId)
    if not DoesEntityExist(entity) then return false end

    -- Set the requesting player as the owner
    SetEntityRoutingBucket(entity, GetPlayerRoutingBucket(src))

    return true
end

-- client/ownership.lua
-- Request network control of an entity
local function TakeEntityControl(entity, timeout)
    timeout = timeout or 2000
    local start = GetGameTimer()

    NetworkRequestControlOfEntity(entity)

    while not NetworkHasControlOfEntity(entity) do
        Wait(100)
        NetworkRequestControlOfEntity(entity)
        if GetGameTimer() - start > timeout then
            return false
        end
    end

    return true
end

-- Usage: Move an entity you need control of
local function MoveEntity(entity, targetCoords)
    if TakeEntityControl(entity) then
        SetEntityCoords(entity, targetCoords.x, targetCoords.y, targetCoords.z, false, false, false, false)
        return true
    end

    print('[WARN] Could not get control of entity')
    return false
end

إنشاء وإدارة الشخصيات غير القابلة للعب

الكيانات Ped (المشاة) تُستخدم للشخصيات غير القابلة للعب، بائعي المتاجر، معطِي المهام، والشخصيات المحيطة. تتطلب معالجة خاصة لأنها تحتوي على ذكاء اصطناعي يمكن أن يجعلها تتجول، تتفاعل مع البيئة، أو تهاجم اللاعبين إذا لم تُضبط بشكل صحيح. دائمًا جمد وعطّل الذكاء الاصطناعي للشخصيات الثابتة.

-- client/ped_spawner.lua
local function SpawnStaticPed(modelName, coords, heading, scenario)
    local model = joaat(modelName)

    RequestModel(model)
    while not HasModelLoaded(model) do
        Wait(10)
    end

    local ped = CreatePed(0, model, coords.x, coords.y, coords.z - 1.0, heading, false, true)

    -- Make the ped static and non-interactive with ambient AI
    SetEntityAsMissionEntity(ped, true, true)
    SetBlockingOfNonTemporaryEvents(ped, true)
    SetPedFleeAttributes(ped, 0, false)
    SetPedCombatAttributes(ped, 17, true)
    SetPedDiesWhenInjured(ped, false)
    SetEntityInvincible(ped, true)
    FreezeEntityPosition(ped, true)

    -- Play a scenario animation if specified
    if scenario then
        TaskStartScenarioInPlace(ped, scenario, 0, true)
    end

    SetModelAsNoLongerNeeded(model)
    RegisterEntity(ped, 'npc')

    return ped
end

-- Spawn a shop vendor
local vendor = SpawnStaticPed(
    's_m_y_ammucity_01',
    vector3(22.0, -1105.0, 29.8),
    160.0,
    'WORLD_HUMAN_STAND_IMPATIENT'
)

نصائح الأداء لإدارة الكيانات

  • تحديد الكيانات النشطة. تستهلك كل كيان ذاكرة ودورات عرض. استخدم أنظمة البث لتحميل الكيانات عندما يكون اللاعب قريبًا وتفريغها عندما يغادر المنطقة.
  • استخدم تجمعات الكائنات. للكيانات التي تظهر وتختفي بشكل متكرر مثل أغطية الرصاص أو تأثيرات الجسيمات، أعد استخدام مقابض الكيانات بدلاً من إنشاء وحذف جديدة باستمرار.
  • يفضل الكيانات على جانب العميل. إذا كان الكيان يحتاج فقط لأن يكون مرئيًا للاعب واحد (مثل عنصر واجهة مستخدم أو كائن معاينة)، فأنشئه كغير متصل بالشبكة لتقليل حمل الخادم.
  • استدعِ دائمًا SetModelAsNoLongerNeeded. النماذج المحملة تبقى في الذاكرة حتى يتم تحريرها صراحة. نسيان هذا الاستدعاء هو أحد أكثر الأسباب شيوعًا لتسرب الذاكرة في سكربتات FiveM.
  • تحقق من DoesEntityExist قبل العمليات. يمكن حذف الكيانات بواسطة محرك اللعبة أو سكربتات أخرى أو بسبب عدم التزامن. تحقق دائمًا من وجود الكيان قبل محاولة تعديله.
  • استخدم حقائب حالة الكيان للبيانات الوصفية. بدلاً من الحفاظ على جداول بحث منفصلة لبيانات الكيانات، استخدم Entity(entity).state لتخزين خصائص مخصصة مباشرة على الكيان.

جاهز للبدء؟

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