Blood module - item asylum, Combat Warriors styled blood

Just a heads up, it seems broken.

Games with simulated blood like this must be set to 13+ or risk ban.

Could you show me some bugs that you’ve encountered?

you can also do child or alternative which works the same, but less verbose

local sample = workspace.Baseplate

local function f()
  print( sample and sample or "sample is nil" )  
  print( sample or "sample is nil" )

  print( sample and (sample:IsDescendantOf(workspace) and sample or "sample is not parented to workspace") or "sample is nil")
  -- ^ (not recommended for readability) a ternary to check IF sample is not nil, if so, check if its in workspace and return the sample, if not in workspace, return "sample is not parented to workspace", if sample is nil, return "sample is nil"
  print( sample:IsDescendantOf(workspace) and sample or "sample is not parented to workspace" ) 
  -- ^ a ternary to check if sample is a descendant of workspace, if so, return sample, else return "sample is not parented to workspace"
end

f()
sample = nil
f()
1 Like

dude the blood isnt even that gory i’m not putting an entire gore system on community resources :sob:

I always love things like stylized blood, it looks so much more interesting and satisfying than generic blood splatters.

I love the way games like Ultrakill do blood where it just rains down like fireworks rather than just being boring splatters.
It’s nicely stylized and feels unique.

Surprisingly it’s also not that hard to script since you can essentially just do a bunch of raycasts that follow a curve and spawn the splatter wherever it hits.

I just decided to release this as a resource since i was wondering how developers made this kinda blood, so i decided to make this and release it as a community resource so everyone can use it.

1 Like

Btw I have a great suggestion for optimizing this module further and making it more customizeable.

First off, when blood splatters are really close to each other, you could make them merge into a slightly larger one but limit the maximum size to let’s say 5 studs to prevent blood splatters from becoming aburdly large.

Second, if your blood splatters do the thing where they fade and become transparent before disappearing, you might want to make this optional because transparency is expensive if used extensively.
You can also make blood splatters shrink after a while which makes it look like as if they’re slowly evaporating.

For customization / bonus features, if it is not a thing yet.

  • Being able to customize it’s size (of both the splatter and it’s trail) is a nice addition.

  • Having blood drip if on a wall or ceiling would make for a very cool effect. (Just check which direction it’s facing, if the splatter is at 90 degrees or more, you can insert a particle effect of droplets falling down. You can use LookVector and normal to check direction.)

2 Likes

YO!!! SICKK!!! FINALLY oh my god

Thanks! I’ll try implementing these.

1 Like

Version 1.0.3

  • Added GRAVITY to settings
  • Added more information in the main module
  • Added size parameter to spawnDroplet

Is there a way to prevent this?
image

I’ll try fixing this bug soon, thanks for reporting!

1 Like

is this using partcache now?

yes, it’s more faster thanks to @Synitx’s fixes

Version 1.0.4

  • Attempted to fix blood staying on characters and staying inbetween limbs

A friend and I fixed the issue that causes the blood to stick on the player.
You had to set the exclusions of the character along with the original exclusions and not after.

local filter = {droplet, Settings.DEFAULT_PUDDLE_PARENT, Settings.DEFAULT_DROPLET_PARENT}
    for _, player in Players:GetPlayers() do
        table.insert(filter, player.Character)
    end
    for _, humanoid in workspace:GetDescendants() do
        if humanoid:IsA("Humanoid") then
            local character = humanoid.Parent
            table.insert(filter, character)
        end
    end
    raycastParams.FilterDescendantsInstances = filter

I should specify that I identified the issue and my friend actually fixed it.
Hope this helps.

3 Likes

This is so helpful, I am creating a zombie game with real blood and gore, thanks a lot.

Version 1.0.5

  • Actually fixed the blood staying on characters and being stuck inbetween limbs this time

What function was this fixed? I edited your module to include sounds and new parts.

local Debris = game:GetService("Debris")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local TweenService = game:GetService("TweenService")
local RunService = game:GetService("RunService")
local Players = game:GetService("Players")
local dropletpart = ReplicatedStorage.Effects.droplet
local puddlepart = ReplicatedStorage.Effects.puddle
local Settings = require(script.Settings)
local PartCache = require(script.PartCache)
-- Base RNG for everything in code.
local RNG = Random.new()

local Blood = {}

-- Internal function to create a blood puddle.
function Blood:_makePuddle(
	speed: number,
	size: number,
	cframe: CFrame,
	puddleFadeTime: number,
	color: Color3?,
	owner:StringValue?
) 
	color = color or Color3.fromRGB(255, 0, 0)
	
	local partCache = PartCache.new(puddlepart, 1)
	local puddle = partCache:GetPart()
	puddle.CFrame = cframe
	puddle.Color = color
	puddle.Owner.Value = owner
	puddle.Parent = Settings.DEFAULT_PUDDLE_PARENT
	puddle:FindFirstChild("blood-drip0"..math.random(1,3)):Play()
	local sizeTweenInfo = TweenInfo.new(
		speed,
		Enum.EasingStyle.Sine,
		Enum.EasingDirection.InOut
	)
	local sizeTween = TweenService:Create(
		puddle, 
		sizeTweenInfo,
		{
			Size = Vector3.new(size, 0.1, size),
		}
	)
	sizeTween:Play()
	task.delay(Settings.BLOOD_LIFETIME, function()
		local transparencyTween = TweenService:Create(puddle, TweenInfo.new(RNG:NextNumber(0.4, 1.2)), {
			Size = Vector3.new(0.0001, 0.0001, 0.0001),
			Transparency = 1,
		})
		transparencyTween:Play()
		transparencyTween.Completed:Connect(function()
			puddle:Destroy()
		end)
	end)
end

function Blood:_makeDroplet(color: Color3)
	local partCache = PartCache.new(dropletpart, 1)
	local droplet = partCache:GetPart()
	droplet.Trail.Enabled = true
	pcall(function()
		droplet.Trail.Enabled = true
	end)
	droplet.Size = Vector3.new(RNG:NextNumber(0.20, 0.25), RNG:NextNumber(0.20, 0.25), RNG:NextNumber(0.20, 0.25))
	droplet.Parent = Settings.DEFAULT_DROPLET_PARENT
	droplet.Color = color
	return droplet
end

function Blood.spawnDroplet(
	position: Vector3,
	velocity: Vector3,
	color: Color3,
	puddleTransparency: number?,
	owner
)
	local connection
	local droplet = Blood:_makeDroplet(color)
	
	color = color or Color3.fromRGB(255, 0, 0)
	puddleTransparency = puddleTransparency or 0.5
	
	local raycastParams = RaycastParams.new()
	raycastParams.FilterType = Enum.RaycastFilterType.Exclude
	raycastParams.FilterDescendantsInstances = {droplet, Settings.DEFAULT_PUDDLE_PARENT, Settings.DEFAULT_DROPLET_PARENT}
	raycastParams.IgnoreWater = true -- in synitx's demo, i noticed the blood puddles are actually on top of water...
	for _, player in Players:GetPlayers() do
		table.insert(raycastParams.FilterDescendantsInstances, player.Character)
	end
	connection = RunService.Stepped:Connect(function(_, deltaTime)
		local newPosition = position + velocity * deltaTime
		
		local raycast = workspace:Raycast(position, newPosition - position, raycastParams)
		droplet.Position = newPosition
			
		if raycast then
			droplet:Destroy()
			connection:Disconnect()
			local puddleSize = RNG:NextNumber(1.1, 2.6)
			Blood:_makePuddle(RNG:NextNumber(0.9, 1.3), puddleSize, CFrame.lookAt(raycast.Position, raycast.Position + raycast.Normal) * CFrame.Angles(math.pi / 2, 0, 0), 0.5, color, owner)
			return
		end
		
		velocity += Vector3.new(0, -75, 0) * deltaTime
		position = newPosition
		if droplet.Position.Y < Settings.FALLEN_DROPLETS_DESTROY_HEIGHT then
			droplet:Destroy()
			return
		end
	end)
end

return {
	spawnDroplet = Blood.spawnDroplet
}

Edit: The droplet just goes on forever? It doesn’t drop or aynthing it stays afloat forever…