I would recommend re-working your logic. In it’s current state, it seems overly complex to me. You don’t need the while loop, you don’t need to save colors that way, and most prominently your conditions are kind of confusing.
There are two approaches that I would take to this system. First and foremost, it is recommended not to use the Mouse
object returned from Player:GetMouse()
anymore, so I will be using alternative methods.
Fundamentally, both of my approaches rely on the same logic for highlighting, and that is this:
local Players = game:GetService("Players")
--Variables
local players = Players.LocalPlayer
local camera = workspace.CurrentCamera
local raycastParams = RaycastParams.new()
raycastParams.FilterType = Enum.RaycastFilterType.Include
raycastParams.FilterDescendantsInstances = {workspace.Parts} --You would change this for your use-case
local highlightedColor = Color3.fromRGB(200, 200, 200)
local lastTarget = nil
local lastTargetColor = nil
--Using one of two approaches I will explain further on, find the mouse location, and then cast a ray to find the part it intersects.
local screenRay = camera:ViewportPointToRay(mousePos.X, mousePos.Y)
local rayResult = workspace:Raycast(screenRay.Origin, screenRay.Direction * 500, raycastParams) --I am multiplying the direction to extend the length of the ray. This essentially dictates how far away a part can be that a player can hover over.
--Guard clause
if not rayResult then
--This means it didn't hit a part
if lastTarget then --Protecting incase it's nil
lastTarget.Color = lastTargetColor --Set the old target's color back
end
lastTarget = nil
lastTargetColor = nil
return
end
local target = rayResult.Instance
if target ~= lastTarget then
if lastTarget then --Protecting incase it's nil
lastTarget.Color = lastTargetColor --Set the old target's color back
end
--Set this new target as the lastTarget
lastTarget = target
lastTargetColor = target.Color
--Apply the highlight color to this new target
target.Color = highlightedColor
end
Now, you may use that above and apply it to your method to solve your logic issue. Alternatively, you can read on as I explain the two methods by which the mouse location can be found.
Approach 1: UserInputService:GetMouseLocation
To use this method, one must either create an infinite while loop
, or use RunService:BindToRenderStepped
. Here is this method in full:
--Services
local Players = game:GetService("Players")
local UserInputService = game:GetService("UserInputService")
local RunService = game:GetService("RunService")
--Variables
local players = Players.LocalPlayer
local camera = workspace.CurrentCamera
local raycastParams = RaycastParams.new()
raycastParams.FilterType = Enum.RaycastFilterType.Include
raycastParams.FilterDescendantsInstances = {workspace.Parts} --You would change this for your use-case
local highlightedColor = Color3.fromRGB(200, 200, 200)
local lastTarget = nil
local lastTargetColor = nil
--Check every frame
function checkMouse()
local mousePos = UserInputService:GetMouseLocation()
local screenRay = camera:ViewportPointToRay(mousePos.X, mousePos.Y)
local rayResult = workspace:Raycast(screenRay.Origin, screenRay.Direction * 500, raycastParams) --I am multiplying the direction to extend the length of the ray. This essentially dictates how far away a part can be that a player can hover over.
--Guard clause
if not rayResult then
--This means it didn't hit a part
if lastTarget then --Protecting incase it's nil
lastTarget.Color = lastTargetColor --Set the old target's color back
end
lastTarget = nil
lastTargetColor = nil
return
end
local target = rayResult.Instance
if target ~= lastTarget then
if lastTarget then --Protecting incase it's nil
lastTarget.Color = lastTargetColor --Set the old target's color back
end
--Set this new target as the lastTarget
lastTarget = target
lastTargetColor = target.Color
--Apply the highlight color to this new target
target.Color = highlightedColor
end
end
RunService:BindToRenderStep("CheckMouse", Enum.RenderPriority.Last.Value, checkMouse)
Approach 2: ContextActionService
This is arguably more efficient as the logic doesn’t have to run each frame, however when the logic does run, there is a bit more of it, and it may be harder to understand/add to. Here is this method in full:
--Services
local Players = game:GetService("Players")
local ContextActionService = game:GetService("ContextActionService")
--Variables
local players = Players.LocalPlayer
local camera = workspace.CurrentCamera
local raycastParams = RaycastParams.new()
raycastParams.FilterType = Enum.RaycastFilterType.Include
raycastParams.FilterDescendantsInstances = {workspace.Parts} --You would change this for your use-case
local highlightedColor = Color3.fromRGB(200, 200, 200)
local lastTarget = nil
local lastTargetColor = nil
--Use ContextActionService
local function handleAction(actionName, inputState, inputObject)
if actionName == "mouseMove" then
if inputState == Enum.UserInputState.Change then
--NOTE: This position value takes the GUI Inset into account, so a different method is used below.
local screenRay = camera:ScreenPointToRay(inputObject.Position.X, inputObject.Position.Y)
local rayResult = workspace:Raycast(screenRay.Origin, screenRay.Direction * 500, raycastParams) --I am multiplying the direction to extend the length of the ray. This essentially dictates how far away a part can be that a player can hover over.
--Guard clause
if not rayResult then
--This means it didn't hit a part
if lastTarget then --Protecting incase it's nil
lastTarget.Color = lastTargetColor --Set the old target's color back
end
lastTarget = nil
lastTargetColor = nil
return
end
local target = rayResult.Instance
if target ~= lastTarget then
if lastTarget then --Protecting incase it's nil
lastTarget.Color = lastTargetColor --Set the old target's color back
end
--Set this new target as the lastTarget
lastTarget = target
lastTargetColor = target.Color
--Apply the highlight color to this new target
target.Color = highlightedColor
end
end
end
return Enum.ContextActionResult.Pass --So that other things using the mouse will still work
end
ContextActionService:BindAction("mouseMove", handleAction, false, Enum.UserInputType.MouseMovement)
And, in case you’d like it, here is my full script, including both methods (which you can comment out to test the differences (they look the same):
Full Script
--Services
local Players = game:GetService("Players")
local UserInputService = game:GetService("UserInputService")
local ContextActionService = game:GetService("ContextActionService")
local RunService = game:GetService("RunService")
--Variables
local players = Players.LocalPlayer
local camera = workspace.CurrentCamera
local raycastParams = RaycastParams.new()
raycastParams.FilterType = Enum.RaycastFilterType.Include
raycastParams.FilterDescendantsInstances = {workspace.Parts} --You would change this for your use-case
local highlightedColor = Color3.fromRGB(200, 200, 200)
local lastTarget = nil
local lastTargetColor = nil
--Check every frame
--function checkMouse()
-- local mousePos = UserInputService:GetMouseLocation()
-- local screenRay = camera:ViewportPointToRay(mousePos.X, mousePos.Y)
-- local rayResult = workspace:Raycast(screenRay.Origin, screenRay.Direction * 500, raycastParams) --I am multiplying the direction to extend the length of the ray. This essentially dictates how far away a part can be that a player can hover over.
-- --Guard clause
-- if not rayResult then
-- --This means it didn't hit a part
-- if lastTarget then --Protecting incase it's nil
-- lastTarget.Color = lastTargetColor --Set the old target's color back
-- end
-- lastTarget = nil
-- lastTargetColor = nil
-- return
-- end
-- local target = rayResult.Instance
-- if target ~= lastTarget then
-- if lastTarget then --Protecting incase it's nil
-- lastTarget.Color = lastTargetColor --Set the old target's color back
-- end
-- --Set this new target as the lastTarget
-- lastTarget = target
-- lastTargetColor = target.Color
-- --Apply the highlight color to this new target
-- target.Color = highlightedColor
-- end
--end
--RunService:BindToRenderStep("CheckMouse", Enum.RenderPriority.Last.Value, checkMouse)
--Use ContextActionService
local function handleAction(actionName, inputState, inputObject)
if actionName == "mouseMove" then
if inputState == Enum.UserInputState.Change then
--NOTE: This position value takes the GUI Inset into account, so a different method is used below.
local screenRay = camera:ScreenPointToRay(inputObject.Position.X, inputObject.Position.Y)
local rayResult = workspace:Raycast(screenRay.Origin, screenRay.Direction * 500, raycastParams) --I am multiplying the direction to extend the length of the ray. This essentially dictates how far away a part can be that a player can hover over.
--Guard clause
if not rayResult then
--This means it didn't hit a part
if lastTarget then --Protecting incase it's nil
lastTarget.Color = lastTargetColor --Set the old target's color back
end
lastTarget = nil
lastTargetColor = nil
return
end
local target = rayResult.Instance
if target ~= lastTarget then
if lastTarget then --Protecting incase it's nil
lastTarget.Color = lastTargetColor --Set the old target's color back
end
--Set this new target as the lastTarget
lastTarget = target
lastTargetColor = target.Color
--Apply the highlight color to this new target
target.Color = highlightedColor
end
end
end
return Enum.ContextActionResult.Pass --So that other things using the mouse will still work
end
ContextActionService:BindAction("mouseMove", handleAction, false, Enum.UserInputType.MouseMovement)
Here is a video of it working:
If you have any further questions please ask!