What is wrong with my laser script?

I was playing around with ray casting and I found some odd behavior. I don’t understand. This script was never meant for a game but just for practice and I expect it to bounce off of things and launch but it acts weird. Script is local in startcharscripts
I don’t know much about vector math and stuff but I would love to learn.
Script Below:

local plr = game.Players.LocalPlayer

local mouse = plr:GetMouse()

local head = plr.Character:WaitForChild("Head")

local maxBounces = 10

local laserParts = workspace:WaitForChild("LaserStuff")

function createPoint(pos)
	local part = Instance.new("Part")
	part.CanCollide = false
	part.CanTouch = false
	part.Transparency = 1
	part.Locked = true
	part.Name = "LaserBouncePoint"
	part.Anchored = true
	part.Parent = laserParts
	local attachment = Instance.new("Attachment")
	attachment.Parent = part
	return part,attachment

function fireLaser()
	local params = RaycastParams.new()
	params.FilterDescendantsInstances = {head.Parent, laserParts}
	local headVector = head.CFrame.lookVector
	local lastResult
	local currentVector = headVector * 100
	local lastPart = head
	local lastHitPos = nil
	local bounces = 0
		local ray = workspace:Raycast(lastPart.Position, currentVector, params)
		lastResult = ray
		if ray then
			currentVector = ray.Normal + (ray.Normal - currentVector) -- should be normal of reflection
			currentVector *=100
			local newPart, attach = createPoint()
			local newPart2, attach2 = createPoint() -- Create the beam
			if lastHitPos == nil then
				newPart.Position = head.Position
				newPart.Position = lastHitPos
			lastHitPos = ray.Position
			newPart2.Position = ray.Position
			local newBeam = Instance.new("Beam")
			newBeam.Parent = laserParts
			newBeam.Attachment0 = attach
			newBeam.Attachment1 = attach2
			bounces +=1
	until lastResult == nil or bounces >= maxBounces
	print("done casting")


This right here doesn’t look like a reflection formula. It looks close, but isn’t quite correct.
The formula for bouncing a vector off of a surface using it’s normal is

reflected = direction - 2 * direction:Dot(normal) * normal -- for vectors facing outwards from the surface

reflected = 2 * direction:Dot(normal) * normal - direction -- for vectors facing inwards towards the surface

Normal should ideally be a unit vector, which I believe ray casts already give a unit vector normal.

The reflected vector would be the same length as the input vector so you wouldn’t need to scale it unless you had specific behavior in mind to make it respond to reflections.

The basic idea behind a reflection and why this formula works is that you want to project the
vector onto the normal at a right angle using the properties of a right triangle. You can then get the vector pointing from the input vector to the projected vector, the light purple arrow, and add it to the input vector twice to get a perfectly mirrored reflection vector.

This works because the dot product used in the projection has a cosine in it that describes the angle between the normal and the input vector. You can use this to find the right ratio between the surface normal and the vector that allows you to find the length to make the normal to form a right triangle with the vector.

projection = vector:Dot(normal) * normal -- outwards facing vector, though technically works with inwards facing ones too. the projection will just be multiplied by -1 instead

This is only one interpretation of the math though, and there are many different correct ways to interpret it.


Thanks for the help, I am going to try out the changes. I was using the angle of reflection script I used in scratch by finding the angle of incidence and adding it to the surface angle but I guess it’s different.