# How can I cast reflective rays?

Hello Developers,

I’m currently trying to develop reflective rays to create the illusion of Rays “bouncing off” a disco ball.

However, my partner @xM_ighty and I cannot seem to find a solution for ray casting. We have had multiple attempts.

Here is our closest we’ve ever gotten with raycasting.

Here is another attempt:

``````   local Part = Instance.new("Part")
Part.Name = "Beam"
Part.Transparency = .25
Part.Reflectance = .25
Part.FormFactor = "Custom"
Part.Anchored = true
Part.BrickColor = BrickColor.Red()
Part.Size = Vector3.new(.2, .2, (Current - Ray_Pos).Magnitude)
Part.CFrame = CFrame.new(Current + (Ray_Pos - Current) / 2, Ray_Pos)
Part.Parent = workspace.Ignore
end

function Not_Zero(Value)
local Up = math.ceil(Value + .1)
local Down = math.floor(Value - .1)
if Up ~= 0 and Up ~= -0 then
return Up
elseif Down ~= 0 and Down ~= -0 then
return Down
end
end

Current = Vector3.new(0, 3, 0)

Ray_Part, Ray_Pos = workspace:FindPartOnRay(Ray.new(Current, Heading * 900), workspace.Ignore)
if Ray_Part then
Trace_Ray(Ray_Pos)
Current = Ray_Pos
Angle = Ray_Part.CFrame.lookVector
local R_X, R_Z = (Angle.X + Heading.X) / 2, (Angle.Z + Heading.Z) / 2
end
repeat
Ray_Part, Ray_Pos = workspace:FindPartOnRay(Ray.new(Current, Heading * 250), workspace.Ignore)
if Ray_Part then
Trace_Ray(Ray_Pos)
Current = Ray_Pos
Angle = Ray_Part.CFrame.lookVector

local R_X, R_Z = (Angle.X + Heading.X) / 2, (Angle.Z + Heading.Z) / 2
end
until not Ray_Part or Ray_Part.Name == "Stop"
Trace_Ray(RayPos)

-- Calculate real-time laser reflections!
while wait() do
Current = workspace.Emitter.Position
for ,Beam in ipairs(workspace.Ignore:GetChildren())do
Ray_Part, Ray_Pos = workspace:FindPartOnRay(Ray.new(Current, Heading * 900), workspace.Ignore)
if Ray_Part then
Beam.Size = Vector3.new(.2, .2, (Current - Ray_Pos).Magnitude)
Beam.CFrame = CFrame.new(Current + (Ray_Pos - Current) / 2, Ray_Pos)
Current = Ray_Pos - Heading * .1
Angle = Ray_Part.CFrame.lookVector
local R_X, R_Z = (Angle.X + Heading.X) / 2, (Angle.Z + Heading.Z) / 2
else
Beam:Destroy()
end
end
Ray_Part, Ray_Pos = workspace:FindPartOnRay(Ray.new(Current, Heading * 900), workspace.Ignore)
if Ray_Part and #workspace.Ignore:GetChildren() < 15 then
Trace_Ray(Ray_Pos)
end
end
``````

Any help would be appreciated. Thank you.

UPDATE:
Thanks for all the great responses, it helped us through the process a little bit. But after a while, we hit a brick wall. Literally and metaphorically. our ray was casted and it did reflect off the wall. The laser ray is casted off the block and reflected off of 2 walls.

But however, when I place a block rotated at 45 degrees, I expected the ray to reflect off the 45 degree wall and in the direction of that wall. But however, this occurs.

No ray is casted, nor reflected.
Here is our script placed within the block:

``````function Shoot()

local laser = Instance.new("Part")
laser.Name = "Laser"
laser.FormFactor = Enum.FormFactor.Custom
laser.TopSurface, laser.BottomSurface = 0, 0
laser.Size = Vector3.new(0.2, 0.2, 0.2)
laser.BrickColor = BrickColor.Random()
laser.Anchored = true
laser.CanCollide = false
laser.Locked = true
laser.CFrame = script.Parent.CFrame
laser.Parent = game.Workspace

local maxDistance = 50000000
local curDistance = 0

local stepDistance = 500
local stepWait = 0

local currentPos = script.Parent.Position
local currentNormal = script.Parent.CFrame.lookVector

local function Step(overrideDistance)

-- Cast ray:
local ray = Ray.new(currentPos, currentNormal * (overrideDistance or stepDistance))
local hit, pos, norm = game.Workspace:FindPartOnRayWithIgnoreList(ray, {script.Parent})

-- Update laser position:
laser.Size = Vector3.new(0.4, 0.4, (pos - currentPos).magnitude)
laser.CFrame = CFrame.new(currentPos:lerp(pos, 0.5), pos)

local oldPos = currentPos
currentPos = pos

if (hit) then
-- r = d - 2(d DOT n)n
-- Reflect:
local reflect = (currentNormal - (2 * currentNormal:Dot(norm) * norm))
currentNormal = reflect
Step(stepDistance - (pos - oldPos).magnitude)
return
end

curDistance = (curDistance + (pos - oldPos).magnitude)

-- Apply fade effect to laser as it approaches max distance from < 75 studs:
if (curDistance > (maxDistance - 75)) then
local d = (curDistance - (maxDistance - 75)) / 75
laser.Transparency = d
end

-- Recurse if max distance not reached:
if (curDistance < maxDistance) then
wait(stepWait)
Step()
end
end

Step()

end

Shoot()
wait(1)
``````

(Note that I changed the `maxDistance` to an exaggerated amount to test the reflection physics. However, it’s not making any sense)
Any thoughts or suggestions?
Thank you.
-Andy

2 Likes
2 Likes

Thanks Kian, editing post

Uhh a little confused if I’m interpreting the question right, because I’m not seeing a disco ball or anything like it in the pictures linked.

Regardless, if I do understand the question then have you checked the dot product article? It has a section on reflecting rays.

3 Likes
1 Like

An issue I see with that tutorial’s application is that afaik firing a ray at a sphere will return a surface normal made for a brick. It would need some modifications to work with reflecting off a sphere.

NormalId documentation

1 Like

I’ve used this before, and it bounces off of spheres perfectly.

1 Like

Exactly what I’m looking for. Thank you.

1 Like

Yes, this is also extremely useful; sorry for the confusion I just didn’t know how to explain it well. But however, thank you. Now i need to keep the Trig Functions in mind

NormalId is completely irrelevant and no that is if you use a brick and a sphere special mesh which is different from using an actual sphere. Rays are designed to intersect the collision data of objects. The raycasting functions return a surface normal vector which represents a directional unit vector that points away from the contact surface. The collision data for spheres is completely round (due to the radius based calculations instead of using polygons) so it will point away from the center no matter its size (since spheres themselves are constrained to be regular shapes).

1 Like

Noooo! Could you please, if possible, provide another link? the provided one doesn’t seem to work, and I couldn’t get the info within it.

You are in luck!

7 Likes