This may only be something that would useful for builders, but I know I would find myself using this feature quite often. Right now you have a few options to move objects: you could drag them, move them by typing in position coordinates, or by using the move tool’s arrows, which of course makes you use the mouse. My suggestion is that there’s some sort of way you can use certain keys (possibly the arrow keys) to move parts or models in your game. This would be nice for devs who are constantly on the move and working from laptops, or for people who just have an extremely unsteady hand. Of course this could still follow the move increment feature in studio. So if you have your lock set to one stud, for example, then when you pressed the left arrow it could move by one stud to the left. If it’s set to free, then possibly it move by .01 stud or even .001 stud. Although, using arrow keys could cause problems as they’re also used for moving around studio, perhaps you could provide some suggestions as to how it could work. Again, this mostly would help builders, but I believe it would be a useful feature to see in studio.
This feature is usually called ‘nudge’ fwiw
here;s a plugin I started hacking together a few weeks ago and haven’t had time to pickup again. it will increment movements like you’re talking about, but i didn’t add keyboard control (don’t know how yet). it also does rotations but only on the y axis, but making the functions for x and z are the same as y. was also going to make a button to reset rotation to (0,0,0) and a textbox for custom number and a few other things i don’t remember right now. i just don’t have time to finish it anytime real soon, so if anyone would like to take the useful parts and make something pretty with it and publish it, i would be delighted. btw it works as is, it’s just got a minimal ugly gui and missing a few functions to be Publlsh worthy
local hasLoaded = plugin:GetSetting("pluginHasLoaded")
if not hasLoaded then
plugin:SetSetting("pluginHasLoaded", true)
end
local toolbar = plugin:CreateToolbar("increment Move Selected")
local button = toolbar:CreateButton(
"move by increment",
"by johnnygadget",
"http://www.roblox.com/asset/?id=145723965"
)
local gui = Instance.new('ScreenGui',game:GetService('CoreGui'))
gui.Name = 'move increments'
gui.Enabled = false
local Frame = Instance.new("Frame")
local up = Instance.new("TextButton")
local down = Instance.new("TextButton")
local left = Instance.new("TextButton")
local right = Instance.new("TextButton")
local back = Instance.new("TextButton")
local forth = Instance.new("TextButton")
local size1 = Instance.new("TextButton")
local size2 = Instance.new("TextButton")
local size3 = Instance.new("TextButton")
local size4 = Instance.new("TextButton")
local size5 = Instance.new("TextButton")
local size6 = Instance.new("TextButton")
local rotate1 = Instance.new("TextButton")
local rotate01 = Instance.new("TextButton")
local rotate15 = Instance.new("TextButton")
local rotate225 = Instance.new("TextButton")
local rotate5 = Instance.new("TextButton")
local zero = Instance.new("TextButton")
Frame.Parent = gui
Frame.BackgroundColor3 = Color3.new(1, 1, 1)
Frame.Position = UDim2.new(0.0103734434, 0, 0.0223642178, 0)
Frame.Size = UDim2.new(0.149377599, 0, 0.690095842, 0)
up.Name = "up"
up.Parent = Frame
up.BackgroundColor3 = Color3.new(1, 1, 1)
up.Position = UDim2.new(0.37538898, 0, 0.273148149, 0)
up.Rotation = -90
up.Size = UDim2.new(0.20854944, 0, 0.0694444478, 0)
up.Style = Enum.ButtonStyle.Custom
up.Font = Enum.Font.SourceSans
up.FontSize = Enum.FontSize.Size14
up.Text = ">"
up.TextSize = 14
up.TextWrapped = true
down.Name = "down"
down.Parent = Frame
down.BackgroundColor3 = Color3.new(1, 1, 1)
down.Position = UDim2.new(0.37538898, 0, 0.347222209, 0)
down.Rotation = 90
down.Size = UDim2.new(0.20854944, 0, 0.0694444478, 0)
down.Style = Enum.ButtonStyle.Custom
down.Font = Enum.Font.SourceSans
down.FontSize = Enum.FontSize.Size14
down.Text = ">"
down.TextSize = 14
down.TextWrapped = true
left.Name = "left"
left.Parent = Frame
left.BackgroundColor3 = Color3.new(1, 1, 1)
left.Position = UDim2.new(0.23635605, 0, 0.42592594, 0)
left.Rotation = 180
left.Size = UDim2.new(0.20854944, 0, 0.0694444478, 0)
left.Style = Enum.ButtonStyle.Custom
left.Font = Enum.Font.SourceSans
left.FontSize = Enum.FontSize.Size14
left.Text = ">"
left.TextSize = 14
left.TextWrapped = true
right.Name = "right"
right.Parent = Frame
right.BackgroundColor3 = Color3.new(1, 1, 1)
right.Position = UDim2.new(0.50051868, 0, 0.42592594, 0)
right.Size = UDim2.new(0.20854944, 0, 0.0694444478, 0)
right.Style = Enum.ButtonStyle.Custom
right.Font = Enum.Font.SourceSans
right.FontSize = Enum.FontSize.Size14
right.Text = ">"
right.TextSize = 14
right.TextWrapped = true
back.Name = "back"
back.Parent = Frame
back.BackgroundColor3 = Color3.new(1, 1, 1)
back.Position = UDim2.new(0.44490549, 0, 0.578703701, 0)
back.Rotation = 45
back.Size = UDim2.new(0.20854944, 0, 0.0694444478, 0)
back.Style = Enum.ButtonStyle.Custom
back.Font = Enum.Font.SourceSans
back.FontSize = Enum.FontSize.Size14
back.Text = ">"
back.TextSize = 14
back.TextWrapped = true
back.Rotation = 45
forth.Name = "forth"
forth.Parent = Frame
forth.BackgroundColor3 = Color3.new(1, 1, 1)
forth.Position = UDim2.new(0.26416263, 0, 0.523148119, 0)
forth.Rotation = 225
forth.Size = UDim2.new(0.20854944, 0, 0.0694444478, 0)
forth.Style = Enum.ButtonStyle.Custom
forth.Font = Enum.Font.SourceSans
forth.FontSize = Enum.FontSize.Size14
forth.Text = ">"
forth.TextSize = 14
forth.TextWrapped = true
forth.Rotation = 225
size1.Name = "size1"
size1.Parent = Frame
size1.BackgroundColor3 = Color3.new(1, 1, 1)
size1.Position = UDim2.new(0.0417098887, 0, 0.0833333358, 0)
size1.Size = UDim2.new(0.23635605, 0, 0.0740740746, 0)
size1.Font = Enum.Font.SourceSans
size1.FontSize = Enum.FontSize.Size14
size1.Text = ".01"
size1.TextSize = 14
size1.TextWrapped = true
size2.Name = "size2"
size2.Parent = Frame
size2.BackgroundColor3 = Color3.new(1, 1, 1)
size2.Position = UDim2.new(0.31977582, 0, 0.0833333358, 0)
size2.Size = UDim2.new(0.23635605, 0, 0.0740740746, 0)
size2.Font = Enum.Font.SourceSans
size2.FontSize = Enum.FontSize.Size14
size2.Text = ".05"
size2.TextSize = 14
size2.TextWrapped = true
size3.Name = "size3"
size3.Parent = Frame
size3.BackgroundColor3 = Color3.new(1, 1, 1)
size3.Position = UDim2.new(0.611745059, 0, 0.0833333358, 0)
size3.Size = UDim2.new(0.23635605, 0, 0.0740740746, 0)
size3.Font = Enum.Font.SourceSans
size3.FontSize = Enum.FontSize.Size14
size3.Text = ".1"
size3.TextSize = 14
size3.TextWrapped = true
size4.Name = "size4"
size4.Parent = Frame
size4.BackgroundColor3 = Color3.new(1, 1, 1)
size4.Position = UDim2.new(0.0417098887, 0, 0.175925925, 0)
size4.Size = UDim2.new(0.23635605, 0, 0.0740740746, 0)
size4.Font = Enum.Font.SourceSans
size4.FontSize = Enum.FontSize.Size14
size4.Text = ".2"
size4.TextSize = 14
size4.TextWrapped = true
size5.Name = "size5"
size5.Parent = Frame
size5.BackgroundColor3 = Color3.new(1, 1, 1)
size5.Position = UDim2.new(0.31977582, 0, 0.175925925, 0)
size5.Size = UDim2.new(0.23635605, 0, 0.0740740746, 0)
size5.Font = Enum.Font.SourceSans
size5.FontSize = Enum.FontSize.Size14
size5.Text = ".5"
size5.TextSize = 14
size5.TextWrapped = true
size6.Name = "size6"
size6.Parent = Frame
size6.BackgroundColor3 = Color3.new(1, 1, 1)
size6.Position = UDim2.new(0.611745059, 0, 0.175925925, 0)
size6.Size = UDim2.new(0.23635605, 0, 0.0740740746, 0)
size6.Font = Enum.Font.SourceSans
size6.FontSize = Enum.FontSize.Size14
size6.Text = "1"
size6.TextSize = 14
size6.TextWrapped = true
rotate1.Name = "rotate1"
rotate1.Parent = Frame
rotate1.BackgroundColor3 = Color3.new(1, 1, 1)
rotate1.Position = UDim2.new(0.40319559, 0, 0.722222209, 0)
rotate1.Size = UDim2.new(0.23635605, 0, 0.0740740746, 0)
rotate1.Font = Enum.Font.SourceSans
rotate1.FontSize = Enum.FontSize.Size14
rotate1.Text = "1*"
rotate1.TextSize = 14
rotate1.TextWrapped = true
rotate01.Name = "rotate01"
rotate01.Parent = Frame
rotate01.BackgroundColor3 = Color3.new(1, 1, 1)
rotate01.Position = UDim2.new(0.0834197775, 0, 0.722222209, 0)
rotate01.Size = UDim2.new(0.23635605, 0, 0.0740740746, 0)
rotate01.Font = Enum.Font.SourceSans
rotate01.FontSize = Enum.FontSize.Size14
rotate01.Text = ".1*"
rotate01.TextSize = 14
rotate01.TextWrapped = true
rotate15.Name = "rotate15"
rotate15.Parent = Frame
rotate15.BackgroundColor3 = Color3.new(1, 1, 1)
rotate15.Position = UDim2.new(0.0973230749, 0, 0.842592597, 0)
rotate15.Size = UDim2.new(0.23635605, 0, 0.0740740746, 0)
rotate15.Font = Enum.Font.SourceSans
rotate15.FontSize = Enum.FontSize.Size14
rotate15.Text = "15*"
rotate15.TextSize = 14
rotate15.TextWrapped = true
rotate225.Name = "rotate225"
rotate225.Parent = Frame
rotate225.BackgroundColor3 = Color3.new(1, 1, 1)
rotate225.Position = UDim2.new(0.40319559, 0, 0.842592597, 0)
rotate225.Size = UDim2.new(0.23635605, 0, 0.0740740746, 0)
rotate225.Font = Enum.Font.SourceSans
rotate225.FontSize = Enum.FontSize.Size14
rotate225.Text = "22.5*"
rotate225.TextSize = 14
rotate225.TextWrapped = true
rotate5.Name = "rotate5"
rotate5.Parent = Frame
rotate5.BackgroundColor3 = Color3.new(1, 1, 1)
rotate5.Position = UDim2.new(0.70906812, 0, 0.722222209, 0)
rotate5.Size = UDim2.new(0.23635605, 0, 0.0740740746, 0)
rotate5.Font = Enum.Font.SourceSans
rotate5.FontSize = Enum.FontSize.Size14
rotate5.Text = "5*"
rotate5.TextSize = 14
rotate5.TextWrapped = true
zero.Name = "zero"
zero.Parent = Frame
zero.BackgroundColor3 = Color3.new(0.72549, 0.72549, 0.72549)
zero.Position = UDim2.new(0.36148572, 0, 0.944444418, 0)
zero.Size = UDim2.new(0.29196921, 0, 0.0416666679, 0)
zero.Font = Enum.Font.SourceSans
zero.FontSize = Enum.FontSize.Size14
zero.Text = "global"
zero.TextSize = 14
zero.TextWrapped = true
local on = false
local moveamount = .05
local global = true
local function rotatepartglobal(part,deg)
if part then
part.CFrame = part.CFrame * CFrame.Angles(0,math.rad(deg),0)
end
end
local function rotatepartlocal(part,deg)
if part then
part.CFrame = part.CFrame:toObjectSpace(part.CFrame * CFrame.Angles(0,math.rad(deg),0))
end
end
local function rotatemodelglobal(model,deg)
if model then
model:SetPrimaryPartCFrame(model.PrimaryPart.CFrame * CFrame.Angles(0,math.rad(deg),0))
end
end
local function rotatemodellocal(model,deg)
if model then
model:SetPrimaryPartCFrame(model.PrimaryPart.CFrame:toObjectSpace(model.PrimaryPart.CFrame * CFrame.Angles(0,math.rad(deg),0)))
end
end
local function movepartglobal(part,direction)
if part then
if direction == 'up' then
part.CFrame = part.CFrame * CFrame.new(0,moveamount,0)
elseif direction == 'down' then
part.CFrame = part.CFrame * CFrame.new(0,-moveamount,0)
elseif direction == 'left' then
part.CFrame = part.CFrame * CFrame.new(-moveamount,0,0)
elseif direction == 'right' then
part.CFrame = part.CFrame * CFrame.new(moveamount,0,0)
elseif direction == 'back' then
part.CFrame = part.CFrame * CFrame.new(0,0,-moveamount)
elseif direction == 'forth' then
part.CFrame = part.CFrame * CFrame.new(0,0,moveamount)
end
end
end
local function movepartlocal(part,direction)
if part then
if direction == 'up' then
part.CFrame = part.CFrame:toObjectSpace(part.CFrame * CFrame.new(0,moveamount,0))
elseif direction == 'down' then
part.CFrame = part.CFrame:toObjectSpace(part.CFrame * CFrame.new(0,-moveamount,0))
elseif direction == 'left' then
part.CFrame = part.CFrame:toObjectSpace(part.CFrame * CFrame.new(-moveamount,0,0))
elseif direction == 'right' then
part.CFrame = part.CFrame:toObjectSpace(part.CFrame * CFrame.new(moveamount,0,0))
elseif direction == 'back' then
part.CFrame = part.CFrame:toObjectSpace(part.CFrame * CFrame.new(0,0,-moveamount))
elseif direction == 'forth' then
part.CFrame = part.CFrame:toObjectSpace(part.CFrame * CFrame.new(0,0,moveamount))
end
end
end
local function movemodelglobal(model,direction)
if model then
local pp = model.PrimaryPart
if not pp then
print('Model has no primary part')
return
else
if direction == 'up' then
model:SetPrimaryPartCFrame(model.PrimaryPart.CFrame * CFrame.new(0,moveamount,0))
elseif direction == 'down' then
model:SetPrimaryPartCFrame(model.PrimaryPart.CFrame * CFrame.new(0,-moveamount,0))
elseif direction == 'left' then
model:SetPrimaryPartCFrame(model.PrimaryPart.CFrame * CFrame.new(-moveamount,0,0))
elseif direction == 'right' then
model:SetPrimaryPartCFrame(model.PrimaryPart.CFrame * CFrame.new(moveamount,0,0))
elseif direction == 'back' then
model:SetPrimaryPartCFrame(model.PrimaryPart.CFrame * CFrame.new(0,0,-moveamount))
elseif direction == 'forth' then
model:SetPrimaryPartCFrame(model.PrimaryPart.CFrame * CFrame.new(0,0,moveamount))
end
end
end
end
local function movemodellocal(model,direction)
if model then
local pp = model.PrimaryPart
if not pp then
print('Model has no primary part')
return
else
if direction == 'up' then
model:SetPrimaryPartCFrame(model.PrimaryPart.CFrame:toObjectSpace(model.PrimaryPart.CFrame * CFrame.new(0,moveamount,0)))
elseif direction == 'down' then
model:SetPrimaryPartCFrame(model.PrimaryPart.CFrame:toObjectSpace(model.PrimaryPart.CFrame * CFrame.new(0,-moveamount,0)))
elseif direction == 'left' then
model:SetPrimaryPartCFrame(model.PrimaryPart.CFrame:toObjectSpace(model.PrimaryPart.CFrame * CFrame.new(-moveamount,0,0)))
elseif direction == 'right' then
model:SetPrimaryPartCFrame(model.PrimaryPart.CFrame:toObjectSpace(model.PrimaryPart.CFrame * CFrame.new(moveamount,0,0)))
elseif direction == 'back' then
model:SetPrimaryPartCFrame(model.PrimaryPart.CFrame:toObjectSpace(model.PrimaryPart.CFrame * CFrame.new(0,0,moveamount)))
elseif direction == 'forth' then
model:SetPrimaryPartCFrame(model.PrimaryPart.CFrame:toObjectSpace(model.PrimaryPart.CFrame * CFrame.new(0,0,-moveamount)))
end
end
end
end
local function checktyperotate(thing,direction)
if thing == nil then return end
for i, v in pairs(thing) do
if v:IsA('Part') then
if global == true then
rotatepartglobal(v,direction)
else
rotatepartlocal(v,direction)
end
elseif v:IsA('Model') then
if global == true then
rotatemodelglobal(v,direction)
else
rotatemodellocal(v,direction)
end
end
end
end
local function checktype(thing,direction)
if thing == nil then return end
for i, v in pairs(thing) do
if v:IsA('Part') then
if global == true then
movepartglobal(v,direction)
else
movepartlocal(v,direction)
end
elseif v:IsA('Model') then
if global == true then
movemodelglobal(v,direction)
else
movemodellocal(v,direction)
end
end
end
end
local function rotateprocess(deg)
local p = game.Selection:Get()
if p then
for i, v in pairs(p) do
checktyperotate(p,deg)
end
end
end
local function moveprocess(way)
local p = game.Selection:Get()
if p then
for i, v in pairs(p) do
checktype(p,way)
end
end
end
zero.MouseButton1Down:connect(function()
if global == true then
global = false
zero.Text = 'local'
else
global = true
zero.Text = 'global'
end
end)
up.MouseButton1Down:connect(function()
moveprocess('up')
end)
down.MouseButton1Down:connect(function()
moveprocess('down')
end)
left.MouseButton1Down:connect(function()
moveprocess('left')
end)
right.MouseButton1Down:connect(function()
moveprocess('right')
end)
back.MouseButton1Down:connect(function()
moveprocess('back')
end)
forth.MouseButton1Down:connect(function()
moveprocess('forth')
end)
size1.MouseButton1Down:connect(function()
moveamount = .01
end)
size2.MouseButton1Down:connect(function()
moveamount = .05
end)
size3.MouseButton1Down:connect(function()
moveamount = .1
end)
size4.MouseButton1Down:connect(function()
moveamount = .2
end)
size5.MouseButton1Down:connect(function()
moveamount = .5
end)
size6.MouseButton1Down:connect(function()
moveamount = 1
end)
rotate01.MouseButton1Down:connect(function()
rotateprocess(.01)
end)
rotate1.MouseButton1Down:connect(function()
rotateprocess(1)
end)
rotate5.MouseButton1Down:connect(function()
rotateprocess(5)
end)
rotate15.MouseButton1Down:connect(function()
rotateprocess(15)
end)
rotate225.MouseButton1Down:connect(function()
rotateprocess(22.5)
end)
button.Click:connect(function()
plugin:Activate(false)
gui.Enabled = true
end)
I honestly think that the there should be an option to activate a movement sequence and move objects according to your camera angle, much like the way blender works. Naturally be given the options to use the axis keys on your keyboard (X, Y, Z) to lock to a specific axis.
Another good idea for the drag tool is to have an option to move an object to a requested direction according to your increment value by simply clicking an arrow, allowing you to do extremely precise and fine movements very easily.
If there was a way to interact with the current given tools using plugins, I would have made a feature like that long ago.
Unless it is possible, but I’m just not aware of it… .-.
this is what the code i posted will do, i just haven’t added the option for user value
If you urgently need this functionality, you can use Building Tools by F3X: https://www.roblox.com/library/144950355/Building-Tools-by-F3X-Plugin
You can drag the parts roughly to where you want them, then perfectly align them with others using snap points (i.e. corners, edge midpoints, face centroids). Just hold R to see & drag snap points in the Move or Resize Tools—gets rid of the need to eyeball
Snapping in the Move Tool:
https://i.gyazo.com/5af8aff72876e15299981bfe9083e0a6.gifv
Snapping in the Resize Tool:
https://i.gyazo.com/a5bb51950a6c11507193f74aa2ed083f.gifv
You can also use nudging (which applies a movement, resize, or rotation in the specified direction by the current increment), which is particularly helpful when dealing with very precise, tiny increments, or when you don’t want to use your mouse:
https://i.gyazo.com/159e2ff412e26d5fa428aab8270e83eb.gifv
You can use this feature through your keyboard’s right-side number pad:
8 & 2 are up and down along the Y axis, respectively
4 & 6 are left and right along the X axis, respectively
1 & 9 are back and forth along the Z axis, respectively
Also if you hate using the mouse at all, it’s probably worth noting that you can type a different increment by simply pressing the - (dash) key, or switch between axes modes by simply pressing the Enter key Hopefully this is helpful!
That’s a really useful plugin. I’ve used it before, and I do really like it. The only issue, for me personally, is the inability to use hotkeys to change the Axes and Increment values. By that I mean a given increment. Currently, I use Alt-1 to 3 to change between a 1 stud, 0.2 studs and no stud increment.
While hotkeys would make it a lot faster to switch between increments, the main issue is having to write down the increment value every time. Would be a lot more convenient to also have given increment values that can be set without having the need to re-write them every time. Believe me, I change increments a lot, and moving my hands from mouse to keyboard and back very frequently will sore my arms.
I made a plugin years ago to do this (before plugins were around). I know some people still use it. A couple years ago, I ported it to be an actual plugin.
It can move individual parts, groups of selected parts, or entire models. I still use it semi-frequently when I need to make little adjustments.
Increment presets feel clunky to me compared to hotkeys for doubling/halving the increment.
The keys for that in studio build suite are 2 & 3.