3D 拖動偵測器

*此內容是使用 AI(Beta 測試版)翻譯,可能含有錯誤。若要以英文檢視此頁面,請按一下這裡

實例可以幫助和促進體驗中與 3D 對象的互動,例如打開門和抽屜、滑動部件、抓取和投擲保齡球、拉回並射擊彈弓,以及更多。關鍵功能包括:

  • 在任何 DragDetectorBasePartModel 下放置一個 使其可拖曳 通過所有輸入(滑鼠、觸摸、遊戲手柄和 VR),而且沒有一行代碼。

  • 從多種 拖式 中選擇,定義對象如何回應運動,並可選擇 軸或移動限制 。

  • 腳本可以回應拖動物件的操作來驅動用戶介面或做出邏輯決定,例如根據滑動牆開關調整房間的光亮度。

  • 玩家可以操縱錨定的零件或模型,並且在發布時保持在你放置它們的位置。

  • 在工作室工作,只要你不使用選擇、移動、縮放或旋轉工具,就可以輕鬆測試和調整可拖曳的對象,而編輯時。

讓物件可拖曳

若要讓任何零件或模型可拖曳,只需添加 DragDetector 作為直接後代。

  1. 檢索器 窗口中,將鼠標懸停在 PartMeshPartModel 上,然後單擊 ⊕ 按鈕。一個上下文菜單顯示。

  2. 從選單中插入 拖動偵測器

  3. 預設情況下,對象現在可以在地面平面上拖曳,但您可以自定義其 DragStyle 、定義它如何回應運動,並可選擇應用軸或運動限制

自訂拖動偵測器

拖式風格

DragDetectors 將鼠標移動轉換為虛擬線和平面,以計算提出的 3D 運動。通過 DragStyle 屬性,您可以從不同的映射中選擇滿足您需求的一個。例如, 翻譯飛機 在虛擬飛機中產生翻譯,而 旋轉軸 在虛擬軸上產生旋轉。

設置說明
TranslateLine沿著偵測器的 Axis 1D 運動,預設世界 Y 軸。
TranslatePlane在偵測器的 Axis 平面垂直的 2D 運動,預設世界 XZ 飛機。
TranslatePlaneOrLine在偵測器的 Axis 平面的 2D 運動,當 修改器 啟用時,沿著偵測器的 Axis 1D 運動。
TranslateLineOrPlane沿著偵測器的Axis和當修改器啟用時,在偵測器的Axis平面上的2D運動。
TranslateViewPlane平面上的2D運動,與相攝影機檢視野垂直。在這種模式下,飛機不斷更新,即使在拖動時,也會始終面對相攝影機的當前檢視。
RotateAxis關於偵測器的 Axis 旋轉,預設世界 Y 軸。
RotateTrackball軌道球旋轉,進一步通過 TrackballRadialPullFactorTrackballRollFactor 屬性進行自訂。
BestForDevice 翻譯飛機或線 對於鼠標和遊戲手柄; 翻譯飛機 對於觸摸; 6DOF 對於 VR。
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在運行體驗和工作室編輯模式中,錨定的對象的位置/方向將被更新,以準確反映提出的動作。對於未錨定的對物件,行為與錨定的對物件相同。然而,在運行體驗中,對象將在拖動開始時被錨定,並在拖動釋放時恢復至未錨定狀態。
Physical錨定的對象將預設為 幾何 行為,因為它不受力量影響。未錨定的對象將被限制力移動,嘗試將其移至建議動作提供的預期位置和/或方向。
Custom對象不會移全部 所有一毫米,但 仍將更新,您可以隨意回應拖動操作。(與錨定相同)

軸與移動限制

預設情況下, 的內在限制之外沒有任何限制 3D 運動。如有需要,您可以對翻譯和旋轉都適用最小和最大限制。請注意,這些不是 限制 ;它們只是阻止拖動偵測器嘗試產生運動,以便保持在限制內。

屬性說明預設
MinDragTranslation MaxDragTranslation在每個維度拖動翻譯的限制。如果 MaxDragTranslation 大於 MinDragTranslation,翻譯將被限制在該範圍內。(0, 0, 0)
MinDragAngle MaxDragAngle僅適用於 DragStyle 設為 旋轉軸 時。如果 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 時,拖動力會在使用者單擊的點施加。當為 true 時,力會在物件的質量中心施加。
MaxForce對物件施加的最大力,以達到其目標。10000000
MaxTorque對物件應用最大扭矩以達到目標。10000
Responsiveness更高的值會讓對象更快地達到目標。10

修改輸入

一些 DragStyle 模式允許使用者按住 修改器 鍵/按鈕來以不同方式操作拖曳的對象。預設情況下,修改器在 PC 上是 LeftControl ,在遊戲手柄上是 ButtonR1 或在 VR 上是 ButtonL2。您可以通過 KeyboardModeSwitchKeyCode , GamepadModeSwitchKeyCodeVRSwitchKeyCode 拖動偵測器實個體、實例的屬性來自訂這些修改器。

複製

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 設為 可執行 ,您可以提供自己的功能,該功能會接收 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)
-- When applicable, remove the constraint function by invoking connection:Disconnect()

範例使用

未錨定的物理對象

基本實現拖動偵測器的遊戲是一個塔平衡遊戲,玩家必須仔細移除碎片並嘗試保持塔保持垂直。在下一個塔結構中,每個部分都有一個子 DragDetector 與預設 DragStyle翻譯飛機 ,因此玩家可以將部分向外拉出,但不向上或向下。

具有可調整零件的錨定模型

您可以輕鬆創建並分享主要被錨定的模型,但模型上有一個或多個子模型/模型,玩家可以拖曳。例如,下面的桌子有兩個抽屜,玩家可以打開以檢查裡面的東西。

拖動偵測器和限制

您可以將拖動偵測器與 Constraints 結合,例如木偶傀儡。在下面的設定中,控制手柄被錨定,身體部位未錨定,約束將馬戲偶維持在一起。使用 TranslateViewPlane 移動手柄,會讓木偶跳舞,個人身體部位也可以用拖動偵測器移動,模型的完整性仍然保持不變。

3D 使用者介面

3D 使用者介面可以透過拖動偵測器輕鬆實現,例如調整基於滑動開關調光器的 SpotLight 亮度。您也可以獨立地偵測 XZ 軸來控制 3D 使用者介面的兩個不同方面,例如 SizeSpeedColorParticleEmitter

拖動偵測器 - 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)