Hey all. I have recently been looking at 2D Raycast Renderers on roblox, and I’ve been noticing a common issue with most peoples’ creations. The main mistake I often see when people do these, is over engineering them. And I thought I’d just make a much more simple, optimized, fast, and higher quality raycast renderer that is free for use!
If you don’t know what a 2D Raycast Renderer is, it is a 2D top-down map that simulates a 3D environment by casting a bunch of rays from a point in the 2D map, with columns that will change size and colour to give the illusion of depth.
Hope you guys find a use for this.
Enjoy!
Engine source code
local Gui = script.Parent
local MainFrame = Gui:WaitForChild("MainFrame")
local Canvas = MainFrame:WaitForChild("Canvas")
local RS = game:GetService("RunService")
local SettingsModule = require(Gui:WaitForChild("MainSettings"))
local CameraPart = SettingsModule.CameraPart
local LevelWorkspace = SettingsModule.CurrentMap
local FrameSizeX = (1 / SettingsModule.Resolution)
local SpritesTable = {}
local function GenerateFrames()
-- Generate columns
for i = 1, SettingsModule.Resolution do
local FramePosX = 1 / SettingsModule.Resolution * (i - 1)
-- Main column
local Frame = Instance.new("Frame")
Frame.ClipsDescendants = true
Frame.LayoutOrder = i
Frame.Name = "Frame" .. i
Frame.BorderSizePixel = 0
Frame.AnchorPoint = Vector2.new(0, SettingsModule.CameraHeight)
Frame.Size = UDim2.fromScale(FrameSizeX, 0)
Frame.Position = UDim2.fromScale(FramePosX, 0.5)
Frame.Parent = Canvas
end
end
local function ClearCanvas() -- Clears any old frames
for i, Frame in ipairs(Canvas:GetChildren()) do
if Frame:IsA("GuiObject") then
Frame:Destroy()
end
end
end
function NormalToFace(normalVector, part)
local function GetNormalFromFace(part, normalId)
return part.CFrame:VectorToWorldSpace(Vector3.FromNormalId(normalId))
end
local TOLERANCE_VALUE = 1 - 0.001
local allFaceNormalIds = {
Enum.NormalId.Front,
Enum.NormalId.Back,
Enum.NormalId.Bottom,
Enum.NormalId.Top,
Enum.NormalId.Left,
Enum.NormalId.Right
}
for _, normalId in pairs(allFaceNormalIds) do
-- If the two vectors are almost parallel,
if GetNormalFromFace(part, normalId):Dot(normalVector) > TOLERANCE_VALUE then
return normalId -- We found it!
end
end
return nil -- None found within tolerance.
end
local function UpdateFrames() -- Render the picture
local Count = -SettingsModule.Resolution / 2
-- Frames
for i, Frame in ipairs(Canvas:GetChildren()) do
if Frame:IsA("Frame") then
local Params = RaycastParams.new()
Params.FilterType = Enum.RaycastFilterType.Whitelist
Params.FilterDescendantsInstances = LevelWorkspace:GetChildren()
local ViewRayOrigin = CameraPart.Position
local RayAngle = CFrame.Angles(0, -math.rad((Count * SettingsModule.FieldOfView) / SettingsModule.Resolution), 0)
local ViewRayDirection = (CameraPart.CFrame * RayAngle).LookVector * SettingsModule.RenderDistance
local ViewRay = workspace:Raycast(ViewRayOrigin, ViewRayDirection, Params)
if ViewRay and ViewRay.Instance and ViewRay.Instance:IsA("BasePart") and ViewRay.Instance.Transparency < 1 then
local HitPart = ViewRay.Instance
--print(ViewRay.Instance)
--print(ViewRay.Position)
if HitPart:FindFirstChild("Texture") and HitPart.Texture:IsA("ImageLabel") and SettingsModule.TexturesEnabled then
if Frame.ImageFrame.Image ~= HitPart.Texture.Image then
-- Implement image for texturing
Frame.ImageFrame.Image = HitPart.Texture.Image
end
elseif SettingsModule.TexturesEnabled then
Frame.ImageFrame.Image = ""
end
local Distance = (ViewRayOrigin - ViewRay.Position).Magnitude
Frame.Size = UDim2.fromScale(FrameSizeX, (1 / Distance) * SettingsModule.ObjectHeightMultiplier)
-- Shading
local Face = NormalToFace(ViewRay.Normal, HitPart)
if Face == Enum.NormalId.Right or Face == Enum.NormalId.Left then
Frame.BackgroundColor3 = Color3.new(HitPart.Color.R / SettingsModule.ShadingLevel, HitPart.Color.G / SettingsModule.ShadingLevel, HitPart.Color.B / SettingsModule.ShadingLevel)
else
Frame.BackgroundColor3 = HitPart.Color
end
-- Debug
--local DebugLaser = Instance.new("Part") -- Visualise the rays
--DebugLaser.Anchored = true
--DebugLaser.Name = "DebugLaser"
--DebugLaser.Size = Vector3.new(0.2, 0.2, Distance)
--DebugLaser.CFrame = CFrame.new(ViewRayOrigin, ViewRay.Position) * CFrame.new(0, 0, -Distance/2)
--DebugLaser.Parent = workspace
else
Frame.BackgroundTransparency = 1
end
Count = Count + 1
end
end
end
ClearCanvas() -- Just incase.
GenerateFrames() -- Generate all the columns that will be used.
UpdateFrames()
script.Parent.Enabled = true
RS.RenderStepped:Connect(function() -- Always render the picture
UpdateFrames()
end)
Roblox place file
Raycast project.rbxl (31.0 KB)