DragDetector 實例促進並鼓勵與遊戲中的 3D 物件互動,例如開門和抽屜、滑動部件、抓取和投擲保齡球、拉回並發射彈弓等等。主要功能包括:
在任何 BasePart 或 Model 下放置一個 DragDetector,以便通過所有輸入(滑鼠、觸控、遊戲手把和 VR)使其可拖曳,而無需編寫一行代碼。
腳本可以 對拖曳物件的操作作出反應,以驅動 UI 或做出邏輯決策,例如根據滑動的牆開關調整房間的光線水平。
玩家可以操作固定的部件或模型,並且在釋放時它們會保持在您放置的位置。
DragDetectors 在 Studio 中工作,只要您不使用 選擇、移動、縮放或 旋轉 工具,這使得在編輯時更容易測試和調整可拖曳物件。
使物件可拖曳
要使任何部件或模型可拖曳,只需將 DragDetector 添加為直接子項。
從菜單中插入 DragDetector。

自定義拖曳檢測器
拖曳樣式
DragDetectors 將游標運動映射到虛擬線條和平面,以計算建議的 3D 運動。通過 DragStyle 屬性,您可以從不同的映射中選擇以滿足您的需求。例如,TranslatePlane 在虛擬平面中產生平移,而 RotateAxis 則圍繞虛擬軸產生旋轉。
| 設置 | 描述 |
|---|---|
| TranslateLine | 沿檢測器的 Axis 的 1D 運動,默認為世界 Y 軸。 |
| TranslatePlane | 在與檢測器的 Axis 垂直的平面中的 2D 運動,默認為世界 XZ 平面。 |
| TranslatePlaneOrLine | 在與檢測器的 Axis 垂直的平面中的 2D 運動,當 修飾符 被激活時,沿檢測器的 Axis 的 1D 運動。 |
| TranslateLineOrPlane | 沿檢測器的 Axis 的 1D 運動,當 修飾符 被激活時,在與檢測器的 Axis 垂直的平面中的 2D 運動。 |
| TranslateViewPlane | 在與相機視圖垂直的平面中的 2D 運動。在此模式下,平面會不斷更新,即使在拖曳時,也會始終面向相機的當前視圖。 |
| RotateAxis | 圍繞檢測器的 Axis 旋轉,默認為世界 Y 軸。 |
| RotateTrackball | Trackball 旋轉,進一步通過 TrackballRadialPullFactor 和 TrackballRollFactor 屬性自定義。 |
| BestForDevice | 對於滑鼠和遊戲手把使用 TranslatePlaneOrLine;對於觸控使用 TranslatePlane;對於 VR 使用 6DOF。 |
| Scriptable | 通過 SetDragStyleFunction() 提供的自定義函數計算所需的運動。 |
拖曳方向
默認情況下,3D 運動和相關的 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 | 在運行的遊戲和 Studio 編輯模式中,固定物件的位置/方向將更新以準確反映建議的運動。 | 對於非固定物件,行為與固定物件相同。然而,在運行的遊戲中,物件將在拖曳開始時被固定,並在拖曳釋放時恢復為非固定。 |
| Physical | 固定物件將默認為 Geometric 行為,因為它不受力的影響。 | 非固定物件將受到 約束力 的影響,這些力試圖將其移動到建議的運動所給出的期望位置和/或方向。 |
| Custom | 物件將完全不移動,但 DragFrame 仍然會更新,您可以根據需要 對拖曳操作作出反應。 | (與固定相同) |
軸和運動限制
默認情況下,除了 DragStyle 的固有限制外,3D 運動沒有其他限制。如果需要,您可以對平移和旋轉應用最小和最大限制。然而,請注意,這些不是約束;它們僅僅是阻礙拖曳檢測器生成運動的嘗試,以保持在限制範圍內。
| 屬性 | 描述 | 默認值 |
|---|---|---|
| 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)物理反應
假設拖曳器的 反應樣式 設置為 Physical 並應用於非固定物件,該物件將受到約束力的影響,這些力試圖將其移動到建議的運動所給出的期望位置/方向。您可以通過以下屬性進一步自定義物理反應:
| 屬性 | 描述 | 默認值 |
|---|---|---|
| ApplyAtCenterOfMass | 當為 false 時,拖曳力應用於用戶單擊的點。當為 true 時,力應用於物件的質心。 | false |
| MaxForce | 應用於物件以達到其目標的最大力。 | 10000000 |
| MaxTorque | 應用於物件以達到其目標的最大扭矩。 | 10000 |
| Responsiveness | 較高的值使物件更快地達到其目標。 | 10 |
修飾符輸入
某些 DragStyle 模式允許用戶按住 修飾符 鍵/按鈕以以不同方式操作拖曳的物件。默認情況下,修飾符為 PC 上的 LeftControl、遊戲手把上的 ButtonR1 或 VR 上的 ButtonL2。您可以通過拖曳檢測器實例的 KeyboardModeSwitchKeyCode、GamepadModeSwitchKeyCode 或 VRSwitchKeyCode 屬性自定義這些修飾符。
複製
當 RunLocally 屬性為 false(默認)時,客戶端解釋所有輸入以生成數據,並將其發送到服務器以執行拖曳。在此模式下,所有自定義事件信號和註冊的函數必須在服務器 Scripts 中。
當 RunLocally 屬性為 true 時,沒有事件被複製到服務器。所有自定義事件信號和註冊的函數必須在客戶端 LocalScripts 中,並且您必須使用 遠程事件 將必要的更改傳播到服務器。
腳本對點擊和拖曳的反應
通過 事件信號、屬性變更、Scriptable 拖曳樣式和自定義函數,腳本可以對拖曳物件的操作作出反應,以驅動 UI 或做出邏輯決策,例如根據滑動的牆開關調整房間的光線水平。
事件信號
通過以下事件信號,您可以檢測用戶何時開始、繼續和結束拖曳物件。
| 事件 | 描述 |
|---|---|
| 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 結合使用,例如木偶。在以下設置中,控制手柄是固定的,身體部件是非固定的,約束將木偶保持在一起。使用 TranslateViewPlane DragStyle 移動手柄使木偶跳舞,並且各個身體部件也可以使用拖曳檢測器移動,同時模型保持其完整性。
3D 用戶界面
3D 用戶界面可以通過拖曳檢測器輕鬆實現,例如根據滑動開關調光器調整 SpotLight 的亮度。您還可以單獨檢測 X 和 Z 軸,以控制 3D 用戶界面的兩個不同方面,例如 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)