I have a function that makes blood spatters on the ground, but they float when I jump, or when stand on a slope. Is there any way to make them to stick to the surface below it?
function splatter(character) local splat = Instance.new("Part") splat.BrickColor = BrickColor.new("Really red") splat.Size = Vector3.new(math.random(1, 2), 0.1, math.random(1, 2)) splat.Anchored = true splat.CanCollide = false splat.Position = Vector3.new(character.Torso.Position.X + math.random(-1, 1), character.Torso.Position.Y - 3, character.Torso.Position.Z + math.random(-1, 1)) splat.Parent = workspace.Splatters game.Debris:AddItem(splat, 10) end
To do this, first we want to create a ray starting at the character's torso and pointing downwards. When creating rays, the first argument is the origin, and the second is direction of the ray. We'll also add some random variance like you were doing in your original function.
local ray = Ray.new(character.Torso.Position + Vector3.new(math.random(-1, 1), 0, math.random(-1, 1)), Vector3.new(0, -500, 0))
Now, to actually cast the ray and find where it intersects with the ground, we call the either FindPartOnRay or FindPartOnRayWithIgnoreList workspace methods. Since we want to ignore the character and previous blood splatters, we'll use the latter method. The first argument will be the ray, and the second is a list of instances to ignore.
local part, position, normal = workspace:FindPartOnRayWithIgnoreList (ray, {character, workspace.Splatters})
The three values it returns are the intersecting part, the position of the intersection, and the normal vector. The normal vector points away from the surface the ray intersected with.
With the position and normal vector, we can correctly position the splat part using a CFrame. A CFrame can be created a number of ways, but in this case we'll create it with two arguments: the position, and the point the object should face towards. We already know where we want to place the splat (the position we got from the ray cast), but to find the point to face towards, we add the first position to the normal vector, and then rotate the CFrame by -90 degrees (-pi/2 radians) along the X axis so that it doesn't face sideways.
splat.CFrame = CFrame.new(position, position + normal) * CFrame.Angles(-math.pi/2, 0, 0)
And now, as part of your script, it will look like this:
function splatter(character) local ray = Ray.new(character.Torso.Position + Vector3.new(math.random(-1, 1), 0, math.random(-1, 1)), Vector3.new(0, -500, 0)) local part, position, normal = workspace:FindPartOnRayWithIgnoreList (ray, {character, workspace.Splatters}) local splat = Instance.new("Part") splat.BrickColor = BrickColor.new("Really red") splat.Size = Vector3.new(math.random(1, 2), 0.1, math.random(1, 2)) splat.Anchored = true splat.CanCollide = false splat.CFrame = CFrame.new(position, position + normal) * CFrame.Angles(-math.pi/2, 0, 0) splat.Parent = workspace.Splatters game.Debris:AddItem(splat, 10) end