Book HomeActionScript: The Definitive GuideSearch this book

9.13. The Multiple-Choice Quiz Revisited

In Example 1-1, we introduced new programmers to a simple scripted movie -- a multiple-choice quiz. Let's revisit that quiz now to see how we can centralize its code.

Our new quiz's layer structure is set up exactly as before. We'll be changing the code on only the first frame, the quizEnd frame, and the buttons.

You can retrieve the .fla file for both the original and revised versions of the quiz from the online Code Depot.

9.13.1. Organizing the Quiz Code into Functions

In our first attempt at creating a multiple-choice quiz, we scattered our code around our movie. We placed the logic of our quiz in three places: on the first frame, where we initialized our quiz; on the buttons, where we tracked the user's answers; and on the quizEnd frame, where we tallied the user's score. We're now going to centralize the quiz logic by performing all of those tasks in the first frame. To initialize the quiz, we'll still use a simple series of statements as we did in the first version, but to track the user's answers and tally the user's score, we'll use two functions, answer( ) and gradeUser( ).

Example 9-10 shows the code on our quiz's first frame. This is where we'll store all the logic needed to run our quiz. Take a look at the code in its entirety, then we'll dissect it.

Example 9-10. A Multiple-Choice Quiz, Version 2

//  Stop the movie at the first question
stop ( );

//  Initialize main timeline variables
var displayTotal;         // Text field for displaying user's score
var numQuestions = 2;     // Number of quiz questions
var q1answer;             // User's answer for question1
var q2answer;             // User's answer for question2
var totalCorrect = 0;     // Number of questions answered correctly
var correctAnswer1 = 3;   // The correct choice for question 1
var correctAnswer2 = 2;   // The correct choice for question 2

//  Function to register user's answers
function answer (choice) {
  answer.currentAnswer++;
  set ("q" + answer.currentAnswer + "answer", choice);
  if (answer.currentAnswer == numQuestions) {
    gotoAndStop ("quizEnd");
  } else {
    gotoAndStop ("q" + (answer.currentAnswer + 1));
  }
}

//  Function to tally user's score
function gradeUser( ) {
  // Count how many questions user answered correctly
  for (i = 1; i <= numQuestions; i++) {
    if (eval("q" + i + "answer") == eval("correctAnswer" + i)) {
      totalCorrect++;
    }
  }

  // Show user's score in an on-screen text field
  displayTotal = totalCorrect;
}

Our first chore is to stop the movie, using the stop( ) function, so that it doesn't play through all the questions. Next, we initialize our quiz's timeline variables. You'll recognize displayTotal, totalCorrect, q1answer, and q2answer from the first version of the quiz. We've also added numQuestions (which we'll use in our answer( ) and gradeUser( ) functions) and correctAnswer1 and correctAnswer2 (which we'll use when grading the quiz).

With our variables initialized, we can create the answer( ) function, which will record the user's answers and advance the playhead to the next question. The answer( ) function expects one parameter, choice, which is the number of the user's answer for each question, so its function declaration begins like this:

function answer (choice) {

Each time an answer is given, the function increments currentAnswer, a function property that tracks the question being answered:

answer.currentAnswer++;

Next, we set the user's choice in a dynamically named timeline variable that corresponds to the question being answered. We use the value of the currentAnswer property to determine the name of our timeline variable (q1answer, q2answer, etc.):

set ("q" + answer.currentAnswer + "answer", choice);

With the user's choice stored in the appropriate variable, if we are on the last question, we go to the end of the quiz; otherwise, we go to the next question, which is at the frame labeled "q" + (answer.currentAnswer + 1):

if (answer.currentAnswer == numQuestions) {
  gotoAndStop ("quizEnd");
} else {
  gotoAndStop ("q"+ (answer.currentAnswer + 1));
}

That takes care of our question-answering logic. The answer( ) function is ready to handle answers from any question in the quiz. Now let's build the function that evaluates those answers, gradeUser( ).

The gradeUser( ) function takes no parameters. It has to compare each user answer with each correct answer and display the user's score. We handle the comparisons in a for loop -- we cycle through the loop body for the number of questions in the quiz:

for (i = 1; i <= numQuestions; i++) {

Inside the loop, a comparison expression tests the user's answers against the correct answers. Using eval( ), we dynamically retrieve the value of each user-answer variable and each correct-answer variable. If the two variables are equal, totalCorrect is incremented:

if (eval("q" + i + "answer") == eval("correctAnswer" + i)) {
  totalCorrect++;
}

After the loop finishes, totalCorrect will contain the number of questions that the user answered correctly. We display that number by setting the dynamic text field displayTotal to totalCorrect:

displayTotal = totalCorrect;

Voila! With both functions in place and our quiz variables initialized, the logic of our quiz system is complete. Notice how much easier it is to follow the quiz's operation when most of the code is contained on a single frame? All that's left is to call the functions from the appropriate points in the quiz.

9.13.1.1. Calling the quiz functions

We'll call the answer( ) function from the buttons that the user clicks to indicate answers, and we'll call the gradeUser( ) function from the quizEnd frame.

On the first answer button of frame 1, we attach the following code:

on (release) {
  answer(1);
}

On the second button, we attach this code:

on (release) {
  answer(2);
}

And on the third button, we attach this code:

on (release) {
  answer(3);
}

Then we attach the exact same code to the three buttons at frame q2. Notice how elegant this process is. With the code needed to handle each button neatly packed into a function, our button code is kept to a bare minimum. The answer and quiz progression logic is not redundantly duplicated as it was in the earlier version of the quiz. Furthermore, creating new buttons is a simple matter of changing the number passed to the answer( ) function. We don't need to do anything special for the last question in the quiz because the answer( ) function handles that automatically.

With the button code attached, we simply need to grade the user at the end of the quiz by attaching this statement to the quizEnd frame:

gradeUser( );

Once again, by placing our grading functionality on the first frame of our movie with the other functions, we've separated the mechanics of the system from the use of the system. Hence, we don't lose those mechanics in the maze of frames and buttons that Flash movies can often become.

Test your quiz to make sure that it works. Experiment to see what happens when you add quiz questions, remove quiz questions, and change anything else that comes to mind. As we continue, think about how the techniques we learn might be applied to our quiz example. What visible features can we add to the user experience? What invisible changes can we make to the code that will make it more flexible or elegant even if the user experience is unchanged?



Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.