🖥️ Making Quest UI
🖥️ Import UI
In order to make this tutorial easier to follow I'll provide some assets to create our first UI! Please install them here and import them into your StarterGui:
https://create.roblox.com/store/asset/17477286969
👀 Example
local Players = game:GetService("Players")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local RoQuest = require(ReplicatedStorage.RoQuest).Client
type Quest = RoQuest.Quest
type QuestObjective = RoQuest.QuestObjective
local QuestStatus = RoQuest.QuestStatus
local localPlayer: Player = Players.LocalPlayer
local playerGui: PlayerGui = localPlayer:WaitForChild("PlayerGui")
local scrollingFrame: ScrollingFrame = playerGui:WaitForChild("Quests"):WaitForChild("Main"):WaitForChild("ScrollingFrame")
local template: Frame = scrollingFrame:WaitForChild("Template")
local clone = template:Clone()
template:Destroy()
template = clone
local Quests = {}
function Quests:UpdateObjective(quest: Quest, objectiveId: string, newValue: number)
local existingFrame: Frame? = scrollingFrame:FindFirstChild(quest.QuestId)
if not existingFrame then
return
end
local objectivesWindow: Frame = existingFrame.TextContainer.Objectives
local objectiveText: TextLabel? = objectivesWindow:FindFirstChild(objectiveId)
if not objectiveText then
return
end
local questObjective: QuestObjective = quest:GetQuestObjective(objectiveId)
objectiveText.Text = string.format(questObjective:GetDescription(), newValue, questObjective:GetTargetProgress())
end
function Quests:UpdateQuestFrame(quest: Quest, frame: Frame)
local textContainer: Frame = frame:WaitForChild("TextContainer")
if quest:GetQuestStatus() == QuestStatus.Completed then
textContainer.Description.Text = "Read to deliver!"
elseif quest:GetQuestStatus() == QuestStatus.Delivered then
frame:Destroy()
else
textContainer.Title.Text = quest.Name
textContainer.Description.Text = quest.Description
end
end
function Quests:UpdateInterface()
local quests: {[string]: Quest} = RoQuest:GetInProgressQuests()
for _, instance: Instance in scrollingFrame:GetChildren() do -- Delete quests that dont exit
if instance:IsA("Frame") and not quests[instance.Name] then
instance:Destroy()
end
end
for questId: string, quest: Quest in quests do
local existingFrame: Frame = scrollingFrame:FindFirstChild(questId)
if existingFrame then
self:UpdateQuestFrame(quest, existingFrame)
continue -- Quest already exists
end
local newTemplate: Frame = template:Clone()
newTemplate.Name = questId
local textContainer: Frame = newTemplate:WaitForChild("TextContainer")
textContainer.Title.Text = quest.Name
textContainer.Description.Text = quest.Description
local objectives: Frame = textContainer:WaitForChild("Objectives")
for _, questObjective: QuestObjective in quest:GetQuestObjectives() do
local objectiveTemplate: TextLabel = textContainer.Description:Clone()
objectiveTemplate.Name = questObjective:GetObjectiveId()
objectiveTemplate.TextSize = 14
objectiveTemplate.Text = string.format(questObjective:GetDescription(), questObjective:Get(), questObjective:GetTargetProgress())
objectiveTemplate.Parent = objectives
end
newTemplate.Parent = scrollingFrame
self:UpdateQuestFrame(quest, newTemplate)
end
end
RoQuest.OnStart():andThen(function()
RoQuest.OnInProgressQuestChanged:Connect(function()
Quests:UpdateInterface()
end)
RoQuest.OnQuestObjectiveChanged:Connect(function(questId: string, objectiveId: string, newValue: number)
Quests:UpdateObjective(RoQuest:GetQuest(questId), objectiveId, newValue)
end)
RoQuest.OnPlayerDataChanged:Connect(function()
Quests:UpdateInterface()
end) -- Hard reset our screens
Quests:UpdateInterface()
end)
The vast majority of this code was just actually setting up the interface UI yet the most important and relevant part is how we gained and updates the data with RoQuest so let's break it down!
📥 Getting Data
In RoQuest you can get either all quests or all the quests with a specific state. In order to display the quests that are in progress an easy way to do that would be with the following method:
local quests: {[string]: Quest} = RoQuest:GetInProgressQuests()
This will return all the quests that are currently in progress
⚡ Listening to Data
Once we have all of our data set we want to update teh UI under 2 circumstances: when either the data of the quests we're showing changes OR when the quests that are currently in progress change. To do that we can use these 3 events:
RoQuest.OnInProgressQuestChanged:Connect(function()
Quests:UpdateInterface()
end)
RoQuest.OnQuestObjectiveChanged:Connect(function(questId: string, objectiveId: string, newValue: number)
Quests:UpdateObjective(RoQuest:GetQuest(questId), objectiveId, newValue)
end)
RoQuest.OnPlayerDataChanged:Connect(function()
Quests:UpdateInterface()
end) -- Hard reset our screens
And just like we did on the server make sure that you only interact with RoQuest AFTER it loaded by using RoQuest.OnStart