Need help making a button play multiple sounds in Adobe Flash - actionscript-3

I'm having an issue trying to get my code work for my Adobe Flash project. Basically I'm trying to get a button to play a random sound everytime it's clicked which works but I can't have that same code on the same frame for a different button which is why it gives me this error.
Here is my code:
import flash.utils.Dictionary;
import flash.events.MouseEvent;
var request:URLRequest = new URLRequest("19103_b.mp3");
var ci_diese:Sound = new Sound();
ci_diese.load(request);
var request_two:URLRequest = new URLRequest("19203_b.mp3");
var d_diese:Sound = new Sound();
d_diese.load(request_two);
var request_three:URLRequest = new URLRequest("19204_b.mp3");
var f_diese:Sound = new Sound();
f_diese.load(request_three);
var play_liste = 0;
var dictSounds = new Dictionary ();
dictSounds[1] = d_diese;
dictSounds[2] = ci_diese;
dictSounds[3] = f_diese;
fireweapon_H3AR.addEventListener (MouseEvent.MOUSE_DOWN, mouseDownHandler);
function mouseDownHandler (event:MouseEvent) : void {
play_liste = Math.ceil(Math.random () *3);
dictSounds[play_liste].play ();
ready_H3AR.addEventListener (MouseEvent.MOUSE_DOWN, mouseDownHandler);
function mouseDownHandler (event:MouseEvent) : void {
play_liste = Math.ceil(Math.random () *3);
dictSounds[play_liste].play ();}
}
import flash.utils.Dictionary;
import flash.events.MouseEvent;
var request:URLRequest = new URLRequest("Readya.mp3");
var ci_diese2:Sound = new Sound();
ci_diese2.load(request);
var request_two:URLRequest = new URLRequest("Readyb.mp3");
var d_diese2:Sound = new Sound();
d_diese2.load(request_two);
var request_three:URLRequest = new URLRequest("Readyc.mp3");
var f_diese2:Sound = new Sound();
f_diese2.load(request_three);
var play_liste = 0;
var dictSounds = new Dictionary ();
dictSounds[1] = d_diese2;
dictSounds[2] = ci_diese2;
dictSounds[3] = f_diese2;
ready_H3AR.addEventListener (MouseEvent.MOUSE_DOWN, mouseDownHandler);
function mouseDownHandler (event:MouseEvent) : void {
play_liste = Math.ceil(Math.random () *3);
dictSounds[play_liste].play ();}
}
Any way to rewrite this to accommodate more than one button? The first half works up until it repeats itself at "import flash." I've been searching everywhere for answers so please help!!

You should not name two functions or two variables the same, because the last ones are going to overwrite the first ones.
For example
var request:URLRequest = new URLRequest("19103_b.mp3");
is then overwritten
var request:URLRequest = new URLRequest("Readya.mp3");
The same happens with the button ready_H3AR an the function listener mouseDownHandler
By the way, you don't need to import the classes twice:
import flash.utils.Dictionary;
import flash.events.MouseEvent;

Usually when a task or a block of code is intended to be used many times, it's better to put it in a function to get a simpler and more maintainable code.
So you can do like this :
var sounds:Array = [
['01.mp3', '02.mp3', '03.mp3'], // sounds for the 1st button
['04.mp3', '05.mp3', '06.mp3'] // sounds for the 2nd button
];
var sound:Sound,
sound_channel:SoundChannel = new SoundChannel();
function play_sound(sound_name:String): void
{
var request:URLRequest = new URLRequest('mp3/' + sound_name);
sound = new Sound();
sound.addEventListener(Event.COMPLETE, on_sound_loaded);
sound.load(request);
}
function on_sound_loaded(e:Event): void
{
// if you want, you can stop the current playing sound
// otherwise you don't need the SoundChannel
sound_channel.stop();
sound_channel = Sound(e.target).play();
}
button_01.addEventListener(MouseEvent.MOUSE_DOWN, on_press);
button_02.addEventListener(MouseEvent.MOUSE_DOWN, on_press);
function on_press(e:MouseEvent): void
{
// to get values : 0, 1 or 2
var random:int = int(Math.random () * sounds[0].length);
// if it's the 1st button so pick the sound from the 1st sounds array
if(e.currentTarget.name == 'button_01'){
play_sound(sounds[0][random]);
} else {
play_sound(sounds[1][random]);
}
}
Hope that can help.

Related

Smooth image on load in as3

I am new here and a complete noob when it comes to as3. Somehow I have managed to put this together with some help from different places. And now I turn to you guys :)
I need to put smoothing on my images and thumbs that Im loading from an XML file. I have tried a lot of things but can't get any of it to work and I get this error:
Scene 1, Layer 'as3', Frame 1, Line 27 1120: Access of undefined property e. -> So I know var bitmapContent:Bitmap = Bitmap( e.target.content ); is the problem. but I have no idea what to use instead of e. I
this i what I have so far:
import flash.net.URLLoader;
import flash.net.URLRequest;
import flash.events.Event;
import flash.events.MouseEvent;
import fl.transitions.Tween;
import fl.transitions.easing.None;
import flash.display.Bitmap;
// Loads the first image//
var i =new Loader();
i.load(new URLRequest("images/1.jpg"));
mainLoader.addChild(i)
//Loads the XML file//
var picsXML:XML;
var xmlLoader:URLLoader = new URLLoader();
xmlLoader.addEventListener(Event.COMPLETE , xmlLoaded);
xmlLoader.load(new URLRequest("imagelist.xml"));
//Loads images into thumbs//
function xmlLoaded(event:Event):void{
picsXML = new XML(xmlLoader.data);
//trace(picsXML);
var bitmapContent:Bitmap = Bitmap( e.target.content );
bitmapContent.smoothing = true;
var thumbLoader:UILoader;
for (var i:uint=0; i<picsXML.image.length(); i++)
{
thumbLoader=UILoader(getChildByName("thumb"+i));
thumbLoader.load(new URLRequest("thumbs/"+picsXML.image[i].#file));
thumbLoader.buttonMode = true;
thumbLoader.addEventListener(MouseEvent.CLICK, thumbClicked);
thumbLoader.addEventListener(MouseEvent.CLICK, tester);
}
}
//Loads large image when thumb is clicked//
function thumbClicked(event:MouseEvent){
//var bitmapImage:Bitmap = event.target.content;
//bitmapImage.smoothing = true;
var thumbName:String = event.currentTarget.name;
var thumbIndex:uint = uint(thumbName.substr(5));
var fullPath:String = "images/"+picsXML.image[thumbIndex].#file;
mainLoader.load(new URLRequest(fullPath));
var myTween:Tween = new Tween(mainLoader,"alpha", None.easeNone, .3,1,18,false);
}
//Removes the first image when thumbs is clicked//
function tester(event:MouseEvent){
if (mainLoader.contains(i)) {
trace("hej")
mainLoader.removeChild(i);
}
}
If i get your code true problem is here: u import an external xml file which is probably contain your url's and name of pic's. U can't turn ur xml file into a bitmap.
If u want smoothing on your pictures u need to it after load ur pics or thumb's.
Probably ur problem will fix like this :) Hope this will help
Functions with arguments work like this: function Name ( input arg Name : input arg Type )
Firstly, your function xmlLoaded(event:Event) cannot have an input called event
but then you try to do a Bitmap( e.target.content ); so for that bitmap line to work you'd have to change your input name to e like so... xmlLoaded( e : Event );
Secondly, var bitmapContent:Bitmap = Bitmap( e.target.content ); is incorrect.
This is more correct var bitmapContent:Bitmap = img_Bytes_Loader.content as Bitmap; set a Loader's content as Bitmap not XML content (text) as Bitmap (pixels).
Anyways assuming your XML file looks like this:
<?xml version="1.0" encoding="UTF-8"?>
<Items>
<mp3>
<url> track1.mp3 </url>
<image> image1.jpg </image>
</mp3>
<mp3>
<url> track2.mp3 </url>
<image> image2.jpg </image>
</mp3>
</Items>
Then your code should look something like this below :
var imgLoader_XML : URLRequest;
var picLoader : Loader = new Loader();
function xmlLoaded(event:Event):void
{
picsXML = new XML(xmlLoader.data);
//trace(picsXML);
imgLoader_XML = new URLRequest( picsXML.mp3[ index ].image ); //[index] could be replaced with [i] if FOR loop
picLoader.contentLoaderInfo.addEventListener(Event.COMPLETE, picloadComplete);
picLoader.load (imgLoader_2);
}
public function picloadComplete(evt:Event)
{
yourContainer.addChild(picLoader);
yourContainer.width = 100;
yourContainer.height = 100;
yourContainer.alpha = 1;
}
Thanks for the inputs guys. It is greatly appreciated.
I figured it out. You were right, of course I can't put smoothing on the UILoader, so I had to target the content of the UILoader and run thumbLoader.addEventListener(Event.COMPLETE, smoothing); each time an image is importet.
Here is the entire working code (maybe it can help someone else :) :
import flash.net.URLLoader;
import flash.net.URLRequest;
import flash.events.Event;
import flash.events.MouseEvent;
import fl.transitions.Tween;
import fl.transitions.easing.None;
import flash.display.Bitmap;
import flash.display.Loader;
// Loads the first image//
var i =new Loader();
i.load(new URLRequest("images/1.jpg"));
mainLoader.addChild(i)
//Loads the XML file//
var picsXML:XML;
var xmlLoader:URLLoader = new URLLoader();
xmlLoader.addEventListener(Event.COMPLETE , xmlLoaded);
xmlLoader.load(new URLRequest("imagelist.xml"));
//Loads images into thumbs//
function xmlLoaded(event:Event):void{
picsXML = new XML(xmlLoader.data);
var thumbLoader :UILoader = new UILoader();
for (var i:uint=0; i<picsXML.image.length(); i++)
{
thumbLoader=UILoader(getChildByName("thumb"+i));
thumbLoader.load(new URLRequest("thumbs/"+picsXML.image[i].#file));
thumbLoader.buttonMode = true;
thumbLoader.addEventListener(MouseEvent.CLICK, thumbClicked);
thumbLoader.addEventListener(MouseEvent.CLICK, tester);
thumbLoader.addEventListener(Event.COMPLETE, smoothing);
}
}
function smoothing(e:Event):void{
if(e.currentTarget.content is Bitmap)
{
Bitmap(e.currentTarget.content).smoothing = true;
}
}
//Loads large image when thumb is clicked//
function thumbClicked(event:MouseEvent){
var thumbName:String = event.currentTarget.name;
var thumbIndex:uint = uint(thumbName.substr(5));
var fullPath:String = "images/"+picsXML.image[thumbIndex].#file;
mainLoader.load(new URLRequest(fullPath));
mainLoader.addEventListener(Event.COMPLETE, smoothing);
var myTween:Tween = new Tween(mainLoader,"alpha", None.easeNone, .3,1,18,false);
}
//Removes the first image when thumbs is clicked//
function tester(event:MouseEvent){
if (mainLoader.contains(i)) {
trace("hej")
mainLoader.removeChild(i);
}
}

RSS gallery as3

Here is my case - I have 10 movie clips on the view and in every movie clip I need to have a loaded picture that RSS. I've managed to get the first one loaded but I need the other 9 as well. Any ideas?
import com.adobe.xml.syndication.generic.FeedFactory;
import com.adobe.xml.syndication.generic.IFeed;
import com.adobe.xml.syndication.generic.IItem;
import flash.display.MovieClip;
import flash.events.MouseEvent;
var rss:XML;
var rss_url:URLRequest = new URLRequest("http://btvnews.bg/lbin/rss.php?section_id=16105?pic_size=300x158");
var rssLoader:URLLoader = new URLLoader(rss_url);
rssLoader.addEventListener(Event.COMPLETE, rssLoaded);
var rssCount;
var rssCounter = 0;
var l = new Loader();
var loaderContext:LoaderContext;
loaderContext= new LoaderContext();
loaderContext.securityDomain=SecurityDomain.currentDomain;
loaderContext.checkPolicyFile = true;
Security.loadPolicyFile('http://img.bg.sof.cmestatic.com/crossdomain.xml');
function rssLoaded(e:Event):void
{
rss = XML(rssLoader.data);
var curImage = rss.channel.item[0].enclosure. # url;
l.load(new URLRequest(curImage));
l.contentLoaderInfo.addEventListener(Event.COMPLETE,doneIt);
function doneIt(e:Event)
{
var bit:Bitmap = e.target.content;
if (bit != null)
{
bit.smoothing = true;
}
}
placer.addChild(l);
After your rss load as you have loaded one images put a for loop for the item
for(var i=0; i < rss.channel.item.length;i++)
{
var img = rss.channel.item[i].enclosure. # url;
//rest of the code to load the image and adding to stage
}

Best way of passing values from a button with as3 event

I develop my own way of doing this simple task. However, I'm now wondering if there is a better way to do it.
The buttons:
var menuBtnEscrit:MovieClip = new MovieClip();
var mbe:btnEscritorio = new btnEscritorio();
menuBtnEscrit.addChild(mbe);
menuBtnEscrit.val = "escrit";
menuBtnEscrit.x = 80;
menuBtnEscrit.addEventListener(MouseEvent.CLICK, novoCont);
dMenu.addChild(menuBtnEscrit);
var menuBtnPublic:MovieClip = new MovieClip();
var mbp:btnPublic = new btnPublic();
menuBtnPublic.addChild(mbp);
menuBtnPublic.val = "public";
menuBtnPublic.x = 244;
menuBtnPublic.addEventListener(MouseEvent.CLICK, novoCont);
dMenu.addChild(menuBtnPublic);
And I can keep going, or create buttons trough some algorithm, and put more properties and take advantage of a MovieClip.
The handler:
private function novoCont(e=null){
if(e!=null) selecCont = new String(e.target.parent.val);
clearDisplay(dSubMenu);
clearDisplay(dConteudo);
var func:String = "cont_"+selecCont;
this[func]();
}
As you can see, there is some common task for all buttons.
If I click the first button, it will call cont_escrit() function. This mechanics works, but it is the best practice? Is there a way of optimize it?
public class BaseButton extends Button {
private var _func:Function;
public function get handler():Function {
return _func;
}
public function set handler(value:Function):void {
_func = value;
}
}
So hero you can create mbe like this
mbe = new BaseButton ();
mbe.hanler = cont_escrit;
The event hanler would be
private function novoCont(e=null){
if(e!=null) {
var selecContent:BaseButton = e.targe as BaseButton ;
var handler:Function = selecContent.handler;
handler();
}
}
Here is an example.
var menuBtnEscrit:MovieClip = new MovieClip();
var mbe:btnEscritorio = new btnEscritorio();
menuBtnEscrit.addChild(mbe);
//menuBtnEscrit.val = "escrit";
menuBtnEscrit.func = this.cont_escrit;
menuBtnEscrit.x = 80;
menuBtnEscrit.addEventListener(MouseEvent.CLICK, novoCont);
private function novoCont(e=null){
if(e!=null) {
var mc:MovieClip = e.target as MovieClip;
var func:Function = mc.func;
func();
}
clearDisplay(dSubMenu);
clearDisplay(dConteudo);
}

Error #2025: The supplied DisplayObject must be a child of the caller - Not sure how to fix

So I get an error saying that the supplied DisplayObject must be a child of the caller. What happens is my game works first time around in that clicking the 'Play' button calls the startGame function and removes the menu so that the game is shown, but then at the end of the game when the playAgainBtn is clicked, instead of simply playing the game again / restarting the game, I get this error:
ArgumentError: Error #2025: The supplied DisplayObject must be a child of the caller.
at flash.display::DisplayObjectContainer/removeChild()
It specifically points to this line:
menuLayer.removeChild(mainMenu);
Here is the code:
package {
import flash.display.MovieClip;
import flash.display.LoaderInfo;
import flash.display.Sprite;
import flash.display.DisplayObject;
import flash.display.Stage;
import flash.events.Event;
import flash.events.MouseEvent;
import flash.events.TimerEvent;
import flash.utils.Timer;
import flash.ui.Mouse;
import flash.media.Sound;
import flash.media.SoundChannel;
import flash.text.TextFormat;
import flash.text.TextField;
import flash.text.AntiAliasType;
import flash.display.Loader;
import flash.net.URLRequest;
import flash.net.URLLoader;
import flash.system.LoaderContext;
import flash.display.Sprite;
import flash.net.Socket;
import caurina.transitions.Tweener;
import flash.text.Font;
public class Main extends MovieClip {
public static var backgroundLayer:Sprite = new Sprite;
public static var gameLayer:Sprite = new Sprite;
public static var interfaceLayer:Sprite = new Sprite;
public static var endGameLayer:Sprite = new Sprite;
public static var menuLayer:Sprite = new Sprite;
public static var gameOverLayer:Sprite = new Sprite;
public static var howToLayer:Sprite = new Sprite;
public static var scoresLayer:Sprite = new Sprite;
public static var aboutLayer:Sprite = new Sprite;
public var mainMenu:menuMain = new menuMain;
public var gameEnd:endGame = new endGame;
public var howtoPlay:howToPlay = new howToPlay;
public var gameAbout:aboutGame = new aboutGame;
public var intro:IntroSound = new IntroSound();
public var soundControl:SoundChannel = new SoundChannel();
public var gameTime:int;
public var levelDuration:int;
public var crosshair:crosshair_mc;
static var score:Number;
var enemyShipTimer:Timer;
var enemyShipTimerMed:Timer;
var enemyShipTimerSmall:Timer;
static var scoreHeader:TextField = new TextField();
static var scoreText:TextField = new TextField();
static var timeHeader:TextField = new TextField();
static var timeText:TextField = new TextField();
static var scoreFormat = new TextFormat("Arial Rounded MT Bold", 20, 0xFFFFFF);
public var gameOverscoreFormat = new TextFormat("Arial Rounded MT Bold", 32, 0xFFFFFF);
public function Main()
{
addChild(gameLayer);
addChild(backgroundLayer);
addChild(interfaceLayer);
addChild(menuLayer);
menuLayer.addChild(mainMenu);
interfaceLayer.addChild(howtoPlay);
interfaceLayer.addChild(gameEnd);
interfaceLayer.addChild(gameAbout);
soundControl = intro.play(0, 100);
addMenuListeners();
}
public function menuReturn(e:Event)
{
addChild(gameLayer);
addChild(backgroundLayer);
addChild(interfaceLayer);
addChild(menuLayer);
menuLayer.addChild(mainMenu);
interfaceLayer.addChild(howtoPlay);
interfaceLayer.addChild(gameEnd);
interfaceLayer.addChild(gameAbout);
}
public function showAbout(e:Event)
{
menuLayer.removeChild(mainMenu);
interfaceLayer.addChild(gameAbout);
}
public function startGame(e:Event)
{
removeMenuListeners();
soundControl.stop();
interfaceLayer.removeChild(howtoPlay);
interfaceLayer.removeChild(gameAbout);
interfaceLayer.removeChild(gameEnd);
menuLayer.removeChild(mainMenu);
levelDuration = 30;
gameTime = levelDuration;
var gameTimer:Timer = new Timer(1000,levelDuration);
gameTimer.addEventListener(TimerEvent.TIMER, updateTime);
gameTimer.addEventListener(TimerEvent.TIMER_COMPLETE, timeExpired)
gameTimer.start();
scoreHeader = new TextField();
scoreHeader.text = String("Score: ");
interfaceLayer.addChild(scoreHeader);
scoreHeader.x = 5;
scoreHeader.selectable = false;
scoreHeader.embedFonts = true;
scoreHeader.antiAliasType = AntiAliasType.ADVANCED;
scoreText = new TextField();
scoreText.text = String("0");
interfaceLayer.addChild(scoreText);
scoreText.x = 75;
scoreText.y = 0;
scoreText.selectable = false;
scoreText.embedFonts = true;
scoreText.antiAliasType = AntiAliasType.ADVANCED;
timeHeader = new TextField();
timeHeader.text = String("Time: ");
interfaceLayer.addChild(timeHeader);
timeHeader.x = 500;
timeHeader.y = 0;
timeHeader.selectable = false;
timeHeader.embedFonts = true;
timeHeader.antiAliasType = AntiAliasType.ADVANCED;
timeText = new TextField();
timeText.text = gameTime.toString();
interfaceLayer.addChild(timeText);
timeText.x = 558;
timeText.y = 0;
timeText.selectable = false;
timeText.embedFonts = true;
timeText.antiAliasType = AntiAliasType.ADVANCED;
scoreHeader.setTextFormat(scoreFormat);
scoreText.setTextFormat(scoreFormat);
timeHeader.setTextFormat(scoreFormat);
timeText.setTextFormat(scoreFormat);
var timeScorebg:Sprite = new Sprite();
backgroundLayer.addChild(timeScorebg);
timeScorebg.graphics.beginFill(0x333333);
timeScorebg.graphics.drawRect(0,0,600,30);
timeScorebg.graphics.endFill();
timeScorebg.y = 0;
enemyShipTimer = new Timer(2000);
enemyShipTimer.addEventListener("timer", sendEnemy);
enemyShipTimer.start();
enemyShipTimerMed = new Timer(2500);
enemyShipTimerMed.addEventListener("timer", sendEnemyMed);
enemyShipTimerMed.start();
enemyShipTimerSmall = new Timer(2750);
enemyShipTimerSmall.addEventListener("timer", sendEnemySmall);
enemyShipTimerSmall.start();
crosshair = new crosshair_mc();
gameLayer.addChild(crosshair);
crosshair.mouseEnabled = crosshair.mouseChildren = false;
Mouse.hide();
gameLayer.addEventListener(Event.ENTER_FRAME, moveCursor);
resetScore();
}
function addMenuListeners():void
{
mainMenu.playBtn.addEventListener(MouseEvent.CLICK, startGame);
mainMenu.howToPlayBtn.addEventListener(MouseEvent.CLICK, showInstructions);
mainMenu.aboutBtn.addEventListener(MouseEvent.CLICK, showAbout);
howtoPlay.backBtn.addEventListener(MouseEvent.CLICK, menuReturn);
gameEnd.playagainBtn.addEventListener(MouseEvent.CLICK, startGame);
gameAbout.backBtn.addEventListener(MouseEvent.CLICK, menuReturn);
}
function removeMenuListeners():void
{
mainMenu.playBtn.removeEventListener(MouseEvent.CLICK, startGame);
mainMenu.howToPlayBtn.removeEventListener(MouseEvent.CLICK, showInstructions);
mainMenu.aboutBtn.removeEventListener(MouseEvent.CLICK, showAbout);
howtoPlay.backBtn.removeEventListener(MouseEvent.CLICK, menuReturn);
gameEnd.playagainBtn.removeEventListener(MouseEvent.CLICK, startGame);
gameAbout.backBtn.removeEventListener(MouseEvent.CLICK, menuReturn);
}
public function showInstructions(e:Event)
{
menuLayer.removeChild(mainMenu);
interfaceLayer.addChild(howtoPlay);
}
function sendEnemy(e:Event)
{
var enemy = new EnemyShip();
gameLayer.addChild(enemy);
gameLayer.addChild(crosshair);
}
function sendEnemyMed(e:Event)
{
var enemymed = new EnemyShipMed();
gameLayer.addChild(enemymed);
gameLayer.addChild(crosshair);
}
function sendEnemySmall(e:Event)
{
var enemysmall = new EnemyShipSmall();
gameLayer.addChild(enemysmall);
gameLayer.addChild(crosshair);
}
static function updateScore(points)
{
score += points;
scoreText.text = String(score);
scoreHeader.setTextFormat(scoreFormat);
scoreText.setTextFormat(scoreFormat);
}
static function resetScore()
{
score = 0;
scoreText.text = String(score);
scoreText.setTextFormat(scoreFormat);
}
function updateTime(e:TimerEvent):void
{
gameTime--;
timeText.defaultTextFormat = scoreFormat;
timeText.text = String(gameTime);
}
function timeExpired(e:TimerEvent):void
{
var gameTimer:Timer = e.target as Timer;
gameTimer.removeEventListener(TimerEvent.TIMER, updateTime)
gameTimer.removeEventListener(TimerEvent.TIMER, timeExpired)
interfaceLayer.addChild(gameEnd);
var thisFont:Font = new myFont();
var myFormat:TextFormat = new TextFormat();
myFormat.font = thisFont.fontName;
scoreText = new TextField();
scoreText.defaultTextFormat = myFormat;
scoreText.text = String(score);
interfaceLayer.addChild(scoreText);
scoreText.x = 278;
scoreText.y = 180;
scoreText.selectable = false;
scoreText.embedFonts = true;
scoreText.antiAliasType = AntiAliasType.ADVANCED;
scoreText.setTextFormat(gameOverscoreFormat);
Mouse.show();
removeChild(gameLayer);
addMenuListeners();
}
function moveCursor(event:Event)
{
crosshair.x=mouseX;
crosshair.y=mouseY;
}
}
}
I'm not quite sure how to fix this, so any advice or solution will be welcome. I can't get it to work the way I intended without getting errors.
Thanks.
I believe the problem is calling menuLayer.removeChild(mainMenu); on the second play-through is throwing the error due to the fact that you'd already removed it once already. The quickest solution would be to do a check to ensure menuLayer contains mainMenu before you try and remove it:
if(menuLayer contains mainMenu)
menuLayer.removeChild(mainMenu);
(Note that I don't have access to the IDE right now, but I think this should work)
A more robust solution would be to call a different method when the play button is clicked from the main menu that removes mainMenu from menuLayer, then calls startGame (where as playAgain calls startGame directly).
EDIT
Ok I see what you mean. Perhaps something like this instead:
mainMenu.playBtn.addEventListener(MouseEvent.CLICK, playGame);
gameEnd.playagainBtn.addEventListener(MouseEvent.CLICK, playGameAgain);
...
public function playGame(e:Event)
{
menuLayer.removeChild(mainMenu);
startGame();
}
...
public function playGameAgain(e:Event)
{
startGame();
}
...
public function startGame()
I have no idea why your code is not working, but there is no need to fret, try:
MovieClip(menuLayer.parent).removeChild(menuLayer);
You remove mainMenu in two different locations. My guess is it is being removed once and then again moments later.
if ( mainMenu.parent == menuLayer ) {
menuLayer.removeChild( mainMenu );
}
This will verify that mainMenu is actually a child of menuLayer before removing it. You cannot remove a child from a parent that isn't actually its parent. Imagine the state taking a child away and taking custody of them from a kidnapper. It's not the prettiest comparison, but it gives the right idea.
I cannot verify this without seeing how the game over is handled, but I think potentially the problem is that you are not removing your event listeners each time the game is played. Therefore when you go back to the main menu and add them again, you now have TWO listeners for playAgainBtn.
So when you end a game and click on the playAgainBtn, startGame gets called TWICE. So the first time it removes things just fine, and the second time - there's nothing to remove. This issue will potentially exist with all of your event listeners given your current design.
If this is the case you simply need to remove your event listeners when the menu is removed.
I suggest that whenever you make the menu active you add the listeners, and then remove them whenever you hide it. Maybe have two methods, addMenuListeners and removeMenuListeners
You could create these two functions and use them where appropriate :
function addMenuListeners():void
{
mainMenu.playBtn.addEventListener(MouseEvent.CLICK, startGame);
mainMenu.howToPlayBtn.addEventListener(MouseEvent.CLICK, showInstructions);
mainMenu.aboutBtn.addEventListener(MouseEvent.CLICK, showAbout);
howtoPlay.backBtn.addEventListener(MouseEvent.CLICK, menuReturn);
gameEnd.playagainBtn.addEventListener(MouseEvent.CLICK, startGame);
gameAbout.backBtn.addEventListener(MouseEvent.CLICK, menuReturn);
}
function removeMenuListeners():void
{
mainMenu.playBtn.removeEventListener(MouseEvent.CLICK, startGame);
mainMenu.howToPlayBtn.removeEventListener(MouseEvent.CLICK, showInstructions);
mainMenu.aboutBtn.removeEventListener(MouseEvent.CLICK, showAbout);
howtoPlay.backBtn.removeEventListener(MouseEvent.CLICK, menuReturn);
gameEnd.playagainBtn.removeEventListener(MouseEvent.CLICK, startGame);
gameAbout.backBtn.removeEventListener(MouseEvent.CLICK, menuReturn);
}
If you follow the rule of always removing the event listeners when not in use, you can avoid this issue.
if( mainMenu.parent ){ mainmenu.parent.removeChild( mainMenu );} Or perhaps its already removed / not added at all?
Change this line
menuLayer.removeChild(mainMenu);
to this one..
if (mainMenu.parent != null && mainMenu.parent == menuLayer)
{
menuLayer.removeChild(mainMenu);
}
Hope it will solve.

How can I make this Flash code work in IE when it's cached?

I have a flash file here, when the flash plays, it works fine, but in IE and if the flash was already loaded once and is now cached, it freezes up. After digging super deep on the internet I was able to find out the following:
There are a bunch of known bugs in
flash 9 and 10, one of those being an
issue with the Event.COMPLETE not
being fired from the main stage when
loading from cache when it's embedded
WMODE = "transparent" I'm not sure if
that's your issue, but it's worth
looking into. I've heard of a few
workarounds to the problem. One of
them being, not listening for for
progress or complete events at all and
just using a timed loop like
ENTER_FRAME or TIMER to watch the
bytesLoaded/bytesTotal.
My WMODE is window, but this makes the most sense to me. The loadText never gets set which tells me its not entering swfProgressHandle function. However the problem is I only wrote half this flash (everything inside init) in conjunction with someone else, but that other person I cannot get in contact with anymore. I am fairly new to flash so really don't know how to take his loading code and make it only run off timer events instead of progress and complete events (as said in the above quote) so that it will work in IE when cached. Can anyone help me on this? Most of the code is fine, it's just the beginning where those progress and complete handlers are for loading stuff that appears to be causing the issue.
package
{
//---Imports---
import flash.display.*;
import fl.transitions.Tween;
import fl.transitions.TweenEvent;
import fl.transitions.easing.*;
import flash.events.Event;
import flash.events.*;
import flash.events.IOErrorEvent;
import flash.events.ProgressEvent;
import flash.net.URLLoader;
import flash.net.URLRequest;
import flash.text.TextField;
import flash.utils.Timer;
import flash.utils.*;
import flash.text.Font;
public class FohLoader extends Sprite
{
//create and start load bar
private var loadBar:Sprite = new Sprite();
private var loadText:TextField = new TextField();
private var loadBarBg:Graphics = loadBar.graphics;
//load XML data
private var xmlLoader:URLLoader = new URLLoader();
private var xmlData:XML = new XML();
private var _queue:Array; //holds data objects of items to be loaded
private var _index:int; //the current item in the _queue
private var _images:Array; //holds DisplayObjects of the loaded images
public function FohLoader()
{
_queue = new Array();
_images = new Array();
_index = 0;
//waits for the stage to be created
addEventListener(Event.ADDED_TO_STAGE, stageReadyHandle);
}
private function stageReadyHandle(e:Event):void
{
removeEventListener(Event.ADDED_TO_STAGE, stageReadyHandle);
loadBarBg.lineStyle();
loadBarBg.beginFill(0x5a96c5, 1);
loadBarBg.drawRect(0, 0, 5, 10);
loadBarBg.endFill();
loadBar.x = (stage.stageWidth - 500)/2;
loadBar.y = 30;
loadBar.width = 5;
loadBar.height = 10;
this.addChild(loadBar);
loadText.x = (stage.stageWidth - 0)/2;
loadText.y = 50;
this.addChild(loadText);
//I have no idea if this crap works
//but you would have to do something like this if you want to keep your project to one swf file.
this.loaderInfo.addEventListener(ProgressEvent.PROGRESS, swfProgressHandle);
}
private function swfProgressHandle(e:ProgressEvent):void
{
//assumes you want the loadbar to be 500px at 100%
var getPercent:Number = bytesLoaded / e.bytesTotal;
trace(bytes_loaded + " of " + bytes_total + " loaded");
loadBar.width = getPercent * 150; //changed 500 to 150
loadText.text = String(Math.round(getPercent * 30) + "%"); //changed 100 to 30
if (e.bytesLoaded / e.bytesTotal >= 1)
{
e.target.removeEventListener(ProgressEvent.PROGRESS, swfProgressHandle);
loadXml();
}
}
private function loadXml()
{
xmlLoader.addEventListener(Event.COMPLETE, ParseXML);
xmlLoader.load(new URLRequest("flash.xml"));
}
private function ParseXML(e:Event):void
{
e.target.removeEventListener(Event.COMPLETE, ParseXML);
flashInputs = new XML(e.target.data);
//declare all XMl variables, terrible way to do it though
var imageURLList:XMLList = flashInputs.image_area.image.image_url;
var firmCount:XMLList = flashInputs.count_area.total_firms;
var quoteMsg:XMLList = flashInputs.quote_area.quote.quote_text;
var quoteOwner:XMLList = flashInputs.quote_area.quote.quote_owner;
var imageURL:XMLList = flashInputs.image_area.image.image_url;
var imageText:XMLList = flashInputs.image_area.image.image_text;
var quoteMsg0:XML = quoteMsg[0];
var quoteMsg1:XML = quoteMsg[1];
var quoteMsg2:XML = quoteMsg[2];
var quoteMsg3:XML = quoteMsg[3];
var quoteMsg4:XML = quoteMsg[4];
var quoteMsg5:XML = quoteMsg[5];
var quoteMsg6:XML = quoteMsg[6];
var quoteOwner0:XML = quoteOwner[0];
var quoteOwner1:XML = quoteOwner[1];
var quoteOwner2:XML = quoteOwner[2];
var quoteOwner3:XML = quoteOwner[3];
var quoteOwner4:XML = quoteOwner[4];
var quoteOwner5:XML = quoteOwner[5];
var quoteOwner6:XML = quoteOwner[6];
var imageText0:XML = imageText[0];
var imageText1:XML = imageText[1];
var imageText2:XML = imageText[2];
var imageText3:XML = imageText[3];
var imageText4:XML = imageText[4];
var imageText5:XML = imageText[5];
var imageText6:XML = imageText[6];
var imageURL0:XML = imageURL[0];
var imageURL1:XML = imageURL[1];
var imageURL2:XML = imageURL[2];
var imageURL3:XML = imageURL[3];
var imageURL4:XML = imageURL[4];
var imageURL5:XML = imageURL[5];
var imageURL6:XML = imageURL[6];
//loops through the imageURL array and adds each item to the queue
for each(var img:XML in imageURL)
{
addItem(String(img));
}
//loads the first item in the queue
loadItem();
}
//creates a new loader for the item
//adds a data object holding the item path and loader into the queue
private function addItem(path:String):void
{
var loader:Loader = new Loader();
_queue.push({loader:loader, path:path});
}
private function loadItem():void
{
_queue[_index].loader.contentLoaderInfo.addEventListener(Event.COMPLETE, imgCompleteHandle);
_queue[_index].loader.contentLoaderInfo.addEventListener(IOErrorEvent.IO_ERROR, IOErrorHandle);
_queue[_index].loader.contentLoaderInfo.addEventListener(ProgressEvent.PROGRESS, imgProgressHandle);
_queue[_index].loader.load(new URLRequest(_queue[_index].path));
}
//checks the progress of each image, and increases the width of the load bar
private function imgProgressHandle(e:ProgressEvent):void
{
var perc:Number = e.bytesLoaded / e.bytesTotal;
//this line assumes you are loading 6 images, and want the loadbar to end up at 500px
//it also assumes the bar has already reached 30% (150px) from loading the swf
loadBar.width = 150 + (_index * (350 / 6)) + ((350 / 6) * perc);
//so the swf's 150 + (how many images have alrady loaded * the width each image needs to affect the bar) +
//(same thing * percent of current image loaded)
//sounds right, might have to mess with that.
}
//this just stops flash from outputting an error if the image fails to load
private function IOErrorHandle(e:IOErrorEvent):void
{
e.target.removeEventListener(Event.COMPLETE, imgCompleteHandle);
e.target.removeEventListener(IOErrorEvent.IO_ERROR, IOErrorHandle);
trace("Error handled, sir.");
trace("The problem was that, " + e);
}
private function imgCompleteHandle(e:Event):void
{
e.target.removeEventListener(Event.COMPLETE, imgCompleteHandle);
e.target.removeEventListener(ProgressEvent.PROGRESS, imgProgressHandle);
e.target.removeEventListener(IOErrorEvent.IO_ERROR, IOErrorHandle);
//adds the image to the _images array
_images.push(e.target.content);
//increments the load counter
_index++;
//checks to see if the queue is finished or not
if (_index < _queue.length)
{
trade("Not done loading, loading another item");
loadItem();
}
else
{
_index = 0;
_queue = [];
killLoadBar();
init();
}
}
private function killLoadBar()
{
this.removeChild(loadBar);
this.removeChild(loadText);
}
If you dont mind the flash loading every time and not using the cache you could just append a cache breaker to the swf url.
Any random thing will do.
Here's a example using swfobject (http://code.google.com/p/swfobject/)
<script type="text/javascript">
var flashvars = {};
var params = {};
var attributes = {};
swfobject.embedSWF("myContent.swf?rand="+Math.random(), "myContent", "300", "120", "9.0.0","expressInstall.swf", flashvars, params, attributes);
</script>
This should work around any IE cache bugs.
If you still get the error with this, then it's should not be cache related.
After taking a quick look at this, I can see what might be happening.
You listen for ADDED_TO_STAGE.
You handle ADDED_TO_STAGE and then start listening for PROGRESS.
You then set the loadText, and load the XML if progress is "done".
The problem here seems to be the "done" part and the progress handling. First, the
loaderInfo object has a COMPLETE event. Why not use it? (http://livedocs.adobe.com/flex/3/langref/flash/display/LoaderInfo.html)
Then you could skip the whole bytesLoaded/bytesTotal check.
Where I think the applicatation might be freezing is when the loaderInfo has cached resources and skips the "PROGRESS" step altogether. Then you would never get into the LoadXML code, and never, therefore, parse the xml.
You can install firebug in Firefox and check whether you're even attempting to load the XML file using the "net" tab. (or use a tool like filemon).