You can write your topic however you want, but you need to answer these questions:
-
What do you want to achieve? Keep it simple and clear!
Flinging blood parts from a character with a module without progressively getting faster -
What is the issue? Include screenshots / videos if possible!
My blood system has blood parts that are flung out of a character based off of damage.
But, the fling seems to get faster every time i hit an enemy, even though i’m dealing the same amount of damage.
- What solutions have you tried so far? Did you look for solutions on the Creator Hub?
I’ve tried multiple AI models, including Claude, but they all didn’t seem to be able to fix it.
After that, you should include more details if you have any. Try to make your topic as descriptive as possible, so that it’s easier for people to help you!
Blood Module
local module = {}
local TweenService = game:GetService("TweenService")
local Debris = game:GetService("Debris")
local blood = game.ReplicatedStorage.Blood
local MAX_POOL_SIZE = 5
local MAX_BLOOD_PARTS = 200
-- Random position offset for splats
local function getRandomOffset()
return Vector3.new(math.random(-5, 5) / 10, 0, math.random(-5, 5) / 10)
end
-- Make one blood splat merge into another (only if new is smaller or equal)
local function mergeBlood(existing, new)
-- Only merge if new blood is smaller or equal in size
if new.Size.X > existing.Size.X then
return false
end
-- Don't let puddles get too big
if existing.Size.X >= MAX_POOL_SIZE then
TweenService:Create(new, TweenInfo.new(0.1), {Transparency = 1}):Play()
Debris:AddItem(new, 0.1)
return true
end
local sizeIncrease = math.random(8, 15) / 100 -- +8–15%
local opacityBoost = 0.15 -- become more visible
local newTransparency = math.max(0.5, math.min(1, existing.Transparency - opacityBoost))
-- Just update size and transparency, no animations
existing.Size = existing.Size * (1 + sizeIncrease)
existing.Transparency = newTransparency
-- Fast fade out the merged one
TweenService:Create(new, TweenInfo.new(0.1), {Transparency = 1}):Play()
Debris:AddItem(new, 0.1)
return true
end
-- Shared function for when blood lands
local function setupBlood(blood_, character, sizeScale)
local hasLanded = false
blood_.Touched:Connect(function(hit)
-- Ignore the character that spawned the blood
if hit:IsDescendantOf(character) then return end
if hit.Parent:FindFirstChild("Humanoid") then return end
if hit.Parent:IsA("Accessory") then return end
if hit.CanCollide == false then return end
-- ALWAYS ignore other blood parts (both in air and on ground)
if hit.Name == "Blood" then return end
-- Landing behavior - only on actual ground/parts
if (hit:IsA("BasePart") or hit:IsA("UnionOperation")) and not hasLanded then
hasLanded = true
blood_.Anchored = true
blood_.CanCollide = false -- Disable collision after landing
local offset = getRandomOffset()
-- random rotation
local randomSize = math.random(10, 15) / 10 * sizeScale
-- Snap to final position immediately
blood_.CFrame = CFrame.new(
blood_.Position.X + offset.X,
hit.Position.Y + hit.Size.Y / 2 + 0.01,
blood_.Position.Z + offset.Z
) * CFrame.Angles(0, math.rad(math.random(0, 360)), 0)
-- Only tween the size
local goal = {
Size = Vector3.new(randomSize, 0.1, randomSize)
}
local expandTween = TweenService:Create(blood_, TweenInfo.new(1), goal)
expandTween:Play()
-- After expansion is complete, check for nearby blood to merge with
expandTween.Completed:Connect(function()
if not blood_ or not blood_.Parent then return end
-- Look for nearby blood puddles
for _, part in pairs(workspace:GetPartBoundsInRadius(blood_.Position, 2)) do
if part:IsA("BasePart") and part.Name == "Blood" and part ~= blood_ and part.Anchored then
if mergeBlood(part, blood_) then
break -- Successfully merged, stop looking
end
end
end
end)
end
end)
-- Cleanup fade
task.delay(10, function()
if blood_ and blood_.Parent then
local goal = {Transparency = 1}
local tween = TweenService:Create(blood_, TweenInfo.new(1), goal)
tween:Play()
Debris:AddItem(blood_, 1)
end
end)
end
-- Spawn blood with batching to reduce lag
local function spawnBlood(character, count, sizeScale, intensity)
local root = character:FindFirstChild("HumanoidRootPart")
if not root then return end
local spawned = 0
while spawned < count do
for i = 1, math.min(10, count - spawned) do
spawned += 1
local blood_ = blood:Clone()
if not root then continue end
blood_.CFrame = root.CFrame * CFrame.new(math.random(-1,1), math.random(-1,1), math.random(-1,1))
blood_.Parent = workspace
blood_.Transparency = 0.6 + math.random()*0.2
-- Use BodyVelocity instead of direct velocity
local bodyVel = Instance.new("BodyVelocity")
bodyVel.MaxForce = Vector3.new(math.huge, math.huge, math.huge)
bodyVel.Velocity = Vector3.new(
math.random(-6,6),
math.random(2,8),
math.random(-6,6)
) * (intensity/1.5)
bodyVel.Parent = blood_
Debris:AddItem(bodyVel, 0.2) -- Remove BodyVelocity after 0.2 seconds
setupBlood(blood_, character, sizeScale)
end
task.wait(0.05)
end
end
-- spawn death explosion
local function spawnDeathExplosion(character)
local humanoidRootPart = character:FindFirstChild("HumanoidRootPart")
if not humanoidRootPart then return end
spawnBlood(character, character.Humanoid.MaxHealth / 2, 1.2, 7)
end
-- Damage-based blood spawning
function module.damage(character, damage)
local humanoid = character:FindFirstChild("Humanoid")
if not humanoid then return end
humanoid.Health -= damage
local isDead = humanoid.Health <= 0
local baseCount = isDead and math.max(15, math.min(MAX_BLOOD_PARTS, math.log(damage+5)*10))
or math.max(5, math.min(MAX_BLOOD_PARTS, math.log(damage+5)*5))
local sizeScale = isDead and 1.2 or 1
local intensity = damage/math.random(5,7) -- Moderate intensity
spawnBlood(character, baseCount, sizeScale, intensity)
-- DEATH EXPLOSION
if isDead then
task.wait(0.1)
spawnDeathExplosion(character)
end
end
-- Just spawn blood, don't apply damage
function module.OnlySpawnBlood(character, damage)
local humanoid = character:FindFirstChild("Humanoid")
if not humanoid then return end
local isDead = humanoid.Health <= 0
local baseCount = isDead and math.max(15, math.min(MAX_BLOOD_PARTS, math.log(damage+5)*10))
or math.max(5, math.min(MAX_BLOOD_PARTS, math.log(damage+5)*5))
local sizeScale = isDead and 1.2 or 1
local intensity = damage/math.random(5,7) -- Moderate intensity
spawnBlood(character, baseCount, sizeScale, intensity)
-- DEATH EXPLOSION
if isDead then
task.wait(0.1)
spawnDeathExplosion(character)
end
end
return module
Although it may be coming from the module, i think it might have something to do with the script that calls the blood module’s functions:
Blood Caller
-- spawn blood on damage
local humanoid = script.Parent:WaitForChild("Humanoid")
local bloodModule = require(game.ServerScriptService.Blood)
local lastHP = humanoid.Health
local connection
connection = humanoid.HealthChanged:Connect(function(health)
-- Skip if humanoid is dead or dying
if health <= 0 then
return
end
-- Calculate damage
local damage = lastHP - health
-- Only spawn blood if actually taking damage (not healing)
if damage > 0 then
bloodModule.OnlySpawnBlood(script.Parent, damage)
end
-- Always update lastHP to current health
lastHP = health
end)
-- Cleanup on death
humanoid.Died:Connect(function()
if connection then
connection:Disconnect()
end
end)
Anything helps! (make it optimized please, if you dont use touch events)
(beginner scripter, sorry stuff isn’t clean/bugfree or i have less knowledge)

