AS3 Quiz - How do I move to the next question? - actionscript-3

using various tutorials I created the next quiz in AS3. It's dynamic and I use an important function to set up the entire thing, and a counter to manage the quiz and arrays.
After picking an answer you click on a check button and then on a "Next" button.
I'm getting no errors, however for some reason calling the setup() function isn't moving the quiz fwd.
Attached is my short code with edits of the useless stuff, I would love some suggestions.
BTW, the foreign language is Hebrew :)
var arrQuestion:Array = [ "?מיהו סטיב ג'ובס", "מהי משמעות הקיצור WWW?"];
var arrAnswers:Array = [["AOL מנכל","יור אורקל","מנכל אפל","מנכל סאן"], ["World Wide Web", "With Web Wins", "Wired Web Window", "Wap Windows War"]];
var arrCorrect:Array = [3, 1];
var btnNext:myNext = new myNext();
setup();
function setup():void {
var i:Number=0;
var thequestion_txt:TextField= new TextField;
addChild(thequestion_txt);
var feedback_txt:TextField= new TextField;
addChild(feedback_txt);
var radio1:RadioButton = new RadioButton();
var radio2:RadioButton = new RadioButton();
var radio3:RadioButton = new RadioButton();
var radio4:RadioButton = new RadioButton();
var radioGrp:RadioButtonGroup = new RadioButtonGroup("radioGrp");
addChild(radio1);
addChild(radio2);
addChild(radio3);
addChild(radio4);
radio1.label = arrAnswers[i][0];
radio1.value = 1;
//etc..
var checkButton:Button = new Button();
addChild(checkButton);
checkButton.x =230;
checkButton.y = 300;
checkButton.label = "בדוק";
checkButton.addEventListener(MouseEvent.CLICK, clickHandler);
function clickHandler(event:MouseEvent):void {
addChild(btnNext);
btnNext.x =230;
btnNext.y = 300;
if (radioGrp.selection.value == (arrCorrect[i])) {
feedback_txt.text = "!נכון מאוד";
btnNext.addEventListener(MouseEvent.CLICK, myRemove);
} else {
feedback_txt.text = "תשובה שגויה";
btnNext.addEventListener(MouseEvent.CLICK, myRemove);
}
}
function myRemove(e:MouseEvent):void {
removeChild(thequestion_txt);
removeChild(feedback_txt);
removeChild(radio1);
removeChild(radio2);
removeChild(radio3);
removeChild(radio4);
removeChild(checkButton);
removeChild(btnNext);
//chaning the counter to change the question and answers
i++;
//shouldn't the call to setting up the entire stage again be here?
//it is't working, I dont get the next question.
setup();
}
}

change:
function setup():void {
var i:Number=0;
to:
var i:Number=0;
function setup():void {
Otherwise, you're incrementing with 'i++' and calling 'setup()', which then resets 'i' to 0 again and the increment never happened.
P.S. using 'Code Sample' formatting would help the readability of your example immensely.

Related

How to get index of a Bitmap Image from bitmapdata(from an Array)?

I am wondering if i have an Array that push content that is Bitmap, how do i get index of a specific image when clicked. I tried to use indexOf but no luck, my codes are below.
Thanks for your time!
Code:
//First Part is where i add the URLRequest and add the image into contentHolder then onto Stage
function loadImage():void {
for(var i:int = 5; i < somedata.length; i++){
if(somedata[i]){
var loader:Loader = new Loader();
loader.load(new URLRequest("http://www.rentaid.info/rent/"+somedata[i]));
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, onImageLoaded);
}
}
}
function onImageLoaded(e:Event):void {
loadedArray.push(e.target.content as Bitmap);
for(var i:int = 0; i < loadedArray.length; i++){
var currentY1:int = 200;
e.currentTarget.loader.content.height =200;
e.currentTarget.loader.content.y += currentY1;
currentY1 += e.currentTarget.loader.content.height +300;
_contentHolder.mouseChildren = false; // ignore children mouseEvents
_contentHolder.mouseEnabled = true; // enable mouse on the object - normally set to true by default
_contentHolder.useHandCursor = true; // add hand cursor on mouse over
_contentHolder.buttonMode = true;
_contentHolder.addChild(loadedArray[i]);
addChild(_contentHolder);
_contentHolder.addEventListener(MouseEvent.CLICK, gotoscene);
}
}
// then the part where i try to get the index
function gotoscene(e:MouseEvent):void {
var index:Number;
index = loadedArray.indexOf(e.target);
trace(index);
}
Edit:
var viewport:Viewport = new Viewport();
viewport.y = 0;
viewport.addChild(_contentHolder);
Your first question has very simple answer:
var image:Bitmap = new Bitmap();
var images:Array = new Array(image);
for (var i:uint = 0; i < images.length; i++) {
// images[i].bitmapData is the original image in your array
// image.bitmapData is searched one
if (images[i].bitmapData == image.bitmapData) {
// found
}
}
But your problem is bigger than this. I see you keep wandering around..
You should add listener to each child, not the content holder as one. I usually don't use Loaders, but get their Bitmaps and wrap them in Sprites or something, that I add into the scene. You should store either this Sprite or your Loader into that array, not the Bitmap. Then add listener to each of them (Sprite or Loader, not Bitmap) and get the target. Depending on what you've stored in the array, you can easily get it as:
function gotoscene(e:MouseEvent):void {
var index:uint = loadedArray(indexOf(e.target));
}
But it's important to store one specific type that will actually be clickable. Don't think about the Bitmap - it's only a graphic representation, and doesn't do much in the code.
**EDIT:
Okay I'm adding the code you need but it's important to understand what you are doing and not just rely on someone else's answer :)
function onImageLoaded(e:Event):void {
var bitmap:Bitmap = e.target.content as Bitmap; // get the Bitmap
var image:Sprite = new Sprite();
image.addChild(bitmap); // wrap it inside new Sprite
// add listener to Sprite!
image.addEventListener(MouseEvent.CLICK, gotoscene);
// gets url of current image (http://website.com/images/image1.jpg)
var url:String = e.target.loaderURL;
// get only the number from that url by replacing or by some other way
// this removes the first part and results in "1.jpg"
var name:String = url.replace("http://website.com/images/image", "");
// this removes the extension and results in number only - 1, 2, 3
// it's important to change this depending on your naming convention
name = name.replace(".jpg", "");
image.name = "button" + name; // results in "button1", "button2", "button3"
// store object, name, or whatever (not really needed in your case, but commonly used)
loadedArray.push(image.name);
image.x = counter * 100; // position so you can see them, at 100, 200, 300, etc.
_contentHolder.addChild(image); // add newly created Sprite to content
}
function gotoscene(e:MouseEvent):void {
var name:String = e.target.name;
// strips down "button" from "button1", and only the number remains,
// which is 1, 2, 3, etc. the number of the scene :)
var scene:uint = name.replace("button", "");
// you're the man now :)
}

addCircle to existing movieclip, using name of clip from an array

I am currently building a project where I have a map with a number of ship and aircraft. What I am trying to achieve is to check the Line of Sight distance between them.
I have set up a LOS Calculator which checks the height of one platform and the height of a second platform then gives a response. That works fine.
I then wanted to addCircle based on the result from this calculator. So if the result was 10 it would draw a circle 10cm in radius. If the result was 100 then it would draw it at 100, you get the picture. This works.
My problem now is that I need to be able to click on one platform either before or after I have made the calculation and the .addCircle be added to that movieClip. I have set up an array to store the movieclips instance names and traced that. I have added a field on stage so that you can click on a platform and it will recognise the platform clicked. I am just lost as to how to get the circle into the movieClip that has been clicked.
I am very new to AS3 so this is starting todo my head in. Any help would be greatly appreciated.
The code I have is attached below. I hope I have inserted this properly. Thanks again
import flash.events.MouseEvent;
import flash.display.MovieClip;
stage.focus=ht1;
// creation of array containing movieclips and code that adds the clicked movieclip to Array-platformClicked
var platformArray:Array = [arunta_mc, f15_mc];
var platformClicked = [];
var selectedPlatform:MovieClip = new MovieClip();
for(var i:int = 0; i < platformArray.length; i++) {
platformArray[i].buttonMode = true;
platformArray[i].addEventListener(MouseEvent.CLICK, item_onClick);
}
function item_onClick(event:MouseEvent):void {
var selectedPlatformArray:Array = platformArray.filter(checkName);
selectedPlatform = selectedPlatformArray[0];
myText.text = event.currentTarget.name + " was clicked";
var platformClicked = String(event.currentTarget.name);
trace(platformClicked);
}
function checkName(item:MovieClip, index:int, array:Array):Boolean
{
return(item.name == platformClicked);
}
//setup of LOS Calculator code
var counter:Number=1;
operator_txt.text = "+";
ht1.restrict="-0123456789.";
ht2.restrict="-0123456789.";
var myresult:Number;
var test = [];
//start of code when equal button is pressed
equal_btn.addEventListener(MouseEvent.CLICK, equalhandler);
var newCircle:Shape = new Shape();//defines circle to be drawn
function equalhandler(event:MouseEvent):void{
newCircle.graphics.lineStyle(1, 0x000000);
newCircle.graphics.beginFill(0x435632);
newCircle.alpha = .1;
//start of result code
result_txt.text = String(int((1.23*(Math.sqrt(Number(parseFloat(ht1.text)+parseFloat(ht2.text)+""))))));
var test = String(int((1.23*(Math.sqrt(Number(parseFloat(ht1.text)+parseFloat(ht2.text)+""))))));
trace(test);
//end of result code
newCircle.graphics.drawCircle(0,0,test);//add circle based on LOS calculation
newCircle.graphics.endFill();
//var selectedPlatform:MovieClip = selectedPlatformArray[0];
selectedPlatform.addChild(newCircle);//this is where I need to add newCircle to the movieClip that is clicked
trace(selectedPlatform);
//trace(platformClicked);
}
//start of code for the clear button
clear_btn.addEventListener(MouseEvent.CLICK, clearhandler);
function clearhandler(event:MouseEvent):void{
ht1.text=ht2.text=result_txt.text="";
removeChild(newCircle);
var test = [];
}
You can use the filter() method to check each item's name, like so:
var selectedPlatformArray:Array = platformArray.filter(checkName);
and somewhere in your code, define the checkName function
function checkName(item:MovieClip, index:int, array:Array):Boolean
{
return (item.name == platformClicked);
}
selectedPlatformArray will now contain all elements that return true for the checkName function, and as long as you don't have multiple MovieClips with the same name the array should only contain one element, which you can retrieve simply by accessing the first element of the array:
var selectedPlatform:MovieClip = selectedPlatformArray[0];
Alternatively, you can also use the getChildByName() function, like so:
var selectedPlatform:MovieClip = stage.getChildByName(platformClicked);
However this depends on where the objects are added to, and if they're not all in the same container (or not added at all), then this isn't the best option. It's a quick and simple solution for small projects though.
Anyway, whatever method you use you can then easily add the circle to it in your equalHandler function as usual:
selectedPlatform.addChild(newCircle);
I'd recommend checking out the documentation for both filter() and getChildByName(), to get a better understanding of how they work, since my examples only showed how you'd use them in this specific situation.
Complete code that you should have:
import flash.events.MouseEvent;
import flash.display.MovieClip;
stage.focus=ht1;
// creation of array containing movieclips and code that adds the clicked movieclip to Array-platformClicked
var platformArray:Array = [arunta_mc, f15_mc];
var platformClicked:String = "";
var selectedPlatform:MovieClip = new MovieClip();
for(var i:int = 0; i < platformArray.length; i++) {
platformArray[i].buttonMode = true;
platformArray[i].addEventListener(MouseEvent.CLICK, item_onClick);
}
function item_onClick(event:MouseEvent):void {
var selectedPlatformArray:Array = platformArray.filter(checkName);
selectedPlatform = selectedPlatformArray[0];
myText.text = event.currentTarget.name + " was clicked";
platformClicked = String(event.currentTarget.name);
trace(platformClicked);
}
function checkName(item:MovieClip, index:int, array:Array):Boolean
{
return(item.name == platformClicked);
}
//setup of LOS Calculator code
var counter:Number=1;
operator_txt.text = "+";
ht1.restrict="-0123456789.";
ht2.restrict="-0123456789.";
var myresult:Number;
var test:String = "";
//start of code when equal button is pressed
equal_btn.addEventListener(MouseEvent.CLICK, equalhandler);
var newCircle:Shape = new Shape();//defines circle to be drawn
function equalhandler(event:MouseEvent):void{
newCircle.graphics.lineStyle(1, 0x000000);
newCircle.graphics.beginFill(0x435632);
newCircle.alpha = .1;
//start of result code
result_txt.text = String(int((1.23*(Math.sqrt(Number(parseFloat(ht1.text)+parseFloat(ht2.text)+""))))));
test = String(int((1.23*(Math.sqrt(Number(parseFloat(ht1.text)+parseFloat(ht2.text)+""))))));
trace(test);
//end of result code
newCircle.graphics.drawCircle(0,0,test);//add circle based on LOS calculation
newCircle.graphics.endFill();
//var selectedPlatform:MovieClip = selectedPlatformArray[0];
selectedPlatform.addChild(newCircle);//this is where I need to add newCircle to the movieClip that is clicked
trace(selectedPlatform);
//trace(platformClicked);
}
//start of code for the clear button
clear_btn.addEventListener(MouseEvent.CLICK, clearhandler);
function clearhandler(event:MouseEvent):void{
ht1.text=ht2.text=result_txt.text="";
selectedPlatform.removeChild(newCircle);
test = "";
}

AS3 multiple file download, check when completed?

I am looping through a small array of file names to grab from a remote server and save locally. This bit works OK.
The problem is, I need to run my next function when the downloads are complete.
Currently, my function, lets call it step2(), gets called before all the FileStreams can finish (infact, its called before that function even starts!)
I read that there is a complete event listener when using openAsynch, but I need to check that all files, e.g. 3 remote swfs) have been written and then start step2() function.
How can I go about this?
Code below:
function onPlaylistComplete(e:Event)
{
var myArray:Array = new Array();
for each(var i in list_of_files)
{
myArray.push(i);
}
for(i = 0; i < myArray.length; i++)
{
swfItem = myArray[i];
var urlString:String = site_url + myArray[i];
var urlReq:URLRequest = new URLRequest(urlString);
var urlStream:URLStream = new URLStream();
urlStream.addEventListener(Event.COMPLETE, urlStreamComplete(swfItem));
urlStream.load(urlReq);
}
function urlStreamComplete(swfItem):Function
{
var downloadSwf:Function = function (event:Event):void {
var fileData:ByteArray = new ByteArray();
var stream = event.target;
stream.readBytes(fileData, 0, stream.bytesAvailable);
try {
var f : File = File.documentsDirectory.resolvePath(swfItem);
var fs : FileStream = new FileStream();
fs.addEventListener(Event.COMPLETE, fileCompleteHandler);
fs.openAsync(f, FileMode.WRITE);
fs.writeBytes(fileData);
}
catch (err : Error) {
trace(err.message);
}
}
return downloadSwf;
}
function fileCompleteHandler(e:Event):void {
e.target.close();
}
step2(); // this seems to run before UrlStreamComplete()
}
Problem with your for loop it will never work with Async model. so better need work to with recursive function.
downloadAllFiles method second arg that is function called when all download are completed.Then you can call step2() or whatever may be.
Make sure if any problem while download file like IOErrorEvent,etc...
downloadAllFiles(myArray,function():void
{
step2();
});
function downloadAllFiles(myArray:Array, complete:Function):void
{
if(myArray && myArray.length == 0)
{
if(complete)
complete.call();
return;
}
swfItem = myArray.shift();
var urlString:String = site_url + swfItem;
var urlReq:URLRequest = new URLRequest(urlString);
var urlStream:URLStream = new URLStream();
urlStream.addEventListener(Event.COMPLETE, function (event:Event):void
{
var fileData:ByteArray = new ByteArray();
var stream = event.target;
stream.readBytes(fileData, 0, stream.bytesAvailable);
try {
var f : File = File.documentsDirectory.resolvePath(swfItem);
var fs : FileStream = new FileStream();
fs.addEventListener(Event.CLOSE, function(closeEvent:Event):void
{
downloadAllFiles(myArray,complete);
return;
});
fs.openAsync(f, FileMode.WRITE);
fs.writeBytes(fileData);
}
catch (err : Error)
{
trace(err.message);
}
});
urlStream.load(urlReq);
}
Can't you just move step2(); at the end of fileCompleteHandler function?
Because at this point step2(); is called right after going through for(i = 0; i < myArray.length; i++)
loop (Which is way to early).
What you are missing is a way to check that all files from your list are loaded, 3vliguy is right and your step2(); should be in the complete handler but, not alone, you have to also test if ALL files are loaded. you can do it in various ways, e.g. use file counter and when it is 0 (all files loaded) you will execute step2() else you will ignore it and just decrease counter.
p.s. the Raja Jaganathan way will work as well, only it is queued loading whereas with the counter you can start loading of all files at the beginning.

using XMPCore to get metadata of a file through actionscript

Is there a simple example to read/write metadata of e.g. a local .jpg?
XMPCore ActionScript Reference
Unfortunately I was not able to apply the instructions from adobe.
A simple example will help me a lot!
Thanks
I've written a small snippet on the matter. this snippet is far from being proper tested, and is most definite not written in a clear and coherent way. But for now it seems to work. I'll update as I work on it.
From http://snipplr.com/view/51037/xmp-metadata-from-jpg/
private function init(event:Event):void
{
var ldr:Loader = new Loader();
ldr.contentLoaderInfo.addEventListener(Event.COMPLETE, imgLoaded);
var s:String = "link/to/asset.jpg";
ldr.load(new URLRequest(s));
}
private function imgLoaded(e:Event):void{
var info:LoaderInfo = e.target as LoaderInfo;
var xmpXML:XML = getXMP(info.bytes);
//trace(xmpXML);
var meta:XMPMeta = new XMPMeta(xmpXML);
}
private function trim(s:String):String{
return s.replace( /^([\s|\t|\n]+)?(.*)([\s|\t|\n]+)?$/gm, "$2" );
}
private function getXMP(ba:ByteArray):XML{
var LP:ByteArray = new ByteArray();
var PACKET:ByteArray = new ByteArray();
var l:int;
ba.readBytes(LP, 2, 2);
/*
http://www.adobe.com/devnet/xmp.html
read part 3: Storage in Files.
that will explain the -2 -29 and other things you see here.
*/
l = LP.readInt() - 2 -29;
ba.readBytes(PACKET, 33, l);
var p:String = trim(""+PACKET);
var i:int = p.search('<x:xmpmeta xmlns:x="adobe:ns:meta/"');
/* Delete all in front of the XMP XML */
p = p.substr(i);
/*
For some reason this left some rubbish in front, so I'll hardcode it out for now
TODO clean up
*/
var ar:Array = p.split('<');
var s:String = "";
var q:int;
var j:int = ar.length;
for(q=1;q<j;q++){
s += '<'+ar[q];
}
i = s.search('</x:xmpmeta>');
i += ('</x:xmpmeta>').length;
s = s.slice(0,i);
/* Delete all behind the XMP XML */
return XML(s);
}

Closure problem? - passing current value of a variable

I'm trying to pass the current value of a variable when an a dynamically generated navigation 'node' is clicked. This needs to just be an integer, but it always results in the last node's value.. have tried some different methods to pass the value, a custom event listener, a setter, but I suspect it's a closure problem.. help would be appreciated ;-)
function callGrid():void {
for (var i:Number = 0; i < my_total; i++) {
var gridnode_url = my_grid[i].#gridnode;
var news_category= my_grid[i].#category;
var newstitle = my_grid[i].#newstitle;
var news_content = my_grid[i]..news_content;
var news_image = my_grid[i]..news_image;
var gridnode_loader = new Loader();
container_mc.addChild(gridnode_loader);
container_mc.mouseChildren = false;
gridnode_loader.load(new URLRequest(gridnode_url));
gridnode_loader.contentLoaderInfo.addEventListener(Event.COMPLETE, gridLoaded);
gridnode_loader.name = i;
text_container_mc = new MovieClip();
text_container_mc.x = 0;
text_container_mc.mouseEnabled = false;
var textY = text_container_mc.y = (my_gridnode_height+18)*y_counter;
addChild(text_container_mc);
var tf:TextSplash=new TextSplash(newstitle,10,0,4 );
container_mc.addChild(tf);
tf.mouseEnabled = false;
tf.height = my_gridnode_height;
text_container_mc.addChild(tf);
var text_container_mc_tween = new Tween(text_container_mc, "alpha", Strong.easeIn, 0,1,0.1, true);
gridnode_loader.x = (my_gridnode_width+5) * x_counter;
gridnode_loader.y = (my_gridnode_height+15) * y_counter;
if (x_counter+1 < columns) {
x_counter++;
} else {
x_counter = 0;
y_counter++;
}
}
}
function gridLoaded(e:Event):void {
var i:uint;
var my_gridnode:Loader = Loader(e.target.loader);
container_mc.addChild(my_gridnode);
_xmlnewstarget = my_gridnode.name;
//||||||||||||||||||||||||||||||||||||||||
//when a particular grid node is clicked I need to send the current _xmlnewstarget value to the LoadNewsContent function...
//||||||||||||| ||||||||||||||||||||||||
my_tweens[Number(my_gridnode.name)]=new Tween(my_gridnode, "alpha", Strong.easeIn, 0,1,0.1, true);
my_gridnode.contentLoaderInfo.removeEventListener(Event.COMPLETE, gridLoaded);
my_gridnode.addEventListener(MouseEvent.CLICK, loadNewsContent);
}
function loadNewsContent(e:MouseEvent):void {
createNewsContainer();
getXMLNewsTarget();
news_category = my_grid[_xmlnewstarget].#category;
var tfnews_category:TextSplash=new TextSplash(news_category,20,16,32,false,false,0xffffff );
tfnews_category.mouseEnabled = false;
newstitle = my_grid[_xmlnewstarget].#newstitle;
var tftitle:TextSplash=new TextSplash(newstitle,20,70,24,false,false,0x333333 );
news_container_mc.addChild(tftitle);
tftitle.mouseEnabled = false;
news_content = my_grid[_xmlnewstarget]..news_content;
var tfnews_content:TextSplash=new TextSplash(news_content,20,110,20,true,true,0x333333,330);
news_container_mc.addChild(tfnews_content);
tfnews_content.mouseEnabled = false;
news_image = my_grid[_xmlnewstarget].#news_image;
loadNewsImage();
addChild(tfnews_category);
addChild(tftitle);
addChild(tfnews_content);
var news_container_mc_tween = new Tween(news_container_mc, "alpha", Strong.easeIn, 0,1,0.3, true);
news_container_mc_tween.addEventListener(Event.INIT, newsContentLoaded);
}
I'm not going to try to read your code (try to work on your formatting, even if it's just indenting), but I'll provide a simplified example:
for (var i = 0; i < my_total; i++) {
var closure = function() {
// use i here
}
}
As you say, when closure is called it will contain the last value of i (which in this case would be my_total). Do this instead:
for (var i = 0; i < my_total; i++) {
(function(i) {
var closure = function() {
// use i here
}
})(i);
}
This creates another function inside the loop which "captures" the current value of i so that your closure can refer to that value.
See also How does the (function() {})() construct work and why do people use it? for further similar examples.
Umm, as mentioned above, the code is a bit dense, but I think you might have a bit of type conversion problem between string and integers, is the "last value" always 0? try making these changes and let me know how you get on.
// replace this gridnode_loader.name = i;
gridnode_loader.name = i.toString();
// explictly type this as an int
_xmlnewstarget = parseInt(my_gridnode.name);
// replace this: my_tweens[Number(my_gridnode.name)] = new Tween(......
my_tweens[parseInt(my_gridnode.name)] = new Tween();
Oh and I think it goes without saying that you should massively refactor this code block once you've got it working.
Edit: after further study I think you need this
//replace this: my_gridnode.addEventListener(MouseEvent.CLICK, loadNewsContent);
var anonHandler:Function = function(e:MouseEvent):void
{
loadNewsContent(_xmlnewstarget);
};
my_gridnode.addEventListener(MouseEvent.CLICK, anonHandler);
Where your loadNewsContent has changed arguements from (e:MouseEvent) to (id:String)
Firstly, you do not need to call addChild for the same loader twice (once in callGrid) and then in (gridLoaded). Then you can try putting inside loadNewsContent: news_category = my_grid[int(e.target.name)].#category;instead of news_category = my_grid[_xmlnewstarget].#category; As _xmlnewstarget seems to be bigger scope, which is why it is getting updated every time a load operation completes.