local coreGui = game:GetService("CoreGui") -- objects local camera = workspace.CurrentCamera local drawingUI = Instance.new("ScreenGui") drawingUI.Name = "Drawing" drawingUI.IgnoreGuiInset = true drawingUI.DisplayOrder = 0x7fffffff drawingUI.Parent = coreGui -- variables local drawingIndex = 0 local uiStrokes = table.create(0) local baseDrawingObj = setmetatable({ Visible = true, ZIndex = 0, Transparency = 1, Color = Color3.new(), Remove = function(self) setmetatable(self, nil) end, Destroy = function(self) setmetatable(self, nil) end }, { __add = function(t1, t2) local result = table.clone(t1) for index, value in t2 do result[index] = value end return result end }) local drawingFontsEnum = { [0] = Font.fromEnum(Enum.Font.Roboto), [1] = Font.fromEnum(Enum.Font.Legacy), [2] = Font.fromEnum(Enum.Font.SourceSans), [3] = Font.fromEnum(Enum.Font.RobotoMono), } -- function local function getFontFromIndex(fontIndex: number): Font return drawingFontsEnum[fontIndex] end local function convertTransparency(transparency: number): number return math.clamp(1 - transparency, 0, 1) end -- main local DrawingLib = {} DrawingLib.Fonts = { ["UI"] = 0, ["System"] = 1, ["Plex"] = 2, ["Monospace"] = 3 } function DrawingLib.new(drawingType) drawingIndex += 1 if drawingType == "Line" then local lineObj = ({ From = Vector2.zero, To = Vector2.zero, Thickness = 1 } + baseDrawingObj) local lineFrame = Instance.new("Frame") lineFrame.Name = drawingIndex lineFrame.AnchorPoint = (Vector2.one * .5) lineFrame.BorderSizePixel = 0 lineFrame.BackgroundColor3 = lineObj.Color lineFrame.Visible = lineObj.Visible lineFrame.ZIndex = lineObj.ZIndex lineFrame.BackgroundTransparency = convertTransparency(lineObj.Transparency) lineFrame.Size = UDim2.new() lineFrame.Parent = drawingUI return setmetatable(table.create(0), { __newindex = function(_, index, value) if typeof(lineObj[index]) == "nil" then return end if index == "From" then local direction = (lineObj.To - value) local center = (lineObj.To + value) / 2 local distance = direction.Magnitude local theta = math.deg(math.atan2(direction.Y, direction.X)) lineFrame.Position = UDim2.fromOffset(center.X, center.Y) lineFrame.Rotation = theta lineFrame.Size = UDim2.fromOffset(distance, lineObj.Thickness) elseif index == "To" then local direction = (value - lineObj.From) local center = (value + lineObj.From) / 2 local distance = direction.Magnitude local theta = math.deg(math.atan2(direction.Y, direction.X)) lineFrame.Position = UDim2.fromOffset(center.X, center.Y) lineFrame.Rotation = theta lineFrame.Size = UDim2.fromOffset(distance, lineObj.Thickness) elseif index == "Thickness" then local distance = (lineObj.To - lineObj.From).Magnitude lineFrame.Size = UDim2.fromOffset(distance, value) elseif index == "Visible" then lineFrame.Visible = value elseif index == "ZIndex" then lineFrame.ZIndex = value elseif index == "Transparency" then lineFrame.BackgroundTransparency = convertTransparency(value) elseif index == "Color" then lineFrame.BackgroundColor3 = value end lineObj[index] = value end, __index = function(self, index) if index == "Remove" or index == "Destroy" then return function() lineFrame:Destroy() lineObj.Remove(self) return lineObj:Remove() end end return lineObj[index] end, __tostring = function() return "Drawing" end }) elseif drawingType == "Text" then local textObj = ({ Text = "", Font = DrawingLib.Fonts.UI, Size = 0, Position = Vector2.zero, Center = false, Outline = false, OutlineColor = Color3.new() } + baseDrawingObj) local textLabel, uiStroke = Instance.new("TextLabel"), Instance.new("UIStroke") textLabel.Name = drawingIndex textLabel.AnchorPoint = (Vector2.one * .5) textLabel.BorderSizePixel = 0 textLabel.BackgroundTransparency = 1 textLabel.Visible = textObj.Visible textLabel.TextColor3 = textObj.Color textLabel.TextTransparency = convertTransparency(textObj.Transparency) textLabel.ZIndex = textObj.ZIndex textLabel.FontFace = getFontFromIndex(textObj.Font) textLabel.TextSize = textObj.Size textLabel:GetPropertyChangedSignal("TextBounds"):Connect(function() local textBounds = textLabel.TextBounds local offset = textBounds / 2 textLabel.Size = UDim2.fromOffset(textBounds.X, textBounds.Y) textLabel.Position = UDim2.fromOffset(textObj.Position.X + (if not textObj.Center then offset.X else 0), textObj.Position.Y + offset.Y) end) uiStroke.Thickness = 1 uiStroke.Enabled = textObj.Outline uiStroke.Color = textObj.Color textLabel.Parent, uiStroke.Parent = drawingUI, textLabel return setmetatable(table.create(0), { __newindex = function(_, index, value) if typeof(textObj[index]) == "nil" then return end if index == "Text" then textLabel.Text = value elseif index == "Font" then value = math.clamp(value, 0, 3) textLabel.FontFace = getFontFromIndex(value) elseif index == "Size" then textLabel.TextSize = value elseif index == "Position" then local offset = textLabel.TextBounds / 2 textLabel.Position = UDim2.fromOffset(value.X + (if not textObj.Center then offset.X else 0), value.Y + offset.Y) elseif index == "Center" then local position = ( if value then camera.ViewportSize / 2 else textObj.Position ) textLabel.Position = UDim2.fromOffset(position.X, position.Y) elseif index == "Outline" then uiStroke.Enabled = value elseif index == "OutlineColor" then uiStroke.Color = value elseif index == "Visible" then textLabel.Visible = value elseif index == "ZIndex" then textLabel.ZIndex = value elseif index == "Transparency" then local transparency = convertTransparency(value) textLabel.TextTransparency = transparency uiStroke.Transparency = transparency elseif index == "Color" then textLabel.TextColor3 = value end textObj[index] = value end, __index = function(self, index) if index == "Remove" or index == "Destroy" then return function() textLabel:Destroy() textObj.Remove(self) return textObj:Remove() end elseif index == "TextBounds" then return textLabel.TextBounds end return textObj[index] end, __tostring = function() return "Drawing" end }) elseif drawingType == "Circle" then local circleObj = ({ Radius = 150, Position = Vector2.zero, Thickness = .7, Filled = false } + baseDrawingObj) local circleFrame, uiCorner, uiStroke = Instance.new("Frame"), Instance.new("UICorner"), Instance.new("UIStroke") circleFrame.Name = drawingIndex circleFrame.AnchorPoint = (Vector2.one * .5) circleFrame.BorderSizePixel = 0 circleFrame.BackgroundTransparency = (if circleObj.Filled then convertTransparency(circleObj.Transparency) else 1) circleFrame.BackgroundColor3 = circleObj.Color circleFrame.Visible = circleObj.Visible circleFrame.ZIndex = circleObj.ZIndex uiCorner.CornerRadius = UDim.new(1, 0) circleFrame.Size = UDim2.fromOffset(circleObj.Radius, circleObj.Radius) uiStroke.Thickness = circleObj.Thickness uiStroke.Enabled = not circleObj.Filled uiStroke.ApplyStrokeMode = Enum.ApplyStrokeMode.Border circleFrame.Parent, uiCorner.Parent, uiStroke.Parent = drawingUI, circleFrame, circleFrame return setmetatable(table.create(0), { __newindex = function(_, index, value) if typeof(circleObj[index]) == "nil" then return end if index == "Radius" then local radius = value * 2 circleFrame.Size = UDim2.fromOffset(radius, radius) elseif index == "Position" then circleFrame.Position = UDim2.fromOffset(value.X, value.Y) elseif index == "Thickness" then value = math.clamp(value, .6, 0x7fffffff) uiStroke.Thickness = value elseif index == "Filled" then circleFrame.BackgroundTransparency = (if value then convertTransparency(circleObj.Transparency) else 1) uiStroke.Enabled = not value elseif index == "Visible" then circleFrame.Visible = value elseif index == "ZIndex" then circleFrame.ZIndex = value elseif index == "Transparency" then local transparency = convertTransparency(value) circleFrame.BackgroundTransparency = (if circleObj.Filled then transparency else 1) uiStroke.Transparency = transparency elseif index == "Color" then circleFrame.BackgroundColor3 = value uiStroke.Color = value end circleObj[index] = value end, __index = function(self, index) if index == "Remove" or index == "Destroy" then return function() circleFrame:Destroy() circleObj.Remove(self) return circleObj:Remove() end end return circleObj[index] end, __tostring = function() return "Drawing" end }) elseif drawingType == "Square" then local squareObj = ({ Size = Vector2.zero, Position = Vector2.zero, Thickness = .7, Filled = false } + baseDrawingObj) local squareFrame, uiStroke = Instance.new("Frame"), Instance.new("UIStroke") squareFrame.Name = drawingIndex squareFrame.BorderSizePixel = 0 squareFrame.BackgroundTransparency = (if squareObj.Filled then convertTransparency(squareObj.Transparency) else 1) squareFrame.ZIndex = squareObj.ZIndex squareFrame.BackgroundColor3 = squareObj.Color squareFrame.Visible = squareObj.Visible uiStroke.Thickness = squareObj.Thickness uiStroke.Enabled = not squareObj.Filled uiStroke.LineJoinMode = Enum.LineJoinMode.Miter squareFrame.Parent, uiStroke.Parent = drawingUI, squareFrame return setmetatable(table.create(0), { __newindex = function(_, index, value) if typeof(squareObj[index]) == "nil" then return end if index == "Size" then squareFrame.Size = UDim2.fromOffset(value.X, value.Y) elseif index == "Position" then squareFrame.Position = UDim2.fromOffset(value.X, value.Y) elseif index == "Thickness" then value = math.clamp(value, 0.6, 0x7fffffff) uiStroke.Thickness = value elseif index == "Filled" then squareFrame.BackgroundTransparency = (if value then convertTransparency(squareObj.Transparency) else 1) uiStroke.Enabled = not value elseif index == "Visible" then squareFrame.Visible = value elseif index == "ZIndex" then squareFrame.ZIndex = value elseif index == "Transparency" then local transparency = convertTransparency(value) squareFrame.BackgroundTransparency = (if squareObj.Filled then transparency else 1) uiStroke.Transparency = transparency elseif index == "Color" then uiStroke.Color = value squareFrame.BackgroundColor3 = value end squareObj[index] = value end, __index = function(self, index) if index == "Remove" or index == "Destroy" then return function() squareFrame:Destroy() squareObj.Remove(self) return squareObj:Remove() end end return squareObj[index] end, __tostring = function() return "Drawing" end }) elseif drawingType == "Image" then local imageObj = ({ Data = "", DataURL = "rbxassetid://0", Size = Vector2.zero, Position = Vector2.zero } + baseDrawingObj) local imageFrame = Instance.new("ImageLabel") imageFrame.Name = drawingIndex imageFrame.BorderSizePixel = 0 imageFrame.ScaleType = Enum.ScaleType.Stretch imageFrame.BackgroundTransparency = 1 imageFrame.Visible = imageObj.Visible imageFrame.ZIndex = imageObj.ZIndex imageFrame.ImageTransparency = convertTransparency(imageObj.Transparency) imageFrame.ImageColor3 = imageObj.Color imageFrame.Parent = drawingUI return setmetatable(table.create(0), { __newindex = function(_, index, value) if typeof(imageObj[index]) == "nil" then return end if index == "Data" then -- later elseif index == "DataURL" then -- temporary property imageFrame.Image = value elseif index == "Size" then imageFrame.Size = UDim2.fromOffset(value.X, value.Y) elseif index == "Position" then imageFrame.Position = UDim2.fromOffset(value.X, value.Y) elseif index == "Visible" then imageFrame.Visible = value elseif index == "ZIndex" then imageFrame.ZIndex = value elseif index == "Transparency" then imageFrame.ImageTransparency = convertTransparency(value) elseif index == "Color" then imageFrame.ImageColor3 = value end imageObj[index] = value end, __index = function(self, index) if index == "Remove" or index == "Destroy" then return function() imageFrame:Destroy() imageObj.Remove(self) return imageObj:Remove() end elseif index == "Data" then return nil -- TODO: add error here end return imageObj[index] end, __tostring = function() return "Drawing" end }) elseif drawingType == "Quad" then local QuadProperties = ({ Thickness = 1, PointA = Vector2.new(); PointB = Vector2.new(); PointC = Vector2.new(); PointD = Vector2.new(); Filled = false; } + baseDrawingObj); local PointA = DrawingLib.new("Line") local PointB = DrawingLib.new("Line") local PointC = DrawingLib.new("Line") local PointD = DrawingLib.new("Line") return setmetatable({}, { __newindex = (function(self, Property, Value) if Property == "Thickness" then PointA.Thickness = Value PointB.Thickness = Value PointC.Thickness = Value PointD.Thickness = Value end if Property == "PointA" then PointA.From = Value PointB.To = Value end if Property == "PointB" then PointB.From = Value PointC.To = Value end if Property == "PointC" then PointC.From = Value PointD.To = Value end if Property == "PointD" then PointD.From = Value PointA.To = Value end if Property == "Visible" then PointA.Visible = true PointB.Visible = true PointC.Visible = true PointD.Visible = true end if Property == "Filled" then -- i'll do this later end if Property == "Color" then PointA.Color = Value PointB.Color = Value PointC.Color = Value PointD.Color = Value end if (Property == "ZIndex") then PointA.ZIndex = Value PointB.ZIndex = Value PointC.ZIndex = Value PointD.ZIndex = Value end end), __index = (function(self, Property) if (string.lower(tostring(Property)) == "remove") then return (function() PointA:Remove(); PointB:Remove(); PointC:Remove(); PointD:Remove(); end) end return QuadProperties[Property] end) }); elseif drawingType == "Triangle" then local triangleObj = ({ PointA = Vector2.zero, PointB = Vector2.zero, PointC = Vector2.zero, Thickness = 1, Filled = false } + baseDrawingObj) local _linePoints = table.create(0) _linePoints.A = DrawingLib.new("Line") _linePoints.B = DrawingLib.new("Line") _linePoints.C = DrawingLib.new("Line") return setmetatable(table.create(0), { __tostring = function() return "Drawing" end, __newindex = function(_, index, value) if typeof(triangleObj[index]) == "nil" then return end if index == "PointA" then _linePoints.A.From = value _linePoints.B.To = value elseif index == "PointB" then _linePoints.B.From = value _linePoints.C.To = value elseif index == "PointC" then _linePoints.C.From = value _linePoints.A.To = value elseif (index == "Thickness" or index == "Visible" or index == "Color" or index == "ZIndex") then for _, linePoint in _linePoints do linePoint[index] = value end elseif index == "Filled" then -- later end triangleObj[index] = value end, __index = function(self, index) if index == "Remove" or index == "Destroy" then return function() for _, linePoint in _linePoints do linePoint:Remove() end triangleObj.Remove(self) return triangleObj:Remove() end end return triangleObj[index] end, }) end end getgenv().Drawing = DrawingLib