This script creates a perspective grid and vanishing line on a GUI object using the EditableImage class. The grid is created by drawing lines at regular intervals, and the vanishing line is created using the camera perspective. The grid and vanishing line can be used to create a 3D effect on a flat surface. To utilize this script ensure the parent is correct at the screenGUI portion on line 27.
local Players = game:GetService("Players")
local AssetService = game:GetService("AssetService")
local RunService = game:GetService("RunService")
local Workspace = game:GetService("Workspace")
local localPlayer = Players.LocalPlayer
local playerGui = localPlayer:WaitForChild("PlayerGui")
local camera = Workspace.CurrentCamera
-- Constants for perspective grid visualization
local CANVAS_SIZE = Vector2.new(512, 512)
local GRID_LINES = 12
local GRID_COLOR = Color3.new(0.2, 0.6, 0.9)
local HORIZON_COLOR = Color3.new(0.9, 0.4, 0.6)
local BACKGROUND_COLOR = Color3.new(0.05, 0.05, 0.1)
local GRID_TRANSPARENCY = 0.2
local HORIZON_TRANSPARENCY = 0
local MOVEMENT_SENSITIVITY = 1
local ROTATION_SENSITIVITY = 0.5
-- Create a new EditableImage
local editableImage = AssetService:CreateEditableImage({
["Size"] = CANVAS_SIZE
})
-- Screen GUI setup
local part = workspace:WaitForChild("Part")
local screenGui = part:WaitForChild("SurfaceGui")
local imageLabel = Instance.new("ImageLabel")
imageLabel.Size = UDim2.new(1, 0, 1, 0)
imageLabel.Position = UDim2.new(0.5, 0, 0.5, 0)
imageLabel.AnchorPoint = Vector2.new(0.5, 0.5)
imageLabel.BackgroundTransparency = 1
imageLabel.Parent = screenGui
-- Store initial camera position and part position
local initialCameraPosition = camera.CFrame.Position
local initialPartPosition = part.Position
local lastVanishingPoint = Vector2.new(CANVAS_SIZE.X / 2, CANVAS_SIZE.Y / 2)
-- Clear the canvas
local function clearCanvas()
editableImage:DrawRectangle(
Vector2.new(0, 0),
CANVAS_SIZE,
BACKGROUND_COLOR,
0,
Enum.ImageCombineType.Overwrite
)
end
-- Draw a line with bounds checking
local function safeDrawLine(p1, p2, color, transparency, combineType)
local p1x = math.clamp(p1.X, 0, CANVAS_SIZE.X - 1)
local p1y = math.clamp(p1.Y, 0, CANVAS_SIZE.Y - 1)
local p2x = math.clamp(p2.X, 0, CANVAS_SIZE.X - 1)
local p2y = math.clamp(p2.Y, 0, CANVAS_SIZE.Y - 1)
editableImage:DrawLine(
Vector2.new(p1x, p1y),
Vector2.new(p2x, p2y),
color,
transparency,
combineType
)
end
-- Draw the perspective grid
local function drawPerspectiveGrid(vanishingPoint)
-- Draw horizon line
safeDrawLine(
Vector2.new(0, vanishingPoint.Y),
Vector2.new(CANVAS_SIZE.X - 1, vanishingPoint.Y),
HORIZON_COLOR,
HORIZON_TRANSPARENCY,
Enum.ImageCombineType.AlphaBlend
)
-- Draw vertical grid lines
local spacing = CANVAS_SIZE.X / (GRID_LINES + 1)
for i = 1, GRID_LINES do
local x = i * spacing
safeDrawLine(
Vector2.new(x, 0),
Vector2.new(x, CANVAS_SIZE.Y - 1),
GRID_COLOR,
GRID_TRANSPARENCY,
Enum.ImageCombineType.AlphaBlend
)
end
-- Draw perspective lines from bottom to vanishing point
spacing = CANVAS_SIZE.X / (GRID_LINES + 1)
for i = 0, GRID_LINES + 1 do
local x = i * spacing
safeDrawLine(
Vector2.new(x, CANVAS_SIZE.Y - 1),
vanishingPoint,
GRID_COLOR,
GRID_TRANSPARENCY,
Enum.ImageCombineType.AlphaBlend
)
end
-- Draw horizontal perspective grid lines
local divisions = 8
for i = 1, divisions - 1 do
local t = i / divisions
local y = vanishingPoint.Y + (CANVAS_SIZE.Y - vanishingPoint.Y) * t
-- Calculate left and right points on the perspective lines
local leftX = (0 - vanishingPoint.X) * t + vanishingPoint.X
local rightX = (CANVAS_SIZE.X - vanishingPoint.X) * t + vanishingPoint.X
safeDrawLine(
Vector2.new(leftX, y),
Vector2.new(rightX, y),
GRID_COLOR,
GRID_TRANSPARENCY * (1 - t * 0.5), -- Fade out with distance
Enum.ImageCombineType.AlphaBlend
)
end
-- Draw vanishing point indicator
editableImage:DrawCircle(
vanishingPoint,
3,
HORIZON_COLOR,
0,
Enum.ImageCombineType.AlphaBlend
)
end
-- Update the ImageLabel
local function updateImageLabel()
imageLabel.ImageContent = Content.fromObject(editableImage)
end
-- Calculate vanishing point based on camera position, angle, and part position
local function calculateVanishingPoint(cameraPosition, cameraLookVector, partPosition)
local center = Vector2.new(CANVAS_SIZE.X / 2, CANVAS_SIZE.Y / 2)
-- Calculate relative movement from initial positions
local relativeCameraX = cameraPosition.X - initialCameraPosition.X
local relativeCameraZ = cameraPosition.Z - initialCameraPosition.Z
local relativePartX = partPosition.X - initialPartPosition.X
local relativePartZ = partPosition.Z - initialPartPosition.Z
-- Calculate relative position between camera and part
local relativeX = (relativeCameraX - relativePartX) * MOVEMENT_SENSITIVITY
local relativeZ = (relativeCameraZ - relativePartZ) * MOVEMENT_SENSITIVITY
-- Calculate rotation offset based on camera look vector
local rotationOffsetX = cameraLookVector.X * ROTATION_SENSITIVITY * CANVAS_SIZE.X
local rotationOffsetY = -cameraLookVector.Y * ROTATION_SENSITIVITY * CANVAS_SIZE.Y
-- Calculate final vanishing point
local vanishingPoint = Vector2.new(
center.X + relativeX + rotationOffsetX,
center.Y + relativeZ + rotationOffsetY
)
-- Apply smoothing between frames (lerp)
vanishingPoint = Vector2.new(
lastVanishingPoint.X + (vanishingPoint.X - lastVanishingPoint.X) * 0.1,
lastVanishingPoint.Y + (vanishingPoint.Y - lastVanishingPoint.Y) * 0.1
)
lastVanishingPoint = vanishingPoint
return vanishingPoint
end
-- Main render loop
RunService.RenderStepped:Connect(function()
local cameraPosition = camera.CFrame.Position
local cameraLookVector = camera.CFrame.LookVector
local partPosition = part.Position
-- Calculate vanishing point based on camera and part positions
local vanishingPoint = calculateVanishingPoint(cameraPosition, cameraLookVector, partPosition)
-- Render the grid
clearCanvas()
drawPerspectiveGrid(vanishingPoint)
updateImageLabel()
end)
Author: StarVSK
Posted: March 1, 2025