Re-Creating a Portal Effect,

Thanks, I can edit it keeping this in mind.

What if you use some really complicated equation to figure out where the lighting is coming from and mirror that to an invisible part which creates the same shadow

2 Likes

I’ve been working on a way to create multiple portals with that system. I think you can understand how to make multiple ones by comparing this one I made with the one already provided in the demo. Basically, it just links portals like “Portal A to B,” “Portal C to D,” and you can keep going and make more. I’ve even developed a system that only shows the nearest pair of portals, but for now, it still needs some fixes, so I won’t post that.

Remember to make Portal C and D inside the “Portals” model for it to work.

local Players = game:GetService("Players")
local Lighting = game:GetService("Lighting")
local RunService = game:GetService("RunService")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local Portal = require(ReplicatedStorage.Portal)

-- Define all four portals
local portalA = Portal.FromPart(workspace.World, workspace.Portals.PortalA, Enum.NormalId.Front)
local portalB = Portal.FromPart(workspace.World, workspace.Portals.PortalB, Enum.NormalId.Front)
local portalC = Portal.FromPart(workspace.World, workspace.Portals.PortalC, Enum.NormalId.Front)
local portalD = Portal.FromPart(workspace.World, workspace.Portals.PortalD, Enum.NormalId.Front)

-- Add skyboxes for all portals
portalA:AddSkybox(portalB:GetPart().Sky)
portalB:AddSkybox(portalA:GetPart().Sky)
portalC:AddSkybox(portalD:GetPart().Sky)
portalD:AddSkybox(portalC:GetPart().Sky)

-- Link portals in pairs (A-B and C-D)
portalA:Link(portalB)
portalB:Link(portalA)
portalC:Link(portalD)
portalD:Link(portalC)

-- World rendering and camera pass-through
local characterPortalSide = portalA
local prevPortalSide = characterPortalSide

local currentSkybox = characterPortalSide:GetPart().Sky:Clone()
currentSkybox.Parent = Lighting

local prevCamCF = workspace.CurrentCamera.CFrame
local prevCamFocus = workspace.CurrentCamera.Focus

RunService:BindToRenderStep("BeforeInput", Enum.RenderPriority.Input.Value - 1, function()
	workspace.CurrentCamera.CFrame = prevCamCF
	workspace.CurrentCamera.Focus = prevCamFocus
end)

RunService.RenderStepped:Connect(function(dt)
	local successTeleA = portalA:AttemptTeleport()
	local successTeleB = portalB:AttemptTeleport()
	local successTeleC = portalC:AttemptTeleport()
	local successTeleD = portalD:AttemptTeleport()
	
	if characterPortalSide == portalA and successTeleA then
		characterPortalSide = portalB
	elseif characterPortalSide == portalB and successTeleB then
		characterPortalSide = portalA
	elseif characterPortalSide == portalC and successTeleC then
		characterPortalSide = portalD
	elseif characterPortalSide == portalD and successTeleD then
		characterPortalSide = portalC
	end
	
	prevCamCF = workspace.CurrentCamera.CFrame
	prevCamFocus = workspace.CurrentCamera.Focus
	
	local cameraPortalSide = characterPortalSide
	
	for _, portal in pairs({portalA, portalB, portalC, portalD}) do
		local surfaceCF, surfaceSize = portal:GetSurface()
		local lp = surfaceCF:PointToObjectSpace(prevCamCF.Position)
		
		if lp.Z < 0.15 then
			continue
		end
		
		local success, cframe, focus = portal:GetCameraPassThrough()

		if success then	
			workspace.CurrentCamera.CFrame = cframe
			workspace.CurrentCamera.Focus = focus
			cameraPortalSide = portal:GetLinked()
			break
		end
	end
	
	-- Step characters and render for all portals
	portalA:StepCharacters()
	portalB:StepCharacters()
	portalC:StepCharacters()
	portalD:StepCharacters()
	
	portalA:Render()
	portalB:Render()
	portalC:Render()
	portalD:Render()
	
	if prevPortalSide ~= cameraPortalSide then
		currentSkybox:Destroy()
		currentSkybox = cameraPortalSide:GetPart().Sky:Clone()
		currentSkybox.Parent = Lighting
	end
	
	prevPortalSide = cameraPortalSide
end)

-- Physics step for all portals
local wasTouchingA = false
local wasTouchingB = false
local wasTouchingC = false
local wasTouchingD = false

RunService.Heartbeat:Connect(function()
	wasTouchingA = portalA:PhysicsStep(wasTouchingA)
	wasTouchingB = portalB:PhysicsStep(wasTouchingB)
	wasTouchingC = portalC:PhysicsStep(wasTouchingC)
	wasTouchingD = portalD:PhysicsStep(wasTouchingD)
	
	if not wasTouchingA and not wasTouchingB and not wasTouchingC and not wasTouchingD then
		portalA:SetTouching(false)
		portalB:SetTouching(false)
		portalC:SetTouching(false)
		portalD:SetTouching(false)
	else
		portalA:SetTouching(true)
		portalB:SetTouching(true)
		portalC:SetTouching(true)
		portalD:SetTouching(true)
	end
end)

-- Character rendering for all portals
local function onCharacterAdded(player, character)
	local cleanupA = portalA:WatchCharacter(player.Character)
	local cleanupB = portalB:WatchCharacter(player.Character)
	local cleanupC = portalC:WatchCharacter(player.Character)
	local cleanupD = portalD:WatchCharacter(player.Character)
	player.CharacterRemoving:Wait()
	cleanupA:Sweep()
	cleanupB:Sweep()
	cleanupC:Sweep()
	cleanupD:Sweep()
end

for _, player in pairs(Players:GetPlayers()) do
	if player.Character then
		task.spawn(onCharacterAdded, player, player.Character)
	end
	
	player.CharacterAdded:Connect(function(character)
		onCharacterAdded(player, character)
	end)
end

Players.PlayerAdded:Connect(function(player)
	player.CharacterAdded:Connect(function(character)
		onCharacterAdded(player, character)
	end)
end)

I’d suggest to not make to many portals close to eachother :smile:

1 Like