So, I have created a rather simple blood splat module. However, it’s always the same pattern and it never turns to a different direction. What I want to do is, depending on what way it is facing and where it is, it turns slightly. Example:
--- /// SETUP /// ---
local BloodSplatterDecalIDs = {91099453641060, 113297356394128}
local BloodSplatterSoundIDs = {7578871246}
local RBXASSETID = "rbxassetid://"
--- /// SERVICES /// --*
local debris = game:GetService("Debris")
local tweenService = game:GetService("TweenService")
local goal = {}
goal.Transparency = 1
--- /// ATTRIBUTE VARIABLES /// ---
local DestroyTime = script:GetAttribute("BloodCleanUpTime")
local VisDestroyTime = script:GetAttribute("VisualizationCleanUpTime")
local FadeDelay = script:GetAttribute("FadeDelay")
local debugMode = script:GetAttribute("Debug")
local bloodVisible = script:GetAttribute("BloodPartVisible")
local Distance = script:GetAttribute("Distance")
local BloodModule = {}
local function createBloodPart(position, normal)
-- // Create our part of varying sizes
local randomSize = math.random(1,10)
local bloodPart = Instance.new("Part")
bloodPart.Size = Vector3.new(randomSize, 0.01, randomSize)
bloodPart.Anchored = true
bloodPart.CanCollide = false
bloodPart.CastShadow = false
bloodPart.CanQuery = false
bloodPart.BrickColor = BrickColor.new("Bright red")
bloodPart.Name = "BloodSplat"
bloodPart.Rotation = Vector3.new()
if bloodVisible then
bloodPart.Transparency = 0
else
bloodPart.Transparency = 1
end
local tweenInfo = TweenInfo.new(DestroyTime, Enum.EasingStyle.Linear, Enum.EasingDirection.In, 0, false, FadeDelay)
-- // Calculate when to destroy the blood splat
local amt = FadeDelay + DestroyTime + 0.5
-- // Calculate right and up vectors for the CFrame
local upVector = normal
local forwardVector = upVector:Cross(Vector3.new(0, 1, 0))
if forwardVector.Magnitude == 0 then
forwardVector = upVector:Cross(Vector3.new(1, 0, 0))
end
local rightVector = forwardVector:Cross(upVector)
bloodPart.CFrame = CFrame.fromMatrix(position, rightVector, upVector)
bloodPart.Parent = workspace.BloodSys.Blood
-- // Create the splat decal
local bloodDecal = Instance.new("Decal")
bloodDecal.Texture = RBXASSETID..tostring(BloodSplatterDecalIDs[math.random(1, #BloodSplatterDecalIDs)])
bloodDecal.Face = Enum.NormalId.Top
bloodDecal.Parent = bloodPart
-- // Make it fade
local tween = tweenService:Create(bloodDecal, tweenInfo, goal)
tween:Play()
debris:AddItem(bloodPart, amt)
return bloodPart
end
local function visualizeRay(origin, direction)
local part = Instance.new("Part")
part.Size = Vector3.new(0.2, 0.2, direction.Magnitude)
part.CFrame = CFrame.new(origin + direction / 2, origin + direction)
part.Anchored = true
part.CanCollide = false
part.CanQuery = false
part.CastShadow = false
part.BrickColor = BrickColor.new("Bright yellow")
part.Transparency = 0.5
part.Name = "RayVisualization"
part.Parent = workspace.BloodSys.Visualizations
debris:AddItem(part, VisDestroyTime)
end
local function castRayAndSpawnBlood(direction, humanoidRootPart)
local rayOrigin = humanoidRootPart.Position
local rayDirection = direction.Unit * Distance
local character = humanoidRootPart.Parent
local raycastParams = RaycastParams.new()
raycastParams.FilterDescendantsInstances = {character}
raycastParams.FilterType = Enum.RaycastFilterType.Exclude
-- Check if visualization is enabled
local visualize = script:GetAttribute("VisualizeRays")
if visualize then
visualizeRay(rayOrigin, rayDirection)
end
local rayResult = workspace:Raycast(rayOrigin, rayDirection, raycastParams)
if rayResult then
local hitPosition = rayResult.Position
local hitNormal = rayResult.Normal
if debugMode then
print("Ray hit something!")
print("Ray hit position: ", hitPosition)
print("Ray hit normal: ", hitNormal)
print("Ray hit ", rayResult.Instance)
end
createBloodPart(hitPosition, hitNormal)
else
if debugMode then
print("Ray did not hit anything.")
end
end
end
local function getRandomDirection()
-- // Generate a random direction vector in a 360° space
local theta = math.random() * 2 * math.pi -- Random angle in the horizontal plane
local phi = math.acos(2 * math.random() - 1) -- Random angle for the vertical plane
-- // Convert spherical coordinates to Cartesian coordinates
local x = math.sin(phi) * math.cos(theta)
local y = math.sin(phi) * math.sin(theta)
local z = math.cos(phi)
return Vector3.new(x, y, z)
end
function BloodModule:Splatter(humanoidRootPart: Part, min: number, max: number)
print("Splatter function called")
local numberOfRays = math.random(min, max) -- Randomly select between 4 and 8 rays to cast
local id = RBXASSETID..tostring(BloodSplatterSoundIDs[math.random(1,#BloodSplatterSoundIDs)])
local sound = Instance.new("Sound")
sound.Name = "Splat"
sound.SoundId = id
sound.RollOffMode = Enum.RollOffMode.Linear
sound.RollOffMaxDistance = 200
sound.RollOffMinDistance = 0
sound.Parent = humanoidRootPart
local effectSound = Instance.new("PitchShiftSoundEffect")
effectSound.Octave = math.random(0.75, 1.25)
effectSound.Parent = sound
sound:Play()
debris:AddItem(sound, 5)
for i = 1, numberOfRays do
local direction = getRandomDirection()
castRayAndSpawnBlood(direction, humanoidRootPart)
end
end
return BloodModule
The script.