Trying to create a gizmo like in blender, very annoying issue

So I’m trying to replicate a move gizmo like the one you would see in blender, I’ve gotten to the point where you can move the gizmo and whatever you’ve selected but I don’t think using a 2D Mouse Delta is how the other programs are calculating where to update the gizmo and what you’ve selected next…

If my im facing one way then it looks like it works as intended…

But then lets say im looking in another direction it’s completely janky

I have a feeling its because I’m using a 2D Mouse Delta to move the gizmo, and there isnt info for it to correctly know where to move??

This is it how it should behave

The code for it is actually really straightforward aswell, any ideas what I could do?

if UserInputService:IsKeyDown(Enum.KeyCode.F) then

  local currentMousePosition = UserInputService:GetMouseLocation()
  local delta = (currentMousePosition - previousMousePosition)

  delta = Vector2.new(math.clamp(delta.X, -1, 1), math.clamp(delta.Y, -1, 1))
    
  if selected then
    local motor = TemplateR15[selected.Name]:FindFirstChildOfClass("Motor6D")
    if closestAxis then
      local hitPosition = mouse.Hit.p

      if closestAxis.Name == "Y" then
        motor.C0 += motor.Part0.CFrame:VectorToObjectSpace(Vector3.new(0, -delta.Y * dt * speed, 0))
      elseif closestAxis.Name == "Z" then
        motor.C0 += motor.Part0.CFrame:VectorToObjectSpace(Vector3.new(0, 0, delta.X * dt * speed))
      elseif closestAxis.Name == "X" then
        motor.C0 += motor.Part0.CFrame:VectorToObjectSpace(Vector3.new(-delta.Y * dt * speed, 0, 0))
      end
      MoveGizmo.CFrame = CFrame.new(selected.Position)
    end
  end

  previousMousePosition = currentMousePosition
end
1 Like

I believe I understand your issue. I’ll explain in a moment, but would you kindly provide us some additional details? I’m not sure what exactly closestAxis is or how your gizmos model is organized.
It would be very helpful if you could send us a picture of the instance in the Explorer with all the children visible and some information about the closestAxis variable.

1 Like

Thank you,

so this is the gizmo

image

And the way I’m getting the closestAxis is by checking the distance between each part inside of the gizmo

2 Likes

I appreciate the information. I was able to quickly put together a prototype to show you using this information.

Please pardon my sluggish reaction time as well.

Your initial question was almost accurate. Since you are only multiplying with one direction, your gizmo will become “janky” if you flip sides because the controls will be inverted. To combat this, I created a function that changes the coordinates from Vector2 to Vector3 depending on your camera. This stage took a while, so eventually I just made on my own function. Keep in mind that this is simply a rough sample to aid you in solving your issue. This is the outcome.

Pretend that there are some really cool direction pointers sticking out as your gyzmo :sunglasses:
You can alter the sensetivity, so don’t worry about it.


We must first declare a few variables.

local UserInputService = game:GetService("UserInputService")
local Players = game:GetService("Players")
local Player = Players.LocalPlayer

local Camera = game.Workspace.CurrentCamera

local Dragging = false
local StartPosition = nil
local InitialPosition = nil

local Sensetivity = 10

local Depth = 10
local Decelerate = 50

local Selected = Instance.new("Part", workspace) -- Demonstration purposes
Selected.Anchored = true

This function transforms the coordinates for us. Though not very exact, it serves its purpose. Later, you can always make improvements.

-- ignore the wierd function name it was actually supposed to be ScreenToWorld but I was and am so sleepy eepy
function MouseToScreen(Position: Vector2, Distance: number, Decelerate: number): Vector3
	local Mouse = Player:GetMouse()
	
	Position = Vector2.new(Position.X - Mouse.ViewSizeX / 2, -(Position.Y - Mouse.ViewSizeY / 2))

	local CameraPosition = Camera.CFrame.Position
	local CameraRotation = Camera.CFrame - CameraPosition

	local objectPosition = CameraPosition + (CameraRotation * CFrame.new(Position.X/Decelerate, Position.Y/Decelerate, -Distance)).Position

	return objectPosition
end

The applications will be explained in this important section. Ever notice when you press G in Blender theres this sort of invisible grid where your mesh “slides” on?
Please excuse my crude illustration


Basically, this is what we do. We have two positions: the origin (Start, Red) and the cursor point in three-dimensional space (End, Green). Then we calculate thier magnitude (Distance) and direction. This results in the same effect as the Blender “plane”. The world directions and drag directions can now be multiplied! Heres a visual demonstration.


These inputs are not intended to be the optimal choices and are only being used for demonstration purposes.

function OnMouseChanged()
	if Dragging then
		local EndPosition = Vector2.new(Player:GetMouse().X, Player:GetMouse().Y)
		
		local StartWorldPos = MouseToScreen(Vector2.new(StartPosition.X, StartPosition.Y), Depth, Decelerate)
		local EndWorldPos = MouseToScreen(Vector2.new(EndPosition.X, EndPosition.Y), Depth, Decelerate)

		local DragDirection = (EndWorldPos - StartWorldPos).Unit
		local DragDistance = (EndWorldPos - StartWorldPos).Magnitude
		
		if DragDistance == 0 then
			Selected.Position = InitialPosition
			return
		end
		
		-- This is just a simulation of your gizmos.
		if UserInputService:IsKeyDown(Enum.KeyCode.X) then
			Selected.Position = DragDirection * Vector3.new(1, 0, 0) * DragDistance * Sensetivity + InitialPosition
		end

		if UserInputService:IsKeyDown(Enum.KeyCode.Y) then
			Selected.Position = DragDirection * Vector3.new(0, 1, 0) * DragDistance * Sensetivity + InitialPosition
		end

		if UserInputService:IsKeyDown(Enum.KeyCode.Z) then
			Selected.Position = DragDirection * Vector3.new(0, 0, 1) * DragDistance * Sensetivity + InitialPosition
		end
	end
end

local function OnMouseDown(input)
	if input.UserInputType == Enum.UserInputType.MouseButton1 then
		Dragging = true
		StartPosition = Vector2.new(Player:GetMouse().X, Player:GetMouse().Y)
		InitialPosition = Selected.Position
	end
end

local function OnMouseUp(input)
	if Dragging and input.UserInputType == Enum.UserInputType.MouseButton1 then
		Dragging = false
	end
end

UserInputService.InputChanged:Connect(OnMouseChanged)
UserInputService.InputEnded:Connect(OnMouseUp)
UserInputService.InputBegan:Connect(OnMouseDown)

That’s basically it. It took over three hours, which surprises me (In a bad way, took way too long :sob:), but hey, it works!


Full source code

local UserInputService = game:GetService("UserInputService")
local Players = game:GetService("Players")
local Player = Players.LocalPlayer

local Camera = game.Workspace.CurrentCamera

local Dragging = false
local StartPosition = nil
local InitialPosition = nil

local Sensetivity = 10

local Depth = 10
local Decelerate = 50

local Selected = Instance.new("Part", workspace)
Selected.Anchored = true

function MouseToScreen(Position: Vector2, Distance: number, Decelerate: number): Vector3
	local Mouse = Player:GetMouse()
	
	Position = Vector2.new(Position.X - Mouse.ViewSizeX / 2, -(Position.Y - Mouse.ViewSizeY / 2))

	local CameraPosition = Camera.CFrame.Position
	local CameraRotation = Camera.CFrame - CameraPosition

	local objectPosition = CameraPosition + (CameraRotation * CFrame.new(Position.X/Decelerate, Position.Y/Decelerate, -Distance)).Position

	return objectPosition
end

function OnMouseChanged()
	if Dragging then
		local EndPosition = Vector2.new(Player:GetMouse().X, Player:GetMouse().Y)
		
		local StartWorldPos = MouseToScreen(Vector2.new(StartPosition.X, StartPosition.Y), Depth, Decelerate)
		local EndWorldPos = MouseToScreen(Vector2.new(EndPosition.X, EndPosition.Y), Depth, Decelerate)

		local DragDirection = (EndWorldPos - StartWorldPos).Unit
		local DragDistance = (EndWorldPos - StartWorldPos).Magnitude
		
		if DragDistance == 0 then
			Selected.Position = InitialPosition
			return
		end
		
		-- This is just a simulation of your gizmos.
		if UserInputService:IsKeyDown(Enum.KeyCode.X) then
			Selected.Position = DragDirection * Vector3.new(1, 0, 0) * DragDistance * Sensetivity + InitialPosition
		end

		if UserInputService:IsKeyDown(Enum.KeyCode.Y) then
			Selected.Position = DragDirection * Vector3.new(0, 1, 0) * DragDistance * Sensetivity + InitialPosition
		end

		if UserInputService:IsKeyDown(Enum.KeyCode.Z) then
			Selected.Position = DragDirection * Vector3.new(0, 0, 1) * DragDistance * Sensetivity + InitialPosition
		end
	end
end

local function OnMouseDown(input)
	if input.UserInputType == Enum.UserInputType.MouseButton1 then
		Dragging = true
		StartPosition = Vector2.new(Player:GetMouse().X, Player:GetMouse().Y)
		InitialPosition = Selected.Position
	end
end

local function OnMouseUp(input)
	if Dragging and input.UserInputType == Enum.UserInputType.MouseButton1 then
		Dragging = false
	end
end

UserInputService.InputChanged:Connect(OnMouseChanged)
UserInputService.InputEnded:Connect(OnMouseUp)
UserInputService.InputBegan:Connect(OnMouseDown)

This example is located in the StarterCharacterScripts and is saved as a local script
image

Ask away if you are having issues or questions. I’m not bothered.

1 Like

bro… I am SO SORRY that I

didn’t reply sooner so basically; I was super excited when I saw your solution… but I just dont like taking code I kinda wanted to make it from scratch on my own using yours as a guide… so FINALLY; LIKE CAN’T THANK YOU ENOUGH; I changed some stuff a bit and I figured out how to make the gizmo stay with the mouse aswell as using the function ScreenPointToRay, I was super confused why you were creating your own custom function for it ngl :sob::sob::sob:

Like your solution HELPED ME SO MUCH but as you know in programming as you get something working something else decides to stop working… countless hours of trying to figure out what was the issue and had to transformation stuff cause im using a motor6D, GOD it was a mess; but your solution paved the way for these new bugs that I could fix!

Thank you so so much, I learned alot these past few days!


luke-skywalker-star-wars

1 Like

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.