373 lines
9.4 KiB
Lua
373 lines
9.4 KiB
Lua
-- LoadingScreen3D.lua --
|
|
-- Written by Kip Turner, copyright ROBLOX 2016 --
|
|
|
|
local GUI_DISTANCE_FROM_CAMERA = 6
|
|
local VERTICAL_SCREEN_PERCENT = 1/3
|
|
local HORIZONTAL_SCREEN_PERCENT = 1/3
|
|
local SECOND_TO_FADE = 2.5
|
|
local ROTATIONS_PER_SECOND = 0.5
|
|
local TEXT_SCROLL_SPEED = 25
|
|
|
|
local CoreGui = game:GetService('CoreGui')
|
|
local RunService = game:GetService('RunService')
|
|
local MarketPlaceService = game:GetService('MarketplaceService')
|
|
local UserInputService = game:GetService('UserInputService')
|
|
local ReplicatedFirst = game:GetService('ReplicatedFirst')
|
|
local GuiService = game:GetService('GuiService')
|
|
|
|
local RobloxGui = CoreGui:WaitForChild("RobloxGui")
|
|
local Util = require(RobloxGui.Modules.Settings.Utility)
|
|
|
|
local function FadeElements(element, newValue, duration)
|
|
duration = duration or 0.5
|
|
if element == nil then return end
|
|
if element:IsA('ImageLabel') or element:IsA('ImageButton') then
|
|
Util:TweenProperty(element, 'ImageTransparency', element.ImageTransparency, newValue, duration, Util:GetEaseInOutQuad())
|
|
end
|
|
if element:IsA('GuiObject') then
|
|
Util:TweenProperty(element, 'BackgroundTransparency', element.BackgroundTransparency, newValue, duration, Util:GetEaseInOutQuad())
|
|
end
|
|
if element:IsA('TextLabel') or element:IsA('TextBox') or element:IsA('TextButton') then
|
|
Util:TweenProperty(element, 'TextTransparency', element.TextTransparency, newValue, duration, Util:GetEaseInOutQuad())
|
|
end
|
|
for _, child in pairs(element:GetChildren()) do
|
|
FadeElements(child, newValue, duration)
|
|
end
|
|
end
|
|
|
|
|
|
local GameInfoProvider = {}
|
|
do
|
|
local LoadingFinishedSignal = Instance.new('BindableEvent')
|
|
GameInfoProvider.Finished = false
|
|
GameInfoProvider.GameAssetInfo = nil
|
|
GameInfoProvider.LoadingFinishedEvent = LoadingFinishedSignal.Event
|
|
|
|
function GameInfoProvider:GetGameName()
|
|
if self.GameAssetInfo ~= nil then
|
|
return self.GameAssetInfo.Name
|
|
else
|
|
return ''
|
|
end
|
|
end
|
|
|
|
function GameInfoProvider:GetCreatorName()
|
|
if self.GameAssetInfo ~= nil then
|
|
return self.GameAssetInfo.Creator.Name
|
|
else
|
|
return ''
|
|
end
|
|
end
|
|
|
|
function GameInfoProvider:IsReady()
|
|
return self.Finished
|
|
end
|
|
|
|
function GameInfoProvider:LoadAssetsAsync()
|
|
spawn(function()
|
|
while game.PlaceId <= 0 do
|
|
wait()
|
|
end
|
|
|
|
-- load game asset info
|
|
local success, result = pcall(function()
|
|
self.GameAssetInfo = MarketPlaceService:GetProductInfo(game.PlaceId)
|
|
end)
|
|
if not success then
|
|
print("LoadingScript->GameInfoProvider:LoadAssets:", result)
|
|
end
|
|
self.Finished = true
|
|
LoadingFinishedSignal:Fire()
|
|
end)
|
|
end
|
|
end
|
|
|
|
|
|
|
|
|
|
local LoadingScreen = {}
|
|
|
|
|
|
local surfaceGuiAdorn = Util:Create'Part'
|
|
{
|
|
Name = "LoadingGui";
|
|
Transparency = 1;
|
|
CanCollide = false;
|
|
Anchored = true;
|
|
Archivable = false;
|
|
FormFactor = Enum.FormFactor.Custom;
|
|
RobloxLocked = true;
|
|
Parent = RobloxGui;
|
|
}
|
|
|
|
local loadingSurfaceGui = Util:Create'SurfaceGui'
|
|
{
|
|
Name = "LoadingSurfaceGui";
|
|
Adornee = surfaceGuiAdorn;
|
|
ToolPunchThroughDistance = 1000;
|
|
CanvasSize = Vector2.new(500, 500);
|
|
Archivable = false;
|
|
Parent = CoreGui;
|
|
}
|
|
|
|
local backgroundImage = Util:Create'ImageLabel'
|
|
{
|
|
Name = 'LoadingBackground';
|
|
Size = UDim2.new(1,0,1,0);
|
|
Image = 'rbxasset://textures/ui/LoadingScreen/BackgroundLight.png';
|
|
ScaleType = Enum.ScaleType.Slice;
|
|
SliceCenter = Rect.new(70,70,110,110);
|
|
BackgroundTransparency = 1;
|
|
Parent = loadingSurfaceGui;
|
|
}
|
|
|
|
local spinnerRotation = 0
|
|
local spinnerImage = Util:Create'ImageLabel'
|
|
{
|
|
Name = 'Spinner';
|
|
Size = UDim2.new(0.25,0,0.25,0);
|
|
Position = UDim2.new(0.5 - (0.25/2), 0, 0.45 - (0.25/2), 0);
|
|
Image = 'rbxasset://textures/ui/LoadingScreen/LoadingSpinner.png';
|
|
BackgroundTransparency = 1;
|
|
Parent = backgroundImage;
|
|
}
|
|
|
|
|
|
local loadingText = Util:Create'TextLabel'
|
|
{
|
|
Name = 'LoadingText';
|
|
Text = 'Loading...';
|
|
BackgroundTransparency = 1;
|
|
Font = Enum.Font.SourceSans;
|
|
FontSize = Enum.FontSize.Size60;
|
|
Position = UDim2.new(0.5,0,0.2,0);
|
|
Parent = backgroundImage;
|
|
}
|
|
|
|
local gameNameText = Util:Create'TextLabel'
|
|
{
|
|
Name = 'GameNameText';
|
|
Text = '';
|
|
BackgroundTransparency = 1;
|
|
Font = Enum.Font.SourceSans;
|
|
FontSize = Enum.FontSize.Size60;
|
|
Size = UDim2.new(0.9, 0, 0.1, 0);
|
|
Position = UDim2.new(0.05,0,0.65,0);
|
|
ClipsDescendants = true;
|
|
Parent = backgroundImage;
|
|
}
|
|
|
|
|
|
local creatorTextContainer = Util:Create'Frame'
|
|
{
|
|
Name = 'CreatorTextContainer';
|
|
Size = UDim2.new(0.9, 0, 0.1, 0);
|
|
Position = UDim2.new(0.05,0,0.77,0);
|
|
BackgroundTransparency = 1;
|
|
ClipsDescendants = true;
|
|
Parent = backgroundImage;
|
|
}
|
|
|
|
local creatorTextPosition = 0
|
|
local creatorText = Util:Create'TextLabel'
|
|
{
|
|
Name = 'CreatorText';
|
|
Text = '';
|
|
BackgroundTransparency = 1;
|
|
Font = Enum.Font.SourceSans;
|
|
FontSize = Enum.FontSize.Size42;
|
|
Size = UDim2.new(1, 0, 1, 0);
|
|
Parent = creatorTextContainer;
|
|
}
|
|
|
|
|
|
local function ScreenDimsAtDepth(depth)
|
|
local camera = workspace.CurrentCamera
|
|
if camera then
|
|
local aspectRatio = camera.ViewportSize.x / camera.ViewportSize.y
|
|
local studHeight = 2 * depth * math.tan(math.rad(camera.FieldOfView/2))
|
|
local studWidth = studHeight * aspectRatio
|
|
|
|
return Vector2.new(studWidth, studHeight)
|
|
end
|
|
return Vector2.new(0,0)
|
|
end
|
|
|
|
local CleanedUp = false
|
|
local freeze = true
|
|
delay(2.5, function()
|
|
freeze = false
|
|
end)
|
|
local function UpdateLayout(delta)
|
|
local screenDims = ScreenDimsAtDepth(GUI_DISTANCE_FROM_CAMERA)
|
|
|
|
surfaceGuiAdorn.Size = Vector3.new(screenDims.x * HORIZONTAL_SCREEN_PERCENT, screenDims.y * VERTICAL_SCREEN_PERCENT, 1)
|
|
local camera = workspace.CurrentCamera
|
|
if camera then
|
|
surfaceGuiAdorn.Parent = camera
|
|
end
|
|
|
|
|
|
if creatorText.TextBounds.X < creatorTextContainer.AbsoluteSize.X then
|
|
creatorText.Position = UDim2.new(0, 0, 0, 0)
|
|
creatorText.Size = UDim2.new(1, 0, 1, 0)
|
|
elseif delta ~= nil then
|
|
creatorText.Size = UDim2.new(0, creatorText.TextBounds.X, 1, 0)
|
|
if not freeze then
|
|
local newX = (creatorTextPosition - delta * TEXT_SCROLL_SPEED)
|
|
if newX + creatorText.AbsoluteSize.X < creatorTextContainer.AbsoluteSize.X then
|
|
freeze = true
|
|
spawn(function()
|
|
Util:TweenProperty(creatorText, 'TextTransparency', creatorText.TextTransparency, 1, 1, Util:GetEaseInOutQuad())
|
|
wait(1.5)
|
|
if CleanedUp then return end
|
|
creatorTextPosition = 0
|
|
Util:TweenProperty(creatorText, 'TextTransparency', creatorText.TextTransparency, 0, 1, Util:GetEaseInOutQuad())
|
|
wait(1.5)
|
|
freeze = false
|
|
end)
|
|
else
|
|
creatorTextPosition = newX
|
|
end
|
|
end
|
|
creatorText.Position = UDim2.new(0, creatorTextPosition, 0, 0)
|
|
end
|
|
|
|
if not gameNameText.TextFits then
|
|
gameNameText.Size = UDim2.new(0.9, 0, 0.3, 0)
|
|
gameNameText.Position = UDim2.new(0.05,0,0.5,0)
|
|
gameNameText.TextScaled = true
|
|
gameNameText.TextWrapped = true
|
|
|
|
spinnerImage.Position = UDim2.new(0.5 - (0.25/2), 0, 0.225, 0)
|
|
loadingText.Position = UDim2.new(0.5,0,0.15,0)
|
|
end
|
|
|
|
end
|
|
|
|
local CameraChangedConn = nil
|
|
local WorkspaceChangedConn = nil
|
|
|
|
local function CleanUp()
|
|
if CleanedUp then return end
|
|
CleanedUp = true
|
|
|
|
FadeElements(loadingSurfaceGui, 1, SECOND_TO_FADE)
|
|
wait(SECOND_TO_FADE)
|
|
|
|
RunService:UnbindFromRenderStep("LoadingGui3D")
|
|
surfaceGuiAdorn.Parent = nil
|
|
if CameraChangedConn then
|
|
CameraChangedConn:disconnect()
|
|
CameraChangedConn = nil
|
|
end
|
|
if WorkspaceChangedConn then
|
|
WorkspaceChangedConn:disconnect()
|
|
WorkspaceChangedConn = nil
|
|
end
|
|
end
|
|
|
|
local function OnGameInfoLoaded()
|
|
local creatorName = GameInfoProvider:GetCreatorName()
|
|
if creatorName and creatorName ~= '' then
|
|
creatorName = string.format("By %s", tostring(creatorName))
|
|
end
|
|
gameNameText.Text = GameInfoProvider:GetGameName()
|
|
creatorText.Text = creatorName
|
|
end
|
|
|
|
local function OnReplicatingFinishedAsync()
|
|
local function OnGameLoaded()
|
|
CleanUp()
|
|
end
|
|
|
|
if game:IsLoaded() then
|
|
OnGameLoaded()
|
|
else
|
|
game.Loaded:wait()
|
|
OnGameLoaded()
|
|
end
|
|
end
|
|
|
|
local function OnDefaultLoadingGuiRemoved()
|
|
CleanUp()
|
|
end
|
|
|
|
|
|
local function UpdateSurfaceGuiPosition()
|
|
local camera = workspace.CurrentCamera
|
|
if camera then
|
|
local cameraCFrame = camera.CFrame
|
|
local cameraLook = cameraCFrame.lookVector
|
|
|
|
surfaceGuiAdorn.CFrame = (cameraCFrame * CFrame.Angles(0,math.pi,0)) + cameraLook * GUI_DISTANCE_FROM_CAMERA
|
|
end
|
|
end
|
|
|
|
do
|
|
local lastUpdate = tick()
|
|
RunService:BindToRenderStep("LoadingGui3D", Enum.RenderPriority.Last.Value, function()
|
|
local now = tick()
|
|
local delta = now - lastUpdate
|
|
|
|
UpdateSurfaceGuiPosition()
|
|
UpdateLayout(delta)
|
|
|
|
local rotation = delta * ROTATIONS_PER_SECOND * 360
|
|
spinnerRotation = spinnerRotation + rotation
|
|
spinnerImage.Rotation = spinnerRotation
|
|
|
|
lastUpdate = now
|
|
end)
|
|
UpdateSurfaceGuiPosition()
|
|
UpdateLayout()
|
|
|
|
local function connectCameraEvent()
|
|
if workspace.CurrentCamera then
|
|
if CameraChangedConn then
|
|
CameraChangedConn:disconnect()
|
|
end
|
|
CameraChangedConn = workspace.CurrentCamera:GetPropertyChangedSignal("CFrame"):connect(function()
|
|
UpdateSurfaceGuiPosition()
|
|
end)
|
|
end
|
|
end
|
|
WorkspaceChangedConn = workspace.Changed:connect(function(prop)
|
|
if prop == 'CurrentCamera' then
|
|
connectCameraEvent()
|
|
end
|
|
end)
|
|
|
|
connectCameraEvent()
|
|
end
|
|
|
|
GameInfoProvider:LoadAssetsAsync()
|
|
if GameInfoProvider:IsReady() then
|
|
OnGameInfoLoaded()
|
|
end
|
|
GameInfoProvider.LoadingFinishedEvent:connect(OnGameInfoLoaded)
|
|
|
|
|
|
if ReplicatedFirst:IsFinishedReplicating() then
|
|
spawn(OnReplicatingFinishedAsync)
|
|
else
|
|
ReplicatedFirst.FinishedReplicating:connect(OnReplicatingFinishedAsync)
|
|
end
|
|
|
|
if ReplicatedFirst:IsDefaultLoadingGuiRemoved() then
|
|
OnDefaultLoadingGuiRemoved()
|
|
else
|
|
ReplicatedFirst.RemoveDefaultLoadingGuiSignal:connect(OnDefaultLoadingGuiRemoved)
|
|
end
|
|
|
|
GuiService.ErrorMessageChanged:connect(function()
|
|
-- TODO
|
|
end)
|
|
|
|
GuiService.UiMessageChanged:connect(function(type, newMessage)
|
|
-- TODO
|
|
end)
|
|
|
|
return LoadingScreen
|
|
|