Phiên bản DragDetector tạo điều kiện và khuyến khích sự tương tác với các đối tượng 3D trong một trải nghiệm, chẳng hạn như mở cửa và ngăn kéo, kéo một phần quanh, cầm và ném bóng bowling, kéo lại và bắn ná, và nhiều hơn nữa. Những tính năng chính bao gồm:
Đặt DragDetector dưới bất kỳ BasePart hoặc Model nào để làm cho nó có thể kéo được thông qua tất cả các đầu vào (chuột, chạm, gamepad và VR), tất cả mà không cần một dòng mã nào.
Chọn từ nhiều kiểu kéo, xác định cách mà đối tượng phản ứng với chuyển động, và tùy chọn áp dụng giới hạn trục hoặc chuyển động.
Các script có thể phản ứng với việc thao tác các đối tượng bị kéo để điều khiển giao diện người dùng hoặc đưa ra các quyết định logic, chẳng hạn như điều chỉnh mức ánh sáng trong một phòng dựa trên công tắc tường trượt.
Người chơi có thể thao tác các phần hoặc mô hình được neo và chúng sẽ ở lại đúng vị trí mà bạn đặt chúng khi thả ra.
DragDetectors hoạt động trong Studio miễn là bạn không sử dụng các công cụ Chọn, Di chuyển, Tỉ lệ, hoặc Xoay, giúp dễ dàng thử nghiệm và điều chỉnh các đối tượng có thể kéo trong quá trình chỉnh sửa.
Làm cho các đối tượng có thể kéo được
Để làm cho bất kỳ phần hoặc mô hình nào có thể kéo được, chỉ cần thêm một DragDetector làm con cháu trực tiếp.
Từ menu, chèn một DragDetector.

Theo mặc định, đối tượng sẽ trở nên có thể kéo được trên mặt đất, nhưng bạn có thể tùy chỉnh DragStyle, xác định cách nó phản ứng với chuyển động, và tùy chọn áp dụng giới hạn trục hoặc chuyển động.
Tùy chỉnh bộ phát hiện kéo
Kiểu kéo
DragDetectors ánh xạ chuyển động của con trỏ đến các đường và mặt phẳng ảo để tính toán chuyển động 3D được đề xuất. Thông qua thuộc tính DragStyle, bạn có thể chọn từ nhiều ánh xạ khác nhau để phù hợp với nhu cầu của bạn. Ví dụ, TranslatePlane dẫn đến dịch chuyển trong một mặt phẳng ảo, trong khi RotateAxis dẫn đến xoay quanh một trục ảo.
| Cài đặt | Mô tả |
|---|---|
| TranslateLine | Chuyển động 1D theo Axis của bộ phát hiện, mặc định là trục Y thế giới. |
| TranslatePlane | Chuyển động 2D trong mặt phẳng vuông góc với Axis của bộ phát hiện, mặc định là mặt phẳng XZ thế giới. |
| TranslatePlaneOrLine | Chuyển động 2D trong mặt phẳng vuông góc với Axis của bộ phát hiện và, khi thao tác hoạt động, chuyển động 1D theo Axis của bộ phát hiện. |
| TranslateLineOrPlane | Chuyển động 1D theo Axis của bộ phát hiện và, khi thao tác hoạt động, chuyển động 2D trong mặt phẳng vuông góc với Axis của bộ phát hiện. |
| TranslateViewPlane | Chuyển động 2D trong mặt phẳng vuông góc với tầm nhìn của camera. Trong chế độ này, mặt phẳng được cập nhật liên tục, ngay cả khi đang kéo, và sẽ luôn hướng về tầm nhìn hiện tại của camera. |
| RotateAxis | Xoay quanh Axis của bộ phát hiện, mặc định là trục Y thế giới. |
| RotateTrackball | Xoay theo trackball, điều chỉnh thêm thông qua các thuộc tính TrackballRadialPullFactor và TrackballRollFactor. |
| BestForDevice | TranslatePlaneOrLine cho chuột và gamepad; TranslatePlane cho cảm ứng; 6DOF cho VR. |
| Scriptable | Tính toán chuyển động mong muốn thông qua một chức năng tùy chỉnh được cung cấp thông qua SetDragStyleFunction(). |
Hướng kéo
Theo mặc định, chuyển động 3D và DragStyle tương ứng ánh xạ đến không gian thế giới. Tuy nhiên, bạn có thể muốn thay đổi ReferenceInstance, Orientation, hoặc Axis, chẳng hạn khi xây dựng các bộ phát hiện kéo vào các mô hình có các phần điều chỉnh.
| Thuộc tính | Mô tả | Mặc định |
|---|---|---|
| ReferenceInstance | Một thể hiện có trục cung cấp khung tham chiếu cho bộ phát hiện kéo. DragFrame được diễn đạt tương đối với khung tham chiếu này có thể được lấy thông qua GetReferenceFrame(). Nếu khung tham chiếu là nil, quá trình dịch chuyển sẽ theo hướng (hoặc trong mặt phẳng vuông góc với) thuộc tính Axis trong không gian thế giới. | nil |
| Orientation | Xác định độ YXZ xoay của các trục chuyển động tương đối với khung tham chiếu (không thay đổi hướng của chính khung tham chiếu). Dịch chuyển tuyến tính và xoay trục sẽ diễn ra trên trục Y được tái định hướng này, và dịch chuyển mặt phẳng trong mặt phẳng XZ. Thay đổi giá trị này sẽ tự động cập nhật Axis và ngược lại. | (0, 0, 0) |
| Axis | Trục chính của chuyển động, được diễn đạt tương đối với khung tham chiếu. Thay đổi giá trị này sẽ tự động cập nhật Orientation và ngược lại. | (0, 1, 0) |
Phản ứng với chuyển động
Thuộc tính ResponseStyle xác định cách một đối tượng phản ứng với chuyển động được đề xuất, tùy thuộc vào việc đối tượng có Anchored hay không.
| Cài đặt | Hành vi neo | Hành vi không được neo |
|---|---|---|
| Geometric | Ngay cả trong trải nghiệm đang chạy và trong chế độ chỉnh sửa Studio, vị trí/hướng của một đối tượng neo sẽ được cập nhật để phản ánh chính xác chuyển động được đề xuất. | Đối với một đối tượng không được neo, hành vi giống như một đối tượng được neo. Tuy nhiên, trong trải nghiệm đang chạy, đối tượng sẽ được neo tại đầu kéo và trở lại không được neo khi thả kéo. |
| Physical | Một đối tượng được neo sẽ mặc định là hành vi Hình học, vì nó không bị ảnh hưởng bởi các lực. | Một đối tượng không được neo sẽ bị di chuyển bởi các lực ràng buộc cố gắng đưa nó đến vị trí và/hoặc hướng mong muốn được đưa ra bởi chuyển động đề xuất. |
| Custom | Đối tượng sẽ không di chuyển chút nào, nhưng DragFrame vẫn sẽ được cập nhật và bạn có thể phản ứng với việc thao tác kéo theo cách bạn muốn. | (giống như khi được neo) |
Giới hạn trục & chuyển động
Theo mặc định, không có giới hạn nào đối với chuyển động 3D ngoài những hạn chế vốn có của DragStyle. Nếu cần thiết, bạn có thể áp dụng giới hạn tối thiểu và tối đa cho cả dịch chuyển và xoay. Tuy nhiên, hãy lưu ý rằng đây không phải là các ràng buộc; chúng chỉ cản trở nỗ lực của bộ phát hiện kéo tạo ra chuyển động để duy trì trong các giới hạn.
| Các thuộc tính | Mô tả | Mặc định |
|---|---|---|
| Giới hạn dịch chuyển kéo trong mỗi chiều. Nếu MaxDragTranslation lớn hơn MinDragTranslation, dịch chuyển sẽ bị kẹp trong khoảng đó. | (0, 0, 0) | |
| Chỉ liên quan nếu DragStyle được đặt thành RotateAxis. Nếu MaxDragAngle lớn hơn MinDragAngle, quay sẽ bị kẹp trong khoảng đó. | 0 |
Quyền kéo
Quyền của người chơi để tương tác với một thể instance của bộ phát detector kéo có thể được chỉ định bằng thuộc tính PermissionPolicy. Giới hạn này được đặt thành Enum.DragDetectorPermissionPolicy.Everybody theo mặc định, và cũng có thể được thay đổi để hỗ trợ các điều khiển quyền được kịch bản như được thể hiện trong mẫu mã.
| Cài đặt | Mô tả |
|---|---|
| Nobody | Không có người chơi nào có thể tương tác với DragDetector. |
| Everybody | Tất cả người chơi có thể tương tác với DragDetector. |
| Scriptable | Quyền kéo của người chơi sẽ được xác định bởi một hàm được đăng ký thông qua SetPermissionPolicyFunction(). Dưới cài đặt này, việc không đăng ký một hàm hoặc trả về một kết quả không hợp lệ sẽ ngăn tất cả người chơi kéo. |
DragDetector - Quyền kéo có kịch bản
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)
Phản hồi vật lý
Giả sử phong cách phản ứng của một bộ phát hiện kéo được đặt thành Vật lý và nó áp dụng cho một đối tượng không được neo, đối tượng đó sẽ được di chuyển bởi các lực ràng buộc cố gắng đưa nó đến vị trí/hướng do chuyển động được đề xuất. Bạn có thể tùy chỉnh thêm phản hồi vật lý thông qua các thuộc tính sau:
| Thuộc tính | Mô tả | Mặc định |
|---|---|---|
| ApplyAtCenterOfMass | Khi là false, lực kéo được áp dụng tại điểm mà người dùng nhấp vào. Khi true, lực được áp dụng tại trọng tâm của đối tượng. | false |
| MaxForce | Lực tối đa áp dụng cho đối tượng để đạt được mục tiêu của nó. | 10000000 |
| MaxTorque | Moment xoắn tối đa áp dụng cho đối tượng để đạt được mục tiêu của nó. | 10000 |
| Responsiveness | Các giá trị cao hơn khiến đối tượng đạt được mục tiêu của nó nhanh hơn. | 10 |
Thao tác đầu vào
Một số chế độ DragStyle cho phép người dùng giữ phím/hộp thao tác để thao tác đối tượng kéo theo những cách khác nhau. Theo mặc định, thao tác là LeftControl trên PC, ButtonR1 trên gamepad, hoặc ButtonL2 trên VR. Bạn có thể tùy chỉnh những thao tác này thông qua các thuộc tính KeyboardModeSwitchKeyCode, GamepadModeSwitchKeyCode, hoặc VRSwitchKeyCode của thể instance bộ phát hiện kéo.
Sao chép
Khi thuộc tính RunLocally là false (mặc định), máy khách diễn giải tất cả đầu vào để sản xuất dữ liệu mà nó gửi đến máy chủ để thực hiện kéo. Trong chế độ này, tất cả các tín hiệu sự kiện tùy chỉnh và các chức năng đã đăng ký phải ở máy chủ Scripts.
Khi thuộc tính RunLocally là true, không có sự kiện nào được sao chép đến máy chủ. Tất cả các tín hiệu sự kiện tùy chỉnh và hàm đã đăng ký phải ở máy khách LocalScripts và bạn phải sử dụng sự kiện từ xa để truyền đạt các thay đổi cần thiết đến máy chủ.
Phản ứng kịch bản với việc nhấp chuột và kéo
Thông qua tín hiệu sự kiện, thay đổi thuộc tính, kiểu kéo Scriptable, và các chức năng tùy chỉnh, các kịch bản có thể phản ứng với việc thao tác các đối tượng được kéo để điều khiển giao diện người dùng hoặc đưa ra các quyết định logic, chẳng hạn như điều chỉnh mức ánh sáng trong một phòng dựa trên công tắc tường trượt.
Tín hiệu sự kiện
Thông qua các tín hiệu sự kiện sau, bạn có thể phát hiện khi nào một người dùng bắt đầu, tiếp tục và kết thúc việc kéo một đối tượng.
| Sự kiện | Mô tả |
|---|---|
| DragStart | Bắn ra khi người dùng bắt đầu kéo đối tượng. |
| DragContinue | Bắn ra khi người dùng tiếp tục kéo đối tượng sau khi DragStart đã được khởi động. |
| DragEnd | Bắn ra khi người dùng dừng kéo đối tượng. |
DragDetector - Tín hiệu sự kiện
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)
Thay đổi khung kéo
Ngoài tín hiệu sự kiện, bạn có thể theo dõi các thay đổi của DragFrame của bộ phát hiện kéo trực tiếp.
DragDetector - Thay đổi DragFrame
local dragDetector = script.Parent.DragDetector
dragDetector:GetPropertyChangedSignal("DragFrame"):Connect(function()
local currentDragTranslation = dragDetector.DragFrame.Position
print(currentDragTranslation)
end)
Kiểu kéo có kịch bản
Nếu bạn đặt DragStyle của bộ phát hiện thành Kịch bản, bạn có thể cung cấp chức năng của riêng mình mà nhận vào một Ray và trả về một CFrame không gian thế giới. Bộ phát hiện sẽ di chuyển chuyển động để đối tượng kéo đến vị trí/hướng tùy chỉnh đó.
DragDetector - Kiểu kéo có kịch bản
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)
-- Loại trừ đối tượng kéo khỏi phát hiện 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)
Hàm ràng buộc tùy chỉnh
Các bộ phát hiện kéo không có các quy tắc chuyển động tích hợp sẵn về các lưới và snap, nhưng bạn có thể đăng ký các hàm ràng buộc tùy chỉnh để chỉnh sửa DragFrame của bộ phát hiện trước khi nó được áp dụng. Ví dụ, bạn có thể giữ chuyển động trên một lưới bằng cách làm tròn các vị trí thành bội số của bước lưới, hoặc mô phỏng một trò chơi cờ với các quy tắc chuyển động hợp lệ cho từng quân cờ.
DragDetector - Hàm ràng buộc tùy chỉnh
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)
-- Khi cần, hãy loại bỏ hàm ràng buộc bằng cách gọi connection:Disconnect()
Ví dụ sử dụng
Các đối tượng vật lý không được neo
Một ứng dụng đơn giản của các bộ phát hiện kéo là một trò chơi cân bằng tháp, nơi người chơi phải cẩn thận gỡ bỏ các mảnh và cố gắng giữ cho tháp thẳng đứng. Trong cấu trúc tháp sau, mỗi mảnh đều có một DragDetector con với kiểu DragStyle mặc định là TranslatePlane để người chơi có thể kéo các mảnh ra ngoài nhưng không kéo lên hoặc xuống.
Các mô hình neo với các phần điều chỉnh
Bạn có thể dễ dàng tạo và chia sẻ các mô hình chủ yếu được neo, nhưng có một hoặc nhiều phần/mô hình con mà người chơi có thể kéo. Ví dụ, bàn phía dưới có hai ngăn kéo mà người chơi có thể mở ra để kiểm tra bên trong.
Bộ phát hiện kéo và các ràng buộc
Bạn có thể kết hợp các bộ phát hiện kéo với Constraints, chẳng hạn như một con rối múa. Trong thiết lập sau, các tay cầm điều khiển được neo, các bộ phận cơ thể không được neo, và các ràng buộc giữ cho con rối lại với nhau. Di chuyển các tay cầm với kiểu kéo DragStyle TranslateViewPlane khiến con rối nhảy múa, và các bộ phận cơ thể riêng lẻ cũng có thể được di chuyển bằng các bộ phát hiện kéo, tất cả trong khi mô hình giữ được tính toàn vẹn của nó.
Giao diện người dùng 3D
Giao diện người dùng 3D rất dễ đạt được thông qua các bộ phát hiện kéo, chẳng hạn như điều chỉnh độ sáng của một SpotLight dựa trên công tắc dimmer trượt. Bạn cũng có thể phát hiện các trục X và Z riêng lẻ để điều khiển hai khía cạnh khác nhau của một giao diện người dùng 3D, chẳng hạn như Size, Speed, và Color của một ParticleEmitter.
DragDetector - Giao diện người dùng 3D
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
-- Điều chỉnh kích thước và tốc độ hạt dựa trên yếu tố X của bộ phát hiện kéo
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)
-- Điều chỉnh màu hạt dựa trên yếu tố Z của bộ phát hiện kéo
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)