Screen overlay [open-source]

Hi all!

Note: I have made multiple edits to this so if you’ve seen this before, you may be viewing an older version.

GitHub link:
https://github.com/7eoeb/ScreenOverlayHandler
Use it to share your customized code (if you want) and see/use other peoples’ code!

I’ve made a ModuleScript that you can use to make a screen overlay and have a function happen in the background. I figured since I probably wasn’t going to use it, that I’d share it here for anyone to use for free.

The way it works is that obviously, you first need to require it.

AssetID (if desired but not recommended as cannot call from LocalScripts):

107300885671775

Link to asset

The code in the LocalScript I used to create the overlay:

local screenOverlayHandler = require(game.ReplicatedStorage:WaitForChild("ClientModuleScripts").Gui.ScreenOverlayHandler)

local function x()
	print("@")
end

local function setAction()
	game.Players.LocalPlayer.Character:WaitForChild("HumanoidRootPart").CFrame = CFrame.new(0, 1000, 0)
	print("Hello World!")
	task.wait(1)
	x() -- This will run! I was surprised by that
end

game.UserInputService.InputBegan:Connect(function(input, proc)
	if not proc and input.KeyCode == Enum.KeyCode.R then
		screenOverlayHandler:Create(setAction, "This is a test. As you can see you can have long text or short text.")
	end
end)

Here is a video of it in action, feel free to edit the code to your likings:

As you can see, the :Create() function of the ModuleScript takes in a function that is executed after the screen is completely covered. This is good for things like teleportations and cutscenes. There is also a textLabel that is made which can display input text (inputting text is optional).

If you don’t input a function, it wont error and the text and all will still run. But why would you not input a function-- that is the entire point of this model: to cover up the screen while stuff happens in the background.

The only credit I’m asking for is for you to reply to this post saying so. I would like to see cool things made with this!

Any comments/ideas/feedback?

Alternative Code:

local screenOverlayHandler = {}

local TweenService = game:GetService("TweenService")
local Workspace = game:GetService("Workspace")
local cam = Workspace.CurrentCamera
local rotationThread = nil
local activeAnimation = false

local openInfo = TweenInfo.new(
	1.75,
	Enum.EasingStyle.Sine,
	Enum.EasingDirection.InOut,
	0,
	false,
	0
)

local particleInfo = TweenInfo.new(
	0.5,
	Enum.EasingStyle.Exponential,
	Enum.EasingDirection.InOut,
	0,
	false,
	0
)

local uiCornerInfo = TweenInfo.new(
	0.25,
	Enum.EasingStyle.Quint,
	Enum.EasingDirection.In,
	0,
	false,
	0
)

local assetInfo = TweenInfo.new(
	1,
	Enum.EasingStyle.Quart,
	Enum.EasingDirection.InOut,
	0,
	false,
	0
)

local function createParticleEffects(screenGui: ScreenGui)
	if not screenGui:IsA("ScreenGui") then return end

	local activeFrames = {}
	local maxSizePercent = 0.05
	local viewportSize = cam.ViewportSize
	local sizeValue = math.ceil(math.max(viewportSize.X * maxSizePercent, viewportSize.Y * maxSizePercent))
	local frameSize = UDim2.fromScale(sizeValue / viewportSize.X, sizeValue / viewportSize.Y)

	local xFrames = math.ceil(viewportSize.X / sizeValue) + 3
	local yFrames = math.ceil(viewportSize.Y / sizeValue) + 3

	local totalWidth = xFrames * sizeValue
	local totalHeight = yFrames * sizeValue

	local startX = (viewportSize.X / 2) - ((xFrames - 2) * sizeValue / 2) - sizeValue
	local startY = (viewportSize.Y / 2) - ((yFrames - 2) * sizeValue / 2) - sizeValue

	for x = -1, xFrames do
		activeFrames[x] = {}
		for y = -1, yFrames do
			local frame = Instance.new("Frame", screenGui)
			frame.Size = frameSize
			frame.Position = UDim2.fromScale((startX + (x * sizeValue)) / viewportSize.X, (startY + (y * sizeValue)) / viewportSize.Y)
			frame.BackgroundColor3 = Color3.fromRGB(0, 0, 0)
			frame.AnchorPoint = Vector2.new(0.5, 0.5)
			frame.Name = "TemporaryOverlayFrame"
			
			Instance.new("UIAspectRatioConstraint", frame).AspectRatio = 1

			local uiCorner = Instance.new("UICorner", frame)
			uiCorner.CornerRadius = UDim.new(0, 0)

			activeFrames[x][y] = frame
		end
	end

	task.delay(0.15, function()
		local frame = screenGui:FindFirstChild("ScreenOverlayFrame")
		if frame then
			frame:Destroy()
		end
	end)

	local minSum = (-1) - yFrames
	local maxSum = xFrames + 1

	for sum = minSum, maxSum do
		task.wait(0.015)
		for x = -1, xFrames do
			local y = x - sum
			if activeFrames[x] and activeFrames[x][y] then
				local frame = activeFrames[x][y]
				local frameTween = TweenService:Create(frame, particleInfo, {Size = UDim2.fromScale(0, 0)})
				frameTween:Play()
				frameTween.Completed:Connect(function()
					frame:Destroy()
				end)

				local uiCorner = frame:FindFirstChildOfClass("UICorner")
				if uiCorner then
					local uiCornerTween = TweenService:Create(uiCorner, uiCornerInfo, {CornerRadius = UDim.new(0.5, 0)})
					uiCornerTween:Play()
					uiCornerTween.Completed:Connect(function()
						uiCornerTween:Destroy()
					end)
				end
			end
		end
	end

	task.wait(particleInfo.Time)
	screenGui:Destroy()
	activeAnimation = false
end

function screenOverlayHandler:Create(action, message : string)
	if activeAnimation then return end
	
	if typeof(message) ~= "string" then
		message = ""
	end
	
	if typeof(action) ~= "function" then
		action = function() end
	end
	
	activeAnimation = true
	
	local function getDiagonal()
		local camViewportSize = cam.ViewportSize
		return math.sqrt((camViewportSize.X ^ 2) + (camViewportSize.Y ^ 2)) * 5
	end

	local diagonal = getDiagonal()
	local targetSize = UDim2.fromOffset(diagonal, diagonal)

	local playerGui = game:GetService("Players").LocalPlayer.PlayerGui
	local screenGui = Instance.new("ScreenGui", playerGui)
	local frame = Instance.new("Frame", screenGui)
	Instance.new("UICorner", frame).CornerRadius = UDim.new(1, 0)

	screenGui.Name = "ScreenOverlayGui"
	screenGui.IgnoreGuiInset = true

	frame.Name = "ScreenOverlayFrame"
	frame.AnchorPoint = Vector2.new(0.5, 0.5)
	frame.Size = UDim2.fromScale(0, 0)
	frame.BackgroundColor3 = Color3.fromRGB(0, 0, 0)
	frame.Position = UDim2.fromScale(0.5, 0.5)
	
	local textLabel = Instance.new("TextLabel", screenGui)
	Instance.new("UITextSizeConstraint", textLabel).MaxTextSize = math.ceil(cam.ViewportSize.Y * 0.025) + 1
	Instance.new("UIAspectRatioConstraint", textLabel).AspectRatio = 10
	textLabel.BackgroundTransparency = 1
	textLabel.AnchorPoint = Vector2.new(0.5, 0.5)
	textLabel.Size = UDim2.fromScale(1, 0.1)
	textLabel.TextTransparency = 1
	textLabel.Position = UDim2.fromScale(0.5, 0.725)
	textLabel.TextScaled = true
	textLabel.TextColor3 = Color3.fromRGB(255, 255, 255)
	textLabel.Font = Enum.Font.GothamBold
	textLabel.Name = "TemporaryTextLabel"
	textLabel.Text = message
	
	local textLabelTween = TweenService:Create(textLabel, assetInfo, {TextTransparency = 0.1, Position = UDim2.fromScale(0.5, 0.7)})
	textLabelTween:Play()
	textLabelTween.Completed:Connect(function()
		textLabelTween:Destroy()
	end)

	rotationThread = task.spawn(function()
		local image = Instance.new("ImageLabel", frame)
		image.AnchorPoint = Vector2.new(0.5, 0.5)
		image.Size = UDim2.fromScale(0, 0)
		image.Position = UDim2.fromScale(0.5, 0.5)
		image.Image = "rbxassetid://" .. 98007758191436
		image.BackgroundTransparency = 1

		local maxSizePercent = 0.15
		local viewportSize = cam.ViewportSize
		local sizeValue = math.ceil(math.max(viewportSize.X * maxSizePercent, viewportSize.Y * maxSizePercent))

		local imageTween = TweenService:Create(image, assetInfo, {Size = UDim2.fromOffset(sizeValue, sizeValue)})
		imageTween:Play()
		imageTween.Completed:Wait()
		imageTween:Destroy()

		Instance.new("UIAspectRatioConstraint", image)

		while true do
			local imageTween = TweenService:Create(image, assetInfo, {Rotation = image.Rotation - 180})
			imageTween:Play()
			imageTween.Completed:Wait()
			imageTween:Destroy()
		end
	end)

	local frameTween = TweenService:Create(frame, openInfo, {Size = targetSize})
	frameTween:Play()
	frameTween.Completed:Wait()
	frameTween:Destroy()

	action()

	task.spawn(function()
		if rotationThread then
			task.cancel(rotationThread)
		end

		local image = frame:FindFirstChildOfClass("ImageLabel")
		if image then
			local shrinkTween = TweenService:Create(image, assetInfo, {Size = UDim2.fromScale(0, 0), Rotation = math.floor(image.Rotation / 360) * 360})
			shrinkTween:Play()
			shrinkTween.Completed:Connect(function()
				shrinkTween:Destroy()
			end)
		end
		
		local textLabel = screenGui:FindFirstChildOfClass("TextLabel")
		if textLabel then
			local textLabelTween = TweenService:Create(textLabel, assetInfo, {TextTransparency = 1, Position = UDim2.fromScale(0.5, 0.675)})
			textLabelTween:Play()
			textLabelTween.Completed:Connect(function()
				textLabelTween:Destroy()
			end)
		end
		
		task.wait(assetInfo.Time)

		createParticleEffects(screenGui)
	end)
end

return screenOverlayHandler
27 Likes

this is a huge time saver now i wont have to make my own transition effect all the time
i think maybe you could add some more customization like for example maybe add a loading screen mode so the ui stays until some function or loop finishes and then the ui gets hidden so you can make a loading screen that waits until like a certain map loads or something
also you could add some like image or text that could be on the overlay/transition ui

Yes, I’m planning on doing that and publishing it!
I just need some time. I will also be changing it a bit so it looks better

1 Like

I’ve updated it with a new AssetId and video. What are your thoughts!?

1 Like

it looks really cool now!
i’ll use this module whenever im making a game that needs transitions like this

1 Like

I updated it again, probably for the last time but I hope this helps you create amazing things!

1 Like

This is what I was looking for yesterday :sneezing_face:, I’m excited to try this one :face_in_clouds:. Thanks, I really appreciate your hard work!

Wow, it looks great! I’m going to use it in the future for sure.

Thank you :smiley:

Thanks for sharing this! this is a really cool module and i played around with it a bit and made a halftone transition based on what anime adventures has (sort of)

https://gyazo.com/f0e97db7bdbc1c8aa4ab11c58cce01c0

1 Like

Can you put this source code in your GitHub repo? I’m happy to contribute to this open-source project. :slightly_smiling_face:

Edit:
I just made some improvements and added two features: :Start and :Stop. These features provide flexibility when starting and stopping under different conditions. I’ll submit these changes if you put the code on your GitHub repo or provide any other method for me to contribute. Then, you can review and decide whether to approve them or not.

Yes, that’s amazing!

I’ll setup a repository soon

2 Likes

Here is the repository:
https://github.com/7eoeb/ScreenOverlayHandler
This is my first time using GitHub so tell me if I’m doing something wrong

2 Likes

Feel free to make a PR to the repository if you not using it for a personal project and are willing to share!

1 Like

Cool, thanks! I’ll make a PR to your repo. Oh, I have a question before I push the PR. Have you heard of Rojo and Wally?

If you’ve heard of them, they could be beneficial for your package. For example, Rojo can help automate building .rbxm files, and with Wally, users can easily install your package. When there’s an update, they just need to change the version and run wally install.

I can also set it up in the PR for you, but I want to make sure you’re familiar with them first. If not, I recommend learning about Rojo and Wally before proceeding.

Please review my PR :grin:. Improvements and I added two features: :Start and :Stop for manual animation control.

Edit:
Demo

how 2 made of duration long pls