Понадобилось мне интерактивное управление объектами в игре «как в Roblox Studio» вечер покопавшись я нашёл два инструмента которые вроде как это позволяют.
- Handles — управления в стиле смещение и изменение размера объекта
- ArcHandles — управление поворотом объекта
Что даёт нам документация? Практически ничего. Но из неё можно узнать что:
- это элементы GUI, а следовательно размещаться должны в StarterGUI, чтобы попадать в GUI игрока
- т.к. это часть GUI, то взаимодействовать с ними мы можем только через локальные скрипты
Чем то это это напоминает UI Drag Detector и 3D Drag Detector т.к. по факту это их объединение в одно целое.
Ну да ладно. Вернёмся к нашим баранам.
Размещаем три парта в workspace и наши хандлеры в StarterGui:

В параметре Adornee каждого из них указываем на нужный парт и в итоге видим следующую картину (у простой ручки меняем параметр Style на Resize чтобы получить вариант Scale):

А далее не буду лить воду и просто дам локальные скрипты под каждой из ручек лежащий:
-- перемещение local handle = script.Parent -- управляющая ручка local part = handle.Adornee -- управляемый парт local minSize = 1 -- минимальный размер local oldDelta = 0 -- смещение от последнего движения handle.MouseDrag:Connect(function(face, distance) -- Enum направления и смещение local delta = distance - oldDelta -- новое смещение local direction = Vector3.fromNormalId(face) -- единичный вектор смещения -- смещение центра парта handle.Adornee:PivotTo(handle.Adornee.CFrame * CFrame.new(direction * delta)) oldDelta = distance end) -- сброс смещения когда отпущена кнопка handle.MouseButton1Up:Connect(function() oldDelta = 0 end)
-- изменение размера local handle = script.Parent local part = handle.Adornee local minSize = 1 -- минимальный размер local oldDelta = 0 -- смещение от последнего движения handle.MouseDrag:Connect(function(face, distance) -- Enum направления и смещение local delta = distance - oldDelta -- новое смещение local direction = Vector3.fromNormalId(face) -- единичный вектор смещения local size = Vector3.new(math.abs(direction.X), math.abs(direction.Y), math.abs(direction.Z)) -- положительный вектор local endSize = handle.Adornee.Size + delta * size -- расчёт нового размера handle.Adornee.Size = endSize handle.Adornee:PivotTo(handle.Adornee.CFrame * CFrame.new(direction / 2 * delta)) -- смещение центра парта oldDelta = distance end) handle.MouseButton1Up:Connect(function() -- сброс смещения когда отпущена кнопка oldDelta = 0 end)
-- изменение поворота
local handle = script.Parent -- управляющая ручка
local part = handle.Adornee -- управляемый парт
local lastCFrame = nil -- стартовый CFrame
local increment = 2 -- скорость поворота
-- расчёт смещения с учётом ускорения
function round(number)
return math.floor((number / increment) + 0.5) * increment
end
function AngleFromAxis(axis, r)
local relativeAngle = math.rad(round(math.deg(r))) -- получаем смещение угла
return axis == Enum.Axis.X and {relativeAngle, 0, 0} -- поворот в зависимости от оси
or axis == Enum.Axis.Y and {0, relativeAngle, 0}
or axis == Enum.Axis.Z and {0, 0, relativeAngle}
end
handle.MouseDrag:Connect(function(axis, relativeAngle, delta)
-- сложение CFrame в плане поворота
part.CFrame = lastCFrame * CFrame.Angles(unpack(AngleFromAxis(axis, relativeAngle)))
end)
-- стартовый CFrame от которого начинается вращение
handle.MouseButton1Down:Connect(function()
lastCFrame = part.CFrame
end)
Изменить их под себя вы можете самостоятельно. В том числе и в модуль запихнуть. 🙂