L'istanza DragDetector facilita e incoraggia l'interazione con oggetti 3D in un'esperienza, come l'apertura di porte e cassetti, lo scorrimento di una parte, la presa e il lancio di una palla da bowling, il tiro e il lancio di una fionda, e molto altro. Le funzionalità principali includono:
Posizionare un DragDetector sotto qualsiasi BasePart o Model per renderlo trascinabile tramite tutti gli input (mouse, touch, gamepad e VR), senza una sola riga di codice.
Scegliere tra diversi stili di trascinamento, definire come l'oggetto risponde al movimento, e applicare opzionalmente limiti di asse o movimento.
Gli script possono rispondere alla manipolazione degli oggetti trascinati per guidare l'interfaccia utente o prendere decisioni logiche, come regolare il livello di illuminazione in una stanza in base a un dimmer a scorrimento.
I giocatori possono manipolare parti o modelli ancorati e rimarranno esattamente dove li si posiziona al rilascio.
DragDetectors funzionano in Studio finché non si sta utilizzando gli strumenti Seleziona, Sposta, Scala o Ruota, rendendo più facile testare e modificare oggetti trascinabili durante la modifica.
Rendere gli oggetti trascinabili
Per rendere qualsiasi parte o modello trascinabile, basta aggiungere un DragDetector come discendente diretto.
Dal menu, inserisci un DragDetector.

Per impostazione predefinita, l'oggetto sarà ora trascinabile nel piano di terra, ma puoi personalizzare il suo DragStyle, definire come risponde al movimento, e opzionalmente applicare limiti di asse o movimento.
Personalizzare i rilevatori di trascinamento
Stile di trascinamento
DragDetectors mappano il movimento del cursore a linee e piani virtuali per calcolare il movimento 3D proposto. Attraverso la proprietà DragStyle, puoi scegliere tra diverse mappature per soddisfare le tue esigenze. Ad esempio, TranslatePlane produce traduzione in un piano virtuale, mentre RotateAxis produce rotazione attorno a un asse virtuale.
| Impostazione | Descrizione |
|---|---|
| TranslateLine | Movimento 1D lungo l'asse Axis del rilevatore, per impostazione predefinita l'asse Y del mondo. |
| TranslatePlane | Movimento 2D nel piano perpendicolare all'asse Axis del rilevatore, per impostazione predefinita il piano XZ del mondo. |
| TranslatePlaneOrLine | Movimento 2D nel piano perpendicolare all'asse Axis del rilevatore e, quando il modificatore è attivo, movimento 1D lungo l'asse Axis del rilevatore. |
| TranslateLineOrPlane | Movimento 1D lungo l'asse Axis del rilevatore e, quando il modificatore è attivo, movimento 2D nel piano perpendicolare all'asse Axis del rilevatore. |
| TranslateViewPlane | Movimento 2D nel piano perpendicolare alla vista della camera. In questa modalità, il piano viene costantemente aggiornato, anche durante il trascinamento, e farà sempre fronte alla vista corrente della camera. |
| RotateAxis | Rotazione attorno all'asse Axis del rilevatore, per impostazione predefinita l'asse Y del mondo. |
| RotateTrackball | Rotazione tipo trackball, ulteriormente personalizzata tramite le proprietà TrackballRadialPullFactor e TrackballRollFactor. |
| BestForDevice | TranslatePlaneOrLine per mouse e gamepad; TranslatePlane per touch; 6DOF per VR. |
| Scriptable | Calcola il movimento desiderato tramite una funzione personalizzata fornita tramite SetDragStyleFunction(). |
Direzione di trascinamento
Per impostazione predefinita, il movimento 3D e il corrispondente DragStyle si mappano allo spazio del mondo. Tuttavia, potresti voler cambiare l'ReferenceInstance, l'Orientation o l'Axis, ad esempio quando costruisci rilevatori di trascinamento all'interno di modelli con parti regolabili.
| Proprietà | Descrizione | Predefinito |
|---|---|---|
| ReferenceInstance | Un'istanza il cui pivot fornisce il quadro di riferimento per il rilevatore di trascinamento. Il DragFrame è espresso rispetto a questo quadro di riferimento, il quale può essere recuperato tramite GetReferenceFrame(). Se il quadro di riferimento è nil, la traduzione sarà nella direzione (o nel piano perpendicolare) della proprietà Axis nello spazio del mondo. | nil |
| Orientation | Specifica la rotazione YXZ degli assi di movimento rispetto al quadro di riferimento (non cambia l'orientamento del quadro di riferimento stesso). La traduzione lineare e la rotazione assiale saranno su questo asse Y riorientato, e la traduzione piana nel piano XZ. Modificare questo valore aggiorna automaticamente Axis e viceversa. | (0, 0, 0) |
| Axis | L'asse principale di movimento, espresso rispetto al quadro di riferimento. Modificare questo valore aggiorna automaticamente Orientation e viceversa. | (0, 1, 0) |
Risposta al movimento
La proprietà ResponseStyle specifica come un oggetto risponde al movimento proposto, a seconda che l'oggetto sia Anchored o meno.
| Impostazione | Comportamento ancorato | Comportamento non ancorato |
|---|---|---|
| Geometric | Sia all'interno dell'esperienza in esecuzione che in modalità modifica Studio, la posizione/orientamento di un oggetto ancorato verrà aggiornato per riflettere esattamente il movimento proposto. | Per un oggetto non ancorato, il comportamento è lo stesso di quello di un oggetto ancorato. Tuttavia, in un'esperienza in esecuzione, l'oggetto sarà ancorato all'inizio del trascinamento e ripristinato come non ancorato al rilascio del trascinamento. |
| Physical | Un oggetto ancorato avrà il comportamento predefinito Geometric, poiché non è influenzato dalle forze. | Un oggetto non ancorato sarà spostato da forze di vincolo che tentano di portarlo alla posizione e/o orientamento desiderati dati dal movimento proposto. |
| Custom | L'oggetto non si muoverà affatto, ma il DragFrame verrà comunque aggiornato e puoi rispondere alla manipolazione di trascinamento come preferisci. | (stesso di ancorato) |
Limiti di asse e movimento
Per impostazione predefinita, non ci sono limiti al movimento 3D oltre le restrizioni insite dello DragStyle. Se necessario, puoi applicare limiti minimi e massimi sia alla traduzione che alla rotazione. Nota però che questi non sono vincoli; impediscono semplicemente ai tentativi del rilevatore di trascinamento di generare movimento per rimanere all'interno dei limiti.
| Proprietà | Descrizione | Predefinito |
|---|---|---|
| Limiti alla traduzione di trascinamento in ogni dimensione. Se MaxDragTranslation è maggiore di MinDragTranslation, la traduzione sarà limitata all'interno di quel range. | (0, 0, 0) | |
| Rilevante solo se DragStyle è impostato su RotateAxis. Se MaxDragAngle è maggiore di MinDragAngle, la rotazione sarà limitata all'interno di quel range. | 0 |
Permessi di trascinamento
Il permesso dei giocatori di interagire con una determinata istanza di rilevatore di trascinamento può essere specificato tramite la proprietà PermissionPolicy. Questa è impostata su Enum.DragDetectorPermissionPolicy.Everybody per impostazione predefinita e può essere cambiata anche per supportare controlli di permesso scriptati come mostrato nell'esempio di codice.
| Impostazione | Descrizione |
|---|---|
| Nobody | Nessun giocatore può interagire con il DragDetector. |
| Everybody | Tutti i giocatori possono interagire con il DragDetector. |
| Scriptable | I permessi di trascinamento dei giocatori saranno determinati da una funzione registrata tramite SetPermissionPolicyFunction(). Sotto questa impostazione, il mancato riconoscimento di una funzione o il ritorno di un risultato non valido impedirà a tutti i giocatori di trascinare. |
DragDetector - Permesso di trascinamento scriptato
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)
Risposta fisica
Si suppone che lo stile di risposta di un trascinatore sia impostato su Physical e che venga applicato a un oggetto non ancorato; quell'oggetto sarà spostato da forze di vincolo che tentano di portarlo alla posizione/orientamento dato dal movimento proposto. Puoi ulteriormente personalizzare la risposta fisica tramite le seguenti proprietà:
| Proprietà | Descrizione | Predefinito |
|---|---|---|
| ApplyAtCenterOfMass | Quando è falso, la forza di trascinamento è applicata al punto in cui l'utente clicca. Quando è vero, la forza è applicata al centro di massa dell'oggetto. | false |
| MaxForce | Forza massima applicata affinché l'oggetto raggiunga il suo obiettivo. | 10000000 |
| MaxTorque | Massima coppia applicata affinché l'oggetto raggiunga il suo obiettivo. | 10000 |
| Responsiveness | Valori più alti fanno sì che l'oggetto raggiunga il suo obiettivo più rapidamente. | 10 |
Input modificatore
Alcuni modi DragStyle consentono agli utenti di tenere premuto un tasto/pulsante modificatore per manipolare l'oggetto trascinato in modi diversi. Per impostazione predefinita, il modificatore è LeftControl su PC, ButtonR1 su gamepad, o ButtonL2 su VR. Puoi personalizzare questi modificatori tramite le proprietà KeyboardModeSwitchKeyCode, GamepadModeSwitchKeyCode, o VRSwitchKeyCode dell'istanza di rilevatore di trascinamento.
Replicazione
Quando la proprietà RunLocally è falsa (predefinito), il client interpreta tutti gli input per produrre dati che invia al server per eseguire il trascinamento. In questa modalità, tutti i segnali di eventi personalizzati e le funzioni registrate devono essere in Scripts del server.
Quando la proprietà RunLocally è vera, nessun evento viene replicato al server. Tutti i segnali di eventi personalizzati e le funzioni registrate devono essere in LocalScripts del client e devi utilizzare eventi remoti per propagare i cambiamenti necessari al server.
Risposte dello script ai clic e trascinamenti
Attraverso i segnali di eventi, cambi delle proprietà, lo stile di trascinamento Scriptable, e funzioni personalizzate, gli script possono rispondere alla manipolazione degli oggetti trascinati per guidare l'interfaccia utente o prendere decisioni logiche, come regolare il livello di luce in una stanza in base a un dimmer a scorrimento.
Segnali di eventi
Attraverso i seguenti segnali di eventi, puoi rilevare quando un utente inizia, continua e termina di trascinare un oggetto.
| Evento | Descrizione |
|---|---|
| DragStart | Si attiva quando un utente inizia a trascinare l'oggetto. |
| DragContinue | Si attiva quando un utente continua a trascinare l'oggetto dopo l'inizio di DragStart. |
| DragEnd | Si attiva quando un utente smette di trascinare l'oggetto. |
DragDetector - Segnali di eventi
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)
Cambiamenti del frame di trascinamento
In aggiunta ai segnali di eventi, puoi monitorare i cambiamenti al DragFrame del rilevatore direttamente.
DragDetector - Cambiamenti del DragFrame
local dragDetector = script.Parent.DragDetector
dragDetector:GetPropertyChangedSignal("DragFrame"):Connect(function()
local currentDragTranslation = dragDetector.DragFrame.Position
print(currentDragTranslation)
end)
Stile di trascinamento scriptato
Se imposti lo DragStyle di un rilevatore su Scriptable, puoi fornire la tua funzione che accetta un Ray e restituisce un CFrame in spazio del mondo. Il rilevatore muoverà il movimento in modo che l'oggetto trascinato vada in quella posizione/orientamento personalizzati.
DragDetector - DragStyle scriptato
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)
-- Escludi l'oggetto trascinato dalla rilevazione della 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)
Funzione di vincolo personalizzata
I rilevatori di trascinamento non hanno regole di movimento integrate riguardo griglie e sincronizzazione, ma puoi registrare funzioni di vincolo personalizzate per modificare il DragFrame del rilevatore prima che venga applicato. Ad esempio, puoi mantenere il movimento su una griglia arrotondando le posizioni a multipli dell'incremento della griglia, o simulare una partita a scacchi con regole di movimento legali per ciascun pezzo.
DragDetector - Funzione di vincolo personalizzata
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)
-- Quando necessario, rimuovi la funzione di vincolo invocando connection:Disconnect()
Esempio di utilizzo
Oggetti fisici non ancorati
Un'implementazione di base dei rilevatori di trascinamento è un gioco di bilanciamento della torre dove i giocatori devono rimuovere con attenzione i pezzi e tentare di mantenere la torre eretta. Nella seguente struttura della torre, ogni pezzo ha un DragDetector figlio con un DragStyle predefinito di TranslatePlane in modo che i giocatori possano tirare i pezzi verso l'esterno ma non verso l'alto o verso il basso.
Modelli ancorati con parti regolabili
Puoi facilmente creare e condividere modelli che sono principalmente ancorati, ma che hanno una o più parti/modelli figli che i giocatori possono trascinare. Ad esempio, la seguente scrivania ha due cassetti che i giocatori possono aprire per ispezionare cosa c'è dentro.
Rilevatori di trascinamento e vincoli
Puoi combinare i rilevatori di trascinamento con Constraints, ad esempio un burattino marionetta. Nella seguente configurazione, le maniglie di controllo sono ancorate, le parti del corpo sono non ancorate e i vincoli mantengono insieme la marionetta. Muovendo le maniglie con lo stile di trascinamento TranslateViewPlane, la marionetta balla, e le singole parti del corpo possono anche essere spostate con i rilevatori di trascinamento, il tutto mentre il modello mantiene la sua integrità.
Interfacce utente 3D
Le interfacce utente 3D sono facilmente realizzabili tramite i rilevatori di trascinamento, come regolare la luminosità di un SpotLight in base a un dimmer a scorrimento. Puoi anche rilevare gli assi X e Z individualmente per controllare due diversi aspetti di un'interfaccia utente 3D, come le dimensioni Size, la velocità Speed e il colore Color di un ParticleEmitter.
DragDetector - Interfaccia utente 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
-- Regola la dimensione e la velocità delle particelle in base al fattore X del rilevatore di trascinamento
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)
-- Regola il colore delle particelle in base al fattore Z del rilevatore di trascinamento
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)