Hello Fellows, I’m getting fancy.
Say hello to a long project that I’ve had on my mind. A 3D raycast renderer.
What is that? It takes a 2D plane, raycasts in that plane to detect walls, and generates a 3D-esque view of the world.
Current inspiration comes from:
My Process
-
Pseudo-Code Example: https://scratch.mit.edu/projects/176255219/
-
In Game Algorithm Role Model:
-
Simple Machine Based on Pseudo-Code:
I have more renderers coming, but I’ve hit a snag (as any project does). See, the entire map I’ve based on behaves weirdly with the raycast system I’ve set up:
With an apparent 90 degree FOV, we should see 2 openings on the left according to the minimap, but either the raycast is failing to detect, or the grid is too small? The issue with raising the FOV is that halls seem way smaller due to my screen ratio/set-up.
My current worry is simply the render, making the program more efficient and less laggy is my last priority at the moment, though if its the issue I’m willing to fix.
Client Side Render Script
--scripted by GreekForge
local rs = game:GetService("RunService")
local lib = require(script.Parent.Parent:WaitForChild("Library")) --cuts down on other uses, isn't involved in rendering
local coms = game.ReplicatedStorage.Remotes.Simple
local gui = script.Parent
local pFold = gui.Pixels --folder that contains the pixels
local on = false
local loading = false
local grid = 50 --70 is a lil mucho
local fovM = 0.5 -- 0.5 is about 90 degree fov
local pixel = Instance.new("TextLabel")
pixel.Text = "" --lmao
pixel.Size = UDim2.new(0, (800/grid), 0, (800/grid)) --its 800 because thats the size of the surface gui
pixel.BackgroundColor3 = Color3.fromRGB(170, 0, 0)
pixel.BorderSizePixel = 0
--What we shall use to render, the MINIMAP si
local mach = workspace.MinimapMachine
local minMap = mach.MiniMap
local user = minMap.User --this is the lil part in the minimap machine
local function generatePixels()
loading = true
coroutine.wrap(function()
for x=0, (grid-1) do
coroutine.wrap(function()
for y=0, (grid-1) do
local p = pixel:Clone()
p.Position = UDim2.new(0, x*(800/grid), 0, y*(800/grid))
p.Name = "Pixel("..x..","..y..")"
p.Parent = pFold
wait()
end
end)()
--wait()
end
repeat wait() until #pFold:GetChildren() >= (grid*grid)
loading = false
end)()
end
local function clear() --creates the "sky box"
for i=0, (grid-1) do
local gH = grid/2
for j=0, gH do
local nam = "Pixel("..i..","..j..")"
local pix = pFold:FindFirstChild(nam)
pix.BackgroundColor3 = Color3.fromRGB(150, 150, 255)
end
for j=(gH+1), (grid-1) do
local nam = "Pixel("..i..","..j..")"
local pix = pFold:FindFirstChild(nam)
pix.BackgroundColor3 = Color3.fromRGB(150, 100, 100)
end
end
end
coms.OnClientEvent:Connect(function(pass) --si, wasteful I guess
if pass == "On" then
on = true
elseif pass == "Off" then
on = false
end
end)
coroutine.wrap(function()
while wait() do
if on then
local kys = lib.getKeys()
coms:FireServer(kys) --means the part is server side, possibly not a good idea
wait(0.05)
end
end
end)()
while wait(1/23) do --23 frames per second (cut on lag)
if on then --if not using, why render?
if #pFold:GetChildren() == 0 and not loading then
generatePixels()
end
if not loading then
clear()
local uCF = user.CFrame
local edge1 = -uCF.LookVector + (uCF.RightVector*fovM)
local edge2 = -uCF.LookVector + (-uCF.RightVector*fovM)
for i=0, (grid-1) do
local dir = edge1:Lerp(edge2, ((i+0.5)/grid)) --starts on edge1 and as we travel on the x axis of the gui we get closer to edge2
local hit, pos = workspace:FindPartOnRayWithIgnoreList(Ray.new(user.Position, dir*9), {user, user.Face})
if hit then
local gH = grid/2
local m1 = 1 - math.clamp(((pos - uCF.Position).Magnitude)/10, 0, 1) --simply the ratio of how far
local m2 = math.floor((m1 * grid)/2) --gets the pixels needed to fill, by half
local s1 = gH - m2
if m2 ~= 0 then
for j=s1, gH do --top half
local nam = "Pixel("..i..","..j..")"
local pix = pFold:FindFirstChild(nam)
if pix then
local perc = ((pos - user.Position).Magnitude / 2) or 1
pix.BackgroundColor3 = Color3.new(perc, perc, perc)
end
end
for j=(gH+1), math.clamp(((gH+m2)-1), (gH+1), (grid-1)) do --bottom half
local nam = "Pixel("..i..","..j..")"
local pix = pFold:FindFirstChild(nam)
if pix then
local perc = ((pos - user.Position).Magnitude / 2) or 1
pix.BackgroundColor3 = Color3.new(perc, perc, perc)
end
end
end
end
end
end
end
end
Game: 2D Raycast Renderer - Roblox
NOTES:
- That scratch project is the overall feel I want for the first actual renderer I’m gonna make.
- I’m tired rn, so I may not have explained this correctly, just ask and I can explain what I am doing in the comments.