I’m creating a drawing canvas. The issue I’m facing is that the InputChange function doesn’t fire fast enough, as such, there are weird gaps in my drawings as seen in this video:
Here’s how my code looks:
local player = game.Players.LocalPlayer
local frame = script.Parent
local dragging = false
-- turn on dragging when the mouse is pressed or the screen is touched
frame.InputBegan:Connect(function(input)
if input.UserInputType == Enum.UserInputType.MouseButton1 or input.UserInputType == Enum.UserInputType.Touch then
dragging = true
end
end)
-- turn off dragging when its released
frame.InputEnded:Connect(function(input)
if input.UserInputType == Enum.UserInputType.MouseButton1 or input.UserInputType == Enum.UserInputType.Touch then
dragging = false
end
end)
-- helper function to draw one point at the given x,y position
local function DrawAt(x, y)
-- need the parent's position so we can subtract it from the child's
local topLeft = frame.AbsolutePosition
local point = Instance.new("Frame")
point.Size = UDim2.fromOffset(10, 10)
-- ... like this
point.Position = UDim2.fromOffset(x - topLeft.X - 5, y - topLeft.Y - 5)
point.BackgroundColor3 = Color3.new(0.991119, 0.549386, 0.255436)
point.BorderSizePixel = 0
point.Parent = frame
local rounded = Instance.new("UICorner")
rounded.CornerRadius = UDim.new(0.5, 0)
rounded.Parent = point
end
-- fired when the mouse or finger moves
frame.InputChanged:Connect(function(input)
if dragging and input.UserInputType == Enum.UserInputType.MouseMovement or input.UserInputType == Enum.UserInputType.Touch then
DrawAt(input.Position.X, input.Position.Y)
end
end)
``
The problem is that you simply move your mouse too fast for ROBLOX to perfectly keep track. Even if you use some workarounds like RunService.Heartbeat, you will still see gaps in your line. It will probably decrease the size of the gaps, so it is definitely recommendable.
I recommend you to use RunService:BindToRenderstep(‘CanvasUpdate’, Enum.RenderPriority.Input.Value + 1, yourFunctionHere) with interpolation. Also, you keep adding new points with DrawAt. Maybe this can be optimized.
Or you could just use Runservice.Renderstepped to draw the frames. Every time the event fires, check if the current mouse position is different from the last mouse position, and if it is then use the mouses position to draw the frame.
Replace:
With:
local mouse = player:GetMouse()
local lastX, local lastY = 0,0
game:GetService("RunService").RenderStepped:Connect(function()
if mouse.X ~= lastX and mouse.Y ~= lastY and dragging then
lastX, lastY = mouse.X, mouse.Y
DrawAt(mouse.X, mouse.Y)
end
end)
I would use heartbeat or renderstepped instead of inputchanged for mouse/finger movement. It will fire at the fps of your screen so it should line up with the mouse better. Something like this but I would make sure to check that the position doesn’t equal the previous position. This is just a rough idea.
local mouse = game:GetService("Players").LocalPlayer:GetMouse()
game:GetService("RunService").Heartbeat:Connect(function()
if dragging then
DrawAt(mouse.X, mouse.Y)
end
end)
The only thing that you will need to calculate now because it isn’t using frame input for the mouse but actually the mouse object within the player itself is that you will need to calculate where the mouse position on your screen is relative to the drawing frame and then do the necessary calculations to line up the draw spot. Otherwise you will need to do interpolation as Brickman suggested.
I have tried RunService.Heartbeat and RunService.RenderStepped but there were still gaps between my points. I resorted to creating filler lines between the points. Here’s how I did it:
-- helper function to draw one point at the given x,y position
local function DrawAt(x, y)
-- need the parent's position so we can subtract it from the child's
local topLeft = frame.AbsolutePosition
if previousPosition then
local fillerLine = Instance.new("Frame")
local previousPointX = previousPosition.X
local previousPointY = previousPosition.Y
if previousPointX == x and previousPointY == y then
return
end
local length = math.sqrt((x - previousPointX)^2 + (y - previousPointY)^2)
local position = Vector2.new((x + previousPointX)/2, (y + previousPointY)/2)
local opposite = y - previousPointY
local adjacent = x - previousPointX
local angleRadian = math.atan(opposite/adjacent)
local angleDegree = angleRadian*180/math.pi
fillerLine.Size = UDim2.fromOffset(length, strokeThickness)
fillerLine.Position = UDim2.fromOffset(position.X - topLeft.X - length/2, position.Y - topLeft.Y - strokeThickness/2)
fillerLine.Rotation = angleDegree
fillerLine.BackgroundColor3 = strokeColor
fillerLine.BorderSizePixel = 0
fillerLine.Parent = frame
local rounded = Instance.new("UICorner")
rounded.CornerRadius = UDim.new(0.5, 0)
rounded.Parent = fillerLine
end
previousPosition = Vector2.new(x, y)
local newPoint = Instance.new("Frame")
newPoint.Size = UDim2.fromOffset(strokeThickness, strokeThickness)
newPoint.Position = UDim2.fromOffset(x - topLeft.X - strokeThickness/2, y - topLeft.Y - strokeThickness/2)
newPoint.BackgroundColor3 = strokeColor
newPoint.BorderSizePixel = 0
newPoint.Parent = frame
local rounded = Instance.new("UICorner")
rounded.CornerRadius = UDim.new(0.5, 0)
rounded.Parent = newPoint
end