Raycast passing through wall

Hello! I made a weapon system, but when I put the weapon inside the wall, it shoots inside the wall and goes through walls

i have the local and server code

Local code

-- feel free to use it in other games :3
-- credit is appreciated, but totally up to you!

local Workspace = game:GetService("Workspace")
local Players = game:GetService("Players")
local UserInputService = game:GetService("UserInputService")

local tool = script.Parent
local equipped = true

local player = Players.LocalPlayer
local character = player.Character or player.CharacterAdded:Wait()
local head = character:WaitForChild("Head")
local humanoid = character:WaitForChild("Humanoid")

local playerGui = player:WaitForChild("PlayerGui")
local mobileShiftlockGui = playerGui:WaitForChild("ShiftlockGui")
local shiftlockOn = mobileShiftlockGui:WaitForChild("ShiftlockOn")

local mouse = player:GetMouse()
local fireWeapon = tool:WaitForChild("FireWeapon")

local camera = Workspace.CurrentCamera
tool.Parent = character

local tapStart = 0
local TAP_THRESHOLD = 0.2

local onIcon = "rbxassetid://79658449"
local offIcon = ""

Workspace:GetPropertyChangedSignal("CurrentCamera"):Connect(function() 
	camera = Workspace.CurrentCamera 
end)

player.CharacterAdded:Connect(function(newCharacter)
	character = newCharacter
	head = newCharacter:WaitForChild("Head")
	humanoid = newCharacter:WaitForChild("Humanoid")
end)

local raycastParams = RaycastParams.new()
raycastParams.FilterType = Enum.RaycastFilterType.Exclude
raycastParams.FilterDescendantsInstances = {character}
raycastParams.IgnoreWater = true
raycastParams.RespectCanCollide = true

local function shoot()
	if not equipped or not tool.Enabled or humanoid.Health <= 0 then
		return
	end
	
	local targetPosition
	local isFirstPerson = camera and (camera.CFrame.Position - head.Position).Magnitude < 2 or shiftlockOn.Value

	if isFirstPerson and camera then
		local viewportSize = camera.ViewportSize
		local screenCenter = Vector2.new(viewportSize.X / 2, viewportSize.Y / 2)

		local ray = camera:ViewportPointToRay(screenCenter.X, screenCenter.Y)
		local rayOrigin = ray.Origin
		local rayDirection = ray.Direction * 400

		local result = Workspace:Raycast(rayOrigin, rayDirection, raycastParams)

		if result then
			targetPosition = result.Position	
		else
			targetPosition = rayOrigin + rayDirection
		end
	else
		targetPosition = mouse.Hit.Position
	end

	if targetPosition then
		fireWeapon:FireServer(targetPosition)
	end
end

UserInputService.TouchStarted:Connect(function(input, gameProcessedEvent)
	if not gameProcessedEvent then 
		tapStart = tick()
	end
end)

UserInputService.TouchEnded:Connect(function(input, gameProcessedEvent)
	if gameProcessedEvent then
		return
	end
	local tapDuration = tick() - tapStart
	if tapDuration < TAP_THRESHOLD and equipped and tool.Enabled and humanoid.Health > 0 then
		shoot()
	end
end)

UserInputService.InputBegan:Connect(function(input, gameProcessedEvent)
	if input.UserInputType == Enum.UserInputType.MouseButton1 or input.KeyCode == Enum.KeyCode.ButtonR2 and not gameProcessedEvent and equipped and tool.Enabled and humanoid.Health > 0 then
		shoot()
	end
end)

tool.Unequipped:Connect(function()
	mouse.Icon = offIcon
	equipped = false
	tool.Enabled = false
end)

tool.Equipped:Connect(function()
	mouse.Icon = onIcon
	equipped = true
	tool.Enabled = true
end)

Server code

local ReplicatedStorage = game:GetService("ReplicatedStorage")
local Players = game:GetService("Players")
local Debris = game:GetService("Debris")
local Workspace = game:GetService("Workspace")

local tool = script.Parent
local handle = tool:WaitForChild("Handle")
local shotPart = handle:WaitForChild("ShotPart")

local hitSound = handle:WaitForChild("Hit")
local fireSound = handle:WaitForChild("Fire")
local reloadSound = handle:WaitForChild("Reload")

local reloadTime = reloadSound.TimeLength

local eventsFolder = ReplicatedStorage:WaitForChild("Events")
local playerKilledEvent = eventsFolder:WaitForChild("KilledEvent")

local player
repeat
	task.wait()
	player = Players:GetPlayerFromCharacter(tool.Parent)
until player

local character = tool.Parent
local humanoid = character:WaitForChild("Humanoid")

local animations = {
	R6 = {
		Shoot = "rbxassetid://79982511838181",
		Idle = "rbxassetid://137613173471058",
		Reload = "rbxassetid://98606465452921",
	},
	R15 = {
		Shoot = "rbxassetid://82248551381958",
		Idle = "rbxassetid://134190544576252",
		Reload = "rbxassetid://91459325337687",
	}
}

local function killedFunction(killer, victim)
	if killer and victim then
		playerKilledEvent:Fire(killer, victim)
	end
end


local function raycast(origin, direction, blacklist, range)
	local params = RaycastParams.new()
	params.FilterType = Enum.RaycastFilterType.Exclude
	params.FilterDescendantsInstances = blacklist
	params.IgnoreWater = true

	local result = Workspace:Raycast(origin, direction.Unit * range, params)

	if result then
		return result.Instance, result.Position
	end
end

local function createBullet(position, color)
	local bullet = Instance.new("Part")
	bullet.Shape = Enum.PartType.Ball
	bullet.Size = Vector3.new(0.1, 0.1, 0.1)
	bullet.Material = Enum.Material.SmoothPlastic
	bullet.CanCollide = false
	bullet.Anchored = true
	bullet.CastShadow = false
	bullet.Color = color
	bullet.CFrame = CFrame.new(position)
	bullet.Parent = Workspace:FindFirstChild("Bullets") or Workspace
	Debris:AddItem(bullet, 5)
	return bullet
end

local function createTrail(startPosition, endPosition)
	local distance = (endPosition - startPosition).Magnitude
	local midPoint = (startPosition + endPosition) / 2

	local trail = Instance.new("Part")
	trail.Size = Vector3.new(0.1, 0.1, distance)
	trail.Anchored = true
	trail.CanCollide = false
	trail.Material = Enum.Material.Neon
	trail.Color = Color3.fromRGB(255, 255, 255)
	trail.CFrame = CFrame.new(midPoint, endPosition)
	trail.Parent = Workspace
	Debris:AddItem(trail, 0.1)
end

local humanoid = character:WaitForChild("Humanoid")
local animator = humanoid:FindFirstChildOfClass("Animator") or Instance.new("Animator", humanoid)

local rigType = humanoid.RigType == Enum.HumanoidRigType.R6 and "R6" or "R15"

local shootAnim = Instance.new("Animation")
shootAnim.AnimationId = animations[rigType].Shoot
local shootTrack = animator:LoadAnimation(shootAnim)

local idleAnim = Instance.new("Animation")
idleAnim.AnimationId = animations[rigType].Idle
local idleTrack = animator:LoadAnimation(idleAnim)
idleTrack.Looped = true
idleTrack:Play()

local reloadAnim = Instance.new("Animation")
reloadAnim.AnimationId = animations[rigType].Reload
local reloadTrack = animator:LoadAnimation(reloadAnim)

local function shoot(_, target)
	if not tool.Enabled or humanoid.Health <= 0 then
		return
	end

	local direction = (target - shotPart.Position).Unit
	local hitPart, hitPos = raycast(shotPart.Position, direction, {character, handle, shotPart}, 400)

	tool.Enabled = false
	fireSound:Play()
	shootTrack:Play()

	if hitPos then
		createBullet(hitPos, hitPart and hitPart.Color or Color3.fromRGB(255, 255, 255))
		createTrail(shotPart.Position, hitPos)

		if hitPart and hitPart.Parent then
			local hitHumanoid = hitPart.Parent:FindFirstChildOfClass("Humanoid")
			if hitPart.Parent:IsA("Accessory") or hitPart.Parent:IsA("Hat") then
				hitHumanoid = hitPart.Parent.Parent:FindFirstChildOfClass("Humanoid")
			end
			
			if hitHumanoid and hitHumanoid ~= humanoid and hitHumanoid.Health > 0 and not (hitPart.Parent:FindFirstChildWhichIsA("ForceField") or hitPart.Parent.Parent:FindFirstChildWhichIsA("ForceField")) then
				if fireSound.Playing then
					fireSound:Stop()
				end
				
				hitSound:Play()
				hitHumanoid.Health = 0

				killedFunction(player, Players:GetPlayerFromCharacter(hitHumanoid.Parent))
			end
		end
	end

	task.wait(0.5)
	if not reloadTrack.IsPlaying then
		reloadSound:Play()
		reloadTrack:Play()
	end
	task.wait(reloadTime)
	tool.Enabled = true
end

tool.Equipped:Connect(function()
	idleTrack:Play()
end)

tool.Unequipped:Connect(function()
	if idleTrack.IsPlaying then
		idleTrack:Stop()
	end
	
	if reloadTrack.IsPlaying then
		reloadTrack:Stop()
	end
	
	if shootTrack.IsPlaying then
		shootTrack:Stop()
	end
end)

local fireEvent = tool:WaitForChild("FireWeapon")
fireEvent.OnServerEvent:Connect(shoot)
1 Like

“shotpart” is inside the wall whenever you shoot, it should be used for visualization rather than actually firing from it, use humanoidrootpart, or head, depends on what you’re looking for i suppose

1 Like

If a raycast starts inside of a part, that part will not collide with the raycast. Try casting from the HumanoidRootPart or Head instead!

2 Likes

But if I shoot something up close it will be inaccurate (sorry for my English, I’m translating this from the translator)

A solution i’ve seen be used is that from the server you raycast from the shoot part to the HumanoidRootPart/Head, i don’t have any code example unfortunately, but that’s something you can try

Not exactly what i had in mind, but what is the issue? the raycast from the shoot part to the model should be a debounce and not interfere with the actual gun

use this as reference:

local function GetDestination()
	local MouseLocation = UserInputService:GetMouseLocation()
	local UnitRay = CurrentCamera:ViewportPointToRay(MouseLocation.X, MouseLocation.Y)
	local origin, direction = UnitRay.Origin, UnitRay.Direction * 1024
	local RaycastResult = workspace:Raycast(origin, direction, RaycastParams)
	local destination = RaycastResult and RaycastResult.Position or origin + direction
	createVisual(destination, RaycastResult and Color3.new(1, 1, 0) or Color3.new())
	return destination
end

local function OnActivated()
	local destination = GetDestination()
	local origin = RootPart:GetPivot().Position
	local direction = (destination - origin).Unit * 1024
	local RaycastResult = workspace:Raycast(origin, direction, RaycastParams)
	local pos = RaycastResult and RaycastResult.Position or origin + direction
	createVisual(pos, RaycastResult and Color3.new(0, 1, 0) or Color3.new(1, 0, 0))
end

you should set the ray origin to head or something.

I don’t know what would happen for third person, but I think it is a common issue for most fps games though


I remember once I saw this ridiculous scene on some Valorant pro league. (sorry I couldnt find a video)

2 Likes

I made a raycast from hrp, but the trail came from the shootpart

Do a raycast from the HumanoidRootPart to check if it hits a wall. If theres no wall, do the shooting raycast from the gun.

Edit: Make the raycast from the HumanoidRootPart be slightly longer than the distance from the HumanoidRootPart to the tip of the gun

1 Like

Don’t change the ray cast for the bullet itself, check if the player character and the shoot part can see each other in a separate ray cast

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