Help making a Fluid SprayPaint System

  1. What do you want to achieve?

    I want to create a fluid Spray Paint System Like the one in the famous game Spray Paint! However, I don’t seem to be getting the Fluidity right as I keep getting gaps when I move the mouse at a moderate speed. I have scanned the Developer Forum and I still haven’t found a Solution So Far. I’m terrible at math so some code would be appreciated along with some Insight.

  2. What is the issue?

    There are Gaps whenever the mouse moves at moderate speed even when render stepped is used.

Here is the Code that ive been using!

local tool = script.Parent
local UserInputService = game:GetService("UserInputService")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local ToolClient = require(script.ToolClient)

tool.Equipped:Connect(function(mouse)
	local player = game.Players:GetPlayerFromCharacter(tool.Parent)
	local isMouseDown = false

	UserInputService.InputBegan:Connect(function(input)
		if input.UserInputType == Enum.UserInputType.MouseButton1 then
			isMouseDown = true
		end
	end)

	UserInputService.InputEnded:Connect(function(input)
		if input.UserInputType == Enum.UserInputType.MouseButton1 then
			isMouseDown = false
		end
	end)
		
	game["Run Service"].RenderStepped:Connect(function()
		if isMouseDown then
			local sprayCFrame = ToolClient:CalculateSpray(player)
			local part = script.Octagon:Clone()
			part.Anchored = true
			part.CanCollide = false
			part.Color = Color3.new(0, 1, 1)
			part.CFrame =  sprayCFrame
			part.Parent = game.Workspace.Art
		end
	end)
end)

Here is what’s inside the ToolClient if that Helps.

local module = {}

function module:CalculateSpray(player: Player)
	local mouse = player:GetMouse()
	local ray = Ray.new(
		mouse.UnitRay.Origin,
		mouse.UnitRay.Direction * 1000
	)
	local part, position, normal = workspace:FindPartOnRayWithIgnoreList(ray,{player.Character,workspace.Art})
	local sprayCFrame = CFrame.new(position, position + normal)
	local lookOBJSpace = sprayCFrame:PointToObjectSpace(player.Character:FindFirstChild("HumanoidRootPart").Position)
	local FaceRadians = math.atan2(lookOBJSpace.Y, lookOBJSpace.X)
	local REALSprayCFrame = sprayCFrame * CFrame.Angles(math.rad(-180), math.rad(90), 0)
	
	if part then
		return REALSprayCFrame
	end
end

return module

Feedback Appreciated!

1 Like

Maybe try heartbeat instead of renderstepped and see what happens.

1 Like

Is renderstepped not faster? I ddi some research and it seems as it is

1 Like

Should be faster as it is before the frames, but maybe not in this case, not totally sure.

Try instead of making dots, create lines, and keep connecting a line from the newest point to the last point

1 Like

I would create 2 circular GUI Objects, and one line. The line will reach from the last Mouse position to the current position, and the circles will be placed on both ends. (What the person above me said. 10 seconds earlier.)

1 Like

Can i get a code example,confused lol

1 Like

I’m don’t have time, but here is another explanation. Every heartbeat or renderstepped, you save the mouse position. The next heartbeat or renderstepped, you create a line from the last mouse position, to the current position. (So you should have a blocky path whereever your mouse goes.) Finally, you will put 2 circles smack dab at the current position, and the last posiiton, to give the effect of many small circles. Make sense?

1 Like

idk why but im still not understanding the line part

Okay, i’ll give you something.

1 Like

It’s not perfect, but it gives you the general idea.
LocalScript -

local Mouse = game.Players.LocalPlayer:GetMouse()
local LastMouseP =  Vector3.new(0,0,0)
local LastPart = game.Workspace.Part4
local LastDot = game.Workspace.Part4
while true do
	task.wait(.1)

	
	local MouseHit = Mouse.Hit.Position
	local Dot = Instance.new("Part")
	Dot.Anchored = true
	Dot.Size = Vector3.new(1,1,(MouseHit - LastMouseP).Magnitude)
	Dot.Parent = game.Workspace
	
	Dot.CFrame = CFrame.lookAt(MouseHit,LastMouseP)
	
	LastMouseP = MouseHit
		LastPart = Dot
	
end

Alright I will test and let you know if I plan to improve

It’s not very good, It needs to have some sort of offset added in.

what would the purpose of this offset be? and it does its job though.

To have the lines connect. When I tested, the lines were unhinged. They weren’t connected at the end.

Not me wondering how to put in this offset I changed a few things in the code. I started using RenderStepped to make it faster.

local Mouse = game.Players.LocalPlayer:GetMouse()
local LastMouseP =  Vector3.new(0,0,0)
local LastPart

game["Run Service"].RenderStepped:Connect(function()
	local MouseHit = Mouse.Hit.Position
	local Dot = Instance.new("Part")
	Dot.Anchored = true
	Dot.Size = Vector3.new(1,1,(MouseHit - LastMouseP).Magnitude)
	Dot.Parent = game.Workspace
	Dot.CFrame = CFrame.lookAt(MouseHit,LastMouseP)
	LastMouseP = MouseHit
	LastPart = Dot
end)

image