Wednesday, 15 February 2012

Actionscript 3 Quiz game with XML

In this tutorial you will learn how to create an Actionscript 3 Quiz game with XML. The game is basically a sequence of questions. The player reads the question, and then chooses an answer. The game ends when all the questions are answered. The quiz is built using only Actionscript code so there will be no movie clips on the stage, or any frames on the timeline.


Actionscript 3 Quiz using XML part 1

Step 1 – Set up FLA

Open a new AS3 file with the stage dimensions 500 x 400 pixels. Set the Document class with the name: Quiz. Then create a new AS3 class file and save it with the name: Quiz. In the FLA, select Text > Font Embedding from the menu bar. In the Options tab select the uppercase, lowercase, and numeral character ranges. I have used the Calibri font, but you can use whatever font you wish. Then in the Actionscript tab select the ‘Export for Actionscript’ and click ok. This embeds the characters ranges which makes the font available to the swf file. For more information about embedding fonts, look here.


Step 2 – QuizBar class

Create a new AS3 class called QuizButton and type the following code below. This class creates a rounded rectangle shape with a text field on top. I have used the drawRoundRect() method to create the rounded rectangle, and used the Textfield class to create the text field on top of the rectangle shape. I have passed the parameter ‘txt’ which will be populated with the quiz answers.

package  {
import flash.display.Sprite;
 import flash.display.Shape;
 import flash.text.*;
 
 public class QuizButton extends Sprite {

  public function QuizButton (txt:String) {
   
   buttonMode = true;
   mouseChildren = false;
   
   var buttonBg:Shape = new Shape();
   buttonBg.graphics.beginFill(0xFF9900);
   buttonBg.graphics.drawRoundRect(0,0,200,40,30);
   buttonBg.graphics.endFill();
   addChild(buttonBg);
   
   var buttonHigh:Shape = new Shape();
   buttonHigh.graphics.beginFill(0xFFCC33, 0.3);
   buttonHigh.graphics.moveTo(0, 15);
   buttonHigh.graphics.curveTo(0, 0, 15, 0);
   buttonHigh.graphics.lineTo(180, 0);
   buttonHigh.graphics.curveTo(194, 0, 194, 15);
   buttonHigh.graphics.lineTo(0, 15);
   buttonHigh.graphics.endFill();
   buttonHigh.x = buttonHigh.y = 3;
   addChild(buttonHigh);
   
   var tf:TextField = new TextField();
   var f:TextFormat = new TextFormat()
   f.font = new Font1().fontName ;
   f.size = 18;
   f.color = 0x0000CC;
   f.align = TextFormatAlign.CENTER;
   tf.x = 5;
   tf.y = 7;
   tf.width = 192; 
   tf.embedFonts = true;
   tf.antiAliasType = AntiAliasType.ADVANCED;
   tf.defaultTextFormat = f;
   tf.selectable = false;
   tf.multiline = true;
   tf.wordWrap = true;
   tf.autoSize = TextFieldAutoSize.LEFT;
   tf.text = txt; 
   addChild(tf);
  }
 }
}


Step 3 – Set up XML

Open up your favourite text editor and enter the following XML and save it with the name quiz.xml. I have only added two question and answer sets as an example, but you can add as many questions and answers as you wish. Please note that the first answer node will have to be the correct answer.

<?xml version="1.0" encoding="utf-8" ?>
<quiz>
 <question>What was Mr. Incredibles name?</question>
 <answers>
  <answer>Bob</answer>
  <answer>Billy</answer>
  <answer>Barry</answer>
 </answers>
 
 <question>What is Dumbledore's name?</question>
 <answers>
  <answer>Albus</answer>
  <answer>Percival</answer>
  <answer>Wulfric</answer>
 </answers>
</quiz>


Step 3 – Quiz class

Open up the Quiz class add the following import statements and variables. In constructor I have created a game sprite to hold the quiz contents, and loaded the quiz.xml using the URLLoader() class.

package  { 
 import flash.display.MovieClip;
 import flash.display.Sprite;
 import flash.events.Event;
 import flash.events.MouseEvent;
 import flash.net.URLLoader;
 import flash.net.URLRequest;
 import flash.text.*;
 import org.casalib.util.ArrayUtil;
 
 public class Quiz extends MovieClip {
  
  private const NUM_QUESTIONS:uint = 3;
  
  private var questionNumber:uint = 0;
  private var correctNumber:uint = 0;
  private var correctAnswer:String;
  
  private var game:Sprite;
  private var randomAnswer:Array;
  private var xml:XML;
  
  public function Quiz() {
    game = new Sprite();
    addChild(game);

    var loader:URLLoader = new URLLoader();
    loader.addEventListener(Event.COMPLETE, xmlLoaded);
    loader.load(new URLRequest("quiz.xml"));
  }
 }
}

The xmlLoaded function takes the loaded data from e.target.data and converts it to XML to be assigned by the xml variable. The initQuestion() function also gets called.

private function xmlLoaded(e:Event):void 
{
xml = new XML(e.target.data);
initQuestion();
}

The initQuestion() function assigned the correct answer and randomises the answers to be displayed using the randomiseArray() function. The quiz question is displayed using the createTextField() function, and three buttons are added to the stage with the questionClicked mouse click event.

private function initQuestion():void 
{  
 //correct answer and randomise answers
correctAnswer = xml.answers[questionNumber].answer[0];
randomAnswer = randomiseArray( xml.answers[questionNumber].answer.children() ); 
   
 //question  
var tf:TextField = createTextField(xml.question[questionNumber], 24 , 60, 458);
game.addChild(tf);
  
 //answers  
 for (var i:uint = 0; i < NUM_QUESTIONS; i++) {
  var qButton:QuizButton  = new QuizButton(randomAnswer[i]);
  qButton.x = 157;
  qButton.y = 201 + i * (qButton.height+10);
  qButton.addEventListener(MouseEvent.CLICK, questionClicked);
  game.addChild(qButton);
 }
} 

The questionClicked function removes all the display objects in the game sprite. The correctNumber variable is incremented if the correct answer is clicked. The questionNumber variable is incremented and the initQuestion() is called. The endQuiz() function gets called if the all the questions are answered.

private function questionClicked(e:MouseEvent):void 
{
// You can also use game.removeChildren() from FP11.
 while (game.numChildren) game.removeChildAt(0);
   
 if(correctAnswer == e.target.getChildAt(e.target.numChildren - 1).text) correctNumber++;
   
 if (questionNumber == xml.answers.length()-1) {
  endQuiz();
 }else {
  questionNumber++;
  initQuestion();
 }
   
}

The endQuiz() function displays the ‘Quiz Finished’ and ‘Number of correct questions’ message using the createTextField() function and adds it to the game sprite.

private function endQuiz():void{
 var tf1:TextField = createTextField("Quiz Finished",150,110,198,35);
 game.addChild(tf1);
   
 var tf2:TextField = createTextField("Number of correct questions: " + correctNumber, 95,202,303,20);
 game.addChild(tf2);
}

These are the two utility functions. The createTextField() function is used to create a text fields and the randomiseArray() function randomises the array values. I have used the Array randomize method from CASA Lib.

private function createTextField(txt:String,xpos:uint, ypos:uint, w:uint, fSize:uint = 30, col:uint = 0x0000CC, align:String = TextFormatAlign.CENTER):TextField {
 var tf:TextField = new TextField();
 var f:TextFormat = new TextFormat();
 f.font = new Font1().fontName;
 f.size = fSize;
 f.color = col;
 f.align = align;
 tf.width = w; 
 tf.embedFonts = true;
 tf.antiAliasType = AntiAliasType.ADVANCED;
 tf.defaultTextFormat = f;
 tf.selectable = false;
 tf.multiline = true;
 tf.wordWrap = true;
 tf.autoSize = TextFieldAutoSize.LEFT;
 tf.text = txt; 
 tf.x = xpos;
 tf.y = ypos;
 return tf;
}
  
public function randomiseArray(answers:XMLList):Array {
 var rArray:Array = new Array();
 for (var i:uint = 0; i < NUM_QUESTIONS; i++) {
  rArray.push(answers[i]);
 }
 return ArrayUtil.randomize(rArray);
}

Step 4 – Export Movie 

Export movie Ctrl + Enter.




Additional effects

Here are some additional effects you can add to the Quiz. Firstly, I have added a rollover effect to the buttons by adding a mouse over and out listeners to show and hide a different colour tint. You will need to create a new private member variable called: ‘buttonRoll’ and then update the QuizButton class with the following code.

buttonRoll = new Sprite();
buttonRoll.graphics.beginFill(0x3366FF);
buttonRoll.graphics.drawRoundRect(0,0,200,40,30);
buttonRoll.graphics.endFill();
buttonRoll.visible = false;
addChild(buttonRoll);

addEventListener(MouseEvent.MOUSE_OVER, mouseHandler);
addEventListener(MouseEvent.MOUSE_OUT, mouseHandler);

private function mouseHandler(e:MouseEvent):void 
{
if (e.type == MouseEvent.MOUSE_OVER) buttonRoll.visible = true;
if (e.type == MouseEvent.MOUSE_OUT) buttonRoll.visible = false; 
}

Currently when you finish the Quiz, only the number of correct questions gets displayed. I have reused the QuizButton class to create two new buttons. The first button will allow you to play the quiz again, and the second button will show you the answers. Inside the endQuiz() function add the following code below.

var playagain: QuizButton = new QuizButton ("Play Again");
playagain.x = 119;
playagain.y = 296;
playagain.scaleX = playagain.scaleY = 0.6;
playagain.addEventListener(MouseEvent.CLICK, playagainClicked);
game.addChild(playagain);
   
var answers: QuizButton = new QuizButton ("Answers");
answers.x = 264;
answers.y = 296;
answers.scaleX = answers.scaleY = 0.6;
answers.addEventListener(MouseEvent.CLICK, answersClicked);
game.addChild(answers);

Now add the following functions. The playagainClicked() function remove everything all the stage, resets the questionNumber and correctNumber and calls the initQuestion() function to restart the quiz.

private function playagainClicked(e:MouseEvent):void{
e.target.removeEventListener(MouseEvent.CLICK, playagainClicked);
 while (game.numChildren) game.removeChildAt(0);
 questionNumber = 0;
 correctNumber = 0;
 initQuestion();
}

The answersClicked() function adds the questions and correct answers using the createTextField() function. I have again reused the QuizButton class to create a button to restart the quiz.

private function answersClicked(e:MouseEvent):void{
 e.target.removeEventListener(MouseEvent.CLICK, answersClicked);
 while (game.numChildren) game.removeChildAt(0);
  
 var answerTf:TextField = createTextField("Answers", 215,20,81,20,0x000000);
 game.addChild(answerTf);
   
 for(var i:uint = 0; i < xml.question.length(); i++){
  var questions:TextField = createTextField(xml.question[i],0,0,0,13,0x000000,TextFormatAlign.LEFT);
  questions.x = 30;
  questions.y = 80 + i * (questions.textHeight + 20); 
  questions.width = questions.textWidth;
  game.addChild(questions);
    
  var answers:TextField = createTextField(xml.answers[i].answer[0],0,0,0,15,0xFF9900,TextFormatAlign.LEFT);
  answers.x = questions.x + questions.textWidth + 10;
  answers.y = questions.y - 1;
  answers.width = answers.textWidth + 5;
  game.addChild(answers);
 }
   
 var playagain:QuizButton  = new QuizButton("Play Again");
 playagain.x = 191;
 playagain.y = 350;
 playagain.scaleX = playagain.scaleY = 0.7;
 playagain.addEventListener(MouseEvent.CLICK, playagainClicked);
 game.addChild(playagain);
}

You can download the source files here.

9 comments:

Unknown 16 March 2012 at 11:55  

how do you randomize the questions?

iliketo 16 March 2012 at 12:18  

@Unknown

One way you can do this is to add the questions into an Array and call the randomiseArray function. Then in the initQuestion() function you would reference the Array.

carlos 16 April 2012 at 09:58  

Could u post the source code pls-

because in the page the swf show blank

iliketo 16 April 2012 at 13:20  

@Carlos

The source code is all in the blog post. If you read it carefully and understand the code you should be able to get it working. Alternatively, you can help support my blog by downloading the source files, the link is supplied above.

SenatorSmurf 24 April 2012 at 15:22  

Brilliant!!

Soremite 7 May 2012 at 03:49  

I don't know where to put this code.

private function xmlLoaded(e:Event):void
{
xml = new XML(e.target.data);
initQuestion();
}

It was going great, then you just jumped and assumed we knew more than we do. I'm getting a blank screen too.

iliketo 7 May 2012 at 14:17  

@Soremite,

'I don't know where to put this code.’ - Are you joking ?!

In the previous section I added the event listener for the xmlLoaded function. Now in section you mentioned I am creating the function.

BLOGTOKOK 5 March 2014 at 07:03  

undefined method Font1. Why?
thanks for your attention

iliketo 7 March 2014 at 11:16  

@Blogtokok,

In Step1 make sure you set the class name as Font1.

  COPYRIGHT © 2014 · ILIKE2FLASH · Theme by Ourblogtemplates

Back to TOP