Create a Quiz Application Using AS3 Classes

The QuizApp Class (the Document Class) Part Two
Jody Hall
My interest in Flash started mostly because of a Jib-Jab cartoon ("This Land") in 2004. I'm the author of a feature I call "Mazoons," which are a combination of mazes and cartoons. In 2002, I even had a book published, "Super Silly Mazes." I'm not a professional programmer, but making my mazes interactive by programming them with Flash has become a hobby/obsession of mine in recent years. My efforts so far can be seen at http://www.mazoons.com/Flash_mazes.htm.
View all articles by Jody HallThere is a certain logic to the next, previous, and finish buttons that we need to work into the code. For example, when the user clicks the "next" button, we want the program to advance to the next question in the array, unless the current question is the last question. Also, we probably want to disallow moving to the next question until the user has supplied an answer to the current one.
Similarly, when the "previous" button is clicked, we want to display the previous question in the array, unless the current question is the very first one in the array. In this case, we won't put any check to make sure the user has answered the current question, as they will most likely move forward again and return to it.
Finally, when the "finish" button is clicked, which can be done at any time, we want the program to go ahead and compute the score, but only if all of the questions have been answered. If that's the case, then it will go ahead and compute their score, and display the results.
All this logic will be programmed into the buttons; we just have to translate the above into Actionscript. But first, it would be nice if we had something like a text box where we could display messages to the user. If the "next" button is clicked, and the user hasn't answered the current question, we could use this message box to tell them so. Similarly, any other messages we need to get across could be diplayed in this box. We could also use it to display the final results.
So, once again, we will import the TextField and TextFieldAutoSize classes:
import flash.text.TextField;
import flash.text.TextFieldAutoSize;
Add the following command to the constructor, just after createButtons but before firstQuestion:
createStatusBox();
Add the following to the variables list:
private var status:TextField;
Now we're all set to write the createStatusBox function. Add in this function just after the createQuestions and createButtons functions, but before the firstQuestion one:
private function createStatusBox() {
status = new TextField();
status.autoSize = TextFieldAutoSize.LEFT;
status.y = stage.stageHeight - 80;
addChild(status);
}
This just creates a new TextField instance named "status," autosizes it to the left (so it'll expand to the right), and places it 80 pixels above the bottom of the stage, which will be just above the row of buttons. We're going to be calling on this textbox to display messages so often that it will be handy to make a function for that purpose, which we'll call showMessage. So add this function right after the createStatusBox one:
private function showMessage(theMessage:String) {
status.text = theMessage;
status.x = (stage.stageWidth / 2) - (status.width / 2);
}
This function accepts a String as a parameter, which will be whatever message we want to display. It sets it as the text of the status textbox, and then the next line centers it on the screen.
Now, we've got our array of quizQuestions, we've got our buttons, and we've got a way to display messages on the screen. The next obvious step is to program the buttons. But first, let's create a function that will put all of the questions on the display list. Then, we'll create another function that will make them all invisible. After that, when we want to hide or show a question, we'll just toggle its visibility using its "visible" property. Here are the functions to add:
private function addAllQuestions() {
for(var i:int = 0; i < quizQuestions.length; i++) {
addChild(quizQuestions[i]);
}
}
private function hideAllQuestions() {
for(var i:int = 0; i < quizQuestions.length; i++) {
quizQuestions[i].visible = false;
}
}
Also, add these function calls to the constructor, after everything else but (once again) before firstQuestion:
addAllQuestions();
hideAllQuestions();
Your constructor function should now look like this:
public function QuizApp() {
quizQuestions = new Array();
createQuestions();
createButtons();
createStatusBox();
addAllQuestions();
hideAllQuestions();
firstQuestion();
}
Next, change the firstQuestion function to the following:
private function firstQuestion() {
currentQuestion = quizQuestions[0];
currentQuestion.visible = true;
}
Now, we can program the buttons. We'll replace those trace messages with the real actions we want to perform. Earlier, I described the logic. Here's the code for the "previous" button:
private function prevHandler(event:MouseEvent) {
showMessage("");
if(currentIndex > 0) {
currentQuestion.visible = false;
currentIndex--;
currentQuestion = quizQuestions[currentIndex];
currentQuestion.visible = true;
} else {
showMessage("This is the first question, there are no previous ones");
}
}
The first action here is to clear the status text box of anything that might already be in it. We'll include this as the first action of all the buttons. The if statement checks to see if the currentIndex is greater than 0. If it's not, it's 0, and we're at the first question, and there is no previous question to go to, in which case the else block takes over, and our custom "showMessage" function is employed to tell the user that.
But if currentIndex is greater than 0, we first hide currentQuestion, take away one from the currentIndex, then set currentQuestion equal to quizQuestions[currentIndex]. Finally, we make currentQuestion visible again. But now it's a different question than it was three lines ago; it's now the previous one in the array of questions.
Here's the code for the "next" button:
private function nextHandler(event:MouseEvent) {
showMessage("");
if (currentQuestion.userAnswer == 0) {
showMessage("Please answer the current question before continuing");
return;
}
if (currentIndex < (quizQuestions.length - 1)) {
currentQuestion.visible = false;
currentIndex++;
currentQuestion = quizQuestions[currentIndex];
currentQuestion.visible = true;
} else {
showMessage("That's all the questions! Click Finish to Score, or Previous to go back");
}
}
The logic here is similar to the last one, with a few differences. This time, the first check is to see if the user has answered the question in front of them. Remember that "userAnswer" is one of the "getter" functions built into QuizQuestion. Whenever the user answers the question, inside of QuizQuestion the variable "theUserAnswer" gets set to the value of whatever multiple choice button they clicked, and the "userAnswer" getter function returns that. Long story short, if it's still 0, they haven't answered the question yet. The return statement there causes the function to stop executing.
The next if statement checks to make sure the user is not at the last question. If that's the case, the following statements proceed to the next question, and if so, the showMessage function is called to display a message telling you so.
Here's the code for the "finish" button:
private function finishHandler(event:MouseEvent) {
showMessage("");
var finished:Boolean = true;
for (var i:int = 0; i < quizQuestions.length; i++) {
if (quizQuestions[i].userAnswer == 0) {
finished = false;
break;
}
}
if (finished) {
prevButton.visible = false;
nextButton.visible = false;
finishButton.visible = false;
hideAllQuestions();
computeScore();
} else {
showMessage("You haven't answered all of the questions");
}
}
This function first sets up a local Boolean variable called "finished," which is set to true. Next, all the questions are run through in a loop and each one tested to see if any of them haven't been answered yet. The first question that passes this if test will cause the "finished" variable to be set to false. The loop is then terminated. The next if statement uses the result of that test to see if the user really answered all the questions. If so, it hides all the buttons, hides all the questions, and makes a call to a function (which we'll write next) called computeScore. If not, it displays an appropriate message.
Now, the final thing to write is the computeScore function. First, though, we need a variable named "score" in our variables list, so add this line there:
private var score:int = 0;
Here's the code for computeScore:
private function computeScore() {
for (var i:int = 0; i < quizQuestions.length; i++) {
if (quizQuestions[i].userAnswer == quizQuestions[i].correctAnswer) {
score++;
}
}
showMessage("You answered " + score + " correct out of " + quizQuestions.length + " questions.");
}
Once again, the array of quizQuestions is run through, and each one's user answer is compared to the correct answer. If they match, the score is incremented by 1. Finally, after the loop completes, showMessage is once again called upon to display the results.
Here's the entire completed QuizApp file:
package {
import flash.display.Sprite;
import fl.controls.Button;
import flash.events.MouseEvent;
import flash.text.TextField;
import flash.text.TextFieldAutoSize;
public class QuizApp extends Sprite {
//for managing questions:
private var quizQuestions:Array;
private var currentQuestion:QuizQuestion;
private var currentIndex:int = 0;
//the buttons:
private var prevButton:Button;
private var nextButton:Button;
private var finishButton:Button;
//scoring and messages:
private var score:int = 0;
private var status:TextField;
public function QuizApp() {
quizQuestions = new Array();
createQuestions();
createButtons();
createStatusBox();
addAllQuestions();
hideAllQuestions();
firstQuestion();
}
private function createQuestions() {
quizQuestions.push(new QuizQuestion("What color is an orange?",
1,
"Orange",
"Blue",
"Purple",
"Brown"));
quizQuestions.push(new QuizQuestion("What is the shape of planet earth?",
3,
"Flat",
"Cube",
"Round",
"Shabby"));
quizQuestions.push(new QuizQuestion("Who created SpiderMan?",
2,
"Jack Kirby",
"Stan Lee and Steve Ditko",
"Stan Lee",
"Steve Ditko",
"none of the above"));
quizQuestions.push(new QuizQuestion("Who created Mad?",
2,
"Al Feldstein",
"Harvey Kurtzman",
"William M. Gaines",
"Jack Davis",
"none of the above"));
}
private function createButtons() {
var yPosition:Number = stage.stageHeight - 40;
prevButton = new Button();
prevButton.label = "Previous";
prevButton.x = 30;
prevButton.y = yPosition;
prevButton.addEventListener(MouseEvent.CLICK, prevHandler);
addChild(prevButton);
nextButton = new Button();
nextButton.label = "Next";
nextButton.x = prevButton.x + prevButton.width + 40;
nextButton.y = yPosition;
nextButton.addEventListener(MouseEvent.CLICK, nextHandler);
addChild(nextButton);
finishButton = new Button();
finishButton.label = "Finish";
finishButton.x = nextButton.x + nextButton.width + 40;
finishButton.y = yPosition;
finishButton.addEventListener(MouseEvent.CLICK, finishHandler);
addChild(finishButton);
}
private function createStatusBox() {
status = new TextField();
status.autoSize = TextFieldAutoSize.LEFT;
status.y = stage.stageHeight - 80;
addChild(status);
}
private function showMessage(theMessage:String) {
status.text = theMessage;
status.x = (stage.stageWidth / 2) - (status.width / 2);
}
private function addAllQuestions() {
for(var i:int = 0; i < quizQuestions.length; i++) {
addChild(quizQuestions[i]);
}
}
private function hideAllQuestions() {
for(var i:int = 0; i < quizQuestions.length; i++) {
quizQuestions[i].visible = false;
}
}
private function firstQuestion() {
currentQuestion = quizQuestions[0];
currentQuestion.visible = true;
}
private function prevHandler(event:MouseEvent) {
showMessage("");
if(currentIndex > 0) {
currentQuestion.visible = false;
currentIndex--;
currentQuestion = quizQuestions[currentIndex];
currentQuestion.visible = true;
} else {
showMessage("This is the first question, there are no previous ones");
}
}
private function nextHandler(event:MouseEvent) {
showMessage("");
if(currentQuestion.userAnswer == 0) {
showMessage("Please answer the current question before continuing");
return;
}
if(currentIndex < (quizQuestions.length - 1)) {
currentQuestion.visible = false;
currentIndex++;
currentQuestion = quizQuestions[currentIndex];
currentQuestion.visible = true;
} else {
showMessage("That's all the questions! Click Finish to Score, or Previous to go back");
}
}
private function finishHandler(event:MouseEvent) {
showMessage("");
var finished:Boolean = true;
for(var i:int = 0; i < quizQuestions.length; i++) {
if(quizQuestions[i].userAnswer == 0) {
finished = false;
break;
}
}
if(finished) {
prevButton.visible = false;
nextButton.visible = false;
finishButton.visible = false;
hideAllQuestions();
computeScore();
} else {
showMessage("You haven't answered all of the questions");
}
}
private function computeScore() {
for(var i:int = 0; i < quizQuestions.length; i++) {
if(quizQuestions[i].userAnswer == quizQuestions[i].correctAnswer) {
score++;
}
}
showMessage("You answered " + score + " correct out of " + quizQuestions.length + " questions.");
}
}
}
Closing Comments:
That concludes this tutorial. There are a lot of ways this application could be jazzed up, including:
Styling the text using a different font.
Making a fancier transition when changing questions (maybe fly-in, fly-out?)
Give feedback at the end as to the correct answer to each question.
Get the question and answer data from an XML file.
This application is "bare bones" and needs a lot more work. In actuality, though, it's not that the quiz itself is any big deal. It just served as a simple example application. The concept of classes and objects is the thing to take away from this. Maybe you don't want to make a quiz, but rather a slideshow, or an MP3 player, or a menu. The details may differ in what you want to make, but the underlying concepts of objects and classes are going to remain the same.
I hope you enjoyed this article. I enjoy writing these, and I've been getting a lot of good feedback from them. I'll try to keep them coming as I myself learn more.
Jody Hall
PS. See below to download the attached ZIP file.
Spread The Word
Attachments
2 Responses to "Create a Quiz Application Using AS3 Classes" 
|
said this on 14 Feb 2008 5:06:55 AM CDT
I have been struggling for a while with getting a class to 'use' another class. Everything is either too complicated or lacks any content or practical sample. Mr Hall writes - and explains - very clearly and comprehensively; I did not 'get lost' at any stage doing this tutorial (and I generally loathe tutorials). I recommend this to anyone who is trying to get to grips with AS3 Classes.
|
|
said this on 12 Mar 2008 4:44:37 PM CDT
for anyone having trouble getting this tutorial working (getting bunch of errors), read this:
http://www.actionscript.org/forums/showthread.php3?t=158139 |



Author/Admin)