Raycasting Issue: Laser Beam goes off target

I’m currently working on a script for a laser beam that should stay on the target when the mouse is held over it. However, I’ve encountered an issue where, intermittently, the beam seems to behave unexpectedly, as illustrated in the attached GIF.

e480f51e439bc236ec83dc5bb8fdf6cf

I suspect there might be an occasional problem with the raycasting calculations or other aspects of the script. Most of the time, it works correctly, but I’m keen to ensure a consistently smooth experience for users.

Anyway, whoever is reading this thank you in advance for your time and expertise!

image

Clientscript:

-- Services
local runService = game:GetService("RunService")
local players = game:GetService("Players")

-- Player-related
local player = players.LocalPlayer
local mouse = player:GetMouse() 

-- Tool-related
local tool = script.Parent

-- Folders
local remoteEventsFolder = tool:WaitForChild("RemoteEventsFolder")
local remoteEvent = remoteEventsFolder:WaitForChild("RemoteEvent")

-- Configuration
local gunConfig = tool:WaitForChild("Configuration")
local currentAmountOfAmmo = gunConfig:WaitForChild("CurrentAmountOfAmmo")

-- Other
local activated = false -- Flag to track the activation state
local heartbeat -- Variable to store the heartbeat connection

function Fire()
	remoteEvent:FireServer(mouse.Hit.Position)
end

tool.Activated:Connect(function()
	activated = true -- Set the activation flag to true

	-- Check if the gun is automatic
	if gunConfig:WaitForChild("IsAutomatic").Value == true then
		-- Connect to the Heartbeat event for automatic firing
		heartbeat = runService.Heartbeat:Connect(function()
			if activated then
				Fire() -- Fire the remote event
			else
				heartbeat:Disconnect() -- Disconnect the heartbeat connection
			end
		end)
	else
		remoteEvent:FireServer(mouse.Hit.Position) -- Fire the remote event with the hit position of the mouse as an argument
	end
end)

tool.Deactivated:Connect(function()
	activated = false -- Set the activation flag to false when the tool is deactivated
	if heartbeat then
		heartbeat:Disconnect() -- Disconnect the heartbeat connection if it exists
	end
end)

tool.Unequipped:Connect(function()
	activated = false -- Set the activation flag to false when the tool is unequipped
	if heartbeat then
		heartbeat:Disconnect() -- Disconnect the heartbeat connection if it exists
	end
end)

tool.Equipped:Connect(function()
	-- Additional logic when the tool is equipped
end)

Serverscript:

-- Services
local Debris = game:GetService("Debris")
local TweenService = game:GetService("TweenService")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local runService = game:GetService("RunService")

-- Parts
local tool = script.Parent
local beamPart = ReplicatedStorage:FindFirstChild("Beam")
local gunPointerFromHandle = tool.Handle.GUN:WaitForChild("GunPointA")

-- Configuration
local GunConfig = tool:WaitForChild("Configuration")
local OnCooldown = false

-- Folders
local remoteEventsFolder = tool:WaitForChild("RemoteEventsFolder")

-- RemoteEvents
local remoteEvent = remoteEventsFolder:WaitForChild("RemoteEvent")

-- Services
local Debris = game:GetService("Debris")
local TweenService = game:GetService("TweenService")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local runService = game:GetService("RunService")

-- Parts
local tool = script.Parent
local bullet = ReplicatedStorage:FindFirstChild("Beam")
local gunPointerFromHandle = tool.Handle.GUN:WaitForChild("GunPointA")

-- Configuration
local GunConfig = tool:WaitForChild("Configuration")
local range = GunConfig:WaitForChild("Range")
local OnCooldown = false

-- Folders
local remoteEventsFolder = tool:WaitForChild("RemoteEventsFolder")

-- RemoteEvents
local remoteEvent = remoteEventsFolder:WaitForChild("RemoteEvent")


remoteEvent.OnServerEvent:Connect(function(Player, mouseTarget)
	local startPos = gunPointerFromHandle.Position
	local aimDirection = (mouseTarget - startPos).unit
	local distance = (mouseTarget - startPos).magnitude
	print(distance)
	print(aimDirection)

	if mouseTarget and Player.Character and range.Value >= distance and not OnCooldown then
		print("Range reached, calculating ray result")

		-- Normalize the aimDirection and multiply by range
		local aimVector = aimDirection * range.Value

		local rayResult = workspace:Raycast(startPos, aimVector)

		if rayResult then
			print("Ray hit something")
			local hitPart = rayResult.Instance
			local hitPos = rayResult.Position

			local laserPart = Instance.new("Part")
			laserPart.Anchored = true
			laserPart.Name = "Laser"
			laserPart.BrickColor = BrickColor.new("Really red")
			laserPart.CanCollide = false
			laserPart.Size = Vector3.new(0.2, 0.2, distance)
			laserPart.CFrame = CFrame.new(startPos, hitPos) * CFrame.new(0, 0, -distance / 2)
			laserPart.Parent = workspace

			task.wait(0.1)
			laserPart:Destroy()

		else
			print("Ray did not hit anything")
		end
	else
		print("Conditions not met. Either mouseTarget is nil, player doesn't have a character, range is too short, or tool is on cooldown.")
	end
end)


Hi there, @PixelCrypth

I see that you’re facing an issue with maintaining synchronization while retaining rapid firing for your laser beam script in Roblox. You’re definitely on the right track with your concerns, and I’d be happy to help.

Client-side Synchronization:

The problem you’re encountering may be due to slight differences in cursor positions between the client and server. To improve synchronization, you can make a small change to your client script. Instead of firing the remote event when the tool is activated, you can continuously update the remote event with the mouse’s current position while the tool is activated. Here’s a modified client script:

-- Services
local runService = game:GetService("RunService")
local players = game:GetService("Players")

-- Player-related
local player = players.LocalPlayer
local mouse = player:GetMouse() 

-- Tool-related
local tool = script.Parent

-- Folders
local remoteEventsFolder = tool:WaitForChild("RemoteEventsFolder")
local remoteEvent = remoteEventsFolder:WaitForChild("RemoteEvent")

-- Configuration
local gunConfig = tool:WaitForChild("Configuration")
local currentAmountOfAmmo = gunConfig:WaitForChild("CurrentAmountOfAmmo")

-- Flag to track the activation state
local activated = false
local heartbeat -- Variable to store the heartbeat connection

-- Function to update the remote event with mouse position
function UpdateRemote()
    if activated then
        remoteEvent:FireServer(mouse.Hit.Position)
    end
}

-- Modify the Activated event to continuously update the remote event
tool.Activated:Connect(function()
    activated = true

    -- Check if the gun is automatic
    if gunConfig:WaitForChild("IsAutomatic").Value == true then
        -- Connect to the Heartbeat event for automatic firing
        heartbeat = runService.Heartbeat:Connect(UpdateRemote)
    else
        UpdateRemote()
    end
})

-- Modify the Deactivated event to disconnect the heartbeat
tool.Deactivated:Connect(function()
    activated = false
    if heartbeat then
        heartbeat:Disconnect()
    end
})
  • These modifications will continuously update the remote event with the mouse’s position while the tool is activated, allowing for rapid firing while maintaining synchronization.
Server-side Logic:

On the server side, it’s important to ensure that the OnCooldown variable is correctly managed. You can set it to true when the gun fires and reset it after the cooldown period to prevent rapid, unintended firing.

Additionally, if the issue persists, consider adding more debugging information to your code to pinpoint the exact cause of the problem, such as printing the mouse position you receive from the client.

I hope these suggestions help you resolve the synchronization issue while retaining rapid firing. If you encounter any issues during testing or If you have any more questions or need further assistance, feel free to ask. We’re here to help!

Best regards,
[Zero]

Works like a charm after upgrading the server and client script to your suggestion.
I did notice your usage of ChatGPT, however I am still happy with the results. So thank you! :slight_smile:

If anyone has similiar issues and you are reading upon this topic, feel free to take a look at edited version:

Client:

-- Services
local runService = game:GetService("RunService")
local players = game:GetService("Players")

-- Player-related
local player = players.LocalPlayer
local mouse = player:GetMouse() 

-- Tool-related
local tool = script.Parent

-- Folders
local remoteEventsFolder = tool:WaitForChild("RemoteEventsFolder")
local remoteEvent = remoteEventsFolder:WaitForChild("RemoteEvent")

-- Configuration
local gunConfig = tool:WaitForChild("Configuration")
local currentAmountOfAmmo = gunConfig:WaitForChild("CurrentAmountOfAmmo")

-- Flag to track the activation state
local activated = false
local heartbeat -- Variable to store the heartbeat connection

-- Function to update the remote event with mouse position
function UpdateRemote()
	if activated then
		remoteEvent:FireServer(mouse.Hit.Position)
	end
end

-- Modify the Activated event to continuously update the remote event
tool.Activated:Connect(function()
	activated = true

	-- Check if the gun is automatic
	if gunConfig:WaitForChild("IsAutomatic").Value == true then
		-- Connect to the Heartbeat event for automatic firing
		heartbeat = runService.Heartbeat:Connect(UpdateRemote)
	else
		UpdateRemote()
	end
end)

-- Modify the Deactivated event to disconnect the heartbeat
tool.Deactivated:Connect(function()
	activated = false
	if heartbeat then
		heartbeat:Disconnect()
	end
end)


tool.Equipped:Connect(function()
	-- Additional logic when the tool is equipped
end)

Server:

-- Services
local Debris = game:GetService("Debris")
local TweenService = game:GetService("TweenService")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local RunService = game:GetService("RunService")

-- Parts
local tool = script.Parent
local beamPart = ReplicatedStorage:FindFirstChild("Beam")
local gunPointerFromHandle = tool.Handle.GUN:WaitForChild("GunPointA")

-- Configuration
local GunConfig = tool:WaitForChild("Configuration")
local range = GunConfig:WaitForChild("Range")
local shootSpeed = GunConfig:WaitForChild("ShootSpeed").Value
local OnCooldown = false

-- Folders
local remoteEventsFolder = tool:WaitForChild("RemoteEventsFolder")

-- RemoteEvents
local remoteEvent = remoteEventsFolder:WaitForChild("RemoteEvent")

remoteEvent.OnServerEvent:Connect(function(Player, mouseTarget)
	local startPos = gunPointerFromHandle.Position
	local aimDirection = (mouseTarget - startPos).unit
	local distance = (mouseTarget - startPos).magnitude
	print("Mouse Target Position:", mouseTarget)
	print("Distance:", distance)
	print("Aim Direction:", aimDirection)

	if mouseTarget and Player.Character and range.Value >= distance and not OnCooldown then
		print("Range reached, calculating ray result")

		-- Set OnCooldown to true when the gun fires
		OnCooldown = true

		-- Normalize the aimDirection and multiply by range
		local aimVector = aimDirection * range.Value
		local raycastParams = RaycastParams.new()
		raycastParams.FilterDescendantsInstances = {Player.Character, tool.Handle} --ignore the character and tool when raycasting
		raycastParams.FilterType = Enum.RaycastFilterType.Exclude
		local rayResult = workspace:Raycast(startPos, aimVector, raycastParams)

		if rayResult then
			print("Ray hit something")
			local hitPart = rayResult.Instance
			local hitPos = rayResult.Position

			local laserPart = Instance.new("Part")
			laserPart.Anchored = true
			laserPart.Name = "Laser"
			laserPart.BrickColor = BrickColor.new("Really red")
			laserPart.CanCollide = false
			laserPart.Size = Vector3.new(0.2, 0.2, distance)
			laserPart.CFrame = CFrame.new(startPos, hitPos) * CFrame.new(0, 0, -distance / 2)
			laserPart.Parent = workspace

			task.wait(shootSpeed)
			laserPart:Destroy()

			-- Reset OnCooldown after the cooldown period
			task.wait(shootSpeed)  -- Change this value to your desired cooldown period
			OnCooldown = false

		else
			print("Ray did not hit anything")
			-- Reset OnCooldown even if the ray did not hit anything
			task.wait(shootSpeed)  -- Change this value to your desired cooldown period
			OnCooldown = false
		end
	else
		print("Conditions not met. Either mouseTarget is nil, player doesn't have a character, range is too short, or tool is on cooldown.")
	end
end)

1 Like

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.