- Home /
how to repeat randomize questions in a list?
I have a quiz game with randomize quiestions. My problem is after the first game ended and I restarted the game, the index of random questions didn't restarted. The remaining questions is only the one it displays so the error becomes out of range because all of my questions has been used up. Like I have 15 questions prepared, then the first time I play the game it uses 10 questions and when I play again only the remaining 5 it will display. How can I make it repeat other questions to reused again?
This is my code for my Game Controller.
using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.UI; using TMPro; using UnityEngine.SceneManagement; using System.Linq;
public class lf_idGameController : MonoBehaviour {
public TMP_Text questionText;
public TMP_Text isCorrectText;
public TMP_Text scoreText;
public TMP_Text resultText;
public TMP_InputField InputTextAnswer;
public GameObject questionDisplay;
public GameObject resultDisplay;
public AudioSource BGMResult;
private lf_idAnswerData IDanswerData;
private lf_idDataController dController;
private lf_idRoundData currentRound;
private lf_idQuestionData[] questionPool;
private static List<lf_idQuestionData> unansweredQuestions;
private lf_idQuestionData currentQuestion;
bool isRoundActive;
private int qIndex;
private int playerScore;
private int qCount;
void Start () {
isCorrectText.text = "";
dController = FindObjectOfType<lf_idDataController> ();
currentRound = dController.ID_getCurrentRoundData ();
questionPool = currentRound.questions;
playerScore = 0;
qIndex = 0;
if (unansweredQuestions == null || unansweredQuestions.Count == 0) {
unansweredQuestions = questionPool.ToList<lf_idQuestionData> ();
}
ShowQuestion ();
//SetCurrentQuestion ();
isRoundActive = true;
}
private void ShowQuestion()
{
qIndex = Random.Range (0, unansweredQuestions.Count);
currentQuestion = unansweredQuestions [qIndex];
// QuestionData questionData = questionPool [qIndex];
questionText.text = currentQuestion.questionText;
unansweredQuestions.RemoveAt (qIndex);
}
public void AnswerButtonClicked(string answerText)
{
if (answerText == currentQuestion.answer.answerText ) {
playerScore += currentRound.points;
scoreText.text = "Score: " + playerScore.ToString ();
StartCoroutine ("Correct");
}else{
StartCoroutine ("Wrong");
}
InputTextAnswer.text = "";
if (playerScore >= 1 && playerScore <=5) {
resultText.text = "Result: GOOD!";
BGMResult.clip = Resources.Load<AudioClip> ("Sounds/ResultPassed");
} else if (playerScore >= 6 && playerScore <= 9) {
resultText.text = "Result: VERY GOOD!";
BGMResult.clip = Resources.Load<AudioClip> ("Sounds/ResultPassed");
} else if (playerScore == 10) {
resultText.text = "Result: PERFECT!";
BGMResult.clip = Resources.Load<AudioClip> ("Sounds/ResultPassed");
} else if (playerScore == 0) {
resultText.text = "Result: REVIEW!";
BGMResult.clip = Resources.Load<AudioClip> ("Sounds/ResultFailed");
}
if (questionPool.Length > qIndex + 1) {
qIndex++;
ShowQuestion ();
if (qCount == 10)//(unansweredQuestions.Count == 0)
EndRound ();
}
}
public void EndRound()
{
isRoundActive = false;
questionDisplay.SetActive (false);
resultDisplay.SetActive (true);
}
public void ReturnToMenu()
{
SceneManager.LoadScene ("Main");
}
IEnumerator Correct()
{
qCount = qCount + 1;
isCorrectText.text = "Correct!";
yield return new WaitForSeconds (1);
isCorrectText.text = "";
}
IEnumerator Wrong()
{
qCount = qCount + 1;
isCorrectText.text = "Wrong!";
yield return new WaitForSeconds (1);
isCorrectText.text = "";
}
}
Here is the error I get.
ArgumentOutOfRangeException: Argument is out of range. Parameter name: index System.Collections.Generic.List`1[lf_idQuestionData].get_Item (Int32 index) (at /Users/builduser/buildslave/mono/build/mcs/class/corlib/System.Collections.Generic/List.cs:633) lf_idGameController.ShowQuestion () (at Assets/Script/LFQuiz/LFQuiz_ID/lf_idGameController.cs:63) lf_idGameController.AnswerButtonClicked (System.String answerText) (at Assets/Script/LFQuiz/LFQuiz_ID/lf_idGameController.cs:106) UnityEngine.Events.InvokableCall`1[System.String].Invoke (System.Object[] args) (at C:/buildslave/unity/build/Runtime/Export/UnityEvent.cs:189) UnityEngine.Events.InvokableCallList.Invoke (System.Object[] parameters) (at C:/buildslave/unity/build/Runtime/Export/UnityEvent.cs:637) UnityEngine.Events.UnityEventBase.Invoke (System.Object[] parameters) (at C:/buildslave/unity/build/Runtime/Export/UnityEvent.cs:773) UnityEngine.Events.UnityEvent`1[T0].Invoke (.T0 arg0) (at C:/buildslave/unity/build/Runtime/Export/UnityEvent_1.cs:53) TMPro.TMP_InputField.SendOnEndEdit () TMPro.TMP_InputField.DeactivateInputField () TMPro.TMP_InputField.OnDeselect (UnityEngine.EventSystems.BaseEventData eventData) UnityEngine.EventSystems.ExecuteEvents.Execute (IDeselectHandler handler, UnityEngine.EventSystems.BaseEventData eventData) (at C:/buildslave/unity/build/Extensions/guisystem/UnityEngine.UI/EventSystem/ExecuteEvents.cs:113) UnityEngine.EventSystems.ExecuteEvents.Execute[IDeselectHandler] (UnityEngine.GameObject target, UnityEngine.EventSystems.BaseEventData eventData, UnityEngine.EventSystems.EventFunction`1 functor) (at C:/buildslave/unity/build/Extensions/guisystem/UnityEngine.UI/EventSystem/ExecuteEvents.cs:261) UnityEngine.EventSystems.EventSystem:Update()
Answer by AaronBacon · Mar 02, 2019 at 02:42 PM
Store the full list of Questions in a separate list that is never edited. Then, at the beginning of a new game, set the list of questions that are used to the full list, so it will now be restored from that list, and will always have all questions on a new game.
Sorry for asking this, I get your point but I dont even know how to start. I am not that really good at scripting stuff. Btw, thanks.
Answer by sath · Mar 02, 2019 at 08:17 PM
I think your problem is that unansweredQuestions list is static.
Change
if (unansweredQuestions == null || unansweredQuestions.Count == 0) {
unansweredQuestions = questionPool.ToList<lf_idQuestionData> ();
}
To
if (unansweredQuestions.Count < questionPool.Length)
{
unansweredQuestions = questionPool.ToList<lf_idQuestionData>();
}
Answer by misher · Mar 04, 2019 at 08:23 AM
The easiest way to manage your questions is to make scriptable objects for each question, so you can easily edit them in the editor. Put them all in a resource folder or reference in a list from your game controller script. in Start() you may also copy all questions into a new dynamic "unanswered" Queue randomizing the order at that point, then you can Dequeue() questions when needed. I can provide a code sample if you are interested...
UPDATE: Here it is:
using UnityEngine;
[CreateAssetMenu(menuName ="Questions/Question", fileName ="Question")]
public class QuestionAsset : ScriptableObject
{
public int questionId;
public string questionText;
public string answerText;
// other staff...
}
using System.Collections.Generic;
using UnityEngine;
[CreateAssetMenu(menuName = "Questions/Questions collection", fileName = "Questions collection")]
public class QuestionCollectionAsset : ScriptableObject
{
public List<QuestionAsset> questionsAssetsList;
}
using System.Collections.Generic;
using UnityEngine;
public class QuestionManager : MonoBehaviour
{
public QuestionCollectionAsset questionCollection;
public Queue<QuestionAsset> randomizedQuestionsQueue;
// Start is called before the first frame update
void Start()
{
ResetQuestions();
}
public void ResetQuestions()
{
randomizedQuestionsQueue = GenerateQuestionsQueue(questionCollection);
}
private Queue<QuestionAsset> GenerateQuestionsQueue(QuestionCollectionAsset questionCollection)
{
// shuffeling temporary list
var shuffledList = new List<QuestionAsset>(questionCollection.questionsAssetsList);
for (int i = 0; i < shuffledList.Count; i++)
{
QuestionAsset temp = shuffledList[i];
int randomIndex = Random.Range(i, shuffledList.Count);
shuffledList[i] = shuffledList[randomIndex];
shuffledList[randomIndex] = temp;
}
// create a queue from shuffeled list
return new Queue<QuestionAsset>(shuffledList);
}
public void ShowNextQuestion()
{
if(randomizedQuestionsQueue != null && randomizedQuestionsQueue.Count > 0)
{
QuestionAsset q = randomizedQuestionsQueue.Dequeue();
DisplayQuestion(q);
} else
{
Debug.Log("Out of questions");
}
}
private void DisplayQuestion(QuestionAsset q)
{
Debug.Log(q.questionId + ": " + q.questionText);
}
[ContextMenu("Test")] // This allows launching the folllofing method from inspector component dropdown menu
public void Test()
{
ResetQuestions();
int numQuestions = randomizedQuestionsQueue.Count;
while (numQuestions > 0) {
ShowNextQuestion();
numQuestions--;
}
}
}
I have a set of questions in array on the persistent scene placed on a DataController gameobject. And I'd gladly want to see the script you have. Thanks.
You don't need to get your questions from persistent scene unless you are creating them dynamically, ins$$anonymous$$d, you can just reference the questions collection asset from anywhere you want
The shuffling algorithm is not the best in the world and can be improved if needed
Those are 3 scripts right? Thanks for help dude. Will try these later. And also, I manage to repeat the questions when restartin the game by simply making the question array equal to exactly ten since I only need to display ten questions. I think in order to redisplay my questions ib my script the set questions should be emptied in order to be restored again.
Yes, 3 separate scripts. The first one is to hold a single question, the second one is the collection of questions, so you can organize different set of questions (even reuse same questions in different collections), then, in the Question$$anonymous$$anager
you reference a collection of questions and create temporary list (queue) of shuffled question to then display them one by one to the user. Every time you invoke the ResetQuestions()
method you will regenerate the full queue of questions based on collection in a new randomized way.
Your answer
Follow this Question
Related Questions
A node in a childnode? 1 Answer
Multiple Cars not working 1 Answer
Distribute terrain in zones 3 Answers
Randomizing numbers on a list. 4 Answers
Random Shuffle Listing 2 Answers