How to use mouse targetFilter twice with out bugging out

Right now im confused on why one script works while the other doesnt

heres a video

heres the script (btw both are same just using a different remote function)

local tool = script.Parent
local cooldown = false
local player = game.Players.LocalPlayer
local character = player.Character or player.CharacterAdded:Wait()
local humanoid = character:WaitForChild("Humanoid")
local animation = script:WaitForChild("Up")
local animater = humanoid:LoadAnimation(animation)
local mouse = player:GetMouse()
local toolequipped = false

local trackingPart = Instance.new("Part")
trackingPart.Size = Vector3.new(1.5, 1.5, 1.5)
trackingPart.Color = Color3.new(0.215686, 1, 0)
trackingPart.Transparency = 0.5
trackingPart.Shape = Enum.PartType.Ball
trackingPart.Material = Enum.Material.Neon
trackingPart.Anchored = true
trackingPart.CanCollide = false
trackingPart.Parent = workspace
mouse.TargetFilter = trackingPart

tool.Equipped:Connect(function()
	toolequipped = true
	while toolequipped do
		local hit = mouse.Hit
		if hit then
			local characterPosition = character.PrimaryPart.Position
			local hitPosition = hit.Position
			local distance = (hitPosition - characterPosition).magnitude
			local minDistance = 5

			if distance > minDistance then
				trackingPart.Position = hitPosition + Vector3.new(0, trackingPart.Size.Y / 2, 0)
			else
				local direction = (hitPosition - characterPosition).unit
				trackingPart.Position = characterPosition + direction * minDistance + Vector3.new(0, trackingPart.Size.Y / 2, 0)
			end
		end

		wait(0.01)
	end
end)

tool.Unequipped:Connect(function()
	toolequipped = false
	trackingPart.Position = Vector3.new(0, -1000, 0)
end)

tool.Activated:Connect(function()
	if not cooldown and toolequipped then
		local pos = trackingPart.CFrame
		cooldown = true
		animater:Play()
		game.ReplicatedStorage.RemoteTools.Blaster.Sans.BlastZone:FireServer(pos)
		trackingPart.Color = Color3.new(1, 0, 0.0156863)
		wait(9)
		cooldown = false
		trackingPart.Color = Color3.new(0.215686, 1, 0)
	end
end)
1 Like

The issue you’re encountering with mouse.TargetFilter likely stems from how TargetFilter works in conjunction with your scripts. TargetFilter sets an instance (or a list of instances) to be ignored by the mouse’s targeting system. If multiple scripts attempt to modify the TargetFilter, they can overwrite or conflict with each other, causing bugs.

Here’s an analysis and potential solutions:


Why One Script Works and the Other Doesn’t

  1. Overwriting TargetFilter:

    • Each script that modifies mouse.TargetFilter overwrites the previous value. If multiple scripts run simultaneously and modify mouse.TargetFilter, only the last set value will remain active.
  2. Shared Mouse Instance:

    • Since Player:GetMouse() returns the same Mouse object for the player, all scripts accessing it are modifying the same Mouse.TargetFilter. This can lead to conflicts if not handled properly.

Solutions

1. Use a Table for TargetFilter

TargetFilter accepts a single instance or a list of instances. Instead of overwriting the TargetFilter in each script, you can maintain a shared table of parts to filter out.

local mouse = player:GetMouse()
local targetFilterParts = {}

-- Add part to TargetFilter
local function addToTargetFilter(part)
    if not table.find(targetFilterParts, part) then
        table.insert(targetFilterParts, part)
        mouse.TargetFilter = targetFilterParts
    end
end

-- Remove part from TargetFilter
local function removeFromTargetFilter(part)
    for i, v in ipairs(targetFilterParts) do
        if v == part then
            table.remove(targetFilterParts, i)
            break
        end
    end
    mouse.TargetFilter = #targetFilterParts > 0 and targetFilterParts or nil
end

Update each script to use these functions for adding and removing parts from TargetFilter.


2. Script Communication for TargetFilter

If multiple scripts are interacting with the TargetFilter, centralize its handling in a single module script.

Module Script Example (TargetFilterHandler):

local TargetFilterHandler = {}
local targetFilterParts = {}

function TargetFilterHandler:AddPart(part)
    if not table.find(targetFilterParts, part) then
        table.insert(targetFilterParts, part)
        game.Players.LocalPlayer:GetMouse().TargetFilter = targetFilterParts
    end
end

function TargetFilterHandler:RemovePart(part)
    for i, v in ipairs(targetFilterParts) do
        if v == part then
            table.remove(targetFilterParts, i)
            break
        end
    end
    game.Players.LocalPlayer:GetMouse().TargetFilter = #targetFilterParts > 0 and targetFilterParts or nil
end

return TargetFilterHandler

In Your Scripts:

local TargetFilterHandler = require(game.ReplicatedStorage.TargetFilterHandler)
TargetFilterHandler:AddPart(trackingPart)

3. Separate Tools or Abilities

If you’re designing tools or abilities that each need their own targeting system, ensure they don’t interfere with each other:

  • Only set TargetFilter when the tool is equipped.
  • Clear TargetFilter when the tool is unequipped.

Updated Script Example

Here’s an updated version of your script using a shared table approach:

local tool = script.Parent
local cooldown = false
local player = game.Players.LocalPlayer
local character = player.Character or player.CharacterAdded:Wait()
local humanoid = character:WaitForChild("Humanoid")
local animation = script:WaitForChild("Up")
local animater = humanoid:LoadAnimation(animation)
local mouse = player:GetMouse()
local toolequipped = false

local trackingPart = Instance.new("Part")
trackingPart.Size = Vector3.new(1.5, 1.5, 1.5)
trackingPart.Color = Color3.new(0.215686, 1, 0)
trackingPart.Transparency = 0.5
trackingPart.Shape = Enum.PartType.Ball
trackingPart.Material = Enum.Material.Neon
trackingPart.Anchored = true
trackingPart.CanCollide = false
trackingPart.Parent = workspace

local targetFilterParts = {trackingPart}
mouse.TargetFilter = targetFilterParts

tool.Equipped:Connect(function()
    toolequipped = true
    while toolequipped do
        local hit = mouse.Hit
        if hit then
            local characterPosition = character.PrimaryPart.Position
            local hitPosition = hit.Position
            local distance = (hitPosition - characterPosition).magnitude
            local minDistance = 5

            if distance > minDistance then
                trackingPart.Position = hitPosition + Vector3.new(0, trackingPart.Size.Y / 2, 0)
            else
                local direction = (hitPosition - characterPosition).unit
                trackingPart.Position = characterPosition + direction * minDistance + Vector3.new(0, trackingPart.Size.Y / 2, 0)
            end
        end

        wait(0.01)
    end
end)

tool.Unequipped:Connect(function()
    toolequipped = false
    trackingPart.Position = Vector3.new(0, -1000, 0)
end)

tool.Activated:Connect(function()
    if not cooldown and toolequipped then
        local pos = trackingPart.CFrame
        cooldown = true
        animater:Play()
        game.ReplicatedStorage.RemoteTools.Blaster.Sans.BlastZone:FireServer(pos)
        trackingPart.Color = Color3.new(1, 0, 0.0156863)
        wait(9)
        cooldown = false
        trackingPart.Color = Color3.new(0.215686, 1, 0)
    end
end)

Final Thoughts

Using a shared table or centralized module ensures that your TargetFilter doesn’t overwrite itself, avoiding bugs when multiple scripts attempt to use it simultaneously. If you still face issues, it’s worth checking for any other scripts that might also modify TargetFilter.

I would recommend using UserInputService rather than Mouse for new work, as it allows for more precise control for filtering.

You can get the mouse’s location on the screen with :GetMouseLocation() and raycast from the camera in order to find where it is in 3D space (with support for different filters at the same time).

The creator hub has some helpful resources if you’re still having trouble, such as this tutorial:
Hit detection with lasers | Documentation - Roblox Creator Hub

3 Likes

So this happens when your code runs:
Tool 1 localscript creates their part and sets mouse.Target filter to that part.

Tool 2 localscript is loaded and creates their part and sets mouse.Target filter to that part.

The target filter now equals to tool2’s part. When you use tool 2, it works fine. But using tool 1 it now breaks, because the target filter was overwritten.

So you can set the target filter of a part in tool.equipped and then it will work. But I suggest you create/destroy the tracking part in tool.equipped/unequipped instead to avoid memory leaks.

minimal solution:

--move this line in both tools' scripts
mouse.TargetFilter = trackingPart

--inside of tool.equipped:
tool.Equipped:Connect(function()
  mouse.TargetFilter = trackingPart
  --rest of code...
  --bonus:
  --toolequipped = true--remove this line
  --why? Because Activated can only fire if a tool is equipped 
  -- you don't need to check again.
  --use runservice to handle positioning instead of a while loop, it is cleaner
end)

Bonus:

It would be better to handle the lifetime of your part inside of equip/unequip though like so:

tool.Equipped:Connect(function()
  trackingPart = instance.new("Part")
  --customize
  mouse.TargetFilter = trackingPart
  --other code 
end)

tool.Unequipped:Connect(function()
  if trackingPart then 
    trackingPart:Destroy() 
    trackingPart = nil
  end
end)

Hope this helps!

one question, how will it detect the tracking part if the cloned part is in the tool.equipped section?

im still kinda confused on how raycast work

Non of your solution seems to fix the problem as it keeps giving errors like “Instance expected, got table”

so i did something kinda simillar you can correct me cus im kinda new to raycast but

it works, just that another part of the script which is suppost to keep making the part follow the mouse is making it float to my screen

tool.Equipped:Connect(function()
	toolequipped = true
	while toolequipped do
		local hit = mouse.Hit
		if hit then
			local characterPosition = character.PrimaryPart.Position
			local hitPosition = hit.Position
			local distance = (hitPosition - characterPosition).magnitude
			local minDistance = 5

			if distance > minDistance then
				trackingPart.Position = hitPosition + Vector3.new(0, trackingPart.Size.Y / 2, 0)
			else
				local direction = (hitPosition - characterPosition).unit
				trackingPart.Position = characterPosition + direction * minDistance + Vector3.new(0, trackingPart.Size.Y / 2, 0)
			end
		end

		wait(0.01)
	end
end)

here is the part i changed

local MAX_MOUSE_DISTANCE = 500
local mouselocation = input:GetMouseLocation()
local screenToWorldRay = workspace.CurrentCamera:ViewportPointToRay(mouselocation.X, mouselocation.Y)
local directionVector = screenToWorldRay.Direction * MAX_MOUSE_DISTANCE
trackingPart.Position = directionVector

You mean like how will you access the variable?
You define it globally at the top of your script:

local trackingPart = nil

And then you assign it a value in equipped/unequipped:

tool.Equipped:Connect(function()
    trackingPart = instance.new("Part")
    --customize
    mouse.TargetFilter = trackingPart
end)

This (& Runservice) will give you this script:

--services
local rs = game:GetService "RunService"

--plr related vars
local plr = game.Players.LocalPlayer
local char = plr.Character or plr.CharacterAdded:Wait()
local mouse = plr:GetMouse()

--other
local tool = script.Parent
local hum = char:FindFirstChildOfClass "Humanoid"
local track = hum:LoadAnimation(script:WaitForChild "Up")

--nil values
local part = nil
local connection = nil

--define constants up here:
local MIN_DISTANCE = 5

--text
tool.Equipped:Connect(function()
   part = instance.new "Part"
     part.Shape = Enum.PartType.Ball
     part.Transparency = 0.5
     part.CanCollide = false
     part.Anchored = true
     part.Size = Vector3.new(1.5, 1.5, 1.5)
     part.Material = Enum.Material.Neon
     part.Color = Color3.fromRGB(55, 255, 0)

   part.Parent = workspace
   mouse.TargetFilter = part
   connection = rs.Heartbeat:Connect(function()
       local hit = mouse.Hit.Position
       local charPos = char.PrimaryPart.Position

       local distance = (hit - charPos).Magnitude --player:DistanceFromCharacter(hit)
       if distance > MIN_DISTANCE then
           part.Position = hit + Vector3.new(0, part.Size.Y / 2, 0)
       else
           local direction = (hit - charPos).Unit
           part.Position = charPos + direction * MIN_DISTANCE + Vector3.new(0, part.Size.Y, 0)
       end
   end)
end)

--clean up resources here
tool.Unequipped:Connect(function()
   if part then 
      part:Destroy()
      part = nil
   end

   if connection then
      connection:Disconnect()
      connection = nil
   end
end)

Hope this helps!

1 Like

To get the result of a raycast you need to use workspace:Raycast().
It also seems like you’re still using mouse.Hit in your while loop.

We need to perform the raycast every time we want to get the mouse position.
I would recommend implementing something that looks like this:

local UserInputService = game:GetService("UserInputService")
local RunService = game:GetService("RunService")

local MAX_MOUSE_DISTANCE = 1000
local function getWorldMousePosition()
	local mouseLocation = UserInputService:GetMouseLocation()
	local screenToWorldRay = workspace.CurrentCamera:ViewportPointToRay(mouseLocation.X, mouseLocation.Y)
	local directionVector = screenToWorldRay.Direction * MAX_MOUSE_DISTANCE
	
	-- THIS IS WHAT FILTERS THE RAYCAST
	local raycastParams = RaycastParams.new()
	raycastParams.FilterType = Enum.RaycastFilterType.Exclude
	raycastParams.FilterDescendantsInstances = {} -- Parts to ignore go in this table
	-- There are some other raycast filter options as well
	
	local raycastResult = workspace:Raycast(screenToWorldRay.Origin, directionVector, raycastParams)

	return raycastResult
end

tool.Equipped:Connect(function()
	toolequipped = true
	while toolequipped do
		local mouseRaycast = getWorldMousePosition()
		if mouseRaycast then
			local characterPosition = character.PrimaryPart.Position
			local hitPosition = mouseRaycast.Position
			local distance = (hitPosition - characterPosition).magnitude
			local minDistance = 5

			if distance > minDistance then
				trackingPart.Position = hitPosition + Vector3.new(0, trackingPart.Size.Y / 2, 0)
			else
				local direction = (hitPosition - characterPosition).unit
				trackingPart.Position = characterPosition + direction * minDistance + Vector3.new(0, trackingPart.Size.Y / 2, 0)
			end
		end
		
		-- This line replaces wait(0.01), as using wait() can have longer delays than intended
		-- If you need to wait more than a single frame, use task.wait() instead
		RunService.Heartbeat:Wait()
	end
end)

You should be able to adapt this code into your current script; hopefully that made sense!
(Raycasting will replace the need for using mouse.TargetFilter as it has its own filtering options)

1 Like

Both of your script helped me i appreciate your help so much