La instancia DragDetector facilita y fomenta la interacción con objetos 3D en una experiencia, como abrir puertas y cajones, deslizar una pieza, agarrar y lanzar una bola de boliche, tirar hacia atrás y disparar una resortera, y mucho más. Las características clave incluyen:
Coloca un DragDetector debajo de cualquier BasePart o Model para hacerlo arrastrable a través de todas las entradas (ratón, toque, gamepad y VR), todo sin una sola línea de código.
Elige entre varios estilos de arrastre, define cómo el objeto responde al movimiento y opcionalmente aplica límites de eje o movimiento.
Los scripts pueden responder a la manipulación de objetos arrastrados para manejar la interfaz de usuario o tomar decisiones lógicas, como ajustar el nivel de luz en una habitación en función de un interruptor deslizante.
Los jugadores pueden manipular partes ancladas o modelos y permanecerán exactamente donde los coloques al soltarlos.
DragDetectors funciona en Studio siempre y cuando no estés usando las herramientas Seleccionar, Mover, Escalar o Rotar, facilitando la prueba y el ajuste de objetos arrastrables mientras editas.
Hacer objetos arrastrables
Para hacer que cualquier parte o modelo sea arrastrable, simplemente añade un DragDetector como un descendiente directo.
En la ventana de Explorador, pasa el mouse sobre el Part, MeshPart o Model y haz clic en el botón ⊕. Se mostrará un menú contextual.
Desde el menú, inserta un DragDetector.

Por defecto, el objeto ahora será arrastrable en el plano de tierra, pero puedes personalizar su DragStyle, definir cómo responde al movimiento y opcionalmente aplicar límites de eje o movimiento.
Personalizar detectores de arrastre
Estilo de arrastre
DragDetectors mapea el movimiento del cursor a líneas y planos virtuales para calcular el movimiento 3D propuesto. A través de la propiedad DragStyle, puedes elegir entre diferentes mapeos para adaptarse a tus necesidades. Por ejemplo, TranslatePlane produce traducción en un plano virtual, mientras que RotateAxis produce rotación alrededor de un eje virtual.
| Ajuste | Descripción |
|---|---|
| TranslateLine | Movimiento 1D a lo largo del Axis del detector, por defecto el eje Y del mundo. |
| TranslatePlane | Movimiento 2D en el plano perpendicular al Axis del detector, por defecto el plano XZ del mundo. |
| TranslatePlaneOrLine | Movimiento 2D en el plano perpendicular al Axis del detector y, cuando el modificador está activo, movimiento 1D a lo largo del Axis del detector. |
| TranslateLineOrPlane | Movimiento 1D a lo largo del Axis del detector y, cuando el modificador está activo, movimiento 2D en el plano perpendicular al Axis del detector. |
| TranslateViewPlane | Movimiento 2D en el plano perpendicular a la vista de la cámara. En este modo, el plano se actualiza constantemente, incluso mientras se arrastra, y siempre se orientará hacia la vista actual de la cámara. |
| RotateAxis | Rotación alrededor del Axis del detector, por defecto el eje Y del mundo. |
| RotateTrackball | Rotación de tipo trackball, además personalizada a través de las propiedades TrackballRadialPullFactor y TrackballRollFactor. |
| BestForDevice | TranslatePlaneOrLine para mouse y gamepad; TranslatePlane para toque; 6DOF para VR. |
| Scriptable | Calcula el movimiento deseado a través de una función personalizada proporcionada a través de SetDragStyleFunction(). |
Dirección de arrastre
Por defecto, el movimiento 3D y el DragStyle asociado se asignan al espacio mundial. Sin embargo, es posible que desees cambiar la ReferenceInstance, Orientation o Axis, por ejemplo, al construir detectores de arrastre en modelos con partes ajustables.
| Propiedad | Descripción | Predeterminado |
|---|---|---|
| ReferenceInstance | Una instancia cuyo pivote proporciona el marco de referencia para el detector de arrastre. El DragFrame se expresa en relación con este marco de referencia que se puede recuperar a través de GetReferenceFrame(). Si el marco de referencia es nil, la traducción será en la dirección de (o en el plano perpendicular a) la propiedad Axis en el espacio mundial. | nil |
| Orientation | Especifica la rotación YXZ de los ejes de movimiento en relación con el marco de referencia (no cambia la orientación del marco de referencia en sí). La traducción lineal y la rotación axial se realizarán en este eje Y reorientado, y la traducción planar en el plano XZ. Cambiar este valor actualiza automáticamente Axis y viceversa. | (0, 0, 0) |
| Axis | El eje primario de movimiento, expresado en relación con el marco de referencia. Cambiar este valor actualiza automáticamente Orientation y viceversa. | (0, 1, 0) |
Respuesta al movimiento
La propiedad ResponseStyle especifica cómo un objeto responde al movimiento propuesto, dependiendo de si el objeto está Anchored o no.
| Ajuste | Comportamiento anclado | Comportamiento no anclado |
|---|---|---|
| Geometric | Tanto dentro de la experiencia en ejecución como en el modo de edición de Studio, la posición/orientación de un objeto anclado se actualizará para reflejar exactamente el movimiento propuesto. | Para un objeto no anclado, el comportamiento es el mismo que para un objeto anclado. Sin embargo, en una experiencia en ejecución, el objeto se anclará al inicio del arrastre y se restaurará a no anclado al soltar el arrastre. |
| Physical | Un objeto anclado tendrá un comportamiento predeterminado de Geométrico, ya que no está afectado por fuerzas. | Un objeto no anclado será movido por fuerzas de restricción que intentan llevarlo a la posición/orientación deseada dada por el movimiento propuesto. |
| Custom | El objeto no se moverá en absoluto, pero el DragFrame aún se actualizará y puedes responder a la manipulación del arrastre como desees. | (igual que anclado) |
Límites de eje y movimiento
Por defecto, no hay límites al movimiento 3D más allá de las restricciones inherentes de DragStyle. Si es necesario, puedes aplicar límites mínimo y máximo tanto para la traducción como para la rotación. Ten en cuenta, sin embargo, que estos no son restricciones; simplemente impiden los intentos del detector de arrastre de generar movimiento para permanecer dentro de los límites.
| Propiedades | Descripción | Predeterminado |
|---|---|---|
| Límites para la traducción de arrastre en cada dimensión. Si MaxDragTranslation es mayor que MinDragTranslation, la traducción se limitará a ese rango. | (0, 0, 0) | |
| Solo relevante si DragStyle está configurado en RotateAxis. Si MaxDragAngle es mayor que MinDragAngle, la rotación se limitará a ese rango. | 0 |
Permisos de arrastre
El permiso de los jugadores para interactuar con una determinada instancia de detector de arrastre se puede especificar mediante la propiedad PermissionPolicy. Esto se establece en Enum.DragDetectorPermissionPolicy.Everybody por defecto, y también se puede cambiar para admitir controles de permisos programáticos como se muestra en el ejemplo de código.
| Ajuste | Descripción |
|---|---|
| Nobody | Ningún jugador puede interactuar con el DragDetector. |
| Everybody | Todos los jugadores pueden interactuar con el DragDetector. |
| Scriptable | Los permisos de arrastre de los jugadores serán determinados por una función registrada a través de SetPermissionPolicyFunction(). Bajo este ajuste, el fallo al registrar una función o devolver un resultado no válido evitará que todos los jugadores arrastren. |
DragDetector - Permiso de Arrastre Programado
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)
Respuesta física
Suponiendo que el estilo de respuesta del arrastre está configurado en Physical y se aplica a un objeto no anclado, ese objeto será movido por fuerzas de restricción que intentan llevarlo a la posición/orientación dada por el movimiento propuesto. Puedes personalizar aún más la respuesta física a través de las siguientes propiedades:
| Propiedad | Descripción | Predeterminado |
|---|---|---|
| ApplyAtCenterOfMass | Cuando es falso, la fuerza de arrastre se aplica en el punto donde hace clic el usuario. Cuando es verdadero, la fuerza se aplica en el centro de masa del objeto. | false |
| MaxForce | Fuerza máxima aplicada para que el objeto alcance su objetivo. | 10000000 |
| MaxTorque | Torque máximo aplicado para que el objeto alcance su objetivo. | 10000 |
| Responsiveness | Valores más altos hacen que el objeto alcance su objetivo más rápidamente. | 10 |
Entrada de modificador
Algunos modos de DragStyle permiten a los usuarios mantener presionada una tecla/botón modificador para manipular el objeto arrastrado de diferentes maneras. Por defecto, el modificador es LeftControl en PC, ButtonR1 en gamepad o ButtonL2 en VR. Puedes personalizar estos modificadores a través de las propiedades KeyboardModeSwitchKeyCode, GamepadModeSwitchKeyCode o VRSwitchKeyCode de la instancia del detector de arrastre.
Replicación
Cuando la propiedad RunLocally es falsa (predeterminada), el cliente interpreta toda la entrada para producir datos que envía al servidor para realizar el arrastre. En este modo, todas las señales de eventos personalizadas y funciones registradas deben estar en el servidor Scripts.
Cuando la propiedad RunLocally es verdadera, no se replican eventos al servidor. Todas las señales de eventos personalizadas y funciones registradas deben estar en el cliente LocalScripts y debes usar eventos remotos para propagar los cambios necesarios al servidor.
Respuestas de scripts a clics y arrastres
A través de señales de eventos, cambios de propiedades, estilo de arrastre Scriptable, y funciones personalizadas, los scripts pueden responder a la manipulación de objetos arrastrados para manejar la interfaz de usuario o tomar decisiones lógicas, como ajustar el nivel de luz en una habitación basado en un dimmer de interruptor deslizante.
Señales de eventos
A través de las siguientes señales de eventos, puedes detectar cuándo un usuario comienza, continúa y termina de arrastrar un objeto.
| Evento | Descripción |
|---|---|
| DragStart | Se activa cuando un usuario comienza a arrastrar el objeto. |
| DragContinue | Se activa cuando un usuario continúa arrastrando el objeto después de que se haya iniciado DragStart. |
| DragEnd | Se activa cuando un usuario deja de arrastrar el objeto. |
DragDetector - Señales de Eventos
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)
Cambios en el marco de arrastre
Además de las señales de eventos, puedes monitorear cambios en el DragFrame del detector directamente.
DragDetector - Cambios en DragFrame
local dragDetector = script.Parent.DragDetector
dragDetector:GetPropertyChangedSignal("DragFrame"):Connect(function()
local currentDragTranslation = dragDetector.DragFrame.Position
print(currentDragTranslation)
end)
Estilo de arrastre programado
Si configuras el DragStyle de un detector en Scriptable, puedes proporcionar tu propia función que toma un Ray y devuelve un CFrame en el espacio mundial. El detector moverá el movimiento de modo que el objeto arrastrado vaya a esa ubicación/orientación personalizada.
DragDetector - Estilo de Arrastre Programado
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)
-- Excluir objeto arrastrado de la detección de 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)
Función de restricción personalizada
Los detectores de arrastre no tienen reglas de movimiento integradas sobre rejillas y encajones, pero puedes registrar funciones de restricción personalizadas para editar el DragFrame del detector antes de que se aplique. Por ejemplo, puedes mantener el movimiento en una rejilla redondeando posiciones a múltiplos del incremento de rejilla, o simular un juego de ajedrez con reglas de movimiento legales para cada pieza.
DragDetector - Función de Restricción Personalizada
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)
-- Cuando sea aplicable, elimina la función de restricción invocando connection:Disconnect()
Ejemplo de uso
Objetos físicos no anclados
Una implementación básica de detectores de arrastre es un juego de equilibrio de torres donde los jugadores deben quitar cuidadosamente piezas y tratar de mantener la torre en pie. En la siguiente estructura de torre, cada pieza tiene un DragDetector como hijo con un DragStyle predeterminado de TranslatePlane para que los jugadores puedan tirar de las piezas hacia afuera pero no hacia arriba o hacia abajo.
Modelos anclados con partes ajustables
Puedes crear y compartir fácilmente modelos que están principalmente anclados, pero que tienen una o más partes/modelos hijos que los jugadores pueden arrastrar. Por ejemplo, el siguiente escritorio tiene dos cajones que los jugadores pueden abrir para inspeccionar lo que hay dentro.
Detectores de arrastre y restricciones
Puedes combinar detectores de arrastre con Constraints, por ejemplo, un marioneta. En la siguiente configuración, las manijas de control están ancladas, las partes del cuerpo no están ancladas, y las restricciones mantienen unidas a la marioneta. Mover las manijas con el estilo de arrastre TranslateViewPlane hace que la marioneta baile, y las partes individuales del cuerpo también pueden moverse con detectores de arrastre, todo mientras el modelo conserva su integridad.
Interfaces de usuario 3D
Las interfaces de usuario 3D son fácilmente alcanzables a través de detectores de arrastre, como ajustar el brillo de un SpotLight en función de un dimmer de interruptor deslizante. También puedes detectar los ejes X y Z individualmente para controlar dos aspectos diferentes de una interfaz de usuario 3D, como el Size, Speed, y Color de un ParticleEmitter.
DragDetector - Interfaz de Usuario 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
-- Ajustar el tamaño de las partículas y la velocidad en función del factor X del detector de arrastre
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)
-- Ajustar el color de las partículas en función del factor Z del detector de arrastre
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)