Quiz Demo¶
Sample scene demonstrating Daily and Weekly quiz functionality.
Scene Overview¶
Locations: - Assets/_IntelliVerseXSDK/Samples/Scenes/IVX_DailyQuiz.unity - Assets/_IntelliVerseXSDK/Samples/Scenes/IVX_WeeklyQuizTest.unity
This sample demonstrates:
- Loading quiz questions
- Answering questions
- Timer functionality
- Score calculation
- Reward claiming
Scene Hierarchy¶
Canvas
├── QuizInfoPanel
│ ├── QuizTitle
│ ├── QuestionCount
│ ├── TimeLimit
│ └── StartButton
├── QuestionPanel
│ ├── QuestionNumber
│ ├── QuestionText
│ ├── AnswerButtons (4x)
│ ├── TimerDisplay
│ └── ProgressBar
├── ResultPanel
│ ├── ScoreText
│ ├── CorrectAnswers
│ ├── RewardDisplay
│ └── ClaimRewardButton
└── LoadingOverlay
Key Components¶
QuizDemoController.cs¶
using IntelliVerseX.Quiz;
using System.Collections.Generic;
using TMPro;
using UnityEngine;
using UnityEngine.UI;
public class QuizDemoController : MonoBehaviour
{
[Header("Panels")]
[SerializeField] private GameObject _infoPanel;
[SerializeField] private GameObject _questionPanel;
[SerializeField] private GameObject _resultPanel;
[Header("Info Panel")]
[SerializeField] private TMP_Text _quizTitle;
[SerializeField] private TMP_Text _questionCountText;
[SerializeField] private Button _startButton;
[Header("Question Panel")]
[SerializeField] private TMP_Text _questionNumberText;
[SerializeField] private TMP_Text _questionText;
[SerializeField] private Button[] _answerButtons;
[SerializeField] private TMP_Text _timerText;
[SerializeField] private Slider _progressBar;
[Header("Result Panel")]
[SerializeField] private TMP_Text _scoreText;
[SerializeField] private TMP_Text _correctAnswersText;
[SerializeField] private TMP_Text _rewardText;
[SerializeField] private Button _claimButton;
private QuizSession _currentSession;
private int _currentQuestionIndex;
private float _timeRemaining;
private bool _isTimerRunning;
async void Start()
{
await LoadQuizInfo();
}
async System.Threading.Tasks.Task LoadQuizInfo()
{
var quizInfo = await IVXDailyQuizManager.Instance.GetQuizInfoAsync();
_quizTitle.text = "Daily Quiz";
_questionCountText.text = $"{quizInfo.QuestionCount} Questions";
_startButton.interactable = !quizInfo.AlreadyCompleted;
_startButton.GetComponentInChildren<TMP_Text>().text =
quizInfo.AlreadyCompleted ? "Completed Today" : "Start Quiz";
ShowPanel(_infoPanel);
}
public async void StartQuiz()
{
_currentSession = await IVXDailyQuizManager.Instance.StartQuizAsync();
_currentQuestionIndex = 0;
ShowNextQuestion();
}
void ShowNextQuestion()
{
if (_currentQuestionIndex >= _currentSession.Questions.Count)
{
ShowResults();
return;
}
var question = _currentSession.Questions[_currentQuestionIndex];
_questionNumberText.text = $"Question {_currentQuestionIndex + 1}/{_currentSession.Questions.Count}";
_questionText.text = question.Text;
// Setup answer buttons
for (int i = 0; i < _answerButtons.Length; i++)
{
if (i < question.Options.Count)
{
_answerButtons[i].gameObject.SetActive(true);
_answerButtons[i].GetComponentInChildren<TMP_Text>().text = question.Options[i];
int index = i;
_answerButtons[i].onClick.RemoveAllListeners();
_answerButtons[i].onClick.AddListener(() => SelectAnswer(index));
}
else
{
_answerButtons[i].gameObject.SetActive(false);
}
}
// Start timer
_timeRemaining = question.TimeLimit;
_isTimerRunning = true;
// Update progress
_progressBar.value = (float)_currentQuestionIndex / _currentSession.Questions.Count;
ShowPanel(_questionPanel);
}
void Update()
{
if (_isTimerRunning)
{
_timeRemaining -= Time.deltaTime;
_timerText.text = Mathf.CeilToInt(_timeRemaining).ToString();
if (_timeRemaining <= 0)
{
TimeUp();
}
}
}
async void SelectAnswer(int answerIndex)
{
_isTimerRunning = false;
// Submit answer
await IVXDailyQuizManager.Instance.SubmitAnswerAsync(
_currentSession.SessionId,
_currentQuestionIndex,
answerIndex
);
// Visual feedback
var question = _currentSession.Questions[_currentQuestionIndex];
bool isCorrect = answerIndex == question.CorrectIndex;
// Highlight correct/incorrect
_answerButtons[answerIndex].image.color = isCorrect ? Color.green : Color.red;
if (!isCorrect)
{
_answerButtons[question.CorrectIndex].image.color = Color.green;
}
// Wait and continue
await System.Threading.Tasks.Task.Delay(1500);
_currentQuestionIndex++;
ResetButtonColors();
ShowNextQuestion();
}
void TimeUp()
{
_isTimerRunning = false;
// Auto-skip with no answer
_currentQuestionIndex++;
ShowNextQuestion();
}
async void ShowResults()
{
var results = await IVXDailyQuizManager.Instance.GetResultsAsync(_currentSession.SessionId);
_scoreText.text = $"Score: {results.Score}";
_correctAnswersText.text = $"Correct: {results.CorrectCount}/{results.TotalQuestions}";
_rewardText.text = $"Reward: {results.RewardAmount} coins";
_claimButton.interactable = !results.RewardClaimed;
ShowPanel(_resultPanel);
}
public async void ClaimReward()
{
await IVXDailyQuizManager.Instance.ClaimRewardAsync(_currentSession.SessionId);
_claimButton.interactable = false;
_claimButton.GetComponentInChildren<TMP_Text>().text = "Claimed!";
}
void ShowPanel(GameObject panel)
{
_infoPanel.SetActive(panel == _infoPanel);
_questionPanel.SetActive(panel == _questionPanel);
_resultPanel.SetActive(panel == _resultPanel);
}
void ResetButtonColors()
{
foreach (var btn in _answerButtons)
{
btn.image.color = Color.white;
}
}
}
How to Use¶
Running the Sample¶
- Open
IVX_DailyQuiz.unityorIVX_WeeklyQuizTest.unity - Ensure authenticated
- Press Play
Daily Quiz Flow¶
- View quiz info (question count, status)
- Click "Start Quiz"
- Answer questions within time limit
- View results
- Claim rewards
Weekly Quiz¶
Same flow, but resets weekly and often includes: - More questions - Higher rewards - Leaderboard ranking
Quiz Types¶
Daily Quiz¶
// Check daily quiz availability
var info = await IVXDailyQuizManager.Instance.GetQuizInfoAsync();
if (!info.AlreadyCompleted)
{
var session = await IVXDailyQuizManager.Instance.StartQuizAsync();
}
Weekly Quiz¶
// Check weekly quiz availability
var info = await IVXWeeklyQuizManager.Instance.GetQuizInfoAsync();
if (info.IsActive && !info.AlreadyCompleted)
{
var session = await IVXWeeklyQuizManager.Instance.StartQuizAsync();
}
Customization¶
Custom Timer¶
[SerializeField] private float _warningThreshold = 5f;
[SerializeField] private Color _warningColor = Color.red;
void UpdateTimer()
{
_timerText.text = Mathf.CeilToInt(_timeRemaining).ToString();
if (_timeRemaining <= _warningThreshold)
{
_timerText.color = _warningColor;
// Add pulse animation
}
}
Answer Feedback¶
IEnumerator ShowAnswerFeedback(int selectedIndex, int correctIndex)
{
bool isCorrect = selectedIndex == correctIndex;
// Highlight selection
_answerButtons[selectedIndex].image.color = isCorrect ? Color.green : Color.red;
// Show correct if wrong
if (!isCorrect)
{
yield return new WaitForSeconds(0.5f);
_answerButtons[correctIndex].image.color = Color.green;
}
// Play sound
AudioManager.Play(isCorrect ? "correct" : "wrong");
yield return new WaitForSeconds(1f);
}
Server Integration¶
Quiz questions come from your Nakama backend:
// Server-side RPC
function rpcGetDailyQuiz(ctx: nkruntime.Context): string {
const userId = ctx.userId;
// Check if already completed today
const lastPlayed = getLastQuizDate(userId);
if (isToday(lastPlayed)) {
return JSON.stringify({ error: "Already completed" });
}
// Get questions
const questions = selectRandomQuestions(5);
return JSON.stringify({ questions });
}