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.
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.
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.
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.
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.
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
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
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.