Is this possible in a gui?

You can write your topic however you want, but you need to answer these questions:

  1. What do you want to achieve? Keep it simple and clear!
    I would like to have a character following my mouse in a gui is possible?
  2. What is the issue? Include screenshots / videos if possible!
    https://gyazo.com/d07d8d315f7f79adcf110411c54ab8ba
  3. What solutions have you tried so far? Did you look for solutions on the Developer Hub?
    I have tried looking at the articles listed below:
    UI Animations | Documentation - Roblox Creator Hub
    Frames | Documentation - Roblox Creator Hub
2 Likes

Get the mouse X and Y positions with:

local Players = game:GetService("Players")
local localPlayer = Players.LocalPlayer -- from a LocalScript
local mouse = Player:GetMouse()
print(mouse.X,mouse.Y)

And then do math to move your character’s head around.

Thank you! I will make sure to try that!

i don’t know if you have your question fully answered yet, but yes “it” is possible to do that with a viewportframe. The easiest way (i can think of) would be to use a function that can convert a mouse position into a 3d position and then from there it should be fairly straight forward as to how you can rotate your character toward your mouse (there are plenty of resources on it). As for the conversion it’s actually quite simple, the main idea is that we need to first find the width and height of the viewing frustum’s far clipping plane, then we use our mouse’s coordinates relative to the viewportframe accordingly to find the mouse’s position as if it were transformed into world space as opposed to screen space. Such a conversion could look like:

local ScreenHeight = Cam_Size.Y
local ScreenWidth = Cam_Size.X	
local AspectRatio = (ScreenWidth/ScreenHeight)
local Tangent = math.tan((math.rad(FieldOfView) * .5)) ---- tan(rad(fov/2))

local fx = ((2 * Depth) * (MousePos.x /(ScreenWidth-1)) -(Depth*1)) --- convert mouse pos.X between -Depth and Depth 
local fy = ((2 * Depth) * (MousePos.y/( ScreenHeight-1)) -(Depth*1))

local X = (AspectRatio * Tangent * fx )  --- "AspectRatio * Tangent" is our width
local Y =  (-Tangent * fy) --- we can use -Tangent as our "height"
local Z = -Depth --- Z is just negative depth

----X, Y, Z is our position however we need to make it relative to our camera!
local CF =  camera.CFrame * CFrame.new(Vector3.new(NX, NY, NZ)) 
Complete Function
local GuiService = game:GetService("GuiService")

local function LocalPos(Pos,Viewport)
	return Pos - Viewport.AbsolutePosition 
end

local function ScreenToWorldSpace(Pos, viewport, camera, Depth, Gui_Inset)
 Pos =  LocalPos(Pos - GuiService:GetGuiInset() , viewport)
 local Cam_Size = Vector2.new(viewport.AbsoluteSize.X , viewport.AbsoluteSize.Y - 36)
 local Height = Cam_Size.Y
 local Width = Cam_Size.X	
 local AspectRatio = (Cam_Size.X/Cam_Size.Y)
 local Cam_Pos = camera.CFrame.Position 
 local Scale = (Depth or 3) 
 local fov  =math.rad(camera.FieldOfView)
 local Tangent = math.tan((fov * .5))
 local fx = ((2 * Scale) * (Pos.x /(Width-1)) -(Scale*1))
 local fy = ((2 * Scale) * (Pos.y/(Height-1)) -(Scale*1))
 local NX = ((AspectRatio * Tangent * fx ))
 local NY = (-Tangent * fy)
 local NZ = -Scale 
 local Translatedcf = (camera.CFrame) * CFrame.new(Vector3.new(NX, NY, NZ))  
 return Translatedcf.Position
end 

With this information all we need to do is make the “character” look at CF, there are multiple ways to do this, but for simplicity, i just used CFrame.new(Pos, LookAtPos) (you can replace it with a custom look at function, if you are worried about it being deprecated) and used CFrame:ToOrientation() so that i can limit the rotation of the character’s Head, to get an okay outcome of:

Which was made by doing essentially doing:

local Character --- character
local Root -- HumanoidRootPart of character
local Head = Character.Head
local Max_X, Min_X = 2000, -10
local Max_Y, Min_Y = 2000, -2000

local function LimitRotation(CF)
	local rx, ry, rz = CF:ToOrientation() --- using ToOrientation to make limiting rotation easier 
	rx = math.clamp(rx, math.rad(Min_X), math.rad(Max_X)) ---- clamp rx angle
	ry = math.clamp(ry, math.rad(Min_Y), math.rad(Max_Y))---- clamp ry angle
	return rx, ry, rz
end

RunService.RenderStepped:Connect(function()
	local  pos  = ScreenToWorldSpace(UIS:GetMouseLocation(), vpf, Camera,3) --- function from above, changed depth to 3 so that everything would rotate toward the returned position just a little more
	local rx, ry, rz = LimitRotation(CFrame.new(Head.Position, pos))  
	local Rotation = Root.CFrame - Root.Position -- remove position from the CFrame, so it is just rotation 
	Head.CFrame =  (CFrame.new(Head.Position) * Rotation) * CFrame.fromOrientation( rx , ry - math.rad(180) , rz)-- make sure that the Head CFrame is also being rotated according to the actual Character rotation 

	local HumanoidCF = CFrame.new(pos) * CFrame.new(0, 0, 10) -- move pos back, which lessens the "strength" of the rotation 
	Character:SetPrimaryPartCFrame(CFrame.new(Root.Position, HumanoidCF.Position))--- rotate entire character 
end)
Full Code
local UIS = game:GetService("UserInputService")
local GuiService = game:GetService("GuiService")
local RunService = game:GetService("RunService")
local vpf = script.Parent.ViewportFrame
local Camera = Instance.new("Camera")


local Character = vpf.Character --- r15 character
Character.Parent = vpf
local Root = Character:WaitForChild("HumanoidRootPart")



Camera.Parent = vpf
vpf.CurrentCamera = Camera
Camera.CFrame = Root.CFrame * CFrame.new(0, 0, 8)



local function LocalPos(Pos, Viewport)
	return Pos - Viewport.AbsolutePosition 
end



local function ScreenToWorldSpace(Pos, viewport, camera, Depth, Gui_Inset)
	Pos =  LocalPos(Pos - GuiService:GetGuiInset() , viewport)
	local Cam_Size = Vector2.new(viewport.AbsoluteSize.X , viewport.AbsoluteSize.Y - 36)
	local Height = Cam_Size.Y
	local Width = Cam_Size.X	
	local AspectRatio = (Cam_Size.X / Cam_Size.Y)
	local Cam_Pos = camera.CFrame.Position 
	local Scale = (Depth or 1) 
	local fov  = math.rad(camera.FieldOfView)
	 local Tangent = math.tan((fov * .5))
	local fx = ((2 * Scale) * (Pos.x / (Width - 1)) - (Scale * 1))
	local fy = ((2 * Scale) * (Pos.y / (Height - 1)) - (Scale * 1))
	local NX = ((AspectRatio * Tangent * fx ))
	local NY = (-Tangent * fy)
	local NZ = -Scale 
	local Translatedcf = (camera.CFrame) * CFrame.new(Vector3.new(NX, NY, NZ))  
	return Translatedcf.Position
end 


local Head = Character.Head
local Max_X, Min_X = 2000, -10
local Max_Y, Min_Y = 2000, -2000

local function LimitRotation(CF)
	local rx, ry, rz = CF:ToOrientation()
	rx = math.clamp(rx, math.rad(Min_X), math.rad(Max_X))
	ry = math.clamp(ry, math.rad(Min_Y), math.rad(Max_Y))
	return rx, ry, rz
end


RunService.RenderStepped:Connect(function()
	local  pos  = ScreenToWorldSpace(UIS:GetMouseLocation(), vpf, Camera, 3)
	local rx, ry, rz = LimitRotation(CFrame.new(Head.Position, pos)) 
	local Rotation = Root.CFrame - Root.Position 
	Head.CFrame =  (CFrame.new(Head.Position) * Rotation) * CFrame.fromOrientation( rx , ry - math.rad(180) , rz) -

	local HumanoidCF = CFrame.new(pos) * CFrame.new(0, 0, 10)
	Character:SetPrimaryPartCFrame(CFrame.new(Root.Position, HumanoidCF.Position))  
end)

Here is an accompanying place file: ViewportTest.rbxl (31.0 KB)


Chances are if i didn’t explain the Screen to World space conversion very well, i would recommend checking out this tutorial that talks more in depth about how conversion between spaces (world & screen) work.

3 Likes

Thank you so much! I’ll mark this as a solution so others can learn from it.

1 Like