Raycasts not going intended direction

back with another problem with my raycast system i can’t seem to get working, my current issue is that i want raycasts to go directly the same direction as the tracer casted however i can’t seem to achieve it. I’ve already tried a method to do it but it didn’t really work as it made more problems so i reverted it.

screenshot of how it looks at an angle:
(red dots is where the ray intersects, lines is the tracer)

			TracerClone.END.WorldPosition =  BarrelAtt + (UnitVector * Range)

			local ray = workspace:Raycast(BarrelAtt, BarrelAtt + (UnitVector * Range))
			if ray then
				local HitLocation = ray.Position
				hitPart = Instance.new("Part")
				hitPart.Size = Vector3.new(0.1,0.1,0.1)
				hitPart.Position = ray.Position
				hitPart.Anchored = true
				hitPart.CanCollide = false
				hitPart.Transparency = 0.5
				hitPart.BrickColor = BrickColor.new("Bright red")
				hitPart.Parent = workspace -- Change parent if needed
				Tracer.END.WorldPosition = HitLocation



				local RayHit = ray.Instance
				local Hum = RayHit.Parent:FindFirstChild("Humanoid")
				if Hum then
					Hum:TakeDamage(Settings["Damage"])
					print("Hit!")
				else
					print("Miss!")
				end
			end
		end

if you need me to share any other code let me know

2 Likes

hello . You got to take note that the perimeter for raycasting is (origin,direction,raycastprams)
so when you raycasy and the 2nd perimeter is the destination, they ray will not go where it should

here is some formula btw
origin = destination - dir
dir = destination - origin
destination= origin+ direction

direction is more of an offset compared to an angle .

1 Like

I tried the formula and it worked with aligning the tracer and ray but i just noticed my issue is that the rays direction is off centered, i am aware of my spread script but all the rays/tracers are off center to the target.

picture:
(dots is where the rays hit)
image

is your destination the mouse position? If it is z please send the client spread script

oh i’ve set the destination for rays to the tracer pos, the tracer is meant to go towards the torso with a slight spread.

local rng_v = Random.new()
function RandomVectorOffset(v, maxAngle) --returns uniformly-distributed random unit vector no more than maxAngle radians away from v
	return (CFrame.new(Vector3.new(), v)*CFrame.Angles(0, 0, rng_v:NextNumber(0, 2*math.pi))*CFrame.Angles(math.acos(rng_v:NextNumber(math.cos(maxAngle), 1)), 0, 0)).LookVector
end

local function GenerateSpread(InitialDirection: Vector3): {Vector3}
	local SpreadDirections = {}
	for i = 0, BulletsPerShot - 1 do
		table.insert(SpreadDirections, RandomVectorOffset(InitialDirection, math.rad(spread)))
	end
	return SpreadDirections
end

btw if it makes it any easier I could drop my whole script for this if you want me to

yeas sure m sorry for the long wait

nah it’s fine it took a while, so here is the full (server) script + local script

local Replicated = game:FindFirstChild("ReplicatedStorage")
local Players = game:GetService("Players")
local Debris = game:GetService("Debris")

local FrameWork = Replicated.WeaponFramework
--local SE = FrameWork.Events.Shoot
local RNG = Random.new()
local TAU = math.pi * 2

local Tool = script.Parent
local Handle = Tool.Handle
local Settings = require(Tool.SettingsModule)
local Mesh = Tool.Mesh
local BarrelAttachment = Mesh:WaitForChild("BarrelAttachment")

local Aiming = false
local Tracer = Replicated.Tracer
local spread = Settings["Spread"]
local Range = Settings["Range"]
local BulletsPerShot = Settings["BulletsPerShot"]
local BurstAmount = Settings["BurstAmount"]
local RecoveryTime = Settings["RecoveryTime"]

local Sounds = Handle.Sounds
local ShootSound = Sounds.Shoot
local Equip = Sounds.Equip

local Flash = BarrelAttachment.Flash
local Smoke = BarrelAttachment.Smoke
local SE = Tool:FindFirstChild("Shoot")

Tool.Equipped:Connect(function()
	Equip:Play()
end)



local rng_v = Random.new()
function RandomVectorOffset(v, maxAngle) --returns uniformly-distributed random unit vector no more than maxAngle radians away from v
	return (CFrame.new(Vector3.new(), v)*CFrame.Angles(0, 0, rng_v:NextNumber(0, 2*math.pi))*CFrame.Angles(math.acos(rng_v:NextNumber(math.cos(maxAngle), 1)), 0, 0)).LookVector
end

local function GenerateSpread(InitialDirection: Vector3): {Vector3}
	local SpreadDirections = {}
	for i = 0, BulletsPerShot - 1 do
		table.insert(SpreadDirections, RandomVectorOffset(InitialDirection, math.rad(spread)))
	end
	return SpreadDirections
end

SE.OnServerEvent:Connect(function(Player, Character, Hit)
Hit = Hit:FindFirstChild("Torso")
print("recieved")
	local tracerClones = {}  -- Create a table to store tracer clones
	local OriginalSpeed = Character:FindFirstChild("Humanoid").WalkSpeed
	local Gryo = nil
	local Troll = Character.PrimaryPart:FindFirstChild("Troll")
	Character:FindFirstChild("Humanoid").AutoRotate = false

	if not Troll then
		Gryo = Tool.Duplicates.BodyGyro:Clone()
		Gryo.Name = "Troll"
		Gryo.Parent = Character.PrimaryPart
		Gryo.MaxTorque = Vector3.new(0, 0, 0)
		Gryo.P = 5000
		Gryo.D = 250
	end

	Aiming = true

	task.spawn(function()
		if Aiming then
			repeat
				task.wait()
				Gryo.CFrame = CFrame.lookAt(Character.PrimaryPart.Position, Hit.Position)
				--print("ROTATE")
			until not Aiming
		end
	end)
	Character:FindFirstChild("Humanoid").WalkSpeed = Settings["Slowdown"]
	Gryo.MaxTorque = Vector3.new(0, 2000, 0)
	task.wait(Settings["AimTime"])

	for j = 0, BurstAmount - 1 do
		task.wait(Settings["BurstDelay"])
		ShootSound:Play()

		local rp = RaycastParams.new()
		rp.IgnoreWater = true
		rp.FilterType = Enum.RaycastFilterType.Blacklist
		rp.FilterDescendantsInstances = {Tool, Character}

					local BarrelAtt = BarrelAttachment.WorldPosition
					local InitialDirection = (Hit.Position - BarrelAtt).Unit
					local SpreadDirections = GenerateSpread(InitialDirection, BulletsPerShot, spread)
					local tracers = {}  -- Table to store tracers for destruction later

					for _, UnitVector in pairs(SpreadDirections) do
			Flash:Emit(100)
			local TracerClone = Tracer:Clone()
			TracerClone.Parent = workspace
			TracerClone.START.WorldPosition = BarrelAtt
			TracerClone.END.WorldPosition = BarrelAtt + (UnitVector * Range)
			table.insert(tracers, TracerClone)  -- Store tracers for later destruction
			local dir = TracerClone.END.WorldPosition - BarrelAtt
			local destination = BarrelAtt + dir

			local ray = workspace:Raycast(BarrelAtt, destination, rp)
		--	local ray = workspace:Raycast(BarrelAtt, BarrelAtt + (UnitVector * Range), rp)
			if ray then
			local HitLocation = ray.Position
				TracerClone.END.WorldPosition = HitLocation
				--hitPart = Instance.new("Part")
				--hitPart.Size = Vector3.new(0.1,0.1,0.1)
				--hitPart.Position = ray.Position
				--hitPart.Anchored = true
				--hitPart.CanCollide = false
				--hitPart.Transparency = 0.5
				--hitPart.BrickColor = BrickColor.new("Bright red")
				--hitPart.Parent = workspace -- Change parent if needed
			Tracer.END.WorldPosition = HitLocation



				local RayHit = ray.Instance
				local Hum = RayHit.Parent:FindFirstChild("Humanoid")
				if Hum then
					Hum:TakeDamage(Settings["Damage"])
					print("Hit!")
				else
					print("Miss!")
				end
			end
		end


		task.wait(0.1)
		--hitPart:Destroy()
		if BurstAmount > 1 then
			for _, tracer in pairs(tracers) do
				tracer:Destroy()  -- Destroy tracers after a delay
			end
		end
	end

	-- Destroy all tracer clones after all iterations
	for _, tracer in ipairs(tracerClones) do
		task.wait(0.1)
		tracer:Destroy()
	end
	if Gryo then
	Gryo:Destroy()
	end
	Character:FindFirstChild("Humanoid").AutoRotate = true
	Character:FindFirstChild("Humanoid").WalkSpeed = OriginalSpeed
Aiming = false
Smoke.Enabled = true
Tool.Enabled = false
task.wait(RecoveryTime)
Smoke.Enabled = false
Tool.Enabled = true
end)

local script:

local Players = game:GetService("Players")
local UserInputService = game:GetService("UserInputService")
local camera = workspace.CurrentCamera

local Tool = script.Parent
local Handle = Tool.Handle
local Mesh = Tool.Mesh
local BarrelAttachment = Mesh:WaitForChild("BarrelAttachment")
local MuzzleFlash = BarrelAttachment.Flash
local Smoke = MuzzleFlash.Parent.Smoke
local Settings = require(Tool:WaitForChild("SettingsModule"))
local player = Players.LocalPlayer


local Shoot = Handle.Sounds.Shoot

local FrameWork = ReplicatedStorage.WeaponFramework


local camera = workspace.CurrentCamera
local SE = Tool:FindFirstChild("Shoot")

local function castRay()
	local mouse = Players.LocalPlayer:GetMouse()
	local raycastParams = RaycastParams.new()
	raycastParams.FilterDescendantsInstances = {Tool.Parent, Tool}
	raycastParams.FilterType = Enum.RaycastFilterType.Exclude -- Choose Exclude or Include

	local barrelAttachment = Tool:WaitForChild("Mesh"):WaitForChild("BarrelAttachment") -- Assuming this structure
	local rayDirection = (mouse.Hit.Position - barrelAttachment.Position).Unit
	local raycastResult = workspace:Raycast(barrelAttachment.Position, rayDirection * Settings["Range"], raycastParams) -- Max distance of 300

	if raycastResult then
local Target = raycastResult.Instance.Parent
if Target:FindFirstChild("Humanoid") then
print(Target)
		-- Create a part at the hit position for debugging
		
		local hitPart = Instance.new("Part")
		hitPart.Size = Vector3.new(0.1,0.1,0.1)
		hitPart.Position = raycastResult.Position
		hitPart.Anchored = true
		hitPart.CanCollide = false
		hitPart.Transparency = 0.5
		hitPart.BrickColor = BrickColor.new("Bright red")
		hitPart.Parent = workspace -- Change parent if needed

		wait(1) 
		hitPart:Destroy()

			local Character = player.Character
		SE:FireServer(Character, Target)
		end
	else
		print("Miss!")
	end
end

UserInputService.InputBegan:Connect(function(input)
	if Tool.Enabled then
	if input.UserInputType == Enum.UserInputType.MouseButton1 and Tool:IsA("Tool") and Tool.Parent == Players.LocalPlayer.Character then
		castRay()
	end
	else
	return
end
end)

Here’s a raycastmodule I made. Just plug in the origin, and the inputObject (mouseChanged for example)

local RaycastModule = {}

-- Constants
local RAYCAST_DISTANCE = 1000

local Params = RaycastParams.new()
Params.FilterDescendantsInstances = {workspace.CurrentCamera}

local function rayCast(origin, direction,passed_Params)
	local raycastParam = passed_Params or Params
	local worldRay = Ray.new(origin, direction.Unit * RAYCAST_DISTANCE)
	local raycastResult = workspace:Raycast(worldRay.Origin, worldRay.Direction * RAYCAST_DISTANCE, raycastParam)

	-- Optional beam visualization
	if raycastResult then
		local size = Vector3.new(0.2, 0.2, (worldRay.Origin - raycastResult.Position).Magnitude-2)
		local cframe = CFrame.lookAt(worldRay.Origin, raycastResult.Position) * CFrame.new(0, 0, -(size.Z/2)-2)
	else
		print("no hit")
	end

	return raycastResult
end

function RaycastModule.rayCastInDirection(origin, direction,passed_Params)
	return rayCast(origin, direction,passed_Params)
end

function RaycastModule.rayCastToPoint(origin, goalPos,passed_Params)
	local direction = goalPos - origin
	return rayCast(origin, direction,passed_Params)
end

function RaycastModule.rayCastToInput(origin, inputObject,passed_Params)
	local Camera = workspace.CurrentCamera
	local worldRay = Camera:ScreenPointToRay(inputObject.Position.X, inputObject.Position.Y)
	return rayCast(origin, worldRay.Direction,passed_Params)
end

return RaycastModule
1 Like

Sorry but your script is very confusing .

  1. your gun seems to be a shotgun yet when you fire, you only cast one ray
UserInputService.InputBegan:Connect(function(input)
	if Tool.Enabled then
	if input.UserInputType == Enum.UserInputType.MouseButton1 and Tool:IsA("Tool") and 			 
               Tool.Parent == Players.LocalPlayer.Character then
		castRay()
	end
	else
	return
end
end)
  1. I know you are trying to make client side hit detection but for client side hit detection, you need to send the direction that have the spread multiplied on .

Anyways I dont think I can help as the script is too complicated but I recommend some stuff.
for me , i usually use CFrame.new().LookVector to get the direction as it works quite well with spread
eg)(CFrame.new(Origin,Direction)*CFrame.Angles(math.rad(),math.rad(),math.rad())).LookVector inside math.rad() but in the spread angle or like math.random(-spread10,spread10)/10 .

You aren’t raycasting correctly.
The second argument of Raycast is meant to be a delta (offset from the origin).
What it looks like to me is, you tried to use raycast as if the second argument was the position you want to cast to.

Instead, what you should do is:

local ray = workspace:Raycast(BarrelAtt, UnitVector * Range)

ah that makes sense now, it works now i tested it with the ray intersect visualization and it for sure works. thanks

1 Like

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