Hey everyone! This is gonna be 2nd tutorial of mine (you can check out the first one here), this time, talking about how to create your own spectate! In here, I’m going to be talking about how to make a UI for such, and how to script it, while going over how it works. Let’s dig into it!
July 2023: Thank you for all the support the past few years, whether through your appreciation in the thread, or personally inquiring me about this tutorial. After looking back, I’ve refactored some of the code a bit to make it a bit simpler! I’ve got a few more tutorials in the works, too, so stay tuned!
Part I: Making the UI!
- I like to start off with making the UI. For this tutorial, I’m thinking of a simplistic-style UI.
- So, I’m going to insert a
ScreenGui
and aFrame
inside it. - I want my frame to be a square shape, so I’ll insert a
UIAspectRatioConstraint
, and set theAspectRatio
to 1, to get the square. I’ll also resize the frame using scaling, to make sure it appears the same on all devices!
Now, I want a rounded square for this, and there are two ways we can go about this:
- Roblox has the
UICorner
element that you can parent to the frame, and adjust it’s rounding with the properties. I suggest playing around to see what you like best! - If you’re wanting to look elsewhere for options, you can also use the amazing Roundify plug-in (credits to @Stelrex!), and round the frame. As such, I’m going to set my
BorderPixel
to 0 before using the plug-in. When using it, I’ll set the Size to 8.
- I’m going to be adding an
ImageButton
into the Frame. I’ll set its anchor point to0.5, 0.5
, and it’s position to {0.5,0},{0.5,0}, so we can center the button. I’ll size it to {0.8,0},{0.8,0}. I’ll setBackgroundTransparency
to 1, and for the image, I’ll be uploading a free stock image from the website Canva. Feel free to use whatever image!
- Now, I also want to make the part so we can show the player, so I’ll make a frame, using scale positioning and sizing (parented to the ScreenGui). I’ll name this “SpectateFrame”. I’ll set it to be transparent (BackgroundTransparency = 1).
- I’m going to add a TextLabel in the center, and 3 TextButtons. One for a “left” button, one for a “right” button, and one to stop spectating. Check it out below.
- Our last thing is to set the
ResetOnSpawn
property of the ScreenGui tofalse
, andVisible
property on the SpectateFrame tofalse
.
It’s key for the scripting portions you named these right, as we’re going to be referencing them!
Part II: Scripting!
- We’re going to start off, and add a
LocalScript
, parented to our ScreenGui. - We’ll start off defining our objects with variables, which will make it easier to reference UI items.
-- Our local script!
local spectateFrame = script.Parent.SpectateFrame
local button = script.Parent.Frame.ImageButton
- I want it so if we click the button, it’ll show the spectate stuff, but click it again, it’ll go away. We can use if statements to help out. If the spectate bar isn’t opened yet when I press the button, I’ll open up the
spectateFrame
. If it already is though, I’ll close it.
-- Our local script!
local spectateFrame = script.Parent.SpectateFrame
local button = script.Parent.Frame.ImageButton
button.MouseButton1Click:Connect(function()
if spectateFrame.Visible == true then --> We're checking if the frame's already opened!
spectateFrame.Visible = false
else
spectateFrame.Visible = true
end
end)
- Now, because we’ll be cycling through players to watch for the spectate, we’re gonna create a table of players, by using the
:GetPlayers()
function. When a player joins, they’ll be added. When they leave, they’ll be taken out. We’ll make a function for this.
-- Our local script!
local spectateFrame = script.Parent.SpectateFrame
local button = script.Parent.Frame.ImageButton
local playerList = game:GetService("Players"):GetPlayers()
local function updatePlayerList() --> For readability, we'll use playerList from here on out, instead of calling the :GetPlayers() function each time
playerList = game:GetService("Players"):GetPlayers()
end
updatePlayerList() -- We'll update it right away, then when a player leaves/joins
game.Players.PlayerAdded:Connect(updatePlayerList)
game.Players.PlayerRemoving:Connect(updatePlayerList)
button.MouseButton1Click:Connect(function()
if spectateFrame.Visible == true then --> We're checking if the frame's already opened!
spectateFrame.Visible = false
else
spectateFrame.Visible = true
end
end)
- Now, we need to change the camera when the player spectates, and also cycle through the players. As such, we’re going to add 2 new variables, one to represent the camera, and another to know what “place” we’re in the table of players. We’ll set it to 1 by default. Another function will be made to change the camera to different players. We’ll also code in the LeftButton and RightButton now, so when clicked, they’ll cycle to the next player, and script the stop spectating button. As such, we’re also going to insert the updateCamera functions when spectate buttons are clicked, and when the spectate opens/closes.
-- Our local script!
local spectateFrame = script.Parent.SpectateFrame
local button = script.Parent.Frame.ImageButton
local playerList = game:GetService("Players"):GetPlayers()
local function updatePlayerList() --> For readability, we'll use playerList from here on out, instead of calling the :GetPlayers() function each time
playerList = game:GetService("Players"):GetPlayers()
end
local function updateCamera(playerSubject) -- New function! This will change the camera view to whatever player is given for the function!
pcall(function() -- To make sure it doesn't throw an error.
spectateFrame.TextLabel.Text = tostring(playerSubject)
cam.CameraSubject = playerSubject.Character
end)
end
updatePlayerList() -- We'll update it right away, then when a player leaves/joins
game.Players.PlayerAdded:Connect(updatePlayerList)
game.Players.PlayerRemoving:Connect(updatePlayerList)
button.MouseButton1Click:Connect(function()
if spectateFrame.Visible == true then --> We're checking if the frame's already opened!
spectateFrame.Visible = false
else
spectateFrame.Visible = true
end
end)
spectateFrame.LeftButton.MouseButton1Click:Connect(function()
position = position - 1
if position < 1 then
position = #playerList -- Change position to last on the list
end
updateCamera(playerList[position]) -- Changes camera to person on the list
end)
spectateFrame.RightButton.MouseButton1Click:Connect(function()
position = position + 1
if position > #playerList then
position = 1 -- Change position to last on the list
end
updateCamera(playerList[position]) -- Changes camera to person on the list
end)
spectateFrame.StopButton.MouseButton1Click:Connect(function()
spectateFrame.Visible = false
updateCamera(game.Players.LocalPlayer) -- Adding it to close so we can change back our camera!
end)
- And just like that, we’re done! Let’s test it out, and see the end result!
End Result!
I hope you find this tutorial helpful, and I wish you good luck on your ventures! Have a good day, and see you soon!