تقنيات والتحويل

*This content is translated using AI (Beta) and may contain errors. To view this page in English, click here.

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

الخصائص المتدفقة

بمجرد تشغيل StreamingEnabled لكائن Workspace في الاستوديو، قم بتعيين خصائصه ذات الصلة إلى القيم الموصى بها التالية:

الخاصيةالتوصية
EnableSLIMAvatarsاستخدم Enabled لعرض أفاتار هيكل النموذج القياسي كنسخ خفيفة الوزن ومتحركة عند الضرورة. راجع الأفاتار الرقيقة لمزيد من المعلومات.
ModelStreamingBehaviorاستخدم Improved لتمكين البث الأكثر كفاءة لـ Models مع الن descendants BasePart.
StreamingIntegrityModeاستخدم PauseOutsideLoadedArea لموازنة نزاهة طريقة اللعب دون التوقف غير الضروري أو المتكرر.
StreamingMinRadiusاستخدم الافتراضي 64 لتعظيم مدى قدرة المحرك على تقليص اللعبة للأجهزة ذات الأداء المنخفض.
StreamingTargetRadiusاستخدم الافتراضي 1024 لتحقيق توازن جيد بين رؤية اللاعبين على الأجهزة الراقية وبصمة ذاكرة معقولة.
StreamOutBehaviorاستخدم Opportunistic للسماح للعميل بجمع المحتوى بشكل عدواني، مما يقلل بشكل كبير من استخدام الذاكرة ويساعد على منع الأعطال بسبب نفاد الذاكرة.

مستوى تفاصيل النموذج

يساعد Model.LevelOfDetail في ملء محتوى Model غير المتدفق بشبكات مركبية خفيفة الوزن أو شبكات مزيفة، مما يجعل العالم يبدو أكمل بصرياً. تعد شبكة SLIM (نموذج تفاعلي خفيف الوزن وقابل للتطوير) المركبية فعالة بشكل خاص، حيث غالباً ما لا يستطيع اللاعبون التمييز بين شبكة SLIM والأصل المتدفقة بالكامل.

للحصول على أفضل النتائج:

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

هيكل النموذج

بجانب تعيين مستوى تفاصيل النموذج، فإن هيكل وإعدادات Models لديك لها تأثير كبير على كيفية أداء البث. أثناء بناء اللعبة أو تحويلها:

  • استخدم نماذج ذرية للتجميع المنطقي — عندما يحتاج السكربت للوصول إلى جميع الأجزاء داخل نموذج، قم بتعيين ModelStreamingMode إلى Atomic. هذا يسمح لسكريبتات جانب العميل بالوصول بأمان إلى الكائنات داخل النموذج دون استخدام مفرط لـ WaitForChild() (على الرغم من أنه يجب أن تستخدم مثل هذه السكربتات أيضًا WaitForChild() للنموذج الذري العام).

  • قلل من النماذج الدائمة — يتم تحميل النماذج الدائمة بعد الانضمام ولا تتدفق أبدًا، مما يشغل ذاكرة بشكل دائم. قم بتعيين ModelStreamingMode لنموذج إلى Persistent فقط إذا كان يجب أن يبقى متاحًا وقابلًا للوصول إلى السكربتات في جميع الأوقات.

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

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

الأفاتار الرقيقة

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

  • بعرض نسخة SLIM عند بث نموذج أفاتار حقيقي.
  • بتبديل SLIM/الفعلي حسب الموارد المتاحة داخل نطاق البث، حيث يمكن أن تكون نماذج SLIM أقل تكلفة بكثير في العرض من النموذج بدقة كاملة.
  • يوجه رسوم SLIM من خلال أهمية المشهد وعرض النطاق الترددي.

لدى نظام مستوى تفاصيل الأفاتار تحسينات محددة للأفاتار. يصف ما سيتم أو لن يتم معالجته بواسطة SLIM ما يلي:

أنماط السكربت

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

الفهرس المباشر إلى الكائنات

يثير الفهرس إلى كائنات Workspace باستخدام المشغل . خطأ إذا كان أي كائن في المسار غير متدفق حاليًا. ينطبق نفس الشيء على FindFirstChild(), FindFirstChildWhichIsA(), و FindFirstChildOfClass() التي تعيد nil إذا لم يتم بث الطفل.

بحث الكائن

local house1 = workspace:FindFirstChild("House1") -- nil إذا لم يتم بث "House1"
local door = workspace.House1.Door -- معطل إذا لم يتم بث "House1" أو "Door"

نمط مشابه هو الوصول إلى Humanoid أو الكائنات الأخرى للشخصية مباشرة داخل اتصال Player.CharacterAdded. تحت البث، يتم ربط نموذج الشخصية بـ Workspace قبل أن يتم نسخ جميع كائناته، لذا ينفشل الفهرس المباشر.

كائنات الشخصية

local Players = game:GetService("Players")
local player = Players.LocalPlayer
player.CharacterAdded:Connect(function(character)
local humanoid = character.Humanoid
end)

إذا لم يستطع السكربت التقدم بدون كائن، انتظر ذلك باستخدام WaitForChild():

بحث الكائن

local house1 = workspace:WaitForChild("House1")
local door = house1:WaitForChild("Door")

الكائنات المرسلة عن بعد

إشارة RemoteEvent/RemoteFunction والكائن الذي تشير إليه تسافر بشكل مستقل، لذا يمكن أن تصل الإشارة إلى العميل قبل أن يكون الكائن موجودًا — أو قد لا يكون الكائن موجودًا على الإطلاق. تشمل سببان محتملان:

  • تحت البث، قد يكون هناك تأخير طفيف بين وقت إنشاء جزء/نموذج على الخادم ووقت نسخه إلى العملاء. بشكل فعال، قد لا يكون الجزء الذي يشير إليه RemoteEvent/RemoteFunction موجودًا بعد، حتى داخل منطقة متدفقة.

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

    سكريبت العميل

    local ReplicatedStorage = game:GetService("ReplicatedStorage")
    local remoteEvent = ReplicatedStorage:FindFirstChildOfClass("RemoteEvent")
    remoteEvent.OnClientEvent:Connect(function(data)
    local checkpoint = data.checkpoint -- أخطاء إذا لم يتم بث "checkpoint"
    local level = workspace.Levels[data.levelPath] -- أخطاء إذا لم يتم بث المسار
    end)

إذا كان السكربت على العميل يحتاج إلى الكائن للمتابعة، قم بإضافة WaitForChild() قبل استخدامه. لاحظ أن هذا يمكن أن ينتظر إلى الأبد إذا لم يتم بث الكائن أبدًا، لذا ضع في اعتبارك إضافة مهلة كمعامل ثانٍ لـ WaitForChild().

عدم التزامن على جانب العميل

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

على سبيل المثال، يمكن أن يؤدي إعادة تمييز كائن محليًا من ReplicatedStorage إلى Workspace إلى جعل ذلك الكائن مؤهلاً للبث. بالمثل، فإن نسخ كائن محليًا (Instance:Clone()) من ReplicatedStorage إلى Workspace ينشئ نسخة خاصة بالعميل لم تعد جزءًا من خط أنابيب النسخ على الخادم ولن تتلقى تحديثات الخصائص من الكائن الأصلي المملوك للخادم.

ينطبق نفس المفهوم عند استدعاء Instance:Destroy() على كائن مملوك للخادم. هذا يزيل الكائن محليًا ولكن لا يزال لدى الخادم، لذا سيتم بثه مرة أخرى مع حالته الأصلية عندما يكون مؤهلاً.

البث الاستباقي

عندما يمكن توقع وجهة اللاعب التالية، قم بإجراء مكالمات على جانب الخادم إلى Player:RequestStreamAroundAsync() لبث المناطق العابرة للتحميل المؤقت، أو استخدم Player:AddReplicationFocus() على أساس محدود للمناطق التي يجب أن تبقى محملة حتى يتم الإفراج عنها بشكل صريح.

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

سكريبت الخادم - نقل شخصية اللاعب

local ReplicatedStorage = game:GetService("ReplicatedStorage")
local teleportEvent = ReplicatedStorage:WaitForChild("TeleportEvent")
local function teleportPlayer(player, teleportTarget)
-- طلب البث حول الموقع المستهدف
player:RequestStreamAroundAsync(teleportTarget)
-- نقل الشخصية
local character = player.Character
if character and character.Parent then
local currentPivot = character:GetPivot()
character:PivotTo(currentPivot * CFrame.new(teleportTarget))
end
end
-- استدعاء دالة النقل عندما يشعل العميل الحدث عن بعد
teleportEvent.OnServerEvent:Connect(teleportPlayer)

قراءة خصائص الكائن

بمجرد أن يتم بث كائن خارج، لم تعد تحديثات خصائصه تتنسخ إلى ذلك العميل. تستمر قراءة الخصائص مثل BasePart.Position في النجاح ولكن تعيد آخر قيمة نسخ التي قد تكون قديمة بشكل عشوائي.


local Players = game:GetService("Players")
local player = Players.LocalPlayer
-- قد تكون الموضع قديمًا إذا كان "target" قد تم بثه
local dist = (target.Position - player.Character.HumanoidRootPart.Position).Magnitude

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

الانتظار على المسار الحرج

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

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

معالجة تغيير الإشارة

تطلق الإشارات مثل Instance.ChildAdded/Instance.ChildRemoved وإشارات CollectionService مثل GetInstanceAddedSignal() أو GetInstanceRemovedSignal() أيضًا عند البث/الخروج، indistinguishable from real spawns/removals. لا يمكن للسكريبتات المستلمة معرفة الفرق من الإشارة وحدها، لذا يجب تحديث المنطق الذي يفترض أن الإشارة تتوافق مع حدث "حقيقي".

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

تتبع السمات

local CollectionService = game:GetService("CollectionService")
local TAG_NAME = "عدو"
CollectionService:GetInstanceAddedSignal(TAG_NAME):Connect(function(enemy)
if not enemy:GetAttribute("Spawned") then
-- تعيين السمة "Spawned" على العدو للظهور الأول
enemy:SetAttribute("Spawned", true)
-- تشغيل تأثيرات صوتية/بصرية لهذا الظهور الأول
playSpawnEffects(enemy)
end
end)

التكرار على المجموعات

تكرارات المجموعات على جانب العميل مثل Instance:GetChildren() و Instance:GetDescendants() تعيد فقط مجموعة الكائنات المتدفقة. ينطبق هذا حتى عندما يتم نسخ الأصل نفسه دائمًا، مثل Folder مباشرة تحت Workspace الذي يتدفق أبناؤه داخل وخارج.


local Players = game:GetService("Players")
local player = Players.LocalPlayer
-- المجلد "Homes" يتم نسخه دائمًا ولكن أطفاله يتدفقون داخل وخارج
-- قد يفوت هذا الحل عددًا من المنازل التي لا تتدفق حاليًا
for _, home in workspace.Homes:GetChildren() do
if home.Settings.Owner.Value == player.Name then
return home
end
end

إذا كانت التعداد الشامل مطلوبًا، قم بالمسح على الخادم ومرر النتيجة إلى اللاعب من خلال RemoteEvent إذا لزم الأمر.

الاستعلامات المكانية

تستجيب الاستعلامات المكانية من جانب العميل مثل WorldRoot:Raycast(), WorldRoot:GetPartBoundsInBox()، و Model:GetBoundingBox() فقط للمحتوى المدفوع. Whether that's a problem depends on what the query is used for.

استخدم الخادم للاستعلامات التي يجب أن تعكس العالم الكامل، على سبيل المثال، راي كاست يتحقق مما إذا كان لدى اللاعب خط رؤية إلى هدف بعيد.

أنماط أخرى

قد تنطبق الأنماط التالية أيضًا ويجب التفكير فيها بعناية:

  • يتوقف Sound أو AudioPlayer المرتبط بكائن ثلاثي الأبعاد عندما يتدفق ذلك الكائن. لتسجيل الصوت الذي يجب أن يستمر بغض النظر عن البث، قم بتوصيل المرسل بنموذج دائم أو حاوية غير متدفقة.

  • في-game UI الكائنات مثل BillboardGui أو SurfaceGui وكذلك التأثيرات البصرية مثل Beams أو Highlights التي يتدفق adornee أو attachment بسيطة يتوقف ببساطة عن العرض. قد يكون هذا هو السلوك المقصود، ولكن يجب عليك التحقق منه.

  • لا تعمل أحداث BasePart.Touched، ProximityPrompts، DragDetectors، و ClickDetectors للاعبين الذين لا يمتلك عميلهم الجزء/النموذج المرتبط المتدفق. إذا كان التفاعل يجب أن يكون ممكنًا من أي مدى، يجب أن يكون النموذج دائمًا أو يحتاج التفاعل إلى آلية مختلفة.

  • بالنسبة لـ PathfindingService والتوجيه على جانب العميل، يرى سلك التفتيش فقط الهندسة المتدفقة على العميل وقد يقوم بتوجيهه عبر عوائق موجودة على الخادم. راجع هنا لاستراتيجيات.

ظروف الاختبار الواقعية

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

  • اختبر مع تعيين Workspace.StreamingTargetRadius إلى أدنى قيمة لها (64). تظهر بعض أخطاء البث فقط عندما تكون المنطقة المتدفقة صغيرة.

  • مر عبر الأنماط الكاملة للعب، انقل بين مناطق بعيدة، وزر المناطق بعد مغادرتها. هذه هي المواقف التي تمارس تدفق الدخول والخروج أكثر.

  • استخدم تراكب تصحيح البث لمراقبة الإعدادات النشطة للبث، والمناطق المحملة حاليًا، ودولة البث في وقت التشغيل.

  • راقب نافذة الإخراج و وحدة التحكم للمطورين بحثًا عن الأخطاء، حيث تنتج العديد من أنماط السكربت أخطاء بدلاً من التصرف الصامت غير الصحيح. انتبه بشكل خاص للأخطاء من الشكل attempt to index nil with ... التي تشير غالبًا إلى وجود مكالمة مفقودة لـ WaitForChild().

  • قم بتجهيز وتنشيط Tools، أطلق الأسلحة، واستجب لتفاعلات اللعبة المختلفة.

©2026 شركة Roblox Corporation. تُعد منصّة Roblox، وشعار Roblox وشعار "توسيع حدود المخيلة"، من ضمن علاماتنا التجارية المسجّلة وغير المسجّلة في الولايات المتحدة وبلدان أخرى.