Issue with rays

I have a system for making a game similar to scan test, anyways here is the code

local rays = {}

local player = game.Players.LocalPlayer
local char = player.Character or player.CharacterAdded:Wait()
local folder = Instance.new("Folder", char)

local rayParams = RaycastParams.new()
rayParams.FilterType = Enum.RaycastFilterType.Exclude

local mouse = player:GetMouse()

local RS = game:GetService("RunService")

local UIS = game:GetService("UserInputService")

local LookVectorInt = 3
local RightVectorInt = -3.5
local UpVectorInt = 0

function CreateRays()
	local humanoidRootPart = char.HumanoidRootPart
	if #rays ~= 100 then
		for i = 1, 10 do
			for n = 1, 10 do
				local newRayPart = Instance.new("Part", folder)
				newRayPart.Anchored = true
				newRayPart.Size = Vector3.new(0.1, 0.1, 0.1)
				newRayPart.Name = tostring(i) .. "_" .. tostring(n)
				newRayPart.CanCollide = false
				newRayPart.Transparency = 1
				newRayPart.CanQuery = false
				local lookVector = humanoidRootPart.CFrame.LookVector
				local rightVector = humanoidRootPart.CFrame.RightVector
				local upVector = humanoidRootPart.CFrame.UpVector

				rays[newRayPart.Name] = {
					LookVector = LookVectorInt,
					rightVector = RightVectorInt,
					upVector = -UpVectorInt
				}

				local setPosition = humanoidRootPart.Position + 
					(lookVector * LookVectorInt) + 
					(rightVector * - RightVectorInt) + 
					(upVector * - UpVectorInt)
				
				newRayPart.Position = setPosition

				RightVectorInt += 0.75
			end
			UpVectorInt -= 0.35
			RightVectorInt = - 3.5
		end
	end
end


function UpdateRays()
	for i, part in pairs(rays) do
		local humanoidRootPart = char.HumanoidRootPart
		local lookVector = humanoidRootPart.CFrame.LookVector
		local rightVector = humanoidRootPart.CFrame.RightVector
		local upVector = humanoidRootPart.CFrame.UpVector

	
		local setPosition = humanoidRootPart.Position + (lookVector * rays[i].LookVector) + (rightVector * rays[i].rightVector) + (upVector * rays[i].upVector)

		local foundPart = folder:FindFirstChild(i)
		if foundPart then
			foundPart.Position = setPosition
		end
	end
end


UIS.InputBegan:Connect(function(input)
	if input and input.KeyCode == Enum.KeyCode.E then
		for i, v in pairs(rays) do
			
			local newRay = workspace:Raycast(folder[i].Position, char.HumanoidRootPart.CFrame.LookVector * 10)
			
			if newRay then
				if newRay.Position and newRay.Instance then
					local newHitEffect = game.ReplicatedStorage.RayHit:Clone()
					newHitEffect.Parent = workspace.HitEffects
					newHitEffect.Position = newRay.Position
					newHitEffect.CanCollide = false
					
				end
			end
		end
	end
end)

local RayCastingMouse = false

function MouseRayCast()
	while task.wait(0.01) do
		if RayCastingMouse == true then
			local HeightIncrease = 0
			local SideIncrease = 0

		
				for n = 1, 20 do
					local Camera = game:GetService("Workspace").CurrentCamera
					local mousePosition = mouse.Hit.Position + Vector3.new(HeightIncrease, SideIncrease, 0)
					if (mouse.Hit.Position - char.HumanoidRootPart.Position).Magnitude >= 20 then continue end
					local rayOrigin = Camera.CFrame.Position
					local rayDirection = (mousePosition - rayOrigin).unit * 1000

					local newRay = workspace:Raycast(rayOrigin, rayDirection, rayParams)

					if newRay then
						if newRay.Position and newRay.Instance then
							local newHitEffect = game.ReplicatedStorage.RayHit:Clone()
							newHitEffect.Parent = workspace.HitEffects
							newHitEffect.Position = newRay.Position
							newHitEffect.CanCollide = false
							newHitEffect.Anchored = true
							if newRay.Instance:IsDescendantOf(game.Workspace.AI) then
								newHitEffect.BrickColor = BrickColor.new("Bright red")
							end
						end
					end

					SideIncrease += math.random(-10, 10)/7
					HeightIncrease += math.random(-10 , 10)/7
			end
		end
	end
end


mouse.Button1Down:Connect(function()
	RayCastingMouse = true
end)

mouse.Button1Up:Connect(function()
	RayCastingMouse = false
end)

CreateRays()

rayParams.FilterDescendantsInstances = {folder, char, workspace.HitEffects}

player.CharacterAdded:Connect(function(newChar)
	char = newChar
	rayParams.FilterDescendantsInstances = {folder, char, workspace.HitEffects}
end)

task.wait(1)

task.spawn(MouseRayCast)
RS.RenderStepped:Connect(UpdateRays)


As you can see in the video the first wall I hold to scan on the first fall it looks nice, but one the second one its randomized in just straight lines which is not the desired effect, i would like to know how to make the effect be like such on the first wall on all walls

2 Likes

something funny that may have happened is that because you started scanning on the other wall it tried spreading the rays the same way they did for the first wall so try to fix whatever is handling the random spread of that

function MouseRayCast()
	while task.wait(0.01) do
		if RayCastingMouse then
			for i = 1, 20 do
				local Camera = game.Workspace.CurrentCamera
				local mousePosition = mouse.Hit.Position
				if (mousePosition - char.HumanoidRootPart.Position).Magnitude >= 20 then
					continue
				end

				local rayOrigin = Camera.CFrame.Position
				local rayDirection = (mousePosition - rayOrigin).unit * 1000

				local newRay = workspace:Raycast(rayOrigin, rayDirection, rayParams)

				if newRay then
					if newRay.Position and newRay.Instance then
						
						local normal = newRay.Normal
						local tangent = Vector3.new(normal.Y, normal.Z, -normal.X).unit
						local binormal = normal:Cross(tangent).unit

				
						local randomRadius = math.random() * 4
						local randomAngle = math.random() * math.pi * 2
						local randomOffset = (tangent * math.cos(randomAngle) + binormal * math.sin(randomAngle)) * randomRadius

						local finalPosition = newRay.Position + randomOffset

					
						local newHitEffect = game.ReplicatedStorage.RayHit:Clone()
						newHitEffect.Parent = workspace.HitEffects
						newHitEffect.Position = finalPosition
						newHitEffect.CanCollide = false
						newHitEffect.Anchored = true

						
						newHitEffect.CFrame = CFrame.new(finalPosition, finalPosition + normal)

						
						if newRay.Instance:IsDescendantOf(game.Workspace.AI) then
							newHitEffect.BrickColor = BrickColor.new("Bright red")
						end
					end
				end
			end
		end
	end
end

I remade it and now it works fine on all surfaces however if you hold near the ledge of part it can generate floating parts

Do you need to use rays or can you use a raycast instead since its the new version.

also why it’s creating floating parts is something i actually managed to figure out before even looking at the code… you’re gonna have to make a raycast for every single dot, then randomize the direction of those raycasts, which might be deadly for performance but it depends on if this is handled serverside or clientside, i assume clientside given the presence of game.Player.LocalPlayer.

what you were essentially doing was just casting a ray from the origin point towards your mouse and then generating random dots off of that, with the code not bothering to check if somehow the dots will go off this ledge which then causes the dots to DO go off the ledge in some cases creating this issue*

I plugged my post into chatgpt and it fixed it

local rays = {}

local player = game.Players.LocalPlayer
local char = player.Character or player.CharacterAdded:Wait()
local folder = Instance.new("Folder", char)

local rayParams = RaycastParams.new()
rayParams.FilterType = Enum.RaycastFilterType.Exclude

local mouse = player:GetMouse()

local RS = game:GetService("RunService")

local UIS = game:GetService("UserInputService")

local LookVectorInt = 3
local RightVectorInt = -3.5
local UpVectorInt = 0

function CreateRays()
	local humanoidRootPart = char.HumanoidRootPart
	if #rays ~= 100 then
		for i = 1, 10 do
			for n = 1, 10 do
				local newRayPart = Instance.new("Part", folder)
				newRayPart.Anchored = true
				newRayPart.Size = Vector3.new(0.1, 0.1, 0.1)
				newRayPart.Name = tostring(i) .. "_" .. tostring(n)
				newRayPart.CanCollide = false
				newRayPart.Transparency = 1
				newRayPart.CanQuery = false
				local lookVector = humanoidRootPart.CFrame.LookVector
				local rightVector = humanoidRootPart.CFrame.RightVector
				local upVector = humanoidRootPart.CFrame.UpVector

				rays[newRayPart.Name] = {
					LookVector = LookVectorInt,
					rightVector = RightVectorInt,
					upVector = -UpVectorInt
				}

				local setPosition = humanoidRootPart.Position + 
					(lookVector * LookVectorInt) + 
					(rightVector * - RightVectorInt) + 
					(upVector * - UpVectorInt)
				
				newRayPart.Position = setPosition

				RightVectorInt += 0.75
			end
			UpVectorInt -= 0.35
			RightVectorInt = - 3.5
		end
	end
end


function UpdateRays()
	for i, part in pairs(rays) do
		local humanoidRootPart = char.HumanoidRootPart
		local lookVector = humanoidRootPart.CFrame.LookVector
		local rightVector = humanoidRootPart.CFrame.RightVector
		local upVector = humanoidRootPart.CFrame.UpVector

	
		local setPosition = humanoidRootPart.Position + (lookVector * rays[i].LookVector) + (rightVector * rays[i].rightVector) + (upVector * rays[i].upVector)

		local foundPart = folder:FindFirstChild(i)
		if foundPart then
			foundPart.Position = setPosition
		end
	end
end


UIS.InputBegan:Connect(function(input)
	if input and input.KeyCode == Enum.KeyCode.E then
		for i, v in pairs(rays) do
			
			local newRay = workspace:Raycast(folder[i].Position, char.HumanoidRootPart.CFrame.LookVector * 10)
			
			if newRay then
				if newRay.Position and newRay.Instance then
					local newHitEffect = game.ReplicatedStorage.RayHit:Clone()
					newHitEffect.Parent = workspace.HitEffects
					newHitEffect.Position = newRay.Position
					newHitEffect.CanCollide = false
					
				end
			end
		end
	end
end)

local RayCastingMouse = false


function MouseRayCast()
	while task.wait(0.01) do
		if RayCastingMouse then
			for i = 1, 20 do
				local Camera = game.Workspace.CurrentCamera
				local mousePosition = mouse.Hit.Position
				if (mousePosition - char.HumanoidRootPart.Position).Magnitude >= 20 then
					continue
				end

				local rayOrigin = Camera.CFrame.Position
				local rayDirection = (mousePosition - rayOrigin).unit * 1000

				local newRay = workspace:Raycast(rayOrigin, rayDirection, rayParams)

				if newRay then
					if newRay.Position and newRay.Instance then

						local normal = newRay.Normal
						local tangent = Vector3.new(normal.Y, normal.Z, -normal.X).unit
						local binormal = normal:Cross(tangent).unit

					
						local randomRadius = math.random() * 4
						local randomAngle = math.random() * math.pi * 2
						local randomOffset = (tangent * math.cos(randomAngle) + binormal * math.sin(randomAngle)) * randomRadius

						local potentialPosition = newRay.Position + randomOffset

						
						local adjustmentRay = workspace:Raycast(
							potentialPosition + normal * 0.1, 
							-normal * 0.2, 
							rayParams
						)

						
						if adjustmentRay and adjustmentRay.Instance then
							local finalPosition = adjustmentRay.Position

							local newHitEffect = game.ReplicatedStorage.RayHit:Clone()
							newHitEffect.Parent = workspace.HitEffects
							newHitEffect.Position = finalPosition
							newHitEffect.CanCollide = false
							newHitEffect.Anchored = true

							newHitEffect.CFrame = CFrame.new(finalPosition, finalPosition + adjustmentRay.Normal)

							if adjustmentRay.Instance:IsDescendantOf(game.Workspace.AI) then
								newHitEffect.BrickColor = BrickColor.new("Bright red")
							end
						end
					end
				end
			end
		end
	end
end





mouse.Button1Down:Connect(function()
	RayCastingMouse = true
end)

mouse.Button1Up:Connect(function()
	RayCastingMouse = false
end)

CreateRays()

rayParams.FilterDescendantsInstances = {folder, char, workspace.HitEffects}

player.CharacterAdded:Connect(function(newChar)
	char = newChar
	rayParams.FilterDescendantsInstances = {folder, char, workspace.HitEffects}
end)

task.wait(1)

task.spawn(MouseRayCast)
RS.RenderStepped:Connect(UpdateRays)

Its client sided, single player game no need for it to be done on server

he is using :Raycast() and not :FindPartOnRay() i think, Ray.new() is still acceptable and isn’t deprecated actually

There is another not really a bug i think roblox feature that if you go too far from the hitEffects they disappear and re appear if you get close, is there a way to stop that?

edit: nevermind its my own bad atmosphere settings

also just remember you need security checks, lots of em because you also don’t want the client to cheat either. sure you can put the scanner on the client but because there is an enemy ai from the literal presence of this in your code:

you would want to handle that on the server, i know it is singleplayer but still

I thought it was deprecated. NVM ignore me.

If someone cheats in a single player horror game I don’t really care, they’d cheat themselves out of the game

ok well it makes sense, by that point if you were just using a normal game engine all of it is on the client, but for multiplayer games i advise you follow this philosophy:

  1. Client > Server information: mouse position, camera position
  2. Client handling: gui, remotefunctions, remote events to the server
  3. Server > Client information: anything that won’t break the security of the game (sometimes none)
  4. Server handling: bullet projectile calculation, raycast validation, damage calculation

what you should NOT do:

  1. Client > Server information: what you hit, damage on something
  2. Client handling: the backbone of the game, damage calculation
  3. Server > Client information: (not much of anything, this is the same)
  4. Server handling: damaging the target according to the client

the second, incorrect way of game security is wrong because this allows exploiters to eat your game from the inside out, if the backbone of the game itself is handled on the client well you’re done, always assume these things as well:

  • they have access to everything on the client
  • they have malicious intent
  • they know the structure of the game from the client

of course the third one excludes things stored in areas like ServerStorage and ServerScriptService and some of the things beyond the distance specified from workspace.StreamingTargetRange causing nothing to load* where literally nothing from there replicates to the client, but still, who knows what could happen?

1 Like

Thanks, I made multiplayer games in the past so I know most of this, for single player games I’m fine with just trusting the client