Quiz Module
The Quiz module provides daily and weekly knowledge challenges with questions fetched from the Nakama backend.
Overview
| |
| Namespace | IntelliVerseX.Quiz |
| Assembly | IntelliVerseX.Quiz |
| UI Assembly | IntelliVerseX.QuizUI |
Features
- Daily Quiz - 10 questions per day, resets at midnight UTC
- Weekly Quiz - Larger quiz with weekly leaderboard
- Question Categories - Multiple choice, true/false, image-based
- Progress Tracking - Resume incomplete quizzes
- Rewards - Coins and XP for completion
Key Classes
| Class | Purpose |
IVXDailyQuizManager | Daily quiz logic |
IVXWeeklyQuizManager | Weekly quiz logic |
IVXQuizQuestion | Question data model |
IVXQuizResult | Quiz completion result |
Daily Quiz
IVXDailyQuizManager
public static class IVXDailyQuizManager
{
// State
public static bool IsAvailable { get; }
public static bool IsCompleted { get; }
public static int QuestionsAnswered { get; }
public static int TotalQuestions { get; }
public static DateTime ResetTime { get; }
// Events
public static event Action<IVXQuizQuestion> OnQuestionLoaded;
public static event Action<bool, int> OnAnswerSubmitted; // correct, coinsEarned
public static event Action<IVXQuizResult> OnQuizComplete;
// Start/Resume quiz
public static async Task<IVXQuizQuestion> StartQuizAsync();
// Submit answer
public static async Task<AnswerResult> SubmitAnswerAsync(int answerIndex);
public static async Task<AnswerResult> SubmitAnswerAsync(string answerId);
// Get next question
public static async Task<IVXQuizQuestion> GetNextQuestionAsync();
// Get results
public static async Task<IVXQuizResult> GetResultsAsync();
// Skip (with cost)
public static async Task SkipQuestionAsync();
}
Usage
using IntelliVerseX.Quiz;
public class DailyQuizController : MonoBehaviour
{
async void Start()
{
// Subscribe to events
IVXDailyQuizManager.OnQuestionLoaded += DisplayQuestion;
IVXDailyQuizManager.OnAnswerSubmitted += HandleAnswer;
IVXDailyQuizManager.OnQuizComplete += ShowResults;
// Check availability
if (IVXDailyQuizManager.IsCompleted)
{
ShowCompletedState();
return;
}
// Start or resume quiz
var firstQuestion = await IVXDailyQuizManager.StartQuizAsync();
DisplayQuestion(firstQuestion);
}
void DisplayQuestion(IVXQuizQuestion question)
{
questionText.text = question.Text;
for (int i = 0; i < question.Answers.Length; i++)
{
answerButtons[i].SetAnswer(i, question.Answers[i]);
}
if (question.HasImage)
{
questionImage.texture = await LoadImage(question.ImageUrl);
}
progressText.text = $"{IVXDailyQuizManager.QuestionsAnswered + 1}/{IVXDailyQuizManager.TotalQuestions}";
}
async void OnAnswerSelected(int index)
{
var result = await IVXDailyQuizManager.SubmitAnswerAsync(index);
if (result.IsCorrect)
{
ShowCorrectFeedback();
AddCoins(result.CoinsEarned);
}
else
{
ShowIncorrectFeedback(result.CorrectAnswerIndex);
}
// Load next or finish
await Task.Delay(1500); // Show feedback
var nextQuestion = await IVXDailyQuizManager.GetNextQuestionAsync();
if (nextQuestion != null)
{
DisplayQuestion(nextQuestion);
}
// OnQuizComplete event will fire when done
}
void ShowResults(IVXQuizResult result)
{
resultsPanel.Show();
correctCountText.text = $"{result.CorrectAnswers}/{result.TotalQuestions}";
coinsEarnedText.text = $"+{result.TotalCoinsEarned}";
accuracyText.text = $"{result.Accuracy:P0}";
}
}
Weekly Quiz
IVXWeeklyQuizManager
Similar API to daily quiz with longer format:
public static class IVXWeeklyQuizManager
{
public static bool IsAvailable { get; }
public static bool IsCompleted { get; }
public static int QuestionsAnswered { get; }
public static int TotalQuestions { get; } // Usually 50
public static DateTime WeekStartTime { get; }
public static DateTime WeekEndTime { get; }
// Same methods as DailyQuizManager
public static async Task<IVXQuizQuestion> StartQuizAsync();
public static async Task<AnswerResult> SubmitAnswerAsync(int answerIndex);
public static async Task<IVXQuizQuestion> GetNextQuestionAsync();
public static async Task<IVXQuizResult> GetResultsAsync();
// Leaderboard
public static async Task<LeaderboardEntry[]> GetLeaderboardAsync(int limit = 50);
}
Data Models
IVXQuizQuestion
public class IVXQuizQuestion
{
public string Id { get; }
public string Text { get; }
public QuestionType Type { get; }
public IVXQuizAnswer[] Answers { get; }
public bool HasImage { get; }
public string ImageUrl { get; }
public string Category { get; }
public string Difficulty { get; } // easy, medium, hard
public int TimeLimit { get; } // seconds, 0 = no limit
public int PointValue { get; }
}
public class IVXQuizAnswer
{
public string Id { get; }
public string Text { get; }
public string ImageUrl { get; }
}
public enum QuestionType
{
MultipleChoice,
TrueFalse,
ImageChoice
}
IVXQuizResult
public class IVXQuizResult
{
public int CorrectAnswers { get; }
public int TotalQuestions { get; }
public float Accuracy { get; }
public int TotalCoinsEarned { get; }
public int XPEarned { get; }
public TimeSpan TotalTime { get; }
public float AverageTimePerQuestion { get; }
public int Rank { get; } // Weekly quiz only
public bool IsPersonalBest { get; }
public string CompletionId { get; }
}
AnswerResult
public class AnswerResult
{
public bool IsCorrect { get; }
public int CorrectAnswerIndex { get; }
public string CorrectAnswerId { get; }
public int CoinsEarned { get; }
public int CurrentStreak { get; }
public string Explanation { get; } // Optional explanation
}
Question Categories
| Category | Description |
General | General knowledge |
Science | Science and nature |
History | Historical events |
Geography | World geography |
Sports | Sports trivia |
Entertainment | Movies, music, TV |
Technology | Tech and computing |
Art | Art and literature |
Rewards System
Daily Quiz Rewards
| Correct Answers | Coins | XP |
| 1-3 | 10 | 5 |
| 4-6 | 25 | 15 |
| 7-9 | 50 | 30 |
| 10 (Perfect) | 100 | 50 |
Weekly Quiz Rewards
| Rank | Coins | XP |
| 1st | 1000 | 500 |
| 2nd-3rd | 500 | 250 |
| 4th-10th | 250 | 125 |
| 11th-50th | 100 | 50 |
| Participated | 50 | 25 |
Timer Implementation
For timed questions:
public class QuizTimerUI : MonoBehaviour
{
[SerializeField] private Slider timerSlider;
[SerializeField] private TMP_Text timerText;
private float _remainingTime;
private bool _isRunning;
public void StartTimer(int seconds)
{
_remainingTime = seconds;
timerSlider.maxValue = seconds;
_isRunning = true;
}
void Update()
{
if (!_isRunning) return;
_remainingTime -= Time.deltaTime;
timerSlider.value = _remainingTime;
timerText.text = Mathf.CeilToInt(_remainingTime).ToString();
if (_remainingTime <= 0)
{
_isRunning = false;
OnTimeExpired?.Invoke();
}
}
public event Action OnTimeExpired;
}
Quiz UI Prefabs
The SDK includes ready-to-use UI prefabs:
| Prefab | Description |
IVXDailyQuizPanel | Complete daily quiz UI |
IVXWeeklyQuizPanel | Complete weekly quiz UI |
IVXQuizQuestionCard | Question display component |
IVXQuizAnswerButton | Answer button component |
IVXQuizResultsPanel | Results summary display |
IVXQuizLeaderboardPanel | Weekly leaderboard UI |
Best Practices
1. Handle Disconnection
// Quiz state is saved server-side
// Player can resume after reconnection
async void OnReconnected()
{
if (IVXDailyQuizManager.QuestionsAnswered > 0 &&
!IVXDailyQuizManager.IsCompleted)
{
ShowResumeDialog();
}
}
2. Prevent Answer Spoofing
// Answers are validated server-side
// Client receives correct answer only after submission
// Cannot cheat by inspecting question data
3. Smooth Transitions
async void ShowNextQuestion()
{
// Animate out
await questionCard.AnimateOut();
// Load next
var next = await IVXDailyQuizManager.GetNextQuestionAsync();
// Animate in
await questionCard.AnimateIn(next);
}
Testing
Test Questions
In development, use test questions:
#if UNITY_EDITOR
// Backend returns test questions in development
// Real questions only in production builds
#endif
Force Reset (Debug)
#if UNITY_EDITOR
[ContextMenu("Reset Daily Quiz")]
void DebugResetQuiz()
{
// Call debug RPC to reset quiz state
IVXNakamaManager.RpcAsync("quiz/debug_reset", new { type = "daily" });
}
#endif