What Is This Script Doing?

Hi, I’m making a multiple choice test. I have this script, I understand what it means, but I don’t know what it’s doing in my game. How do I script the text buttons and questions so they work with the script? So far I have the questions each in different frames and the possible answers all under one screen gui, containing the script shown below.

local questions = {
  {"Red",{"Red","Orange","Yellow","Green","Blue","Purple"},1}
  {"Orange",{"Red","Orange","Yellow","Green","Blue","Purple"},2}
  {"Yellow",{"Red","Orange","Yellow","Green","Blue","Purple"},3}
  {"Green",{"Red","Orange","Yellow","Green","Blue","Purple"},4}
  {"Blue",{"Red","Orange","Yellow","Green","Blue","Purple"},5}
  {"Purple",{"Red","Orange","Yellow","Green","Blue","Purple"},6}
}

local numberCorrect = 0
local NumberOfQuestions = 6

for _ = 1,NumberOfQuestions do
	local TempQuestionNumber = math.random(1,NumberOfQuestions)
	local QuestionData = questions[TempQuestionNumber]
	local Question, Answers, CorrectAnswer = unpack(QuestionData)
	table.remove(question,TempQuestionNumber)
end



local percentCorrect = math.floor(numberCorrect/NumberOfQuestion * 100)
3 Likes

The questions, the possible answers, and the answer for each question

Also, that script doesnt seem to be bad, why did you ask what it is doing?You thought it had something unsafe?

1 Like

I know that it goes through all of the questions until all of the questions are answered, then gets the percent that was correct. So like how do I program the text buttons to work with this script, and same with the question that is being displayed? Sorry for being confusing I’ll fix my post.

Thats not what i mean!

what i mean is that you are using ( }, which would probably throw an error!You are making a table with (} and not {}, thats what i meant!

2 Likes

Alright, this is how it works:

What it does
It will iterate through the number of questions, and select a random number between 1 and the Number of Questions.
This will error, because data is going to be removed. So lets say you take question 1 as the first question, and then question 6 as the second. Because you removed a value from the table it will error, since the table doesn’t have 6 indexes anymore after the first iteration. So set the for loop as for i=1, NumberOfQuestions do , and have the math.random statement set to math.random(1,NumberOfQuestions+1-i).

After it stores the data from your randomly given index, it will unpack the data into the appropriate local variables. Then it will remove this question from the table. But this will error because the table name is questions, not question.
Then after the loop is done, local percent will be set to 0, since nothing incremented the variable numberCorrect.

What this means
Your script has no context, it parses data but does not do anything with that data. Since you’re parsing the data randomly, you should create the UI objects within each iteration or at least position them based off the iteration.
You must also create connections with events and the UI buttons.
Finally, you need to create a connection with a submit button and input event. Because the correct score will be loaded immediately after the questions are loaded.

Approach

local questions = {
  {"Red",{"Red","Orange","Yellow","Green","Blue","Purple"},1}
  {"Orange",{"Red","Orange","Yellow","Green","Blue","Purple"},2}
  {"Yellow",{"Red","Orange","Yellow","Green","Blue","Purple"},3}
  {"Green",{"Red","Orange","Yellow","Green","Blue","Purple"},4}
  {"Blue",{"Red","Orange","Yellow","Green","Blue","Purple"},5}
  {"Purple",{"Red","Orange","Yellow","Green","Blue","Purple"},6}
}

local answers={}

local numberCorrect = 0
local NumberOfQuestions = 6

for i = 1,NumberOfQuestions do
	local TempQuestionNumber = math.random(1,NumberOfQuestions+1-i)
	local QuestionData = questions[TempQuestionNumber]
	local Question, Answers, CorrectAnswer = unpack(QuestionData)
	table.remove(questions,TempQuestionNumber)
	
	-- Iterate through all UI buttons for question TempQuestionNumber and connect them to the context; change method of iteration (This will not work in your current UI context)
	for buttonNumber, button in <all buttons> do
		button.MouseButton1Click:Connect(function ()	-- Will run when a button is clicked
			answers[TempQuestionNumber]= (buttonNumber==CorrectAnswer)	-- If this button is the correct one, store a true value in answers at position TempQuestionNumber
		end)
	end
end


someSubmitButton.MouseButton1Click:Connect(function ()
	-- Increment correct Answers
	for i=1,answer in pairs(answers) do
		if answer==true then
			numberCorrect=numberCorrect+1
		end
	end
	
	local percentCorrect = math.floor(numberCorrect/NumberOfQuestion * 100)
	-- Then do something with this variable; display it
end)
2 Likes

– Iterate through all UI buttons for question TempQuestionNumber and connect them to the context; change method of iteration (This will not work in your current UI context)

So how would I change my UI so it works?

Well I don’t know what current UI structure you have. But the script you’ve provided has the context and style of a loading implementation, so I’m assuming this script is supposed to generate some portion of the UI. So you’ll need to either clone premade question templates or use Instance.new(‘’) to generate UI objects from scratch.

So are you going for a script that generates a UI from the questions table at the top of the script, or do you already have a full UI?

I already have the UI

SetUp

Behind the blue thing are all of the other colors (The blue thing is a frame that is colored in). The text buttons are all of the color choices that the player can pick from. The player clicks on the textbutton that is the correct color that is represented above. I just don’t know how to script this.

Alright cool, I’m assuming your fairly new to scripting. When you want buttons to work in the context of a script, you need to specify specific input events to run the code you want run. What you previously had, was a script parse through a small dataset and finish running in less than a minute. It had no connection or idea what the UI was, looked like, or was intended to do.

Heres something more of what you want, but it likely won’t work if different questions due to the structuring of the scripts datatable and the structuring of the UI. I suggest learning more about roblox events, methods, and functions altogether. It is really important in object oriented programming.

local questions = {
  {"Red",{"Red","Orange","Yellow","Green","Blue","Purple"},1},
  {"Orange",{"Red","Orange","Yellow","Green","Blue","Purple"},2},
  {"Yellow",{"Red","Orange","Yellow","Green","Blue","Purple"},3},
  {"Green",{"Red","Orange","Yellow","Green","Blue","Purple"},4},
  {"Blue",{"Red","Orange","Yellow","Green","Blue","Purple"},5},
  {"Purple",{"Red","Orange","Yellow","Green","Blue","Purple"},6}
}

local numberCorrect = 0
local NumberOfQuestions = 6


local function finish()
	local percentCorrect = math.floor(numberCorrect/NumberOfQuestion * 100)
	-- Still do something with this
end


local Question, Answers, CorrectAnswer
local function changeQuestion()
	-- load question
	local TempQuestionNumber = math.random(1,#questions)
	local QuestionData = questions[TempQuestionNumber]
	Question, Answers, CorrectAnswer = unpack(QuestionData)
	
	-- Set all frames visibility to false
	local UI_objs=script.Parent.Frame:GetChildren()
	for i,obj in pairs(UI_objs) do
		if (obj:IsA('Frame')) then
			obj.Visible=false
		end
	end
	
	-- Set the corresponding frame's visibility to true if it exists
	if script.Parent.Frame:FindFirstChild(Answers[CorrectAnswer]) then
		script.Parent.Frame:FindFirstChild(Answers[CorrectAnswer]).Visible=true
	end
	
	table.remove(questions,TempQuestionNumber)
end


local function answer(name) 
	if string.match(name, Answers[CorrectAnswer]) then	-- If button name contains answer than mark correct; works within this specific context, may not work for another
		numberCorrect = numberCorrect+1
	end
	
	if #questions<=0 then	-- If all questions are taken then finish test
		finish()
	else
		changeQuestion()	-- Otherwise change question
	end
end

-- Connect all buttons to answer function
local UI_objs=script.Parent.Frame:GetChildren()
for i,obj in pairs(UI_objs) do
	if (obj:IsA('TextButton')) then
		obj.MouseButton1Click:Connect(function () answer(obj.Name) end)
	end
end

changeQuestion()
1 Like

So the script works but how do I connect this to an end screen showing how many they got right?

Add onto the finish function above, its the last thing that runs. Whatever objects you want to change or be displayed, you need to change there property values.
I’m guessing you’d want to change the visibility of the test frame and then change the visibility of a score frame. Possibly compare the result with a set value to determine if they pass. Possibly set a textLabels text preoperty to the score.

Heres a good resource for learning more about UI’s, object properties, connections, functions, and handlers. I think it’d be pretty helpful in figuring out how to manipulate UI’s and objects with scripts.

1 Like

When I added some code and an end screen the whole script doesn’t work. No errors come out in the output though. Here’s what I have

local questions = {
  {"Red",{"Red","Orange","Yellow","Green","Blue","Purple"},1},
  {"Orange",{"Red","Orange","Yellow","Green","Blue","Purple"},2},
  {"Yellow",{"Red","Orange","Yellow","Green","Blue","Purple"},3},
  {"Green",{"Red","Orange","Yellow","Green","Blue","Purple"},4},
  {"Blue",{"Red","Orange","Yellow","Green","Blue","Purple"},5},
  {"Purple",{"Red","Orange","Yellow","Green","Blue","Purple"},6}
}

local numberCorrect = 0
local NumberOfQuestions = 6


local function finish()
	local percentCorrect = math.floor(numberCorrect/NumberOfQuestions * 100)
	game.Players.LocalPlayer.PlayerGui.ColorBlindLockScreenTest.Frame.Visible = false
	--I ADDED THIS
	game.Players.LocalPlayer.PlayerGui.ColorBlindLockScreenTest.TestEnd.Visible = true
	script.TestEnd.Number.text = percentCorrect
end


local Question, Answers, CorrectAnswer
local function changeQuestion()
	-- load question
	local TempQuestionNumber = math.random(1,#questions)
	local QuestionData = questions[TempQuestionNumber]
	Question, Answers, CorrectAnswer = unpack(QuestionData)
	
	-- Set all frames visibility to false
	local UI_objs=script.Parent.Frame:GetChildren()
	for i,obj in pairs(UI_objs) do
		if (obj:IsA('Frame')) then
			obj.Visible=false
		end
	end
	
	-- Set the corresponding frame's visibility to true if it exists
	if script.Parent.Frame:FindFirstChild(Answers[CorrectAnswer]) then
		script.Parent.Frame:FindFirstChild(Answers[CorrectAnswer]).Visible=true
	end
	
	table.remove(questions,TempQuestionNumber)
end


local function answer(name) 
	if string.match(name, Answers[CorrectAnswer]) then	-- If button name contains answer than mark correct; works within this specific context, may not work for another
		numberCorrect = numberCorrect+1
	end
	
	if #questions<=0 then	-- If all questions are taken then finish test
		finish()
	else
		changeQuestion()	-- Otherwise change question
	end
end

-- Connect all buttons to answer function
local UI_objs=script.Parent.Frame:GetChildren()
for i,obj in pairs(UI_objs) do
	if (obj:IsA('TextButton')) then
		obj.MouseButton1Click:Connect(function () answer(obj.Name) end)
	end
end

changeQuestion()

The Frame and Text I added (If you have any questions or want to see anything else in more detail please let me know

Thanks for all the help!

Before the frame holding all the questions was named ‘frame’, now its named test. So replace every part that say script.Parent.Frame with script.Parent.Test
Names be important

This is another method of navigation relative to the script. I see you’ve accessed the other frame from the root players which works too. You do have one error in the newly added statement too, the last line on the finish function. It should look like script.Parent.TestEnd.Number.Text
Otherwise you’re assuming the testEnd frame is under script.

1 Like

So I want to make another test using decals instead of frames colored in, and 2 of the text buttons will be taken away. Will this script (changed a little) still work for it?

Its probably just going to get a bit more complicated. What I did, was I checked the testFrame for all buttons, and connected all the buttons to change the question. If the button pressed had the color of the correct answer in its name, then it would mark it correct. So I don’t think its best to handle the multiple tests with the same code.

What I recommend doing, is making a frame for each test with two frames ‘Questions’ and ‘Answers’, and name the buttons so that 1 answer will have the same name as one question. You can then use a variation of this script above per test.

Something like:

local questions = {
  {"Red",{"Red","Orange","Yellow","Green","Blue","Purple"},1},
  {"Orange",{"Red","Orange","Yellow","Green","Blue","Purple"},2},
  {"Yellow",{"Red","Orange","Yellow","Green","Blue","Purple"},3},
  {"Green",{"Red","Orange","Yellow","Green","Blue","Purple"},4},
  {"Blue",{"Red","Orange","Yellow","Green","Blue","Purple"},5},
  {"Purple",{"Red","Orange","Yellow","Green","Blue","Purple"},6}
}

local numberCorrect = 0
local NumberOfQuestions = 6


local function finish()
	local percentCorrect = math.floor(numberCorrect/NumberOfQuestions * 100)
	script.Parent.Test.Test.Visible = false
	--I ADDED THIS
	
end


local Question, Answers, CorrectAnswer
local function changeQuestion()
	-- load question
	local TempQuestionNumber = math.random(1,#questions)
	local QuestionData = questions[TempQuestionNumber]
	Question, Answers, CorrectAnswer = unpack(QuestionData)
	
	-- Set all frames visibility to false
	local UI_objs=script.Parent.Questions:GetChildren()
	for i,obj in pairs(UI_objs) do
		if (obj:IsA('Frame')) then
			obj.Visible=false
		end
	end
	
	-- Set the corresponding frame's visibility to true if it exists
	if script.Parent.Questions:FindFirstChild(Answers[CorrectAnswer]) then
		script.Parent.Questions:FindFirstChild(Answers[CorrectAnswer]).Visible=true
	end
	
	table.remove(questions,TempQuestionNumber)
end


local function answer(name) 
	if name== Answers[CorrectAnswer] then	-- If button name equals correct answer
		numberCorrect = numberCorrect+1
	end
	
	if #questions<=0 then	-- If all questions are taken then finish test
		finish()
	else
		changeQuestion()	-- Otherwise change question
	end
end

-- Connect all buttons to answer function
local UI_objs=script.Parent.Answers:GetChildren()
for i,obj in pairs(UI_objs) do
	if (obj:IsA('TextButton')) then
		obj.MouseButton1Click:Connect(function () answer(obj.Name) end)
	end
end

changeQuestion()

So the frames or UI imagelabels you want displayed will be in Questions, and the buttons will be in answers. You can share the same end test between all tests too, or you can make each test have its on endTest frame. In any case, this will keep the script managable, and organize your UI. You can then use a seperate UI to switch between the tests.

Let me know if you have any questions

1 Like

I set it up like this is it okay?

Also for the first test I made with the colors, the test won’t show once you take it. Is there something in the script that prevents the player from taking it multiple times?

Yeah, there is. The script starts without a connection, so a button doesn’t activate the test. And once you finish a question it removes the question form the table. I modeled the script relatively similar to what you had before. If you want to restart the test there should be a new connection with some button that reactivates the test.

something like, and change the table name at the beginning of the script to const_questions, and add questions=const_questions right below the table.

local function startTest()
    questions=const_questions
    numberCorrect=0
    changeQuestion()
end
1 Like

Do you mean something like a restart button? A platform that the player steps on activates it, so I just want it so when the player steps on the platform again, it will activate it again.

Kay, you want a connection similar to this then. Or you can also use a connection script in the part that sends a signal to the player. But I think you’ll find this easier.

local players=game:GetService('Players')
local player=players.LocalPlayer

player.CharacterAdded:Connect(function (character)
    local humanoid=character:WaitForChild('Humanoid')

    humanoid.Touched:Connect(function (part)
        if part.Name=='Test Platform or whatever restarts it' then
            startTest()
        end
    end)
end)
1 Like