Sometimes color does not go back

Hello, I am working on a world map. When your cursor goes over a territory the territory will change colors to indicate your cursor is hovering over it. When your cursor leaves the territory it is supposed to go back to its original color. It’s inconsistent at this point, sometimes it works sometimes it doesn’t. I’ve tried a lot of different combinations of code and no success.

local Plr = game.Players.LocalPlayer
local Mouse = Plr:GetMouse()
local LastPart = nil

Mouse.Move:Connect(function()
	local newPart = Mouse.Target
	
	if newPart ~= LastPart then
		if newPart:IsA("MeshPart") == true then
			if newPart.Color ~= nil then
				LastPart = newPart
				local ColorSaveR = newPart.Color.R*255
				local ColorSaveG = newPart.Color.G*255
				local ColorSaveB = newPart.Color.B*255

				while newPart == LastPart do
					wait(.001)
					newPart.Color = Color3.fromRGB(177, 229, 166)
					if newPart ~= LastPart then
						newPart.Color = Color3.fromRGB(ColorSaveR, ColorSaveG, ColorSaveB)
					end
		end
			end
		end
	end
end)

Please do not ask people to write entire scripts or design entire systems for you. If you can’t answer the three questions above, you should probably pick a different category.

You are applying the return colour to the wrong part found in the following if statement:

if newPart ~= LastPart then
	newPart.Color = Color3.fromRGB(ColorSaveR, ColorSaveG, ColorSaveB)
end

Instead you should apply it to the last part variable:

if newPart ~= LastPart then
	LastPart.Color = Color3.fromRGB(ColorSaveR, ColorSaveG, ColorSaveB)
end

Hope this helps (:

I appreciate your attempt but this doesn’t work. It just makes the color change permanently. I am trying to do the opposite. I want the part to return to its original color after the cursor leaves the part.

the if statement and the while loop contradict eachother and the if statement is inside the while loop. if you put the if after the while loop the color should return when the mouse is off the territory


while newPart == LastPart do
					wait(.001)
					newPart.Color = Color3.fromRGB(177, 229, 166)
					end

newPart.Color = Color3.fromRGB(ColorSaveR, ColorSaveG, ColorSaveB)
1 Like

Thank you for trying but its still not working. In the top left of this screenshot you can see the highlighted territory (my little white dot crosshair is there.) In the territory I am standing in is where my cursor was previously. The code works 99% of the time then its like it randomly just stops working. My only theory left is the wait command is somehow too fast and I need to slow it down?

Edit: I tried making the wait command slower it did not work.

i think you might not even need a while loop because the code fires everytime the mouse moves. i think this code should work


Mouse.Move:Connect(function()
	local newPart = Mouse.Target
	
	if newPart ~= LastPart then
		if newPart:IsA("MeshPart") == true then
			if newPart.Color ~= nil then
				LastPart = newPart
				local ColorSaveR = newPart.Color.R*255
				local ColorSaveG = newPart.Color.G*255
				local ColorSaveB = newPart.Color.B*255

				if newPart == LastPart do
					wait(.001)
					newPart.Color = Color3.fromRGB(177, 229, 166)

                    else newPart.Color = Color3.fromRGB(ColorSaveR, ColorSaveG, ColorSaveB)
				end

					
		end
			end
		end
	end
end)

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!

2 Likes

Thank you so much! I have so much learning to do. A lot of stuff you used such as Raycast and ContextActionSerivce I don’t fully understand yet but I will 100% learn about them now.

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