Blood splatter code doesn't stick to walls properly

I have this code on the client side where it spawns little balls of flesh that splatter onto whichever surface it touches. the splatters should also change its cframe to almost stick to the wall like this. (see image below)


But the code i made below doesn’t work like that.

Here’s the code

local BloodEvent = game:GetService("ReplicatedStorage").Events.Blood

local function spawnSplatterEffect(pos, normal, hitPart)
	local splatter = Instance.new("Part")
	splatter.Size = Vector3.new(2.2, 0.02, 2.2)
	splatter.Anchored = false
	splatter.CanCollide = false
	splatter.Material = Enum.Material.SmoothPlastic
	splatter.Color = Color3.fromRGB(100, 0, 0)
	splatter.CastShadow = false
	splatter.Name = "BloodSplatter"

	local stableUp = Vector3.new(0, 1, 0)
	if math.abs(normal:Dot(stableUp)) > 0.98 then
		stableUp = Vector3.new(1, 0, 0)
	end

	local cf = CFrame.lookAt(pos + normal * 0.01, pos - normal, stableUp)

	splatter.CFrame = cf
	splatter.Parent = workspace
	game:GetService("Debris"):AddItem(splatter, 10)

	if hitPart and hitPart:IsDescendantOf(workspace) then
		local weld = Instance.new("WeldConstraint")
		weld.Part0 = splatter
		weld.Part1 = hitPart
		weld.Parent = splatter
	end
end

local function spawnBloodProjectile(origin, dir, intensity)
	local part = Instance.new("Part")
	part.Size = Vector3.new(0.4, 0.4, 0.4)
	part.Shape = Enum.PartType.Ball
	part.Material = Enum.Material.SmoothPlastic
	part.Color = Color3.fromRGB(120, 0, 0)
	part.Anchored = false
	part.CanCollide = false
	part.CastShadow = false
	part.Position = origin
	local launchVector = (dir + Vector3.new(
		math.random() - 0.5,
		math.random() - 0.2,
		math.random() - 0.5
		)).Unit * math.random(20, 40) * intensity
	part.Velocity = launchVector

	part.Parent = workspace
	part.Name = "BloodBall"
	game:GetService("Debris"):AddItem(part, 5)

	local touchedConn
	touchedConn = part.Touched:Connect(function(hit)
		if not hit:IsDescendantOf(game.Players.LocalPlayer.Character) and hit.Name ~= "BloodBall" then
			touchedConn:Disconnect()

			local rayParams = RaycastParams.new()
			rayParams.FilterDescendantsInstances = {part}
			rayParams.FilterType = Enum.RaycastFilterType.Blacklist

			local rayOrigin = part.Position - part.Velocity.Unit * 2
			local rayDir = part.Velocity.Unit * 5
			local result = workspace:Raycast(rayOrigin, rayDir, rayParams)

			if result and result.Instance then
				spawnSplatterEffect(result.Position, result.Normal, result.Instance)
			else
			end
		end
	end)
end

BloodEvent.OnClientEvent:Connect(function(data)
	local origin = data.origin
	local dir = data.direction
	local intensity = data.intensity or 1
	local count = data.count or 5

	for i = 1, count do
		spawnBloodProjectile(origin, dir, intensity)
	end
end)

Don’t respond to this post. I already found the answer.

local function spawnSplatterEffect(pos, normal, hitPart)
	local splatter = Instance.new("Part")
	splatter.Size = Vector3.new(2.2, 0.02, 2.2)
	splatter.Anchored = false
	splatter.CanCollide = false
	splatter.Material = Enum.Material.SmoothPlastic
	splatter.Color = Color3.fromRGB(100, 0, 0)
	splatter.CastShadow = false
	splatter.Name = "BloodSplatter"

	local up = normal
	local right = up:Cross(Vector3.new(0, 1, 0))
	if right.Magnitude < 0.01 then
		right = up:Cross(Vector3.new(1, 0, 0))
	end
	right = right.Unit
	local back = right:Cross(up)

	splatter.CFrame = CFrame.fromMatrix(pos + normal * 0.01, right, up, -back)
	splatter.Parent = workspace
	game:GetService("Debris"):AddItem(splatter, 10)

	if hitPart and hitPart:IsDescendantOf(workspace) then
		local weld = Instance.new("WeldConstraint")
		weld.Part0 = splatter
		weld.Part1 = hitPart
		weld.Parent = splatter
	end
end

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