Raycasting in the correct direction

When a sphere is thrown and hits a wall (detected by using a touched function), it fires a ray and creates a decal where the ray hits a surface.
This works perfectly fine when the sphere lands on the floor as the ray direction is Vector3.new(0,-10,0). However, this doesn’t work when a sphere hits a wall instead of a floor.

I could cast 6 rays in all different directions when the sphere hits a surface, but this seems terribly inefficient.

1 Like

6 rays is the best way to do this unless I’m unaware of some method that allows you to arbitrarily find surface normals as well as the point of contact, both of which you need to do some legwork for. Raycasts are very efficient and you can fire off hundreds of them every frame.

Not inefficient but if someone can suggest a method that works with surface normals you should use that over rays. I would imagine it might have to do with deriving a direction from velocity assuming your thrown sphere is physically simulated and the trajectory is not hand-controlled.

You could probably make the ray’s direction face towards the object that was hit by the ball, by doing (ball - hit).Unit, and then you would be able to set the decal’s CFrame equal to (rayCastResults.Position, rayCastResults.Position+ rayCastResults.Normal). That should work unless I misread what you wanted to do.

Could you give an example? I’m not sure what you quite mean.

The sphere’s trajectory is physically simulated, I had used this post from egomoose.
But to get the direction from velocity, wouldn’t I need to find the tangent to the sphere’s arc? This seems a lot harder in a 3d space.

A way of doing it would be like this:

ball.Touched:Connect(function(hit)
	local raycast = workspace:Raycast(ball.Position,ball.CFrame.LookVector * 10)
	if raycast then
		decalPart.CFrame = CFrame.new(raycast.Position,raycast.Position+raycast.Normal)
	end
end)

This creates a raycast originating at the ball’s position, which has a direction towards the instance the ball hit, if the raycast is successful then the decalPart’s CFrame will be set where the raycast hit and will be looking towards it’s position + the normal, which makes it straight, setting it’s front face against the instance the ball hit. Tell me if this is what you meant to do.

Doesn’t seem to work, it seems the ray isn’t actually hitting anything.

Oh sorry I made a mistake, I fixed the code and tested it, it should work now.

Oh yeah another thing, if the ball doesn’t have a direct trajectory and you want it to also mark the ground, add another raycast which goes down, like the one you were originally using with the Vector3.new(0,-10,0).

Are you sure your code works? I had done what you did, however, the ray still doesn’t hit anything.

	local Origin = self.Obj.Position
	local Direction = self.Obj.CFrame.LookVector*10
	
	local RayCastParams = RaycastParams.new()
	local Results = workspace:Raycast(Origin, Direction, RayCastParams)

	if Results then
        print("Results")
		local Splat = Instance.new("Part", workspace.Folder)
		local ran = math.random(1,5)
		Splat.Size = Vector3.new(ran, 0.05, ran)
		Splat.CFrame = CFrame.new(Results.Position, Results.Position+Results.Normal)
		Splat.Transparency = 1
		Splat.Anchored = true
		Splat.CanCollide = false    
    end

Odd, mine is working, I’d guess that yours isn’t because you didn’t put it inside a .Touched event? Here’s my code:

	newApple.Handle.Touched:Connect(function(hit)
			local raycast = workspace:Raycast(newApple.Handle.Position,newApple.Handle.CFrame.LookVector * 10,RaycastParams.new())
			local rayDown = workspace:Raycast(newApple.Handle.Position, Vector3.new(0,-10,0))
			if raycast and not(deb) then
				print("RAY")
				deb = true
				local dec = decalPart:Clone()
				dec.Parent = game.Workspace
				dec.Anchored = true
				dec.CFrame = CFrame.new(raycast.Position,raycast.Position+raycast.Normal)
				newApple:Destroy()
				deb = false
			else if rayDown and not(deb) then
					print("rayDOWN")
					deb = true
					local dec = decalPart:Clone()
					dec.Parent = game.Workspace
					dec.Anchored = true
					dec.CFrame = CFrame.new(rayDown.Position,rayDown.Position+rayDown.Normal)
					newApple:Destroy()
					deb = false
				end	
			end
		end)

Note: you might want to use PapaBreadd’s direction solution which will make it more precise, however I won’t really be adding it here since that adding it actually broke my code lol, I guess that happened since I was using base velocity, but using his solution might be better on the long term.

1 Like

I think the problem might be that the lookvector isnt inherently the same direction as where its going, and therefore it doesnt know where it hit from
You could probably get pretty good results by plugging in the velocity into direction like so:

local Direction = self.Obj.AssemblyLinearVelocity.Unit*10

But this is by no means a perfect system
If, for example, it hit an edge it could possibly splat at the wrong spot, but I doubt this much accuracy is needed
The splat cant be directly on an edge anyways because itd be floating

1 Like

Actually I would need a fairly accurate method of detecting if the ray hits a corner in my case, however I was thinking of raycasting multiple times with randomly sized decals scattered around the original ray in a radius, although I’m unsure how well it would work.

And testing out your suggestion, it still doesn’t seem to work.

I think that would be a nice option
You could use the code made by @Mister_Torrada with the direction as the velocity like I described above, while randomly dispersing the Origin position by some randomness
I think that would work quite nicely, theres a possibility it could have some overlap on corners but thats a more complex problem, maybe you could compare it to the size of the object and the relative position to the object and limit the radius of the splat to the smallest distance to the edge which could be done with some math trickery (I can help you with this tomorrow or something if you want, or someone else can prob help)
I also might suggest one more edit, to whitelist only the part touched gives you in hit using raycastparams

local RayCastParams = RaycastParams.new()
RayCastParams.FilterType = Enum.FilterType.Whitelist
RayCastParams.FilterDescandantsInstances = hit

As I said before, when testing out your suggestion, it doesn’t work and the ray doesn’t hit anything

I tried using AssemblyLinearVelocity as the direction, but for some reason the splat only appears after the ball has hit something and is going the other way, I’m not sure if that’s always going to be the case, but I don’t think using it might be the solution
EDIT: I can’t really test this right now, but try getting the absolute value of the AssemblyLinearVelocity, that could probably work.