3D 拖动检测器

*此内容使用人工智能(Beta)翻译,可能包含错误。若要查看英文页面,请点按 此处

Class.DragDetector 实例在体验中与 3D 对象互动,例如打开门和抽屉、滑动零件周围、抓取和投掷保龄球、拉动并发射弹跳、和 much more。 的关键功能包括:

  • DragDetector 放置在任意 BasePartModel 下,通过所有输入(鼠标、触摸、游戏手柄和 VR),无需单行代验证码。

  • 从多个拖动风格中选择,定义对象如何应对运动,并且可选应用轴或运动限制

  • 脚本可以 回应拖动对象的操作来驱动用户界面或做出理论上的决策,例如根据室内灯光水平调整灯阴控制器的水平。

  • 玩家可以操作锚定的零件或模型,它们将在您释放时保持正确的位置。

  • Class.DragDetector|DragDetectors 在 Studio 中工作,直到您使用 选择 、移动 移动 、0>缩放0> 或DragDetectors3>工具,以便在编辑时测试和调整拖动对象更容易。

让对象可拖动

要将任何部分或模型拖动可拖动,请单击直接后后代 DragDetector

  1. Explorer 窗口中,将鼠标悬停在 PartMeshPart 或 1> Class.Model1> 上,然后单击 ⊕ 按钮。一个上下文菜单显示。

  2. 从菜单中,插入一个 拖动侦测器

  3. 默认情况下,对象现在将在地面平面中拖动,但您可以自定义其 DragStyle ,定义其 响应移动 方式,并且可选择 轴或移动限制 。

自定义拖动探测器

拖动样式

DragDetectors 地图曲线移动到虚拟线和飞机来计算所提出的3D移动。通过DragStyle属性,您可以选择从不同的映射满足您的需求。例如, TranslatePlane 生成在虚拟平面上的翻译,而

设置描述
TranslateLine1D 移动在探测器的 Axis 上,默认世界上的 Y 轴。
TranslatePlane在平面上与探测器的 Axis ,默认为世界上的 XZ 飞机。
TranslatePlaneOrLine在平面上与探测器的 Axis 和,当 调整因素 处于活动状态时, 1D 移动沿探测器的 Axis .
TranslateLineOrPlane1D 移动沿探测器的 Axis ,当 调整器 处于活动状态时,在平面上与探测器的 Axis 之间的 2D 移动。
TranslateViewPlane在飞机上平行于相镜头查看图的 2D 运动。在此模式下,飞机正常更新,即使是拖动,也会始终面向相镜头的当前视查看。
RotateAxis在探测器的 Axis 上旋转,默认世界上的 Y 轴。
RotateTrackball通过 TrackballRadialPullFactorTrackballRollFactor 属性进行跟踪球旋转。
BestForDevice 翻译机或线路图 对于鼠标和游戏手柄; 翻译机 对于触摸; 6DOF 对于 VR。
Scriptable通过提供自定义函数计算所需的移动方式 SetDragStyleFunction()

拖动方向

默认情况下,3D动作和相关的DragStyle 地图到世界空间。 您可以要求ReferenceInstanceOrientation或1> Class.DragDetector.Axis

属性描述默认
ReferenceInstance一个例子,其中的中心为拖动探测器提供 参考框DragFrame 用于此参考框,可以通过 GetReferenceFrame() 获取。 如nil
Orientation指定关键框相对于参考框的移动轴的旋转 YXZ (不会更改参考框自身的方向) 。 线性翻译和轴向旋转将在此重新定向 Y 轴上, 平面翻译在 XZ 平面上。 更改此值会自动更新 1> Class.DragDetector.Axis|Axis1>(0, 0, 0)
Axis移动的主轴,与参考框相对。改变此值会自动更新 Orientation 和 vice versa。(0, 1, 0)

机器人的反应

Class.DragDetector.ResponseStyle|ResponseStyle 属性指定对象在响应提议的运动时如何回应,这取决于对象是否是 Anchored 或不是。

设置锚定行为无锚定义的行为
Geometric在运行体验和 Studio 编辑模式中,固定对象的位置/方向将被更新,以反映所提出的运动。对于未锚定的对象,行为与锚定的对象相同。但在运行体验中,对象将在拖动开始时锚定,并在拖动释放时恢复为未锚定。
Physical一个锚定的对象将默认为 几何学 行为,因为它不受力学影响。一个未锚定的对象将被移动 by 限制力 ,试图将其带到所提供的移动位置和/或方向。
Custom对象将不会移全部,但 DragFrame 仍会更新,您可以 按照拖动操作响应 ,但您喜欢。(与锚定相同)

轴和移动限制

默认情况下,DragStyle 之上的 3D 运动不受限制。 如果需要,您可以对翻译和旋转的最小限制和最大限制进行适用。 请注意,这些不是限制;它们只是阻止拖动探测器在生成运动时遵循限制。

属性描述默认
Class.DragDetector.MinDragTranslation|MinDragTranslation``Class.DragDetector.MaxDragTranslation|MaxDragTranslation限制在每个维度中拖动翻译。如果MaxDragTranslation大于MinDragTranslation,翻译将在该范围内进行压缩。(0, 0, 0)
MinDragAngle MaxDragAngle仅适用于 DragStyle 设置为 RotateAxis 。如果 MaxDragAngle 大于 1> Class.DragDetorer.MinDragAngle|MinDragAngle1> ,旋转将在该范围内受到限制。0

拖动权限

玩家可以通过 PermissionPolicy 属性指定与指定的拖动探测器实例交互的权限。默认为 Enum.DragDetectorPermissionPolicy.Everybody,可以通过 Class.DragDetectorPermission Policy 设置为 2> Class.DragDetectorPermission2> 支持脚本权限控制,如在代码示例中所示。

设置描述
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在错误时,拖动力应用在用户点击的地方。 在真实时,力应用在对象的中心 масс。
MaxForce最大力量已应用于对象达成其目标。10000000
MaxTorque最大扭矩已适用于对象达到目标。10000
Responsiveness更高的值会导致目标更快地达到目标。10

调整因素输入

复制

RunLocally 属性为 false (默认) 时,客户端会将所有输入转换为数据,发送到服务器执行拖动。在此模式中,所有自定义事件信号和注册函数必须位于服务器 Scripts 中。

RunLocally 属性是真的时,不会有事件复制到服务器。所有自定义事件信号和注册函数必须在客户端 LocalScripts 中,您必须使用 远程事件 来传播必要的更改到服务器。

脚本回应单击和拖动

通过事件信号、属性变更、Scriptable 拖动式样式、自定义函数,脚本可以对拖动对象进行操作,驱动用户界面或做出逻辑决策,例如调整室内灯光水平基于滑动墙面开关的暗灯等级。

事件信号

通过以下事件信号,您可以检测到用户开始拖动对象时开始、继续和结束。

事件描述
DragStart当用户开始拖动对象时,火焰会立即点燃。
DragContinueDragStart 已初始化后,用户继续拖动对象时发生。
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 并返回一个世界空间 1> Datatype.CFrame1>。探测器将移动移动器到那个自定义位置/方向。

拖动侦测器 - 脚本拖动式

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 之前。例如,您可以在网格上圆形位置保留移动位置,或者 simulate 一个棋盘游戏,规则的移动对象允许每个位置。

拖动检测器 - 自定义约束函数

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 = math.floor(SNAP_INCREMENT)
if snapIncrement < 1 then
return proposedMotion
end
local newWorldPosition = startPartPosition + proposedMotion.Position
local roundedX = math.floor(newWorldPosition.X / snapIncrement + 0.5) * snapIncrement
local roundedY = math.floor(newWorldPosition.Y / snapIncrement + 0.5) * snapIncrement
local roundedZ = math.floor(newWorldPosition.Z / snapIncrement + 0.5) * 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 用默认 Class.DragDetector.DragStyle|DragStyle 的 DragStyle 以便玩家可以将部件从上向下拖动,但不能从上向

有可调整部件的固定模型

您可以轻松创建和共享主要锚定的模型,但有一个或多个子部件/模型,玩家可以拖动。例如,下面的桌子有两个抽屉,玩家可以打开以检查里面的内容。

拖动探测器和限制器

您可以使用 ConstraintsClass.DragDetector.DragStyle|DragStyle 来组合拖动检测器,例如一个假面傀儡。在下面的设置中,控制手柄是锚定的,身体部位是未锚定的,限制将 marionette �

3D 用户界面

3D用户界面通过拖动检测器(例如调整滑块暗灯器的亮度)轻松达到可见水平。您还可以单独检测到 SpotLight

拖动侦测器 - 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)