Blood Splatter help

Please read update at the bottom of the page first

Hi. I am currently working on a blood splatter system and it partially works. The intention of the system is to create blood splatters which stick to walls, however they dont orient properly on walls and some splatters are underground.
Script:

BloodModule.NewParticle = function(originPos)
	local particle = game.ReplicatedStorage.BloodParticle:Clone()
	local c
	particle.Parent = workspace
	
	particle.Position = originPos
	particle.Velocity = Vector3.new(math.random(minParticle,maxParticle),math.random(minParticle,maxParticle) + 30,math.random(minParticle,maxParticle))
	
	c = particle.Touched:Connect(function(hit)
		if hit.Anchored then
			particle.Anchored = true
			warn("Touched: ".. hit.Name)
			c:Disconnect()
			
			
			local rayParams = RaycastParams.new()
			rayParams.IgnoreWater = true
			rayParams.FilterDescendantsInstances = {hit}
			rayParams.FilterType = Enum.RaycastFilterType.Whitelist
			
			local raycastResult = workspace:Raycast(particle.Position + particle.Size --[[ Adding particle size because otherwise the raycasting barely works? ]], hit.Position, rayParams) -- using raycasting to get surface normals which *should* allow the splatters to correctly orientate
			
			if raycastResult then
				local splatter = Instance.new("Part")
				splatter.Parent = workspace
				splatter.Size = Vector3.new(5,0.5,5)
				splatter.Anchored = true
				splatter.Color = Color3.fromRGB(76, 0, 1)
				splatter.CFrame = CFrame.new(particle.Position, particle.Position + raycastResult.Normal)
				splatter.Rotation = splatter.Rotation + Vector3.new(0,90,0) -- adding rotation because without it, the blood splatters are disoriented even on the ground, might need to change however
			
			end
		end
	end)
	
	return particle
end

Showcase:


Showcase without the splatter.Rotation + Vector3.new(0,90,0) line

UPDATE

I fixed the worked around the rotation problem by rescaling the blood part to 5, 5, 0.5 but this method doesnt work when using cylinder part types because it becomes disoriented.

BloodModule.NewParticle = function(originPos)
	local particle = game.ReplicatedStorage.BloodParticle:Clone()
	local c
	particle.Parent = workspace
	
	particle.Position = originPos
	particle.Velocity = Vector3.new(math.random(minParticle,maxParticle),math.random(minParticle,maxParticle) + 30,math.random(minParticle,maxParticle))
	
	c = particle.Touched:Connect(function(hit)
		if hit.Anchored and hit.Name ~= "BloodSplatter" and hit.Name ~= "BloodParticle" then
			particle.Anchored = true
			warn("Touched: ".. hit.Name)
			c:Disconnect()
			
			
			local rayParams = RaycastParams.new()
			rayParams.IgnoreWater = true
			rayParams.FilterDescendantsInstances = {hit}
			rayParams.FilterType = Enum.RaycastFilterType.Whitelist
			
			local raycastResult = workspace:Raycast(particle.Position + particle.Size --[[ Adding particle size because otherwise the raycasting barely works? ]], hit.Position, rayParams) -- using raycasting to get surface normals which *should* allow the splatters to correctly orientate
			
			if raycastResult then
				local splatter = Instance.new("Part")
				splatter.Parent = workspace
				splatter.Size = Vector3.new(0,0,0)
				local factor = math.random(2,6)
				TS:Create(splatter, TweenInfo.new(1), { Size = Vector3.new(factor, factor,0.5) }):Play()
				splatter.Shape = Enum.PartType.Cylinder
				splatter.Anchored = true
				splatter.Color = Color3.fromRGB(76, 0, 1)
				splatter.CanCollide = false
				splatter.CanTouch = false
				splatter.CFrame = CFrame.new(particle.Position, particle.Position + raycastResult.Normal)
			
			end
		end
	end)
	
	return particle
end

BloodModule.NewParticle = function(originPos)
	local particle = game.ReplicatedStorage.BloodParticle:Clone()
	local c
	particle.Parent = workspace
	
	particle.Position = originPos
	particle.Velocity = Vector3.new(math.random(minParticle,maxParticle),math.random(minParticle,maxParticle) + 30,math.random(minParticle,maxParticle))
	
	c = particle.Touched:Connect(function(hit)
		if hit.Anchored then
			particle.Anchored = true
			warn("Touched: ".. hit.Name)
			c:Disconnect()
			
			
			local rayParams = RaycastParams.new()
			rayParams.IgnoreWater = true
			rayParams.FilterDescendantsInstances = {hit}
			rayParams.FilterType = Enum.RaycastFilterType.Whitelist
			
			local raycastResult = workspace:Raycast(particle.Position + particle.Size --[[ Adding particle size because otherwise the raycasting barely works? ]], hit.Position, rayParams) -- using raycasting to get surface normals which *should* allow the splatters to correctly orientate
			
			if raycastResult then
				local splatter = Instance.new("Part")
				splatter.Parent = workspace
				splatter.Size = Vector3.new(5,0.5,5)
				splatter.Anchored = true
				splatter.Color = Color3.fromRGB(76, 0, 1)
				splatter.CFrame = CFrame.new(raycastResult.Position, raycastResult.Position + raycastResult.Normal) * CFrame.Angles(math.rad(90),0,0)
			
			end
		end
	end)
	
	return particle
end

This should do the trick

2 Likes

Looks promising, ill try it out right now.

Yup it worked! I did have to change up my sizes and the angles to CFrame.Angles(0,math.rad(90),0) but it works really well. Thanks!

1 Like

The second argument to Raycast() should be a Vector3 value which describes a direction not a Vector3 value which describes a position.

local raycastResult = workspace:Raycast(origin.Position, (hit.Position - origin.Position).Unit * 500, rayParams)

Where origin is the ray’s origin, and the second argument represents the ray’s direction and magnitude (range).

What would be the difference in using .Unit and positional coordinates?

It was his code, he could’ve just used

local raycastResult = workspace:Raycast(origin.Position, (hit.Position - origin.Position).Unit * (hit.Position - origin.Position).Magnitude , rayParams)

or

local raycastResult = workspace:Raycast(particle.Position,particle.Velocity.Unit*0.5, rayParams)
(hit.Position - origin.Position).Unit * (hit.Position - origin.Position).Magnitude
(hitPosition - originPosition)

dear heavens, why would you set the parent before setting properties? that is inappropriate behavior, you know…

3 Likes

I am encountering an issue with the raycasting being pretty unreliable. I applied your feedback by changing the raycast parameters:

BloodModule.NewParticle = function(originPos)
	local particle = game.ReplicatedStorage.BloodParticle:Clone()
	local c
	particle.Parent = workspace
	
	particle.Position = originPos
	particle.Velocity = Vector3.new(math.random(minParticle,maxParticle),math.random(minParticle,maxParticle) + 30,math.random(minParticle,maxParticle))
	
	c = particle.Touched:Connect(function(hit)
		if hit.Anchored and hit.Name ~= "BloodSplatter" and hit.Name ~= "BloodParticle" then
			warn("Touched: ".. hit.Name)
			c:Disconnect()
			
			particle.Anchored = true
			
			game.Debris:AddItem(particle, 1)
			
			local rayParams = RaycastParams.new()
			rayParams.IgnoreWater = true
			rayParams.FilterDescendantsInstances = {hit}
			rayParams.FilterType = Enum.RaycastFilterType.Whitelist
			
			local raycastResult = workspace:Raycast(particle.Position, (hit.Position - particle.Position).Unit * 500, rayParams) -- updated method
			if raycastResult then
				local splatter = particle:Clone()
				splatter.Trail:Destroy()
				
				splatter.Size = Vector3.new(0,0,0)
				splatter.Name = "Splatter"
				splatter.CFrame =  CFrame.new(raycastResult.Position, raycastResult.Position + raycastResult.Normal) * CFrame.Angles(0,math.rad(90), 0)
				
				local factor = math.random(1.5,2)
				splatter.Shape = Enum.PartType.Cylinder
				splatter.CanTouch = false
				
				splatter.Parent = workspace
				
				TS:Create(splatter, TweenInfo.new(1), { Size = Vector3.new(0.2, factor ,factor) }):Play()
			end
			
		end
	end)
	
	return particle
end

Showcase with counters. This issue isnt just apparent in this method but the previous ones as well:


Seems to be more reliable on walls

1 Like

Please stop bumping this thread and direct-message me if you need any further help. @ifnotfalse

Is particle.Position even the intended origin point?