There is not a lot of pros using this module, it's more like a concept.
Please note that the image can’t be transparent, since currently how the module works is that it clones the image and offsets it 8 times.
Updates:
- Added outline:rebake()
API:
- outline.new()
- outline:destroy()
outline.new(config)
outline.new(config) is used to create an outline around an image.The config table can have these properties:
Property name | Must-have | Default value | Description |
---|---|---|---|
Object | yes | none | This is what the module will use to make the outline of, preferably the image that you want to have an outline |
Size | no | 3 | How thick the outline will be (recommended size between 1-25) |
Parent | no | object | Where the module will parent the outlines |
Data | no | default data | How the module will construct the outlines |
Sides | no | 8 | How many positional data is in data and the duration of the loop |
Rotation | no | 0 | How much the module should rotate the outlines |
Customize | no | color: black, gradient: true | Table, 2 properties: Color: Color3 and Gradient: boolean. Color stands for the outline’s color, gradient stands for to use gradient to paint the outline, or set ImageColor3 |
outline:rebake()
'Rebakes' the outlines, to match the updated imageoutline:destroy()
Destroys the outlines, and cleans up the class to be later garbage collected.How a custom data table should look like:
local myCustomData = {
[1] = {"%s", "%s"}
}
[1] - the index of the positional data
{"%s", “%s”} - %s later on will be replaced by the Size property, currently these are the only supported data values:
- %s
- -%s
- 0
Currently the module does not support math operations.
Source code
--[[
Outline
Not the best solution to give an image an outline, but at least it somewhat works.
Config:
Name: Must-have: Default value: Description:
Object: GuiBase2D yes none Module will use as the clone template
Size: number no 3 How thicc the outline will be
Parent: Instance no Object Where to parent it
Data: table no DEFAULT_DATA Positional data
Sides: number no 8 For the loop and for the positional data
Rotation: number no 0 How much the module should rotate the outlines
Customize: table
Color: Color3 no black What color
Gradient: boolean no true Use gradient or use ImageColor3
API:
outline.new()
outline:rebake
outline:destroy()
]]
local SIDES = 8
local ALLOWED_PROPERTY_TO_UPDATE = {
"Image",
"ImageRectOffset",
"ImageRectSize",
"ImageTransparency",
}
local NOT_ALLOWED_INSTANCES = {
"Script",
"ModuleScript",
"LocalScript",
}
local DEFAULT_DATA = {
[1] = { "-%s", "0" },
[2] = { "-%s", "-%s" },
[3] = { "0", "-%s" },
[4] = { "%s", "-%s" },
[5] = { "0", "%s" },
[6] = { "%s", "%s" },
[7] = { "-%s", "%s" },
[8] = { "%s", "0" },
}
local function createNewFrame(self)
local clone = self.template:Clone()
clone.Size = UDim2.fromScale(1, 1)
clone.Position = UDim2.fromScale(0.5, 0.5)
clone.ZIndex = 0
clone.AnchorPoint = Vector2.new(0.5, 0.5)
clone.BorderSizePixel = 0
clone.Name = "Outline"
clone.Rotation = self.rotation
if self.useGradient then
local gradient = Instance.new("UIGradient")
if typeof(self.color) == "ColorSequence" then
gradient.Color = self.color
else
gradient.Color = ColorSequence.new(self.color, self.color)
end
gradient.Parent = clone
else
clone.ImageColor3 = self.color
end
clone.Parent = self.outlineParent
return clone
end
local function checkIfNotZero(dirString)
if dirString == "0" then
return 0
else
return tonumber(dirString)
end
end
local function convertString(dataString, size)
local formatted = string.format(dataString, tostring(size))
local converted = checkIfNotZero(formatted)
return converted
end
local function renderize(self)
-- "bake" the lines into the image
for side = 1, self.sides do
if self.data[side] then
local clone = createNewFrame(self)
local pos = clone.Position
local dirData = self.data[side]
local x = convertString(dirData[1], self.outlineSize)
local y = convertString(dirData[2], self.outlineSize)
pos += UDim2.fromOffset(x, y)
clone.Position = pos
self.lines[#self.lines + 1] = clone
else
warn("No dir data found!")
end
end
self.template:Destroy()
end
local function rerenderize(self)
for _, obj in ipairs(self.lines) do
for _, allowedProperty in ipairs(ALLOWED_PROPERTY_TO_UPDATE) do
obj[allowedProperty] = self.obj[allowedProperty]
end
end
end
local function checkIfExists(data, value)
if data then
return data[value]
else
return nil
end
end
local outline = {}
outline.__index = outline
function outline.new(config)
local self = {}
---
self.obj = config.Object or error("Must-have property: Object is missing!")
self.outlineSize = config.Size or 3
self.outlineParent = config.Parent or self.obj
self.data = config.Data or DEFAULT_DATA
self.sides = config.Sides or SIDES
self.rotation = config.Rotation or 0
self.color = checkIfExists(config.Customize, "Color") or Color3.fromRGB(0, 0, 0)
self.useGradient = checkIfExists(config.Customize, "Gradient") or true
self.template = self.obj:Clone()
-- makes sure that no scripts get cloned, or else it will create a recursion
for _, instances in ipairs(self.template:GetDescendants()) do
for _, notAllowedClass in ipairs(NOT_ALLOWED_INSTANCES) do
if instances:IsA(notAllowedClass) then
instances:Destroy()
end
end
end
self.lines = {}
-- makes sure that the ZIndex behavior is global
local screenGui = self.obj:FindFirstAncestorOfClass("ScreenGui")
if screenGui.ZIndexBehavior ~= Enum.ZIndexBehavior.Global then
screenGui.ZIndexBehavior = Enum.ZIndexBehavior.Global
end
-- render the outlines
renderize(self)
---
return setmetatable(self, outline)
end
function outline:rebake()
rerenderize(self)
end
function outline:destroy()
self.obj = nil
self.outlineParent = nil
self.data = nil
for _, v in ipairs(self.lines) do
v:Destroy()
end
for _, v in ipairs(self.changedEvents) do
v:Disconnect()
end
end
return outline
Example
local outline = require(game.ReplicatedStorage.Outline) -- replace to the path of the module
local players = game:GetService("Players")
local player = players.LocalPlayer
local playerGui = player:WaitForChild("PlayerGui")
local myScreenGui = Instance.new("ScreenGui")
myScreenGui.Name = "Outline showcase"
myScreenGui.Parent = playerGui
local original = Instance.new("ImageLabel")
original.Name = "Original"
original.AnchorPoint = Vector2.new(0.5, 0.5)
original.BackgroundTransparency = 1
original.Position = UDim2.fromScale(0.454, 0.5)
original.Size = UDim2.fromScale(0.091, 0.195)
original.Image = "rbxassetid://7893601012"
original.ScaleType = Enum.ScaleType.Fit
original.Parent = myScreenGui
local withOutline = Instance.new("ImageLabel")
withOutline.Name = "With outline"
withOutline.AnchorPoint = Vector2.new(0.5, 0.5)
withOutline.BackgroundTransparency = 1
withOutline.Position = UDim2.new(0.546, 0, 0.5, 0)
withOutline.Size = UDim2.fromScale(0.091, 0.195)
withOutline.Image = "rbxassetid://7893601012"
withOutline.ScaleType = Enum.ScaleType.Fit
withOutline.Parent = myScreenGui
local myOutlineClass = outline.new({
Object = withOutline,
Size = 3,
})
Result:
Have fun using the module!