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 參照此參考幀來表示。如果參考幀為 nil,則平移將在世界空間中沿著(或在垂直於)Axis 屬性進行。 | nil |
| Orientation | 指定相對於參考幀的運動軸的 YXZ 旋轉(不改變參考幀本身的方向)。線性平移和軸旋轉將在此重新定義的 Y 軸上進行,平面平移將在 XZ 平面上進行。更改此值會自動更新 Axis,反之亦然。 | (0, 0, 0) |
| Axis | 主要運動軸,以參考幀為基準來表示。更改此值會自動更新 Orientation,反之亦然。 | (0, 1, 0) |
對運動的反應
ResponseStyle 屬性指定物件如何對擬議的運動作出反應,取決於該物件是否 Anchored 。
| 設置 | 固定行為 | 非固定行為 |
|---|---|---|
| Geometric | 在運行體驗和 Studio 編輯模式下,固定物件的位置/方向將被更新為精確反映擬議的運動。 | 對於未固定的物件,其行為與固定物件相同。然而,在運行體驗中,物件將在拖曳開始時被固定,並在拖曳釋放時恢復為未固定狀態。 |
| Physical | 固定物件將默認為 Geometric 行為,因為它不受力的影響。 | 未固定的物件將受到 約束力 的影響,這將試圖將其移動到擬議的運動所給定的位置和/或方向。 |
| Custom | 該物件將完全不移動,但 DragFrame 仍然會被更新,您可以根據自己的意願 響應拖曳操作。 | (與固定相同) |
軸和移動限制
默認情況下,3D 運動沒有任何限制,僅受 DragStyle 的內在限制。如果需要,您可以對平移和旋轉應用最小和最大限制。請注意,這些不是約束;它們僅僅妨礙拖曳檢測器生成運動的嘗試,以保持在限制之內。
| 屬性 | 描述 | 默認 |
|---|---|---|
| 限制每個維度的拖曳平移。如果 MaxDragTranslation 大於 MinDragTranslation,則平移將被限制在該範圍內。 | (0, 0, 0) | |
| 僅在 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)
物理響應
假定拖動者的 響應樣式 設定為 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 | 當用戶停止拖曳物件時觸發。 |
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)
-- 排除拖曳物件的射線檢測
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,以便玩家可以將物品往外拉,但不能往上或往下拉。
具有可調部件的固定模型
您可以輕鬆創建並分享以固定為主的模型,但有一個或多個子部件/模型供玩家拖曳。例如,以下辦公桌具有兩個抽屜,玩家可以打開以檢查裡面的內容。
拖曳檢測器和約束
您可以將拖曳檢測器與 Constraints 組合,例如布偶。在以下設置中,控制把手是固定的,身體部件是未固定的,而約束將布偶連接起來。使用 TranslateViewPlane DragStyle 移動把手使布偶跳舞,並且各個身體部件也可以使用拖曳檢測器進行移動,同時模型保持完整性。
3D 用戶界面
通過拖曳檢測器,3D 用戶界面很容易實現,例如根據滑動開關調整 SpotLight 的亮度。您還可以單獨檢測 X 和 Z 軸,以控制 3D 用戶界面的兩個不同方面,例如 Size、Speed 和 Color 的 ParticleEmitter。
DragDetector - 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
-- 根據拖曳檢測器的 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)