Inaccurate input on Handles and ArcHandles

Hi, i got told from a moderator to report this minor bug i experienced today in Studito you via Dms since im currently not able to make a post under the bugs category.

Description:
I just found out that by moving the camera to the side of the player using the module in this post: Non-central offset for the Camera's Focus or Field of View the inputs of Handles and ArcHandles are moved away from the displayied handle.

Heres a post i previously made about it, got hidden.
https://devforum.roblox.com/t/buggy-input-on-handlers-and-archandlers/3114865


As you can see the hover input (and any other) is fired when the mouse pointer its not supposed to be. I know this is probably related to the rendering but would be nice to improve it.

Reproduce:
Create a new ArcHandles or Handles in PlayerGui and set the Adornee property to any object.
Require the module and start it with the following script:

How i setupped the module:

local viewHandler = require(module)
viewHandler.Position = UDim2.fromScale(0.35, 0)
viewHandler.Size = UDim2.fromScale(0.75, 0.75)
viewHandler.StartStatic()

My edited version of the module:

local RunService = game:GetService("RunService")
local Workspace = game:GetService("Workspace")
local Camera = Workspace.CurrentCamera

local module = {}
module.Position = UDim2.new(0, 0, 0, 0)
module.Size = UDim2.new(1, 0, 1, 0)

local function UDim2Absolute(udim2)
	local viewSize = Camera.ViewportSize

	return Vector2.new(
		(udim2.X.Scale * viewSize.X) + udim2.X.Offset,
		(udim2.Y.Scale * viewSize.Y) + udim2.Y.Offset
	)
end

--- Compute offset

function module._computePosition()
	local viewSize = Camera.ViewportSize
	local aspectRatio = viewSize.X / viewSize.Y

	local offset = UDim2Absolute(module.Position)
	local position = offset / viewSize

	-- Taken from ScreenSpace
	local hFactor = math.tan(math.rad(Camera.FieldOfView) / 2)
	local wFactor = hFactor * aspectRatio

	return -position.X * wFactor * 2, position.Y * hFactor * 2
end

function module._computeSize()
	local size = UDim2Absolute(module.Size) / Camera.ViewportSize
	return size.X, size.Y
end

function module._getOffset()
	local x, y = module._computePosition()
	local w, h = module._computeSize()

	return CFrame.new(
		0, 0, 0,

		w, 0, 0,
		0, h, 0,
		x, y, 1
	)
end

--- Handler

function module.Start()
	module.Stop()
	RunService:BindToRenderStep("ViewportResizer", Enum.RenderPriority.Camera.Value + 1, function()
		Camera.CFrame = Camera.CFrame * module._getOffset()
	end)
end

function module.StartStatic() -- A lot more efficient when there are no dynamic changes to the properties
	local offset = module._getOffset()
	module.Stop()
	
	RunService:BindToRenderStep("ViewportResizer", Enum.RenderPriority.Camera.Value + 1, function()
		Camera.CFrame = Camera.CFrame * offset
	end)
end

function module.Stop()
	RunService:UnbindFromRenderStep("ViewportResizer")
end

return module

Ty for your time :wink:

Thanks for the report! Just sent this over to the team. We’ll update you as soon as we can :slight_smile:

1 Like

Hi @richiitalia,

The problem here is that you are constructing a matrix that is not an affine matrix. Meaning, it is not a combination of rotate/translate/scale matrices.
Also in roblox your matrix should be strictly a rotation and translation, without scale. Otherwise you cannot count on inputs and rays being calculated correctly.

Your matrix is essentially changing the perspective transformation and centering of the camera using methods that might be legal if our pipeline allowed arbitrary matrices, but it does not.
That is why your input/picking/selection is off.

It sounds like what you want to do is keep the camera always looking to the left of the character.
I made a little script/world that kind of get this right.

In my outer script, I set an OffsetDistance and OffsetRotation on the module. (I used 20 studs and a 90 degree Y rotation to put the camera to the player’s left).
Then in the CameraMoverModule, I use those to place the camera reltaive to the player’s pivot.
Lastly, in order to stop the camera from spinning, I fished out the controlModule from the PlayerScripts, and set the controlModule.activeController.moveVectorIsCameraRelative to false. This last trick means the character always moves in x/z directions, and the camera can be placed around it.

Here is the sample world I created. It has a part, AcrHandles, and the scripts I describe here.
SBT-3207_02.rbxl (57.5 KB)

The module script looks like this:


local RunService = game:GetService("RunService")
local Workspace = game:GetService("Workspace")
local Camera = Workspace.CurrentCamera

local controlModule = require(game.Players.LocalPlayer:WaitForChild("PlayerScripts").PlayerModule:WaitForChild("ControlModule"))

local module = {}
module.OffsetDistance = 1
module.OffsetRotation = CFrame.new()

local function getPlayerPivot()
	local player = game.Players.LocalPlayer
	local rootPart = nil
	if player and player.Character then
		rootPart = player.Character:WaitForChild("HumanoidRootPart")
	end
	if not rootPart then
		print("no rootPart, skipping.")
		return CFrame.new()
	end
	return rootPart:GetPivot()
end

function module.Start()
	module.Stop()
	
	Camera.CameraType = Enum.CameraType.Scriptable

	RunService:BindToRenderStep("CameraMover", Enum.RenderPriority.Camera.Value + 1, function()
		local playerPivot = getPlayerPivot()
		Camera.CFrame = playerPivot * module.OffsetRotation * CFrame.new(Vector3.new(0,0,module.OffsetDistance))
		
		-- Set this so player doesn't move relative to camera; without this you get spinning,
		-- because rotating the camera changes direction of walking, which rotates the camera, etc.
		controlModule.activeController.moveVectorIsCameraRelative = false
	end)
end

function module.Stop()
	RunService:UnbindFromRenderStep("CameraMover")
end

return module

and the script that calls it looks like this:

local handle = script.Parent.ArcHandles
local part = handle.Adornee

local startCFrame = nil
handle.MouseButton1Down:Connect(function(axis)
	startCFrame = part.CFrame
	print("Start")
end)
handle.MouseDrag:Connect(function(axis, relativeAngle)
	print("Drag angle = ", relativeAngle)
	if axis == Enum.Axis.X then
		part.CFrame = startCFrame *  CFrame.fromAxisAngle(Vector3.xAxis, relativeAngle)
	elseif axis == Enum.Axis.Y then
		part.CFrame = startCFrame *  CFrame.fromAxisAngle(Vector3.yAxis, relativeAngle)
	elseif axis == Enum.Axis.Z then
		part.CFrame = startCFrame *  CFrame.fromAxisAngle(Vector3.zAxis, relativeAngle)
	end
end)

local viewHandler = require(script.Parent.CameraMoverModule)

-- offset is 20 studs to the left of the part; 20 studs rotated -90 relative to lookVector
viewHandler.OffsetDistance = 20
viewHandler.OffsetRotation = CFrame.fromAxisAngle(Vector3.new(0,1,0), math.rad(-90))

viewHandler.Start()

1 Like

actually I see you are trying to move the center of the camera to be in a different spot which is kind of different than what I helped you do above.

If you want to do this and still have your ArcHandles work, you are going to have to process inputs yourself which is a much harder problem. I’ll ask around and see if I can get any pointers.

Thank you a lot for reaching out my issue, ill surely check out your code whenever ill get back to the project i was working on, unfortunately im not too experienced when it comes to camera and few other physics or rendering stuff roblox does, and yes i was trying to get both camera and haldes to work property together. Have a nice day! :wink: