Created
November 3, 2024 05:10
-
-
Save unrooot/6ada7e5eb340f5fca951b3d0ab00cbc3 to your computer and use it in GitHub Desktop.
Seamlessly play multiple VideoFrames back to back
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
--[[ | |
-- This script seamlessly* plays videos in a SurfaceGui in order. It's a bit | |
-- hacky for a few reasons (mainly Roblox limitations & random delays with | |
-- VideoFrames) but the biggest one being that Roblox only allows you to upload | |
-- 3, 30 second videos per month (...lol). | |
-- | |
-- * - technically, because of a hack (see line 99), we end each clip early by 0.05 seconds | |
-- | |
-- INSTRUCTIONS: | |
-- 1. Create a SurfaceGui somewhere | |
-- 2. Tag the SurfaceGui's parent with "VideoDisplay" (or change TAG_NAME to something else) | |
-- 3. Add VideoFrames to the SurfaceGui, with a number at the end of the name in order of how they should be played | |
-- | |
-- e.g. with the following hierarchy: | |
-- Part | |
-- SurfaceGui | |
-- VideoFrame1 | |
-- VideoFrame2 | |
-- VideoFrame3 | |
-- | |
-- The videos will play in the order of VideoFrame1 -> VideoFrame2 -> VideoFrame3 -> ... -> VideoFrame1 -> ... | |
-- | |
-- You can put as many VideoFrames as you want, but keep in mind more videos = | |
-- more memory usage. Also, Roblox currently has a limit of 2 VideoFrames | |
-- playing at the same time. | |
-- | |
-- Hopefully something like this won't be needed in the future, but for now, I hope this helps! | |
--]] | |
local CollectionService = game:GetService("CollectionService") | |
local ContentProvider = game:GetService("ContentProvider") | |
-- the name of the tag you wanna use for the video player | |
local TAG_NAME = "VideoDisplay" | |
local setupDisplays = {} | |
local function playVideos(videoDisplay) | |
task.spawn(function() | |
-- prevent the same display from running twice | |
if setupDisplays[videoDisplay] then | |
return | |
end | |
setupDisplays[videoDisplay] = true | |
local currentVideoIndex = 0 | |
local surfaceGui = videoDisplay.VideoSurfaceGui | |
local videoFrames = {} | |
-- try to preload the videos (spoiler: there's still a delay 🗿 but in theory this should help) | |
ContentProvider:PreloadAsync(surfaceGui:GetChildren()) | |
-- this assumes there are only VideoFrames in the SurfaceGui, with a number at | |
-- the end of the name in order of how they should be played | |
for _, video in surfaceGui:GetChildren() do | |
if not video:IsA("VideoFrame") then | |
continue | |
end | |
local index = tonumber(string.match(video.Name, "%d+")) | |
if not index then | |
continue | |
end | |
-- hack: force the clip to load to prevent the other delay when the | |
-- video first plays on a VideoFrame | |
video.Visible = true | |
video.ZIndex = 100 | |
video:Play() | |
repeat task.wait() until video.IsLoaded | |
video:Pause() | |
video.TimePosition = 0 | |
-- reset the video to its original state | |
video.Looped = false | |
video.Visible = false | |
video.ZIndex = index | |
table.insert(videoFrames, index, video) | |
end | |
local function playNextVideo() | |
currentVideoIndex += 1 | |
if currentVideoIndex > #videoFrames then | |
currentVideoIndex = 1 | |
end | |
local currentVideo = videoFrames[currentVideoIndex] | |
local connection | |
currentVideo:Play() | |
currentVideo.Visible = true | |
-- hack: play the next clip early, since .Ended fires late and the | |
-- TimeLength seems to be longer than the actual video, causing a | |
-- delay between clips. this fixes that | |
task.delay(currentVideo.TimeLength - 0.05, function() | |
-- hack: because of the aforementioned delay, if `currentVideo` | |
-- is the last clip, we have to hide it early because the | |
-- ZIndex is higher than the first clip. if we don't, it will | |
-- cause another delay... | |
if currentVideoIndex + 1 > #videoFrames then | |
currentVideo.Visible = false | |
end | |
playNextVideo() | |
end) | |
-- hide & reset the video when it ends | |
task.delay(currentVideo.TimeLength, function() | |
currentVideo.Visible = false | |
currentVideo:Pause() | |
currentVideo.TimePosition = 0 | |
end) | |
end | |
playNextVideo() | |
end) | |
end | |
-- set up video displays | |
for _, player in CollectionService:GetTagged(TAG_NAME) do | |
playVideos(player) | |
end | |
CollectionService:GetInstanceAddedSignal(TAG_NAME):Connect(playVideos) | |
CollectionService:GetInstanceRemovedSignal(TAG_NAME):Connect(function(videoDisplay) | |
setupDisplays[videoDisplay] = nil | |
end) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment