Multiple target filters for mouse

Being able to set Mouse.TargetFilter to an array of objects rather than a single object has been requested on the developer forums before, but every time the response is “Can’t replicate a table as a property”. Replicating it to the server obviously doesn’t matter – what those responses have been referring to is telling other local scripts the new target filter. You can’t do that because even if you give them a pointer to the table’s memory address, that address can change and now the other local scripts don’t have the correct target filter.

Despite that, we absolutely do need to be able to use multiple objects for target filter – it’s not so much that we need other scripts to know what the target filter is, but just so that we A ) don’t overwrite the target filter of other plugins / public scripts that aren’t under our control and B ) don’t have to screw up our game’s hierarchy by making every object that needs to be ignored a descendant of workspace.Ignore (really not a fan of parenting my first person arms / weapon models to workspace.Ignore because then I have to manually clean them up since they don’t get automatically removed with the character)

The solution? Following the example of selection:Set() and selection:Get(). That behavior is exactly what we want with mouse.TargetFilter. We could do mouse:GetTargetFilter() and mouse:SetTargetFilter(), but I have a feeling plugin developers and people who make public domain scripts wouldn’t play nicely and keep in mind that there might be other plugins/scripts that use TargetFilter as well. The best API implementation to ensure cross-compatibility between all plugins/scripts is the following:

mouse:AddToTargetFilter(objects)
mouse:RemoveFromTargetFilter(objects)
mouse:GetTargetFilter() (returns table of target filters)

Adding and removing objects from the target filter would be pretty much specific to individual plugins/scripts and they wouldn’t interfere with each other this way. The only instance you’d really run into problems is if you had a plugin/script intentionally clear the whole target filter, and you could just uninstall that plugin/remove that script since it was that bad – most plugins/scripts would just work perfectly with each other if they used the previously mentioned API.

We obviously can’t remove mouse.TargetFilter for backwards-compatibility, but we don’t want setting mouse.TargetFilter to get rid of all of the items you added with AddToTargetFilter() either, because then older plugins would still overwrite your TargetFilter. The best thing to do would be to have TargetFilter completely separate from the new target filter table. When calculating mouse.Target/mouse.Hit, the mouse would ignore everything in the new target filter table + the object in the TargetFilter property, and setting the TargetFilter property and adding items to the new target filter table would not affect each other.

This new API implementation for the mouse’s TargetFilter would help out tremendously with cross-compatibility between plugins / scripts and save us the trouble of screwing up our game hierarchy just to move something into workspace.Ignore. Please? :frowning:

66 Likes

While not as helpful, what I have done is created my own “Mouse” object using the Camera and UserInputService. It works very similarly, but would allow me to add any number of instances to an ignore table, since it uses raycasting.

If you want, I can paste that code. It’s a standalone module. I imagine you’d be able to figure out how to do it regardless.

Of course, having this done C-side would be best I would assume, thus I agree with you. It would be cool to see this added to the Mouse.

2 Likes

If you’re offering to give me what you’re currently using which would save me the trouble of having to do it myself, then I’m not going to decline your offer. It’d be much appreciated if you provided that, and I’m sure I won’t be the only one who would use it. Unfortunately, I won’t be able to use it with what I’m working on right now because the goal is to add certain items to the TargetFilter and have the default studio tools ignore these items, but until we’re able to officially add items to the target filter, I’d be able to use this a bit elsewhere.

1 Like

Cool. I’m at work right now (on break). I’ll post it here when I get home. Only working half-day today so It’ll be in a couple hours.

1 Like

Or just make TargetFilter take a table as well… problems solved

1 Like
1 Like

Personally, I’d prefer a table to store the target instances.
Or, going with the original idea, I’d say that there should definitely be a method to clear all target filters.

[300th post :D]

It would be. mouse:GetTargetFilter() would return a table.

local targetFilter = mouse:GetTargetFilter()
for i=#targetFilter,1,-1 do
    mouse:RemoveFromTargetFilter(targetFilter[i])
end

or

local targetFilter = mouse:GetTargetFilter()
while #targetFilter > 0 do
    mouse:RemoveFromTargetFilter(targetFilter[1])
end

The whole point in not adding an official method for clearing the entire target filter is because you probably shouldn’t be doing that. You’d be overwriting what other plugins / scripts added to the target filter, effectively breaking them in some way. If you need to clear the target filter and you’re not using a plugin and only using scripts you made, then it’s not that hard to clear it manually.

I’d write up an API proposal for this but I know that it would be instantly shut down because the Mouse object is all but deprecated at this point.

EDIT: In favor of things like UIS, CAS, and Raycasting.

but none of those have the current target of the mouse or the current location of the mouse in the worldspace :frowning:

UIS would be the only one of the three to have those two things, and it doesn’t have a table target filter either (it doesn’t have a target filter to begin with because there’s no reason for it to, but I’d assume the ROBLOX engineers would just stick with what they have and use the same object target filter that they use in the current mouse if they were to add mouse.Hit/Target to UIS).

I think UIS should have a readable MousePosition property.

If that were added would you be able to just use raycasting?

Mouse module:

local mouse = require(mouseModule).new()

-- Methods:
local screenPos, delta = mouse:GetPosition()
local isDown = mouse:IsDown(UserInputType button)
local cframeHit, target, surfaceNormal = mouse:ProjectMouseRay(table ignoreList)

-- Events:
mouse.ButtonDown(UserInputType button)
mouse.ButtonUp(UserInputType button)
mouse.Moved(Vector2 position, Vector2 deltaPosition)
mouse.Scrolled(Integer delta)

Module code:

-- Mouse
-- Crazyman32
-- April 28, 2015


--[[
	
	Usage:
	
	local mouse = require(thisModule).new()
	
	METHODS:
		<Vector2 position, Vector2 delta>				mouse:GetPosition()
		<Boolean isDown>								mouse:IsDown(UserInputType button)
		<CFrame cframe, Object target, Vector3 normal>	mouse:ProjectMouseRay(table ignoreList)
	
	EVENTS:
		mouse.ButtonDown(UserInputType button)
		mouse.ButtonUp(UserInputType button)
		mouse.Moved(Vector2 position, Vector2 deltaPosition)
		mouse.Scrolled(Integer delta)
	
--]]


local Mouse = {}
Mouse.__index = Mouse

function Mouse.new()
	
	local player = game.Players.LocalPlayer
	assert(player, "Could not get player")
	
	local cam = game.Workspace.CurrentCamera
	
	local Ray = Ray.new
	
	local input = game:GetService("UserInputService")
		local button1 = Enum.UserInputType.MouseButton1
		local button2 = Enum.UserInputType.MouseButton2
		local button3 = Enum.UserInputType.MouseButton3
		local mouseMovement = Enum.UserInputType.MouseMovement
		local mouseWheel = Enum.UserInputType.MouseWheel
	
	local mouse = {}
	local mousePos, mouseDelta = Vector2.new(), Vector2.new()
	local clicking = {
		[button1] = false;
		[button2] = false;
		[button3] = false;
	}
	
	local function CreateEvent(eventName)
		local e = Instance.new("BindableEvent")
		mouse[eventName] = e.Event
		return function(...)
			e:Fire(...)
		end
	end
	
	-- Events ---------------------------------------------------------------
	local buttonDown = CreateEvent("ButtonDown")
	local buttonUp = CreateEvent("ButtonUp")
	local moved = CreateEvent("Moved")
	local scrolled = CreateEvent("Scrolled")
	-------------------------------------------------------------------------
	
	-- API ------------------------------------------------------------------
	function mouse:IsDown(inputType)
		return (clicking[inputType] == true)
	end
	
	function mouse:GetPosition()
		return mousePos, mouseDelta
	end
	
	function mouse:ProjectMouseRay(ignoreList)
		local pos = self:GetPosition()
		local ray = cam:ScreenPointToRay(pos.X, pos.Y, 0)
		ray = Ray(ray.Origin, (ray.Unit.Direction * 999))
		local hit, hitPos, normal = game.Workspace:FindPartOnRayWithIgnoreList(ray, ignoreList or {}, true, false)
		local cframe = CFrame.new(hitPos, (hitPos + ray.Unit.Direction))
		return cframe, hit, normal
	end
	-------------------------------------------------------------------------
	
	-- UserInputService Setup -----------------------------------------------
	local function InputBegan(inputObject, gameProcessed)
		if (gameProcessed) then return end
		local inputType = inputObject.UserInputType
		if (inputType == button1 or inputType == button2 or inputType == button3) then
			clicking[inputType] = true
			buttonDown(inputType)
		end
	end
	
	local function InputChanged(inputObject, gameProcessed)
		--if (gameProcessed) then return end
		local inputType = inputObject.UserInputType
		if (inputType == mouseMovement) then
			local newMousePos = Vector2.new(inputObject.Position.X, inputObject.Position.Y)
			local delta = newMousePos - mousePos
			mousePos = newMousePos
			mouseDelta = Vector2.new(delta.X, delta.Y)
			moved(mousePos, mouseDelta)
		elseif (inputType == mouseWheel) then
			local num = inputObject.Position.Z
			scrolled(num < 0 and -1 or num > 0 and 1 or 0)
		end
	end
	
	local function InputEnded(inputObject, gameProcessed)
		--if (gameProcessed) then return end
		local inputType = inputObject.UserInputType
		if (inputType == button1 or inputType == button2 or inputType == button3) then
			clicking[inputType] = false
			buttonUp(inputType)
		end
	end
	
	input.InputBegan:connect(InputBegan)
	input.InputChanged:connect(InputChanged)
	input.InputEnded:connect(InputEnded)
	-------------------------------------------------------------------------
	
	return setmetatable(mouse, Mouse)
	
end


function Mouse:__tostring()
	local pos = self:GetPosition()
	return ("Mouse <" .. tostring(pos) .. ">")
end


return Mouse

Note: This works, but it is still a work in progress. I have a Keyboard module that is similar like this, thus eliminating any need for the Mouse object. Let me know of any issues you find, so that I can continue to fine-tune it.

24 Likes

[code]
local mousePosition = Vector2.new()
local function inputChanged(input)
if input.UserInputType == Enum.UserInputType.MouseMovement then
mousePosition = Vector2.new(input.Position.X, input.Position.Y)
end
end

function script.GetMousePosition.OnInvoke()
return mousePosition
end[/code]

Extending this to add a 3D mouse position that uses a custom raycast would not be hard. Just toss this in a local script and call it your mouse.

EDIT: Ninja’d

[quote] [code]
local mousePosition = Vector2.new()
local function inputChanged(input)
if input.UserInputType == Enum.UserInputType.MouseMovement then
mousePosition = Vector2.new(input.Position.X, input.Position.Y)
end
end

function script.GetMousePosition.OnInvoke()
return mousePosition
end[/code]

Extending this to add a 3D mouse position that uses a custom raycast would not be hard. Just toss this in a local script and call it your mouse.

EDIT: Ninja’d [/quote]

This is awful. Are you telling me that all my scripts that want to pull the mouse position should connect to this? Such a basic feature should be exposed through UIS.

@Ethan and Davidii: How will that work in this situation:

I have collision boxes for certain models. I have a plugin that auto-generates them for certain models, and auto-resizes them when the scale of the model changes. I want to add the collision boxes to the target filter so the default studio tools, CmdUtl, and every other building plugin I use ignores those parts like they don’t exist so I can transform the model at will. I can’t modify the default studio tools, and I would prefer not to fork my own version of all of my building plugins, unsubscribing from future updates.

Let’s say I also want to filter the invisible parts that contain lights from being selected. I don’t want to be able to select them unless toggle markers on and click the marker, but even with the markers off I still want the light to affect the world (which means I have to have a part in the world to contain the light). I’d have to have some sort of target filter to add those lights to.

I agree with the principle behind what Ethan is saying in the sense that something commonly used should be in the API. Remember that workspace:GetGravity() that you requested a while ago? Even though it was easy to remember 9.81*20, you thought it should be in the API because of how frequently it’d be used for gravity scripts. Same principle here.

Though, I’d still need a target filter for the two scenarios mentioned above. GetMousePosition wouldn’t work for me.

[quote] @Ethan and Davidii: How will that work in this situation:

I have collision boxes for certain models. I have a plugin that auto-generates them for certain models, and auto-resizes them when the scale of the model changes. I want to add the collision boxes to the target filter so the default studio tools, CmdUtl, and every other building plugin I use ignores those parts like they don’t exist so I can transform the model at will. I can’t modify the default studio tools, and I would prefer not to fork my own version of all of my building plugins, unsubscribing from future updates.

Let’s say I also want to filter the invisible parts that contain lights from being selected. I don’t want to be able to select them unless toggle markers on and click the marker, but even with the markers off I still want the light to affect the world (which means I have to have a part in the world to contain the light). I’d have to have some sort of target filter to add those lights to.

I agree with the principle behind what Ethan is saying in the sense that something commonly used should be in the API. Remember that workspace:GetGravity() that you requested a while ago? Even though it was easy to remember 9.81*20, you thought it should be in the API because of how frequently it’d be used for gravity scripts. Same principle here.

Though, I’d still need a target filter for the two scenarios mentioned above. GetMousePosition wouldn’t work for me.[/quote]
Raeg, you said it was just needless API bloat. Why in the world are you using it as a positive example now?

I pointed that out to make you remember why adding frequently used things to the API is a good thing. The reason I called it a waste is because it was just so easy to remember. It would have been one thing if we could set it or ROBLOX changed it over time or something, but it’s completely static. If you’re programming something that involves gravity, there’s an extremely high chance you know the gravitational constant 9.81 m/s^2 because that’s common knowledge that you need to know if you ever want to work with gravity in life. The only thing that you might not know is the 20 studs = 1 meter in ROBLOX. I wouldn’t add static gravity as a property for the same reason I wouldn’t create a static property called “StudsPerMeter” in ROBLOX and for the same reason I wouldn’t add Enum.Symmetric.ResizeIncrement (assuming that was possible with enums of course).

yessss please add this.

3 Likes

I really need this right now. I don’t see any reason to not add it and my game is getting messy having to place parts the mouse should ignore in one model. Please reconsider this.

I would recommend using a custom mouse module, it gives better control over the mouse with the same functionality. If you don’t want to write your own then I highly recommend using EgoMoose’s module in place of the default mouse, and switching to this module is fairly straightforward.

Roblox hasn’t officially deprecated the old mouse stuff but all of the functionality exists in other parts of the engine to essentially 1:1 recreate the functionality of the old mouse, with better/more custom tailored functionality.

1 Like