تسهل وتحث مثيلات DragDetector على التفاعل مع العناصر ثلاثية الأبعاد في تجربة، مثل فتح الأبواب والأدراج، وتحريك جزء ما، وإمساك كرة بولينغ ورميها، وسحب وإطلاق سلاح المنجنيق، والعديد من الأمور الأخرى. تشمل الميزات الرئيسية:
ضع كاشف سحب DragDetector تحت أي BasePart أو Model لتجعله قابلاً للسحب عبر جميع المدخلات (الماوس، اللمس، وحدة التحكم، والواقع الافتراضي)، كل ذلك دون كتابة سطر واحد من الكود.
اختر من بين عدة أنماط سحب، عرّف كيف يستجيب العنصر للتحرك، وطبق حدود المحور أو الحركة إذا لزم الأمر.
يمكن للبرامج النصية الاستجابة للتManipulation للعناصر المجرورة لتوجيه واجهة المستخدم أو اتخاذ قرارات منطقية، مثل ضبط مستوى الإضاءة في غرفة بناءً على مفتاح حائط منزلق.
يمكن للاعبين التلاعب بالأجزاء أو النماذج المثبتة، وستبقى تماماً كما وضعتها عند الإفراج عنها.
تعمل DragDetectors في الاستوديو طالما أنك لا تستخدم أدوات تحديد أو تحريك أو تغيير الحجم أو دوران، مما يسهل اختبار وتعديل العناصر القابلة للسحب أثناء التحرير.
جعل العناصر قابلة للسحب
لجعل أي جزء أو نموذج قابلاً للسحب، ما عليك سوى إضافة DragDetector كأحد الأحفاد المباشرة.
من القائمة، قم بإدراج DragDetector.

بشكل افتراضي، سيكون العنصر الآن قابلاً للسحب في مستوى الأرض، لكن يمكنك تخصيص DragStyle، تحديد كيف يستجيب للحركة، وطبق حدود المحور أو الحركة إذا لزم الأمر.
تخصيص كواشف السحب
نمط السحب
تقوم DragDetectors بربط حركة المؤشر بخطوط ومستويات افتراضية لحساب الحركة ثلاثية الأبعاد المقترحة. من خلال خاصية DragStyle، يمكنك اختيار من بين خرائط مختلفة تناسب احتياجاتك. على سبيل المثال، يؤدي TranslatePlane إلى إنتاج ترجمة في مستوى افتراضي، بينما يؤدي RotateAxis إلى إنتاج دوران حول محور افتراضي.
| الإعداد | الوصف |
|---|---|
| TranslateLine | حركة 1D على طول Axis للكاشف، محور Y العادي. |
| TranslatePlane | حركة 2D في المستوى العمودي على Axis للكاشف، المستوى XZ العادي. |
| TranslatePlaneOrLine | حركة 2D في المستوى العمودي على Axis للكاشف وعندما يكون المعدل نشطًا، فإن حركة 1D على طول Axis للكاشف. |
| TranslateLineOrPlane | حركة 1D على طول Axis للكاشف وعندما يكون المعدل نشطًا، حركة 2D في المستوى العمودي على Axis للكاشف. |
| TranslateViewPlane | حركة 2D في المستوى العمودي على رؤية الكاميرا. في هذا الوضع، يتم تحديث المستوى باستمرار، حتى أثناء السحب، وسيوجه دائمًا نحو رؤية الكاميرا الحالية. |
| RotateAxis | دوران حول Axis للكاشف، محور Y العادي. |
| RotateTrackball | دوران كرة المسار، مخصص أكثر من خلال الخصائص TrackballRadialPullFactor و TrackballRollFactor. |
| BestForDevice | TranslatePlaneOrLine للماوس ووحدة التحكم؛ TranslatePlane للمس؛ 6DOF للواقع الافتراضي. |
| Scriptable | تحسب الحركة المطلوبة عبر وظيفة مخصصة مقدمة من خلال SetDragStyleFunction(). |
اتجاه السحب
بشكل افتراضي، ترتبط الحركة ثلاثية الأبعاد وDragStyle بالمكان العالمي. ومع ذلك، قد ترغب في تغيير ReferenceInstance أو Orientation أو Axis، على سبيل المثال عند بناء كواشف سحب في نماذج تحتوي على أجزاء قابلة للتعديل.
| الخاصية | الوصف | القيمة الافتراضية |
|---|---|---|
| ReferenceInstance | مثيل يوفر محور إطار المرجع لكاشف السحب. تعبر DragFrame بالنسبة لإطار المرجع، الذي يمكن استرجاعه من خلال GetReferenceFrame(). إذا كان إطار المرجع nil، ستكون الترجمة في اتجاه (أو في المستوى العمودي على) خاصية Axis في المكان العالمي. | nil |
| Orientation | يحدد دوران YXZ لمحاور الحركة بالنسبة لإطار المرجع (لا يغير اتجاه إطار المرجع نفسه). سيكون الترجمة الخطية والدوران المحوري على هذا المحور المُعاد توجيهه Y، والترجمة المستوية في المستوى XZ. تغييرات هذه القيمة تقوم تلقائيًا بتحديث Axis والعكس صحيح. | (0, 0, 0) |
| Axis | المحور الأساسي للحركة، معبرًا عنه بالنسبة لإطار المرجع. تغييرات هذه القيمة تقوم تلقائيًا بتحديث Orientation والعكس صحيح. | (0, 1, 0) |
الاستجابة للحركة
تحدد خاصية ResponseStyle كيفية استجابة العنصر للحركة المقترحة، اعتمادًا على ما إذا كان العنصر Anchored أم لا.
| الإعداد | سلوك العنصر المثبت | سلوك العنصر غير المثبت |
|---|---|---|
| Geometric | سواء داخل التجربة الجارية أو في وضع تحرير الاستوديو، سيتم تحديث الوضع/الاتجاه للعنصر المثبت ليعكس بدقة الحركة المقترحة. | بالنسبة للعنصر غير المثبت، يكون السلوك كما هو للعنصر المثبت. ومع ذلك، في تجربة جارية، سيبقى العنصر مثبتًا في بداية السحب ويتم استعادته إلى غير مثبت عند إطلاق السحب. |
| Physical | سيكون العنصر المثبت في وضع السلوك الافتراضي إلى Geometric، حيث إنه غير متأثر بالقوى. | سيتم تحريك العنصر غير المثبت بواسطة قوى القيود التي تحاول إعادته إلى الوضع والاتجاه المرغوبين المعطاة بواسطة الحركة المقترحة. |
| Custom | لن يتحرك العنصر على الإطلاق، لكن DragFrame سيتم تحديثه، ويمكنك الاستجابة لتManipulation للسحب كما تشاء. | (نفس الشيء كما المثبت) |
حدود المحور والحركة
بشكل افتراضي، لا توجد حدود للحركة ثلاثية الأبعاد بخلاف القيود الفطرية لـ DragStyle. إذا لزم الأمر، يمكنك تطبيق حدود دنيا وعليا لكلًا من الترجمة والدوران. لاحظ، مع ذلك، أن هذه ليست قيود؛ إنها تعيق فقط محاولات كاشف السحب لتوليد الحركة للبقاء ضمن الحدود.
| الخصائص | الوصف | القيمة الافتراضية |
|---|---|---|
| MinDragTranslation MaxDragTranslation | تحدد حدود السحب في كل بعد. إذا كانت MaxDragTranslation أكبر من MinDragTranslation، سيتم قيد الترجمة ضمن تلك النطاق. | (0, 0, 0) |
| MinDragAngle MaxDragAngle | تنطبق فقط إذا كانت DragStyle مضبوطة على RotateAxis. إذا كانت MaxDragAngle أكبر من MinDragAngle، سيتم قيد الدوران ضمن ذلك النطاق. | 0 |
أذونات السحب
يمكن تحديد إذن اللاعبين للتفاعل مع مثيل كاشف السحب معين من خلال خاصية PermissionPolicy. يتم تعيينها على Enum.DragDetectorPermissionPolicy.Everybody بشكل افتراضي، ويمكن أيضًا تغييرها لدعم عناصر التحكم المصرح بها بواسطة البرنامج كما هو موضح في نموذج الكود.
| الإعداد | الوصف |
|---|---|
| Nobody | لا يمكن للاعبين التفاعل مع DragDetector. |
| Everybody | يمكن لجميع اللاعبين التفاعل مع DragDetector. |
| Scriptable | ستحدد وظائف التفيذ صلاحيات السحب للاعبين من خلال وظيفة مسجلة عبر SetPermissionPolicyFunction(). مع هذا الإعداد، فإن عدم تسجيل وظيفة أو إرجاع نتيجة غير صالحة سيمنع جميع اللاعبين من السحب. |
DragDetector - أذونات السحب المبرمجة
local dragDetector = script.Parent.DragDetector
dragDetector.PermissionPolicy = Enum.DragDetectorPermissionPolicy.Scriptable
dragDetector:SetPermissionPolicyFunction(function(player, part)
if player and player:GetAttribute("IsInTurn") then
return true
elseif part and not part:GetAttribute("IsDraggable") then
return false
else
return true
end
end)
استجابة الفيزياء
عند ضبط نمط استجابة الكاشف على Physic ويتم تطبيقه على عنصر غير مثبت، سيتم تحريك ذلك العنصر بواسطة قوى القيود التي تحاول إعادته إلى الوضع/الاتجاه المعطى بواسطة الحركة المقترحة. يمكنك تخصيص الاستجابة الفيزيائية من خلال الخصائص التالية:
| الخاصية | الوصف | القيمة الافتراضية |
|---|---|---|
| ApplyAtCenterOfMass | عند الخطأ، يتم تطبيق قوة السحب عند النقطة التي ينقر عليها المستخدم. عند الصحة، يتم تطبيق القوة عند مركز ثقل العنصر. | false |
| MaxForce | أقصى قوة مطبقة على العنصر للوصول إلى هدفه. | 10000000 |
| MaxTorque | أقصى عزم مطبق على العنصر للوصول إلى هدفه. | 10000 |
| Responsiveness | القيم الأعلى تجعل العنصر يصل إلى هدفه بسرعة أكبر. | 10 |
إدخال المعدل
تسمح بعض أوضاع DragStyle للمستخدمين بالضغط باستمرار على مفتاح/زر معدل للتلاعب بالعنصر المجرور بطرق مختلفة. بشكل افتراضي، يكون المعدل هو LeftControl على الكمبيوتر، وButtonR1 على وحدة التحكم، أو ButtonL2 على الواقع الافتراضي. يمكنك تخصيص هذه المعدلات من خلال KeyboardModeSwitchKeyCode و GamepadModeSwitchKeyCode و VRSwitchKeyCode للكاشف.
النسخ
عندما تكون خاصية RunLocally خاطئة (افتراضي)، تفسر العميل جميع الإدخالات لإنتاج بيانات ترسل إلى الخادم لأداء السحب. في هذا الوضع، يجب أن تكون جميع إشارات الحدث المخصصة والوظائف المسجلة في خادم Scripts.
عندما تكون خاصية RunLocally صحيحة، لا يتم تكرار أي أحداث على الخادم. يجب أن تكون جميع إشارات الحدث المخصصة والوظائف المسجلة في عميل LocalScripts ويجب استخدام الفعاليات عن بُعد لنشر التغييرات الضرورية إلى الخادم.
استجابات البرنامج النصي للنقر والسحب
من خلال إشارات الأحداث وتغييرات الخصائص وScriptable ونمط السحب والوظائف المخصصة، يمكن للبرامج النصية الاستجابة لتManipulation العناصر المجرورة لتوجيه واجهة المستخدم أو اتخاذ قرارات منطقية، مثل ضبط مستوى الإضاءة في غرفة بناءً على مفتاح حائط منزلق.
إشارات الأحداث
من خلال إشارات الأحداث التالية، يمكنك اكتشاف متى يبدأ المستخدم ويواصل وينهي سحب عنصر.
| الحدث | الوصف |
|---|---|
| DragStart | يتم تشغيله عندما يبدأ المستخدم في سحب العنصر. |
| DragContinue | يتم تشغيله عندما يواصل المستخدم سحب العنصر بعد بدء DragStart. |
| DragEnd | يتم تشغيله عندما يتوقف المستخدم عن سحب العنصر. |
DragDetector - إشارات الأحداث
local dragDetector = script.Parent.DragDetector
local highlight = Instance.new("Highlight")
highlight.Enabled = false
highlight.Parent = script.Parent
dragDetector.DragStart:Connect(function()
highlight.Enabled = true
end)
dragDetector.DragContinue:Connect(function()
end)
dragDetector.DragEnd:Connect(function()
highlight.Enabled = false
end)
تغييرات إطار السحب
بالإضافة إلى إشارات الأحداث، يمكنك مراقبة التغييرات في DragFrame للكاشف مباشرة.
DragDetector - تغييرات DragFrame
local dragDetector = script.Parent.DragDetector
dragDetector:GetPropertyChangedSignal("DragFrame"):Connect(function()
local currentDragTranslation = dragDetector.DragFrame.Position
print(currentDragTranslation)
end)
نمط السحب المبرمج
إذا قمت بضبط DragStyle الخاصة بكاشف على Scriptable، يمكنك تقديم دالة خاصة تأخذ Ray وتعيد CFrame في مساحة العالم. ستقوم الكاشف بتحويل الحركة حتى تذهب العنصر المجرور إلى تلك الموقع/الاتجاه المخصص.
DragDetector - نمط السحب المبرمج
local Workspace = game:GetService("Workspace")
local dragDetector = script.Parent.DragDetector
dragDetector.DragStyle = Enum.DragDetectorDragStyle.Scriptable
local cachedHitPoint = Vector3.zero
local cachedHitNormal = Vector3.yAxis
local function followTheCursor(cursorRay)
-- استثناء العنصر المجرور من اكتشاف raycast
local raycastParams = RaycastParams.new()
raycastParams.FilterDescendantsInstances = {dragDetector.Parent}
raycastParams.FilterType = Enum.RaycastFilterType.Exclude
local hitPoint = Vector3.zero
local hitNormal = Vector3.yAxis
local raycastResult = Workspace:Raycast(cursorRay.Origin, cursorRay.Direction, raycastParams)
if raycastResult then
hitPoint = raycastResult.Position
hitNormal = raycastResult.Normal.Unit
else
hitPoint = cachedHitPoint
hitNormal = cachedHitNormal
end
cachedHitPoint = hitPoint
cachedHitNormal = hitNormal
local lookDir1 = hitNormal:Cross(Vector3.xAxis)
local lookDir2 = hitNormal:Cross(Vector3.yAxis)
local lookDir = if lookDir1.Magnitude > lookDir2.Magnitude then lookDir1.Unit else lookDir2.Unit
return CFrame.lookAt(hitPoint, hitPoint + lookDir, hitNormal)
end
dragDetector:SetDragStyleFunction(followTheCursor)
وظيفة القيود المخصصة
لا تحتوي كواشف السحب على قواعد حركة مدمجة حول الشبكات والتثبيت، لكن يمكنك تسجيل وظائف قيود مخصصة لتعديل DragFrame قبل تطبيقها. على سبيل المثال، يمكنك الحفاظ على الحركة على شبكة من خلال تقريب المواقع إلى مضاعفات قيم الشبكة، أو محاكاة لعبة شطرنج مع قواعد حركة قانونية لكل قطعة.
DragDetector - وظيفة قيود مخصصة
local dragDetector = script.Parent.DragDetector
local startPartPosition = nil
local SNAP_INCREMENT = 4
dragDetector.DragStart:Connect(function()
startPartPosition = script.Parent.Position
end)
dragDetector.DragEnd:Connect(function()
startPartPosition = nil
end)
local function snapToWorldGrid(proposedMotion)
if startPartPosition == nil then
return proposedMotion
end
local snapIncrement = SNAP_INCREMENT // 1
if snapIncrement < 1 then
return proposedMotion
end
local newWorldPosition = startPartPosition + proposedMotion.Position
local roundedX = ((newWorldPosition.X / snapIncrement + 0.5) // 1) * snapIncrement
local roundedY = ((newWorldPosition.Y / snapIncrement + 0.5) // 1) * snapIncrement
local roundedZ = ((newWorldPosition.Z / snapIncrement + 0.5) // 1) * snapIncrement
local newRoundedWorldPosition = Vector3.new(roundedX, roundedY, roundedZ)
return proposedMotion.Rotation + (newRoundedWorldPosition - startPartPosition)
end
local connection = dragDetector:AddConstraintFunction(2, snapToWorldGrid)
-- عندما ينطبق، قم بإزالة وظيفة القيود عن طريق استدعاء connection:Disconnect()
مثال على الاستخدام
عناصر مادية غير مثبتة
تنفيذ أساسي لكاشفات السحب هو لعبة توازن البرج حيث يجب على اللاعبين إزالة القطع بعناية ومحاولة الحفاظ على البرج في وضع مستقيم. في الهيكل البرجي التالي، يحتوي كل قطعة على طفل DragDetector مع نمط سحب افتراضي DragStyle هو TranslatePlane بحيث يمكن للاعبين pulling القطع للخارج ولكن ليس للأعلى أو للأسفل.
نماذج مثبتة مع أجزاء قابلة للتعديل
يمكنك بسهولة إنشاء ومشاركة نماذج تكون مثبتة بشكل أساسي، ولكن تحتوي على جزء أو أكثر من الأجزاء/النماذج التي يمكن للاعبين سحبها. على سبيل المثال، يحتوي المكتب التالي على درجين يمكن للاعبين فتحهما للتحقق مما بداخلها.
كواشف السحب والقيود
يمكنك دمج كواشف السحب مع Constraints، على سبيل المثال دمية ماريونيت. في الإعداد التالي، يتم تثبيت مقابض التحكم، وتكون أجزاء الجسم غير مثبتة، وتحتفظ القيود بالماريونيت معًا. تحريك المقابض باستخدام نمط السحب DragStyle TranslateViewPlane تجعل الماريونيت ترقص، ويمكن أيضًا تحريك أجزاء الجسم الفردية بكواشف السحب، كل ذلك بينما يحافظ النموذج على تكامله.
واجهات مستخدم ثلاثية الأبعاد
تعتبر واجهات المستخدم ثلاثية الأبعاد سهلة التحقيق من خلال كواشف السحب، مثل ضبط سطوع SpotLight بناءً على مفتاح منزلق. يمكنك أيضًا اكتشاف المحاور X و Z بشكل فردي للتحكم في جانبين مختلفين من واجهة مستخدم ثلاثية الأبعاد، مثل Size و Speed و Color لكاشف ParticleEmitter.
DragDetector - واجهة مستخدم ثلاثية الأبعاد
local model = script.Parent
local slider = model.SliderPart
local originPart = model.OriginPart
local emitter = script.Parent.EmitterPart.ParticleEmitter
local dragDetector = slider.DragDetector
dragDetector.ReferenceInstance = originPart
dragDetector.MinDragTranslation = Vector3.zero
dragDetector.MaxDragTranslation = Vector3.new(10, 0, 10)
local dragRangeX = dragDetector.MaxDragTranslation.X - dragDetector.MinDragTranslation.X
local dragRangeZ = dragDetector.MaxDragTranslation.Z - dragDetector.MinDragTranslation.Z
local MIN_PARTICLE_SIZE = 1
local MAX_PARTICLE_SIZE = 1.5
local MIN_PARTICLE_SPEED = 2.5
local MAX_PARTICLE_SPEED = 5
local COLOR1 = Color3.fromRGB(255, 150, 0)
local COLOR2 = Color3.fromRGB(255, 0, 50)
local function updateParticles(emitter)
local dragFactorX = (dragDetector.DragFrame.Position.X - dragDetector.MinDragTranslation.X) / dragRangeX
local dragFactorZ = (dragDetector.DragFrame.Position.Z - dragDetector.MinDragTranslation.Z) / dragRangeZ
-- ضبط حجم الجسيمات وسرعتها بناءً على عامل X للكاشف
emitter.Size = NumberSequence.new{
NumberSequenceKeypoint.new(0, 0),
NumberSequenceKeypoint.new(0.1, MIN_PARTICLE_SIZE + ((MAX_PARTICLE_SIZE - MIN_PARTICLE_SIZE) * dragFactorX)),
NumberSequenceKeypoint.new(1, 0)
}
local speed = MIN_PARTICLE_SPEED + ((MAX_PARTICLE_SPEED - MIN_PARTICLE_SPEED) * dragFactorX)
emitter.Speed = NumberRange.new(speed, speed * 1.2)
-- ضبط لون الجسيمات بناءً على عامل Z للكاشف
local color = COLOR2:Lerp(COLOR1, dragFactorZ)
emitter.Color = ColorSequence.new{
ColorSequenceKeypoint.new(0, color),
ColorSequenceKeypoint.new(1, color)
}
end
dragDetector:GetPropertyChangedSignal("DragFrame"):Connect(function()
updateParticles(emitter)
end)