Can't define the proper direction of a Raycast with mouse.Hit.Position

I’m trying to project a part on top of another part with Raycasts, which is working, but I’m also trying to limit it’s distance but I can’t get it to work using mouse.Hit.Position

mouse.Move:Connect(function()
		local raycast = workspace:Raycast(mouse.Origin.LookVector, mouse.Hit.Position * 15) --this mouse.Hit.Position part is what im refering to
		if raycast ~= nil then 
			if raycast.Instance.Name == "Ground" then 	
				selectedPart.Position = raycast.Instance.Position
			end
		end
	end)
1 Like

The second parameter you passed is the position multiplied by 15, which will just be some random direction (I have no clue what).

You also might want to consider adding some RaycastParams to filter what is and isn’t a valid hit.

I’d recommend casting the ray from the position along the multiplied LookVector.

--I'll use some params as an example
local player = game:GetService("Players").LocalPlayer
local mouse = player:GetMouse()
local char = player.Character or player.CharacterAdded:Wait()

local params = RaycastParams.new()
params.FilterDescendantsInstances = {char}
params.FilterType = Enum.RaycastFilterType.Exclude
params.IgnoreWater = true
params.RespectCanCollide = true

local hit = workspace:Raycast(mouse.Hit.Position, mouse.Hit.LookVector * 15, params)
print(hit)

you should try not to use the mouse object in new projects, this is on the documentation:


Alternatives include Camera:ScreenPointToRay() using UserInputService:GetMouseLocation()

I have tried to use UserInputService and Raycast params before but I couldn’t get them to work properly.
Even now I tried to use UserInputService and params but they just didn’t work.

Also my mistake but I didn’t provide the full script so you’re missing info.

local player = game:GetService("Players").LocalPlayer
local mouse = player:GetMouse()
local tool = script.Parent
local selectedPart = workspace:WaitForChild("SelectedPart")

tool.Equipped:Connect(function()
	selectedPart.SurfaceGui.Frame.Visible = true
	
	mouse.Move:Connect(function()
		local raycast = workspace:Raycast(mouse.Origin.LookVector, mouse.Hit.Position * 15)
		if raycast ~= nil then 
			if raycast.Instance.Name == "Ground" then 	
				selectedPart.Position = raycast.Instance.Position
			end
		end
	end)
	
	tool.Activated:Connect(function()
		local raycast = workspace:Raycast(mouse.Origin.LookVector, mouse.Hit.Position * 15)
		if raycast ~= nil then 
			if raycast.Instance.Name == "Ground" and raycast.Instance.Color == Color3.fromRGB(71, 101, 15) then
				raycast.Instance.Color = Color3.fromRGB(72, 64, 35)
			end
		end
	end)

end)

tool.Unequipped:Connect(function()
	selectedPart.SurfaceGui.Frame.Visible = false
end)

What I wanted to do is once the tool is equipped a part is placed on top of another part so that it kind of works as a selection, but when using mouse.Hit.Position it basically gives it infinite range which is the thing I’m trying to avoid.

(apologies for the bad code)

The ray itself will be infinite, but the cast direction (range at which Roblox counts hits) will not, unless you set it to math.huge. The Vector3 for positions can includes large X, Y and Z numbers in terms of the numbers themselves - it all depends on the distance from Vector3.zero the position is. By multiplying it by 15, you are making the value massive, so it seems infinite. You should cast from the position along the multiplied LookVector.

Could you send your attempt at UserInputService and RaycastParams? I may be able to help with that, they shouldn’t be too hard to implement (especially RaycastParams)

Here’s the attempt.

tool.Equipped:Connect(function()
	selectedPart.SurfaceGui.Frame.Visible = true	
	
	local camera = workspace.CurrentCamera
	local mousePos = UserInputService:GetMouseLocation()
	local mouseRay = camera:ScreenPointToRay(mousePos.X, mousePos.Y)
	local params = RaycastParams.new()
	params.FilterDescendantsInstances = {workspace:WaitForChild("Ground")}
	params.FilterType = Enum.RaycastFilterType.Include
	
	mouse.Move:Connect(function()
		local raycast = workspace:Raycast(mouseRay.Origin, mouseRay.Direction * 15, params)
		if raycast ~= nil then
			selectedPart.Position = raycast 
		else
			print(raycast) 
		end
	end)

end)

Constantly outputs nil.

1 Like

The :Raycast method has 3 arguments.

  1. Origin (positional) Vector 3
  2. Direction (directional, unit) Vector 3
  3. Other Parameters in form of RaycastParams (which can be used to exclude certain things)

If you look closely into your code, you can see that for your positional origin vector, you chose the look vector. The lookvector is a vector that could look like this: Vector3.new(1, 0, 0). It describes the direction that the front face of the part is looking in.

Therefore the first argument should be: mouse.Origin.Position

The second argument is also wrong. In that case we have to use a directional vector (so something like seen above) and multiply it by a max length of the raycast

Therefore the second argument should be: mouse.Origin.LookVector*10

The third argument is optional but I advise you to ignore the player’s character by doing the following:

local params = RaycastParams.new()
params.FilterType = Enum.RaycastFilterType.Exclude
params.FilterDescendantsInstances = {player.Character}

Here are all of my changes in one script:

local params = RaycastParams.new()
params.FilterType = Enum.RaycastFilterType.Exclude
params.FilterDescendantsInstances = {player.Character}

local function raycast()
	local result = workspace:Raycast(mouse.Origin.Position, mouse.Origin.LookVector*200, params)
	return result
end

And here is your updated version (I highly advise you to still read the rest of my post if you haven’t):

local player 		= game:GetService("Players").LocalPlayer
local mouse 		= player:GetMouse()
local tool 			= script.Parent
local selectedPart 	= workspace:WaitForChild("SelectedPart")
local maxDistance 	= 15 --in studs
local equipped 		= false
---------------------
local function raycast()
	local result = workspace:Raycast(mouse.Origin.Position, mouse.Origin.LookVector*maxDistance, params)
	return result
end
---------------------
mouse.Move:Connect(function()
	if equipped then
		local raycast = raycast()
		if raycast == nil then return end

		if raycast.Instance.Name == "Ground" then --only runs when raycast isn't nil
			selectedPart.Position = raycast.Position
		end
	end
end)

tool.Activated:Connect(function()
	local raycast = raycast()
	if raycast == nil then return end

	if raycast.Instance.Name == "Ground" and raycast.Instance.Color == Color3.fromRGB(71, 101, 15) then 
		raycast.Instance.Color = Color3.fromRGB(72, 64, 35)
	end
end)

tool.Equipped:Connect(function()
	selectedPart.SurfaceGui.Frame.Visible = true
	equipped = true
end)

tool.Unequipped:Connect(function()
	selectedPart.SurfaceGui.Frame.Visible = false
	equipped = false
end)
---------------------
1 Like

If you define mouse.Move and tool.Activated inside of tool.Equipped, then they will be called whenever the tool is equipped. When a player equipps the tool 4 times, they will have 4 raycasts being calculated instead of one (this can get really laggy and cause drops in framerate)

Just use these Events outside of tool.Equipped like I did in the updated version.

I added a new value called “equipped”. The raycast will only be calculated when the value is true (so when the tool is equipped) which will prevent it from changing the selectedPart while the tool isn’t equipped!

1 Like

Thank you for helping, this is the result that I was trying to make (also I didn’t even realise it was producing lag, thank you again)

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