Raycasting not hitting correctly?

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

  1. Pseudo-Code Example: https://scratch.mit.edu/projects/176255219/

  2. In Game Algorithm Role Model:
    image

  3. Simple Machine Based on Pseudo-Code:
    image

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.
2 Likes

Just want to note that “Show Decomposition Geometry” in studio settings does not yield any conclusive results sadly…

Change the color of each part. The way it looks from here, the renderer functions properly, the individual pixels just blend in too much.

You should also give each pixel a different shade depending on the angle.

What exactly do you mean a different shade depending on the angle?

I have a depth shading system in place, the further it is the darker it is, but I don’t get what you mean by the angle shading.

The different colored walls is a good idea though.

It will help you debug your code if the walls appear differently depending on what angle you look at them from.

Oh, thanks I figured it out. What happened was that I was raycasting on the wrong plane. I was going vertical to the minimap instead of horizontal. Now my raycasts happen on the same plane.

1 Like