Shoot lasers from a statue's eyes on other parts

Hi, last time I made a post was about me not able to rotate/resize things properly, as I was trying to make a statue that shoots lasers from his eyes on other parts. (screenshot below). Well I tried rotating the eyes and then resizing the laser but that didn’t really work out and I bet there has to be some cleaner/better/simpler way to achieve that.

I need to move the laser to a different Part each let’s say 5 seconds.
Obviously adjust the size too, so it can reach the Part.

My last attempt looked like this, i figured out the “moving” part only not the resizing. But it looks way too chaotic at my opinion, and would like to see some other approaches.

local laserSize = {	
	Vector3.new(0,0,0),  
	Vector3.new(48.8, 0.2, 0.2), --Pillar 1
	Vector3.new(62.75, 0.2, 0.2),
	Vector3.new(79.351, 0.2, 0.2),
	Vector3.new(93.65, 0.2, 0.2),
	Vector3.new(116.55, 0.2, 0.2),
	Vector3.new(154.05, 0.2, 0.2),
	Vector3.new(154.05, 0.2, 0.2)-- Pillar 7
}

local rightEyeRot = {	
	Vector3.new(0,0,0),  
	Vector3.new(-68.26, 59.27, 144.84), --Pillar 1
	Vector3.new(-47.73, 58.37, 166.82),
	Vector3.new(-54.09, 65.47, 153.54),
	Vector3.new(-66.92, 55.09, 150.28),
	Vector3.new(-66.92, 55.09, 150.28),
	Vector3.new(-73.98, 74.48, 131.95),
	Vector3.new(-75.93, 85.72, 118.59) -- Pillar 7
}

local leftEyeRot = {	
	Vector3.new(0,0,0),  
	Vector3.new(-74.76, -16.21, -173.42), --Pillar 1
	Vector3.new(-63.71, -27.15, -160.53),
	Vector3.new(-73.02, -40.65, -145.43),
	Vector3.new(-78.6, -67.54, -122.3),
	Vector3.new(-78.6, -67.54, -122.3),
	Vector3.new(-79.78, -104.99, -79.46),
	Vector3.new(-78.11, -126.98, -57.87) -- Pillar 7
}



local Model = script.Parent
local leftEye = Model:WaitForChild("leftEye")
local leftEyeLaser = leftEye:WaitForChild("leftEyeLaser")
local ClickDetector = Model:WaitForChild("CD"):WaitForChild("ClickDetector")


ClickDetector.MouseClick:Connect(function(player)
	
	local leftEyeOffset = leftEye.CFrame:Inverse() * leftEyeLaser.CFrame
	local rng = math.random(3,8)
	
	leftEye.Orientation = leftEyeRot[rng]

	leftEyeLaser.CFrame = leftEye.CFrame * leftEyeOffset 
	
	--Resize the leftEyeLaser somehow, use the Vec3 array laserSize.

end)

image

Thank you.

1 Like

Not exactly sure what you’re trying to do here, so forgive me if I’m wrong.

I assume you want to resize the laser? If so, you can try setting the leftEyeLaser.Size = laserSize[rng], if that’s what you’re trying to achieve

It’s gonna end up looking like this.
image

1 Like

I would start by using a Raycast with workspace:Raycast.
If the raycast hits something it will return a dictionary.
Using this dictionary, you can get the distance between what the raycast hit and the position of the eyes, as well as the middle between the two positions.
It will look like this:

local RaycastDirection = Eye.CFrame.LookVector * 100 -- This gives the raycast a max distance of 100. Change it if you want.
local RaycastResult = workspace:Raycast(Eye.Position, RaycastDirection, x) -- x = RaycastParams. 
if RaycastResult then
    local Distance = (Eye.Position - RaycastResult.Position).Magnitude
    local MidPos = (Eye.Position + RaycastResult.Position) / 2
end

From here, you can place your laser.

Laser.Size = Vector3.new(x, y, Distance)
Laser.Position = MidPos

To fire lasers at different angles, use a for loop to go through your table leftEyeRot to change the rotation of the eye part, and then repeat the above (I would put it into a function).
I hope this helps.

2 Likes

Might I propose a different solution? You can try to get the size and position using calculations rather than pre-determined sizes, via some math. e.g:

local partPos = -- part position
local eyePos = -- eye position
local distance = (partPos - eyePos).Magnitude -- distance between eye and part
local direction (partPos - eyePos).Unit * distance -- direction of the ray

local midpoint = origin + direction/2 -- middle point
leftEyeLaser.Size = Vector3.new(leftEyeLaser.Size.X, leftEyeLaser.Size.Y, direction.Magnitude) 
leftEyeLaser.CFrame = CFrame.new(midpoint, eyePos) -- position the laser
1 Like

Unless stalog required a ‘line of sight’, I don’t think they would need this.

Not sure why you’re using the LookVector, this would just shoot a laser directly out the ‘front’ of the eye. Seeing as the parts are not in front of the eye, I don’t think this would work very well.

1 Like

Not sure why you’re using the LookVector, this would just shoot a laser directly out the ‘front’ of the eye. Seeing as the parts are not in front of the eye, I don’t think this would work very well.

…which is why the eyes are rotated, according to OP’s tables leftEyeRot and rightEyeRot.
The ‘Laser’ part can be rotated accordingly using the Normal of the RaycastResult.

1 Like

Oh gosh. Forgive me, I didn’t read the script carefully enough to notice that.

1 Like

Thank you all! I can definitely kick off from now!

2 Likes

Not to worry, it happens. Been there, done that lol

3 Likes

Awesome, hope it works out for you :slight_smile:

1 Like

The CFrame.new(orgin, target) constructor has been deprecated and replaced with CFrame.lookAt.

1 Like

Sorry, I’m a little late to the party. However, if I were you I would structure this differently.

  1. Make an array of all the parts you want to shoot at.
  2. Use a Vector3Value | Roblox Creator Documentation and parent it to the model. This is just so that we can tween it.
  3. Whenever the part the laser fires at changes, create a tween, using TweenService | Roblox Creator Documentation, on the Vector3Value.
  4. As the value changes, Raycast and move it accordingly until it reaches the target.
local TweenService = game:GetService("TweenService")
local RunService = game:GetService("RunService")

local Model = script.Parent

local Target = Model:WaitForChild("Target") -- the Vector3Value in the model

local leftEye = Model:WaitForChild("leftEye")
local rightEye = Model:WaitForChild("rightEye")

local leftEyeLaser = leftEye:WaitForChild("leftEyeLaser")
local rightEyeLaser = rightEye:WaitForChild("rightEyeLaser")

local ClickDetector = Model:WaitForChild("CD"):WaitForChild("ClickDetector")

local Pillars = {
	--Each pillar instance in order from 1 to 8
}

local extraDistance = 500 -- the extra distance the laser travels through open air
local laserTravelTimePerStud = 0.025 -- the time you want the laser to travel from one pillar to the next

local function FireLaser(eye, laser, target)
	local eyePos = eye.Position
	
	local direction = target - eyePos
	local result = workspace:Raycast(eyePos, direction)
	direction = if result ~= nil then result.Position else (direction.Unit * (direction.Magnitude + extraDistance))
	
	local midpoint = eyePos + direction/2 -- middle point
	RunService.Stepped:Wait()
	
	laser.Size = Vector3.new(laser.Size.X, laser.Size.Y, direction.Magnitude)
	laser.CFrame = CFrame.lookAt(midpoint, eyePos+direction) -- position the laser
	
	eye.CFrame = CFrame.lookAt(eyePos, target) -- rotate the eye to look at the target
end

ClickDetector.MouseClick:Connect(function(player)
	local Pillar = Pillars[math.random(#Pillars)] -- will chose a random pillar to target
	
	local info = TweenInfo.new(laserTravelTimePerStud * (Target.Value - Pillar.Position).Magnitude, Enum.EasingStyle.Linear) -- you can adjust this for the travel effect you want
	local targetTween = TweenService:Create(Target, info, {Value = Pillar.Position})
	
	local targetUpdated = Target.Changed:Connect(function()
		FireLaser(leftEye, leftEyeLaser, target.Value) -- fire the laser for the left eye
		FireLaser(rightEye, rightEyeLaser, target.Value) -- fire the laser for the right eye
	end)
	
	targetTween:Play()
	targetTween.Completed:Wait()
	
	task.defer(targetUpdated.Disconnect, targetUpdated)
end)

I’m just going to borrow the code @EmeraldLimes provided since it’s already typed.

2 Likes

Hi, thank you, but I’m having one issue, the eye is looking at the right position but the laser is pretty off

The raycast result is returning the correct object tho.
The flickering is maybe caused because the raycast also targets the other laser?

local TweenService = game:GetService("TweenService")
local RunService = game:GetService("RunService")

local Model = script.Parent
local pillarModel = Model:WaitForChild("Pillars")

local Target = Model:WaitForChild("Target") -- the Vector3Value in the model

local leftEye = Model:WaitForChild("leftEye")
local rightEye = Model:WaitForChild("rightEye")

local leftEyeLaser = leftEye:WaitForChild("leftEyeLaser")
local rightEyeLaser = rightEye:WaitForChild("rightEyeLaser")




local Pillars = {
	pillarModel:WaitForChild("Pillar1"),
	pillarModel:WaitForChild("Pillar2"),
	pillarModel:WaitForChild("Pillar3"),
	pillarModel:WaitForChild("Pillar4"),
	pillarModel:WaitForChild("Pillar5"),
	pillarModel:WaitForChild("Pillar6"),
	pillarModel:WaitForChild("Pillar7"),
	pillarModel:WaitForChild("Pillar8"),
	pillarModel:WaitForChild("targetThis") -- the Red block
}

local extraDistance = 500 -- the extra distance the laser travels through open air
local laserTravelTimePerStud = 0.01 -- the time you want the laser to travel from one pillar to the next

local function FireLaser(eye, laser, target)
	local eyePos = eye.Position

	local direction = target - eyePos
	local result = workspace:Raycast(eyePos, direction)
	direction = if result ~= nil then result.Position else (direction.Unit * (direction.Magnitude + extraDistance))

	local midpoint = eyePos + direction/2 -- middle point
	RunService.Stepped:Wait()

	laser.Size = Vector3.new(laser.Size.X, laser.Size.Y, direction.Magnitude)
	laser.CFrame = CFrame.lookAt(midpoint, eyePos+direction) -- position the laser

	eye.CFrame = CFrame.lookAt(eyePos, target) -- rotate the eye to look at the target

end

while true do
	wait(5)
	local Pillar = Pillars[9] -- will chose a random pillar to target

	local info = TweenInfo.new(laserTravelTimePerStud * (Target.Value - Pillar.Position).Magnitude, Enum.EasingStyle.Linear) -- you can adjust this for the travel effect you want
	local targetTween = TweenService:Create(Target, info, {Value = Pillar.Position})

	local targetUpdated = Target.Changed:Connect(function()
		FireLaser(leftEye, leftEyeLaser, Target.Value) -- fire the laser for the left eye
		FireLaser(rightEye, rightEyeLaser, Target.Value) -- fire the laser for the right eye
	end)

	targetTween:Play()
	targetTween.Completed:Wait()

	task.defer(targetUpdated.Disconnect, targetUpdated)

end

I’m garbage at raycasting and I have no idea how it works, but you should probably add a check for that if so. (like, if name == laser2 or something)

Yes, i added a whitelist just for the Pillars table. But i still don’t know why are the lasers way too off.

EDIT:

I changed the direction to be always (direction.Unit * (direction.Magnitude + extraDistance))
and it works how its supposed to.
Not sure why but yeah.

Thank you all once again!

My bad! I forgot to actually calculate the direction of the raycast result position relative to the eye!

Maybe you can replacing the original line with this?

direction = if result ~= nil then (result.Position - eyePos) else (direction.Unit * (direction.Magnitude + extraDistance))

It should work the same, except if something is in the way it will “block” the lasers.

1 Like