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
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
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*
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)
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
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:
Client > Server information: mouse position, camera position
Client handling: gui, remotefunctions, remote events to the server
Server > Client information: anything that won’t break the security of the game (sometimes none)
Server handling: bullet projectile calculation, raycast validation, damage calculation
what you should NOT do:
Client > Server information: what you hit, damage on something
Client handling: the backbone of the game, damage calculation
Server > Client information: (not much of anything, this is the same)
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?