How can I make my script prevent your mouse from leaving the window?

Right now my mouse leaves the window and stops my RTS script from working.


```
local uis = game:GetService("UserInputService")

local camera = workspace.CurrentCamera
local maxDist = 500
local camSpeed = 100 -- in studs/sec
local angle = math.rad(-75) 

local minPerX, minPerZ = 1 / 3, 1 / 4
local x, z = 0, 0 

local minXActivation = 1 / minPerX - 1
local minZActivation = 1 / minPerZ - 1

local userInput 
uis.InputChanged:Connect(function(input)
    userInput = input
end)

uis.InputEnded:Connect(function(input)
    if input == userInput and userInput.UserInputType == Enum.UserInputType.MouseMovement then
        local mousePos = uis:GetMouseLocation()
        local viewport = camera.ViewportSize
        if mousePos.X < 0 or mousePos.X > viewport.X or mousePos.Y < 0 or mousePos.Y > viewport.Y then
            uis:SetMouseLocation(viewport.X/2, viewport.Y/2)
        end
    end
end)

game:GetService("RunService").RenderStepped:Connect(function(dt)
    camera.CameraType = Enum.CameraType.Scriptable
    local viewport = camera.ViewportSize
    local mousePos = uis:GetMouseLocation()
	local viewX, viewZ = viewport.X * minPerX, viewport.Y * minPerZ
	local perX = mousePos.X / viewX
	if perX < 1 then
		-- it is on the left side of the margin
		perX = (1 - perX) * (camSpeed * dt)
		x = math.clamp(x - perX, -maxDist, maxDist)
	elseif perX > minXActivation then
		-- right side
		perX = (perX - minXActivation) * (camSpeed * dt)
		x = math.clamp(x + perX, -maxDist, maxDist)
	end

	local perZ = mousePos.Y / viewZ
	if perZ < 1 then
		perZ = (1 - perZ) * (camSpeed * dt)
		z = math.clamp(z - perZ, -maxDist, maxDist)
	elseif perZ > minZActivation then
		perZ = (perZ - minZActivation) * (camSpeed * dt)
		z = math.clamp(z + perZ, -maxDist, maxDist)
	end

	local currentPos = CFrame.new(x, 50, z)

	camera.CFrame = currentPos * CFrame.Angles(angle, 0, 0)
end)
```

:SetMouseLocation() is not a method you can call, only :GetMouseLocation()
There are several security reasons why moving the player’s mouse for them are not possible, including making players accidently click on purchase prompts and preventing them for accessing things out of the window.

If it breaks your scripts, GET the location, and see if it is outside of the viewport. If it is, then don’t run the function

I just need to lock the cursor to the game window. Like an RTS game where you pan your camera to the edge of the screen and the camera will pan. You can’t have the mouse leaving the window.

Is there any way to achieve this? To lock to the game window?

Nope. Again, for security reasons. You can still detect when it leaves - if the position of the mouse is outside of the viewport, then have it pan.

Oh, so you can get the mouse position even outside of the viewport?

GetMouseLocation
Vector2
This function returns a Vector2 representing the current screen location of the player’s Mouse in pixels relative to the top left corner. This does not account for the GUI inset.
If the location of the mouse pointer is offscreen or the players device does not have a mouse, the value returned will be undetermined instead of Vector2.
As UserInputService is client-side only, this function can only be used in a LocalScript.

According to the docs, if you use GetMouseLocation, and it returns nil, it is either out of the viewport, or the user does not have a mouse connected. Just check that the player is on a computer and it should be good. (ill find an article I had ages ago for finding the device)

edit: Device Type Detection - #46 by Maelstronomer

I don’t understand, if it returns nil when I’m out of viewport, how would I then get it’s location to determine which way to pan?

Good point.

I guess you can determine which way it was going prior to returning nil. Note that if the player moves their mouse REALLY fast (more than half the screen in 1 frame) then it may break. Also wouldn’t work for panning diagonally.

Code is completely untested, so if it errors, Good luck! (I can try to explain things that break if need be). (Hopefully the code hasn’t got a flaw in it)

local LastX, LastY = 0, 0
local ViewportSize = workspace.CurrentCamera.ViewportSize 
workspace.CurrentCamera:GetPropertyChangedSignal("ViewportSize"):Connect(function()
    ViewportSize = game.Workspace.CurrentCamera.ViewportSize -- Incase they change the size
end
while true do -- Change to whichever looping thing you were using
    local pos = UIS:GetMouseLocation()
    if pos then
        LastX = pos.X
        LastY = pos.Y
    else
        local X = math.round(LastX/ViewportSize.X) -- for finding which border it is closest to
        local Y = math.round(LastY/ViewportSize.Y) -- 0,0 is top left, 1,1 is bottom right
    
        local RatioX = LastX/ViewportSize.X
        local RatioY = LastY/ViewportSize.Y
        if RatioX > 0.5 then RatioX = 1-RatioX
        if RatioY > 0.5 then RatioY = 1-RatioY

        if RatioX < RatioY then
            if X == 1 then 
                print("Right")
            else
                print("Left")
            end
        else
            if Y == 1 then
                print("Down")
            else
                print("Up")
            end
        end
        task.wait()
    end
end

I guess it’s just not possible to do this in ROBLOX. Sad :confused:

In answer to your original question: No, you can not prevent the mouse from leaving.
Your goal of panning is still possible. I did some testing and found a better way to pan it than using the method i put above.

The documentation is misleading - :GetMouseLocation DOES return a value if it is outside of the viewport, which is basically the last known location. Using that you can calculate which border direction it is and hence which way to pan

Does it just return last known mouse location? Cause what if someone pans outside at the top, moves mouse around outside the viewport and comes back inside the bottom for example.

Last known location that was inside the viewport, so they could have their mouse position jump from 0,0 to whatever the bottom right position is by moving their mouse around the outside

set the uis.MouseBehavior to lock the mouse to the center

but it the mouse is too fast, it can’t catch it

local uis = game:GetService("UserInputService")
local plrs = game:GetService("Players")

local player = plrs.LocalPlayer
local mouse = player:GetMouse()
local camera = workspace.CurrentCamera
local maxDist = 500
local camSpeed = 100 -- in studs/sec
local angle = math.rad(-75) 

local minPerX, minPerZ = 1 / 3, 1 / 4
local x, z = 0, 0 

local minXActivation = 1 / minPerX - 1
local minZActivation = 1 / minPerZ - 1

local maxMouseBound = 25 

uis.InputChanged:Connect(function(input)
	if uis.MouseBehavior == Enum.MouseBehavior.LockCenter then
		return
	end
	
	if input.UserInputType == Enum.UserInputType.MouseMovement then
		local mousePos = Vector2.new(mouse.X, mouse.Y)
		local viewport = camera.ViewportSize

		if mousePos.X <= maxMouseBound or mousePos.X >= viewport.X - maxMouseBound or mousePos.Y <= maxMouseBound or mousePos.Y >= viewport.Y - maxMouseBound then
			print("tried to go out")
			while game:GetService("RunService").RenderStepped:Wait() and (uis:GetMouseLocation() - viewport / 2).Magnitude > 10 do
				uis.MouseBehavior = Enum.MouseBehavior.LockCenter
				-- repeat until the mouse in in the center because bug
			end
			
			uis.MouseBehavior = Enum.MouseBehavior.Default
		end
	end
end)

game:GetService("RunService").RenderStepped:Connect(function(dt)
	camera.CameraType = Enum.CameraType.Scriptable
	local viewport = camera.ViewportSize
	local mousePos = uis:GetMouseLocation()
	local viewX, viewZ = viewport.X * minPerX, viewport.Y * minPerZ
	local perX = mousePos.X / viewX
	if perX < 1 then
		-- it is on the left side of the margin
		perX = (1 - perX) * (camSpeed * dt)
		x = math.clamp(x - perX, -maxDist, maxDist)
	elseif perX > minXActivation then
		-- right side
		perX = (perX - minXActivation) * (camSpeed * dt)
		x = math.clamp(x + perX, -maxDist, maxDist)
	end

	local perZ = mousePos.Y / viewZ
	if perZ < 1 then
		perZ = (1 - perZ) * (camSpeed * dt)
		z = math.clamp(z - perZ, -maxDist, maxDist)
	elseif perZ > minZActivation then
		perZ = (perZ - minZActivation) * (camSpeed * dt)
		z = math.clamp(z + perZ, -maxDist, maxDist)
	end

	local currentPos = CFrame.new(x, 50, z)

	camera.CFrame = currentPos * CFrame.Angles(angle, 0, 0)
end)
local mouse = game.Players.LocalPlayer:GetMouse()

game:GetService("UserInputService").InputBegan:Connect(function(inputObject)
    if inputObject.UserInputType == Enum.UserInputType.MouseMovement then
        local x = mouse.X
        local y = mouse.Y
        local size = game.Workspace.CurrentCamera.ViewportSize
        if x <= 0 or x >= size.X then
            mouse.X = x < 0 and 0 or size.X
        end
        if y <= 0 or y >= size.Y then
            mouse.Y = y < 0 and 0 or size.Y
        end
    end
end)

if the cursor is detected by the userinputservice to be outside of the windows coordinates it sets the mouse pos to the edge of the screen effectively keeping it inside of the game window

You can’t assign mouse X/Y, it’s read only.

Lock center wont solve this issue?

I guess a way is to constantly check if the mouse is close enough to the edge of thr screen then Center lock it and unlock it, although this would obviously cause your mouse to get locked for a split second

I think this would just cause an infinite locking loop. Since the mouse would never move again.

if you unlock it instantly after locking it, the mouse would end up in the middle of the screen

That doesn’t solve my problem. I’m trying to make an RTS camera script. This janky pullback would be worse than just letting the mouse leave.