Goal: A beam (not part) shoots from a tool to the mouse location.
Stretch Goal: As the player moves, the beam stays connected between the tool handle and the original mouse click position.
Reference Material:
-
I’m modifying the code from this tutorial → Hit Detection with Lasers | Documentation - Roblox Creator Hub
-
Need help making a beam follow the direction of the user's mouse - #4 by Kyl3ST
-
How do I make the beam land on the exact location of the mouse?
The Issue:
The beam end point doesn’t align with the raycasting end point. In the tutorial they are showing a “beam” which is really just a red neon part and it aligns using CFrame. I’m using a real beam and going off of the getWorldMousePosition() function in the code. It’s not working.
What I’ve tried:
I’ve tried lots of things but the most common answer I saw was using the GetMouse instead of the UserINputService:GetMouseLocation(). I tried this, following another forum post, and I’m still getting the beam not aligning properly.
Any help would be greatly appreciated.
StarterPack–>Tool–>LocalScript "ToolController
local UserInputService = game:GetService("UserInputService")
local Players = game:GetService("Players")
local LaserRenderer = require(Players.LocalPlayer.PlayerScripts.LaserRenderer) --Pull in module script that renders laser
local tool = script.Parent
local MAX_MOUSE_DISTANCE = 1000
local MAX_LASER_DISTANCE = 50
local function getWorldMousePosition()
local mouseLocation = UserInputService:GetMouseLocation()
-- Create a ray from the 2D mouseLocation
local screenToWorldRay = workspace.CurrentCamera:ViewportPointToRay(mouseLocation.X, mouseLocation.Y)
-- The unit direction vector of the ray multiplied by a maximum distance
local directionVector = screenToWorldRay.Direction * MAX_MOUSE_DISTANCE
-- Raycast from the ray's origin towards its direction
local raycastResult = workspace:Raycast(screenToWorldRay.Origin, directionVector)
if raycastResult then
-- Return the 3D point of intersection
--print(raycastResult.Position)
return raycastResult.Position
else
-- No object was hit so calculate the position at the end of the ray
return screenToWorldRay.Origin + directionVector
end
end
local function fireWeapon()
local mouseLocation = getWorldMousePosition()
-- Calculate a normalised direction vector and multiply by laser distance
local targetDirection = (mouseLocation - tool.Handle.Position).Unit
-- The direction to fire the weapon multiplied by a maximum distance
local directionVector = targetDirection * MAX_LASER_DISTANCE
-- Ignore the player's character to prevent them from damaging themselves
local weaponRaycastParams = RaycastParams.new()
weaponRaycastParams.FilterDescendantsInstances = {Players.LocalPlayer.Character}
local weaponRaycastResult = workspace:Raycast(tool.Handle.Position, directionVector, weaponRaycastParams)
-- Check if any objects were hit between the start and end position
local hitPosition
if weaponRaycastResult then
hitPosition = weaponRaycastResult.Position
-- The instance hit will be a child of a character model
-- If a humanoid is found in the model then it's likely a player's character
local characterModel = weaponRaycastResult.Instance:FindFirstAncestorOfClass("Model")
if characterModel then
local humanoid = characterModel:FindFirstChild("Humanoid")
if humanoid then
print("Player hit")
end
end
else
-- Calculate the end position based on maximum laser distance
hitPosition = tool.Handle.Position + directionVector
end
LaserRenderer.createLaser(tool.Handle, hitPosition)
end
local function testFire()
local mouseLocation = getWorldMousePosition()
tool.Handle.EndAttachment.Position = Vector3.new(mouseLocation.X, mouseLocation.Y, mouseLocation.Z)
end
local function toolEquipped()
--tool.Handle.Equip:Play()
end
local function toolActivated()
--tool.Handle.Activate:Play()
fireWeapon()
--testFire()
end
-- Connect events to appropriate functions
tool.Equipped:Connect(toolEquipped)
tool.Activated:Connect(toolActivated)
StarterPlayer–>StarterPlayerScripts–>LocalScript “LaserRenderer”
local LaserRenderer = {}
local SHOT_DURATION = 1 -- Time that the laser is visible for
-- Create a laser beam from a start position towards an end position
function LaserRenderer.createLaser(toolHandle, endPosition)
local startPosition = toolHandle.Position
print("Start")
print(startPosition)
print("End")
print(endPosition)
local laserDistance = (startPosition - endPosition).Magnitude
local laserCFrame = CFrame.lookAt(startPosition, endPosition) * CFrame.new(0, 0, -laserDistance / 2)
print("lookat")
print(CFrame.lookAt(startPosition, endPosition))
local laserPart = Instance.new("Part")
laserPart.Size = Vector3.new(0.2, 0.2, laserDistance)
laserPart.CFrame = laserCFrame
laserPart.Anchored = true
laserPart.CanCollide = false
laserPart.Color = Color3.fromRGB(225, 0, 0)
laserPart.Material = Enum.Material.Neon
laserPart.Parent = workspace
--TEST
-- create attachments
local att0 = Instance.new("Attachment")
local att1 = Instance.new("Attachment")
att0.Name = 'Attatchment0'
att1.Name = 'Attachment1'
-- parent to terrain (can be part instead)
att0.Parent = toolHandle
att1.Parent = toolHandle
--local posStart
--posStart = (laserPart.CFrame * CFrame.new(0,0,laserPart.Size.Z/2)).p
--local posEnd
--posEnd = (laserPart.CFrame * CFrame.new(0,0,laserPart.Size.X/2)).p
-- create beam
local beam = Instance.new("Beam")
beam.Attachment0 = att0
beam.Attachment1 = att1
-- position attachments
--att0.Position = Vector3.new(startPosition.X, startPosition.Y, startPosition.Z)
att0.Position = Vector3.new(startPosition)
att1.Position = Vector3.new(endPosition.X, endPosition.Y, endPosition.Z)
print("att0 Pos")
print(att0.Position)
print("att1 Pos")
print(att1.Position)
-- appearance properties
beam.Color = ColorSequence.new({ -- a color sequence shifting from white to blue
ColorSequenceKeypoint.new(0, Color3.fromRGB(255, 255, 255)),
ColorSequenceKeypoint.new(1, Color3.fromRGB(0, 255, 255)),
})
beam.LightEmission = 1 -- use additive blending
beam.LightInfluence = 0 -- beam not influenced by light
--beam.Texture = "rbxasset://textures/particles/sparkles_main.dds" -- a built in sparkle texture
beam.Texture = "rbxassetid://6060542252" -- a built in sparkle texture
beam.TextureMode = Enum.TextureMode.Wrap -- wrap so length can be set by TextureLength
beam.TextureLength = 1 -- repeating texture is 1 stud long
beam.TextureSpeed = 1 -- slow texture speed
beam.Transparency = NumberSequence.new({ -- beam fades out at the end
NumberSequenceKeypoint.new(0, 0),
NumberSequenceKeypoint.new(0.8, 0),
NumberSequenceKeypoint.new(1, 1),
})
beam.ZOffset = 0 -- render at the position of the beam without offset
-- shape properties
--beam.CurveSize0 = 2 -- create a curved beam
--beam.CurveSize1 = -2 -- create a curved beam
beam.FaceCamera = true -- beam is visible from every angle
beam.Segments = 10 -- default curve resolution
beam.Width0 = 2 -- starts small
beam.Width1 = 2 -- ends big
-- parent beam
beam.Enabled = true
beam.Parent = att0
--https://create.roblox.com/docs/reference/engine/classes/Beam#summary
--https://devforum.roblox.com/t/need-help-making-a-beam-follow-the-direction-of-the-users-mouse/2444140/4
-- Add laser beam to the Debris service to be removed & cleaned up
game.Debris:AddItem(laserPart, SHOT_DURATION)
game.Debris:AddItem(beam, SHOT_DURATION)
end
return LaserRenderer