تسهل وتساعد مثيل DragDetector التفاعل مع الكائنات ثلاثية الأبعاد في اللعبة، مثل فتح الأبواب والأدراج، وتحريك جزء حوله، والإمساك بكرة بولينغ ورميها، وسحب وإطلاق مقلاع، والمزيد. تشمل الميزات الرئيسية:
ضع DragDetector تحت أي BasePart أو Model لـ جعلها قابلة للسحب عبر جميع المدخلات (الفأرة، اللمس، وحدة التحكم، وVR)، وكل ذلك دون سطر واحد من التعليمات البرمجية.
اختر من بين عدة أنماط سحب، وحدد كيف يستجيب الكائن للحركة، وطبق حدود المحاور أو الحركة إذا رغبت في ذلك.
يمكن للبرامج النصية الاستجابة لتلاعب الكائنات المسحوبة لدفع واجهة المستخدم أو اتخاذ قرارات منطقية، مثل ضبط مستوى الضوء في غرفة بناءً على مفتاح حائط منزلق.
يمكن للاعبين التلاعب بالأجزاء أو النماذج المثبتة وستبقى بالضبط حيث وضعتها عند الإفراج عنها.
تعمل 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 | سيكون الكائن المثبت افتراضيًا سلوك هندسي، حيث إنه غير متأثر بالقوى. | سيتم تحريك الكائن غير المثبت بواسطة قوى القيود التي تحاول إحضاره إلى الموضع/الاتجاه المطلوبين من خلال الحركة المقترحة. |
| Custom | لن يتحرك الكائن على الإطلاق، ولكن سيتم تحديث DragFrame ولا يزال يمكنك الاستجابة لتلاعب السحب كما تريد. | (نفس المثبت) |
حدود المحاور والحركة
بشكل افتراضي، لا توجد حدود للحركة ثلاثية الأبعاد بخلاف القيود الكامنة في 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(). تحت هذا الإعداد، فإن الفشل في تسجيل دالة أو إرجاع نتيجة غير صالحة سيمنع جميع اللاعبين من السحب. |
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)استجابة الفيزياء
بافتراض أن نمط الاستجابة لكاشف السحب مضبوط على فيزيائي ويتم تطبيقه على كائن غير مثبت، سيتم تحريك هذا الكائن بواسطة قوى القيود التي تحاول إحضاره إلى الموضع/الاتجاه المعطى من خلال الحركة المقترحة. يمكنك تخصيص الاستجابة الفيزيائية بشكل أكبر من خلال الخصائص التالية:
| الخاصية | الوصف | افتراضي |
|---|---|---|
| ApplyAtCenterOfMass | عندما تكون خاطئة، يتم تطبيق قوة السحب في النقطة التي ينقر عليها المستخدم. عندما تكون صحيحة، يتم تطبيق القوة في مركز الكتلة للكائن. | false |
| MaxForce | أقصى قوة مطبقة على الكائن للوصول إلى هدفه. | 10000000 |
| MaxTorque | أقصى عزم مطبق على الكائن للوصول إلى هدفه. | 10000 |
| Responsiveness | القيم الأعلى تجعل الكائن يصل إلى هدفه بشكل أسرع. | 10 |
إدخال المعدل
تسمح بعض أوضاع DragStyle للمستخدمين بالضغط على مفتاح/زر معدل للتلاعب بالكائن المسحوب بطرق مختلفة. بشكل افتراضي، يكون المعدل هو LeftControl على الكمبيوتر الشخصي، وButtonR1 على وحدة التحكم، أو ButtonL2 على VR. يمكنك تخصيص هذه المعدلات من خلال KeyboardModeSwitchKeyCode وGamepadModeSwitchKeyCode أو VRSwitchKeyCode الخاصة بمثيل كاشف السحب.
النسخ المتماثل
عندما تكون خاصية RunLocally خاطئة (افتراضي)، يقوم العميل بتفسير جميع المدخلات لإنتاج بيانات يرسلها إلى الخادم لأداء السحب. في هذا الوضع، يجب أن تكون جميع إشارات الأحداث المخصصة والدوال المسجلة في Scripts الخادم.
عندما تكون خاصية RunLocally صحيحة، لا يتم نسخ أي أحداث إلى الخادم. يجب أن تكون جميع إشارات الأحداث المخصصة والدوال المسجلة في LocalScripts العميل ويجب عليك استخدام الأحداث البعيدة لنشر التغييرات الضرورية إلى الخادم.
استجابات البرامج النصية للنقر والسحب
من خلال إشارات الأحداث وتغييرات الخصائص وScriptable ونمط السحب والدوال المخصصة، يمكن للبرامج النصية الاستجابة لتلاعب الكائنات المسحوبة لدفع واجهة المستخدم أو اتخاذ قرارات منطقية، مثل ضبط مستوى الضوء في غرفة بناءً على مفتاح حائط منزلق.
إشارات الأحداث
من خلال إشارات الأحداث التالية، يمكنك اكتشاف متى يبدأ المستخدم في سحب كائن، ويستمر، وينتهي من سحبه.
| الحدث | الوصف |
|---|---|
| DragStart | يتم إطلاقه عندما يبدأ المستخدم في سحب الكائن. |
| DragContinue | يتم إطلاقه عندما يستمر المستخدم في سحب الكائن بعد بدء DragStart. |
| DragEnd | يتم إطلاقه عندما يتوقف المستخدم عن سحب الكائن. |
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 مباشرة.
local dragDetector = script.Parent.DragDetector
dragDetector:GetPropertyChangedSignal("DragFrame"):Connect(function()
local currentDragTranslation = dragDetector.DragFrame.Position
print(currentDragTranslation)
end)نمط السحب المبرمج
إذا قمت بتعيين DragStyle لكاشف إلى Scriptable، يمكنك توفير دالتك الخاصة التي تأخذ Ray وتعيد CFrame في مساحة العالم. سيقوم الكاشف بتحريك الحركة بحيث يذهب الكائن المسحوب إلى ذلك الموقع/الاتجاه المخصص.
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)
-- استبعاد الكائن المسحوب من كشف الأشعة
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 قبل تطبيقها. على سبيل المثال، يمكنك الحفاظ على الحركة على شبكة عن طريق تقريب المواضع إلى مضاعفات زيادة الشبكة، أو محاكاة لعبة شطرنج بقواعد حركة قانونية لكل قطعة.
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 بحيث يمكن للاعبين سحب القطع للخارج ولكن ليس لأعلى أو لأسفل.
نماذج مثبتة مع أجزاء قابلة للتعديل
يمكنك بسهولة إنشاء ومشاركة نماذج تكون مثبتة في الأساس، ولكن تحتوي على جزء أو أكثر من الأجزاء/النماذج التي يمكن للاعبين سحبها. على سبيل المثال، يحتوي المكتب التالي على درجين يمكن للاعبين فتحهما لفحص ما بداخلهما.
كاشفات السحب والقيود
يمكنك دمج كاشفات السحب مع Constraints، على سبيل المثال دمية الماريونيت. في الإعداد التالي، تكون مقابض التحكم مثبتة، والأجزاء الجسم غير مثبتة، وتحتفظ القيود بالدمية معًا. يجعل تحريك المقابض باستخدام DragStyle TranslateViewPlane الماريونيت ترقص، ويمكن أيضًا تحريك أجزاء الجسم الفردية باستخدام كاشفات السحب، كل ذلك بينما يحتفظ النموذج بسلامته.
واجهات المستخدم ثلاثية الأبعاد
يمكن تحقيق واجهات المستخدم ثلاثية الأبعاد بسهولة من خلال كاشفات السحب، مثل ضبط سطوع SpotLight بناءً على مفتاح منزلق. يمكنك أيضًا اكتشاف المحاور X وZ بشكل فردي للتحكم في جانبين مختلفين من واجهة المستخدم ثلاثية الأبعاد، مثل Size وSpeed وColor لـ ParticleEmitter.
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)