Raycast to check if wall isn't in front of hit humanoids

I am making a combat system and have setup my hitboxes which all works fine. However I want to use raycasting just to make sure there is nothing in front of the player just to prevent damaging through walls/objects. I am trying to cast a ray from the players humanoid root part to the hit humanoids root parts and checking the ray doesn’t hit anything else other than the target but for some reason it doesn’t always print even with no wall or object in front of the player? But when I remove the raycasting code it always prints regardless which works as intended so I’m not sure what is happening and I’m fairly new to raycasting.

for i,v in pairs(hitHumanoids) do
		if (v) then
			
			local rayOrigin, rayDirection = humanoidRootPart.Position, v.PrimaryPart.Position
			local raycastParams = RaycastParams.new()
			raycastParams.FilterDescendantsInstances = {character}
			raycastParams.FilterType = Enum.RaycastFilterType.Exclude
			raycastParams.IgnoreWater = true
			local raycastResult = workspace:Raycast(rayOrigin, rayDirection, raycastParams)
			
			if (raycastResult) then
				
				if (raycastResult.Instance.Parent == v) then
					print(v.Name)
				end
				
			end
			
		end
	end
2 Likes

I’m not sure if this is the correct answer because the way you described what you want to achieve didn’t exactly map in my head, particularly which script does what, but if I understand you correctly, if you don’t want a raycast to go through a wall, you can use this script.

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

local RAY_RADIUS = 1
local RAY_COLOR = BrickColor.Red()
local RAY_TRANSPARENCY = 0.5
local RAY_INTERVAL = 0 -- Ray shoot interval in seconds set it to zero for no interval
local RAY_DURATION = 0 -- How long until we stop shooting


--- function to get player hit returns a player and humanoid
local function GetPlayerHit(origin: Vector3, direction: Vector3): (Player?, Humanoid?)

	local result = workspace:Raycast(origin, direction)

	if result  then

		local character, humanoid =  result.Instance, nil

		--- idk if this is necesery but just repeat get parent until we have the character
		repeat
			character = character.Parent
			humanoid = character  and character:FindFirstChild("Humanoid")
		until character == nil or humanoid

		if not character or not humanoid then
			return
		end

		return Players:GetPlayerFromCharacter(character), humanoid
	end
	
	return nil, nil -- to silent the warning
end

local function DrawRay(origin: Vector3, direction: Vector3)

	local part = Instance.new("Part")
	part.Name = "Ray"
	part.Anchored = true

	local result = workspace:Raycast(origin, direction)

	local len = (origin - (origin + direction)).Magnitude
	if result then
		len = (origin - result.Position).Magnitude
	end

	part.Size = Vector3.new(RAY_RADIUS, RAY_RADIUS , len)
	part.Material = Enum.Material.SmoothPlastic
	part.BrickColor = RAY_COLOR
	part.Transparency = RAY_TRANSPARENCY
	part.CFrame = CFrame.new(origin, direction) * CFrame.new(0, 0, -len*0.5)

	part.Parent = workspace

	return part
end


--- Ray loop
do
	local part = script.Parent
	local lastRayPart = nil
	local timePassed = RAY_INTERVAL

	RunService.Heartbeat:Connect(function(dt: number)
		
		if lastRayPart and lastRayPart.Parent then -- if debris is to slow
			lastRayPart:Destroy()
		end
		
		timePassed += dt
		
		if timePassed < RAY_INTERVAL then -- Return if its not time yet
			return
		end
		
		if timePassed > RAY_INTERVAL + RAY_DURATION then -- Reset the time passed after the duration has been reached
			timePassed = 0
		end

		local origin = part.Position
		local direction = part.CFrame.LookVector*1000

		lastRayPart = DrawRay(origin, direction)

		local player, humanoid = GetPlayerHit(origin, direction)

		if player then  -- do something example
			humanoid:TakeDamage(50)
		end
	end)
end
1 Like

With raycasting code

This is what I mean to hopefully paint a better picture, you can see it’s not always printing when I’m attacking.

But when I remove the raycasting code and just have this:

for i, v in pairs(hitHumanoids) do
		if (v) then
			print(v.Name)
		end
	end

It will work as intended but then you can hit players through walls

Without raycasting code

This is a common mistake with raycasts. Raycast direction IS NOT the position of the target part.
Direction is the vector3 transformed to the raycast orgin object space.

For example, Vector 0,10,0 will not direct the ray to the 10 studs above the middle of the map, but rather 10 studs above the humanoid that shot the ray!

Furthermore, if you want the ray to always hit the target humanoid, it is a good idea to make it a bit longer than the distance to the target.

Here is the code:

	local rayOrigin = humanoidRootPart.Position
	local rayDirection = CFrame.lookAt(rayOrigin, v.PrimaryPart.Position).LookVector 
	-- now that you have a proper direction vector, it size is only 1 stud. You need to multiply it by the distance to the target humanoid, and even a bit more as mentioned
	rayDirection *= (rayOrigin - v.PrimaryPart.Position).Magnitude
	rayDirection *= 1.1 -- optional, make sure the ray always hits the target humanoid
1 Like

Thank you so much! This works exactly as intended! I now understand the raycast direction a little more better now, thank you again.

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