Actionscript 3.0...Preloader not working....Stuck in frame 0? - actionscript-3

I'm trying to make my first preloader come to fruition on my game. I've done quite a bit of experimenting and here's what I've found.
First off, when I simulate my file, I am simply getting a blank white screen until the file...or actually just a large image I'm using att the moment...is fully loaded. I used the Bandwidth Profiler to check what was going on. It is loading at a proper rate and I can see the loading percentage increasing. Unfortunately my loader just doesn't appear, and the weird thing is it says it's in frame 0. I have no idea why it is telling me this.
What I've done to try and fix it. I've made sure that EVERY movieclip in my game has the box for exporting on first frame is unchecked, EXCEPT my preloader, so that shouldn't be the problem. I also created a blank .fla file and simply imported my preloader into it and ran it. Surprisingly, it worked! (once again, just loaded a large image). I didn't change anything....all the same names and stuff. I have no idea why that one would work and this one wouldn't.
My timeline basically is the following: Two layers: First layer first frame actions says stop();. I would assume my preloader should load on the first frame like I've told it too. Once finished loading, it runs through the timeline to frame 3, where my main game should start running since I also put in the actions of frame 3. Second layer second frame I have a MovieClip that contains the image that I want it to load.
Here is my document class
package com.classes
{
import flash.display.MovieClip;
import flash.display.Stage;
import flash.display.Sprite;
import flash.events.Event;
public class DocumentClass extends MovieClip
{
private var preloader:ThePreloader;
public static var enemyList1:Array = new Array();
// moved stickobject1 to a class variable.
private var stickobject1:Stickman2;
private var scoreHud:ScoreHud;
public function DocumentClass() : void
{
preloader = new ThePreloader(390, this.loaderInfo);
stage.addChild(preloader);
preloader.addEventListener("loadComplete", loadAssets);
preloader.addEventListener("preloaderFinished", showSponsors);
var bg1:background1 = new background1();
stage.addChild(bg1);
stickobject1 = new Stickman2(stage);
stage.addChild(stickobject1);
stickobject1.x=50;
stickobject1.y=300;
//running a loop now.... so we can keep creating enemies randomly.
addEventListener(Event.ENTER_FRAME, loop, false, 0, true);
stickobject1.addEventListener("hit", stickobject1Hit, false, 0, true);
scoreHud = new ScoreHud(stage); //create our HUD
stage.addChild(scoreHud); //and display it.
}
private function loadAssets(e:Event) : void
{
this.play();
}
private function showSponsors(e:Event) : void
{
stage.removeChild(preloader);
trace("show sponsors");
}
//our loop function
private function loop(e:Event) : void
{
//run if condition is met.
if (Math.floor(Math.random() * 40) == 5)
{
//create our enemyObj1
var enemyObj1:Enemy1 = new Enemy1(stage, stickobject1);
//listen for enemyObj1 being removed from stage
enemyObj1.addEventListener(Event.REMOVED_FROM_STAGE, removeEnemyObj1, false, 0, true);
enemyObj1.addEventListener("killed", enemy1Killed, false, 0, true);
enemyList1.push(enemyObj1);
stage.addChild(enemyObj1);
}
}
private function enemy1Killed(e:Event) : void
{
scoreHud.updateKills(1); //add 1 to enemy kills
scoreHud.updateScore(e.currentTarget.points); //add that points variable we created earlier
}
private function stickobject1Hit(e:Event) : void
{
scoreHud.updateHits(1); //add 1 to number of hits
}
private function removeEnemyObj1(e:Event)
{
enemyList1.splice(enemyList1.indexOf(e.currentTarget), 1);
}
}
}
and here is my "ThePreloader" class
package com.classes
{
import flash.display.LoaderInfo;
import flash.display.MovieClip;
import flash.events.*;
public class ThePreloader extends MovieClip
{
private var fullWidth:Number; //the width of our mcPreloaderBar at 100%
public var ldrInfo:LoaderInfo;
public function ThePreloader(fullWidth:Number = 0, ldrInfo:LoaderInfo = null)
{
this.fullWidth = fullWidth;
this.ldrInfo = ldrInfo;
addEventListener(Event.ENTER_FRAME, checkLoad);
}
private function checkLoad (e:Event) : void
{
if (ldrInfo.bytesLoaded == ldrInfo.bytesTotal && ldrInfo.bytesTotal != 0)
{
//loading complete
dispatchEvent(new Event("loadComplete"));
phaseOut();
}
updateLoader(ldrInfo.bytesLoaded / ldrInfo.bytesTotal);
}
private function updateLoader(num:Number) : void
{
//num is a number between 0 and 1
mcPreloaderBar.width = num * fullWidth;
}
private function phaseOut() : void
{
removeEventListener(Event.ENTER_FRAME, checkLoad);
phaseComplete();
}
private function phaseComplete() : void
{
//go on to the next phase
dispatchEvent(new Event("preloaderFinished"));
}
}
}
My main confusion is why the Bandwidth Profiler is telling me I'm stuck in frame 0 and until it's pretty much 100% loaded. Thus, not ever displaying the preloader....thank you!

I think your preloader should be listening to LoaderInfo's progress event. Since, you have stopped execution, I don't think ENTER_FRAME would get called for you.
Also, AFAIR, single frame movie are treated specially by the player and player sends ENTER_FRAME periodically for them. Probably, that is the reason you test application worked out.

Related

Access of undefined property issues in AS3

I am having a bit of trouble with some AS3. First time using this language and have more experience with web development then OOP so am getting a bit confused.
I am trying to make it so that when someone clicks a 'powerbutton' which is a "movieclip" symbol within flash then another symbol should then become visible. This is all being done within the Kitchen class.
The code for the main class is which i got from a youtube tutorial video i followed;
package {
import flash.display.MovieClip;
import flash.events.MouseEvent;
import flash.geom.Point;
import flash.events.Event;
import Kitchen
public class DragFood extends MovieClip
{
protected var originalPosition:Point;
var myKitchen:Kitchen
public function DragFood() {
myKitchen = new Kitchen;
originalPosition = new Point (x, y);
buttonMode = true;
addEventListener (MouseEvent.MOUSE_DOWN, down);
}
protected function down (event:MouseEvent):void
{
parent.addChild(this);
startDrag();
stage.addEventListener (MouseEvent.MOUSE_UP, stageUp);
}
protected function stageUp (event:MouseEvent):void
{
stage.removeEventListener (MouseEvent.MOUSE_UP, stageUp);
stopDrag();
if (dropTarget)
{
if(dropTarget.parent.name == "bowl")
{
trace("The " + this.name + " is in the bowl");
this.visible = false;
} else {
returnToOriginalPosition();
}
} else {
returnToOriginalPosition();
}
}
protected function returnToOriginalPosition():void
{
x = originalPosition.x;
y = originalPosition.y;
}
}
}
Within it i call the other class;
import Kitchen
public class DragFood extends MovieClip
{
protected var originalPosition:Point;
var myKitchen:Kitchen
The code for the kitchen class is;
package {
import flash.display.MovieClip;
import flash.events.MouseEvent;
import flash.events.Event;
public class Kitchen extends MovieClip
{
// This is a function. This particular function has the same name as our class and therefore will be executed first
public function Kitchen()
{
// This is a "call" to another function that is defined later in the program.
init();
trace("Hello world");
}
public function init():void
{
// If we want an object (on the screen or otherwise) to be notified about an event we must add a listener for that event to that object.
// We also need to specify what happens everytime the event we are listening for happens.
PowerButton.addEventListener(MouseEvent.CLICK, handleButtonClicks);
}
//This function is called when the oven on button recieves a click.
public function handleButtonClicks(event:MouseEvent):void
{
OvenOn.visible = true;
trace("the oven is being switched on");
}
}
}
The issue i keep getting is that OvenOn and PowerButton are giving me a undefined access issue and im not sure how to fix it. I have found posts on similar subjects like - Access of Undefined property? Actionscript 3
but im not quite sure how to apply it to my issue if anyone could offer any help that would be great.
When you're programming on the timeline, code is referencing the local namespace, and objects you make there (movieclips, textfields, etc.) are automatically instantiated in that namespace so that you can simply call OvenOn.visible = true. However, for each class, their local namespace is whatever is inside the class, so unless you actually created a property on your class called OvenOn, it will most definitely give you Access of Undefined Property errors.
Think of each class as its own island. For them to touch eachother, they need some sort of connection. That connection can be made once the parent instantiates the class in its own namespace. For example...
var foo:String = "Hello!";
var bar:MyClass = new MyClass();
// At this point, whatever code runs inside of MyClass has no concept of foo, or how to access it.
addChild(bar);
// Now that we've added it to the stage, the bar has some properties that have automatically been populated such as "root", "parent", or "stage".
foo.someProperty = "World";
// Since this namespace has a variable pointing to the instance, we can change properties on that class.
Now that we've instantiated MyClass on the stage, we can reference parent properties the class didn't know about. Mind you, this is not necessarily best practice.
package
public class MyClass extends MovieClip {
var someProperty:String = "cheese";
public function MyClass() {
trace(parent.foo) // this will fail
addEventListener(Event.ADDED_TO_STAGE, test);
}
public function test(e:Event):void {
trace(this["parent"].foo); // this will succeed
}
}
}
If you absolutely must change something that is not part of your Kitchen class, pass either the parent of OvenOn or that object specifically as a property of Kitchen. You could do this a couple ways.
with the Constructor...
var something:*;
public function MyClass(someObject:*) {
something = someObject;
}
public function test():void {
something.visible = false;
}
...or by Assigning the Property...
var bar:MyClass = new MyClass();
bar.something = OvenOn;
bar.test(); // will turn off the OvenOn now that 'something' is pointing to it.

AS3 - How to get current scene name in class

I'm playing around with flash, and I've created multiple scenes for things like menu's, buttons, etc. When trying to add event handlers for buttons that are in one scene, but not others, the compiler complains saying that it can't reference to objects that don't exist.
I figured the solution to be simple... Get the scene name, match that against an if statement and load the event handlers through the if statements...
However, after digging around on the net for far too long, I just can't seem to find a way to do this properly. Does anyone know a way?
I've tried using the following :
var scene:Scene = myflvandclassname.currentScene;
var sName:String = MovieClip.currentScene.name;
Both lead to an error "Access of possibly undefined property Scene through a reference with static type Class".
Omit MovieClip and scenes as source for organising your project, and code on the timeline. Use Document class as entry point. This tutorial should help you to grasp main concept.
package {
import flash.display.Sprite;
import flash.display.StageAlign;
import flash.display.StageScaleMode;
import flash.events.Event;
public class StackOverflow extends Sprite {
public function StackOverflow() {
addEventListener(Event.ADDED_TO_STAGE, onAdded);
}
private function onAdded(e:Event):void {
removeEventListener(Event.ADDED_TO_STAGE, onAdded);
stage.align = StageAlign.TOP_LEFT;
stage.scaleMode = StageScaleMode.NO_SCALE;
setup();
}
private function setup():void {
const padding:int = 20;
//Initiate your UI components and place them in the display list
var menu:MyMenu = new MyMenu();
var buttons:MyFooterButtons = new MyFooterButtons();
var etc:AnotherComponent = new AnotherComponent();
addChild(menu);
addChild(buttons);
addChild(etc);
menu.x = menu.y = padding;
//Place them and initialise with concrete information
}
}
}
For example, MyMenu, MyFooterButtons, AnotherComponent could be MovieClip/Sprite in the library with export settings, where you did all your work with placement, styling, etc
I had a same problem. I wanted to check from an external Class the current scene name and depending on the name (name of the game level) to pass some values in some attributes… So what I did and it worked is that.
//main in 1st frame of the fla
stop();
var myCheckSceneClass: CheckSceneClass = new CheckSceneClass();
myCheckSceneClass.myCurrentScene = currentScene;
myCheckSceneClass.checkScene();
//CheckSceneClass
package {
import flash.events.MouseEvent;
import flash.display.MovieClip;
import flash.display.Scene;
public class CheckSceneClass extends flash.display.MovieClip {
public var myCurrentScene : Scene;
public function CheckSceneClass () {
}
public function checkScene() {
switch (myCurrentScene.name) {
case "Scene 1":
trace("It is the 1st Scene");
break;
default:
trace("Error with Scene Name");
break;
}
}
}
}

Actionscript 3.0 method keeps repeating, cant figure out why

I'm having difficulty debugging my audio slider. I'm pretty sure my problems lies in the fact that one of my methods, changeVolumeRedFireball is just constantly repeating at a very fast rate. I get a glitchy sound every once in a while in my game, so it seems to correlate. I traced "output" inside the method and quickly found out it's repeating at a high rate.
Problem is, I cannot figure out WHERE this is coming from! One other note. This only starts repeating once I hold down my slider, hence activating the changeVolumeRedFireball from dragSliderRedFireball
I do have other methods from other classes referencing methods in this class. They only access playSoundRedFireball and stopSoundRedFireball though, so I don't see why that would have any effect. Also, this class is instantiated by my document class upon start up of the game. I suppose I'll put in the relevant code from the document class if requested, but I just didn't think it would affect this problem at all.
package {
import flash.display.Sprite;
import flash.display.Graphics;
import flash.events.MouseEvent;
import flash.events.Event;
import flash.net.URLRequest;
import flash.media.Sound;
import flash.media.SoundChannel;
import flash.media.SoundTransform;
import flash.geom.Rectangle;
public class VolumeRedFireball extends Sprite {
public var redFireballSnd:Sound = new Sound();
public var redFireballChannel:SoundChannel = new SoundChannel();
//URLRequest=new URLRequest("solitude.wav");
//Make sure you pass URLRequest an audio file on your computer.
public var reqRedFireball:EnemyAppearSound = new EnemyAppearSound();
public var boundaryRedFireball:Rectangle;
public var spriteRedFireball:Sprite;
public var sliderRedFireball:Sprite;
public var xPosRedFireball:Number;
public var yPosRedFireball:Number;
public static var volRedFireball:Number = 1;
public function VolumeRedFireball() {
this.addEventListener(Event.ADDED_TO_STAGE, onStageRedFireball,false,0,true);
volRedFireball=1;
redFireballChannel.soundTransform=new SoundTransform(volRedFireball)
}
public function onStageRedFireball(e:Event):void
{
//We remove it immediately so that it doesn't get called multiple times
//As the instance is added to the display list tree
this.removeEventListener(Event.ADDED_TO_STAGE, onStageRedFireball);
xPosRedFireball = 320;
yPosRedFireball = 170;
initRedFireball();
}
public function initRedFireball():void {
spriteRedFireball = new Sprite();
redFireballChannel.stop();
spriteRedFireball.graphics.beginFill(0x999999);
spriteRedFireball.graphics.drawRect(xPosRedFireball,yPosRedFireball,100,5);
spriteRedFireball.graphics.endFill();
addChild(spriteRedFireball);
spriteRedFireball.x-=spriteRedFireball.width/2;
sliderRedFireball = new Sprite();
sliderRedFireball.graphics.beginFill(0xFF0000);
sliderRedFireball.graphics.drawCircle(xPosRedFireball+50,yPosRedFireball, 15);
sliderRedFireball.graphics.endFill();
addChild(sliderRedFireball);
sliderRedFireball.addEventListener(MouseEvent.MOUSE_DOWN, dragsliderRedFireball);
stage.addEventListener(MouseEvent.MOUSE_UP, stopsliderRedFireball);
boundaryRedFireball=new Rectangle(-100,0,100,0);
}
public function dragsliderRedFireball(event:MouseEvent):void {
sliderRedFireball.startDrag(false,boundaryRedFireball);
sliderRedFireball.removeEventListener(MouseEvent.CLICK, dragsliderRedFireball);
sliderRedFireball.addEventListener(Event.ENTER_FRAME, changeVolumeRedFireball);
}
public function stopsliderRedFireball(event:MouseEvent):void {
sliderRedFireball.stopDrag();
sliderRedFireball.removeEventListener(MouseEvent.MOUSE_UP, stopsliderRedFireball);
}
public function changeVolumeRedFireball(event:Event):void {
volRedFireball=1+Math.round(sliderRedFireball.x)/100;
redFireballChannel.soundTransform=new SoundTransform(volRedFireball);
trace("output");
}
public function playSoundRedFireball():void
{
redFireballChannel = reqRedFireball.play();
}
public function stopSoundRedFireball():void
{
redFireballChannel.stop();
}
}
}
Changing a SoundTransform during every frame isn't good, as you are essentially undermining the audio channel. It's better if you use MouseEvent.MOUSE_MOVE to trigger volume change, as if mouse is moved, and volume slider is being dragged, then the SWF user apparently wants the volume to change. But if a user starts dragging the slider but does not move it, why changing the volume?
public function dragsliderRedFireball(event:MouseEvent):void {
sliderRedFireball.startDrag(false,boundaryRedFireball);
sliderRedFireball.removeEventListener(MouseEvent.MOUSE_DOWN, dragsliderRedFireball);
sliderRedFireball.addEventListener(MouseEvent.MOUSE_MOVE, changeVolumeRedFireball);
sliderRedFireball.removeEventListener(MouseEvent.MOUSE_UP, stopsliderRedFireball);
}
public function stopsliderRedFireball(event:MouseEvent):void {
sliderRedFireball.stopDrag();
sliderRedFireball.removeEventListener(MouseEvent.MOUSE_UP, stopsliderRedFireball);
sliderRedFireball.removeEventListener(MouseEvent.MOUSE_MOVE, changeVolumeRedFireball);
sliderRedFireball.addEventListener(MouseEvent.MOUSE_DOWN, dragsliderRedFireball);
}
Also, you have messed up your listeners. First, you are not removing the enterframe listener after you stop dragging the fireball. Second, you are not adding a start-drag listener back after the fireball has been released. And third, in your initRedFireball you are adding stopsliderRedFireball as listener to stage, for a really strange reason, but you are attempting to remove it from sliderRedFireball. Please pay precise attention on where your listeners go, what do they listen and where do you remove them and from which objects. Misuse of an enterframe listener can build up pretty quickly, and spoil you all the fun.

Actionscript 3, adding an audio slider to my game

so I've really been struggling trying to add an audio slider to my game the last couple days and I've about had it, so, let me preface this by saying I have tried EXTREMELY hard and I would love to fully understand the solution, but at this point I really just want the solution, so the more specific you can be the better.
I've thought of a number of approaches, but my actionscript skills have been too faulty to pull any of them off. Let me explain my setup a bit. I have a fully functional class that is an audio slider. It plays music, slides, does everything perfect. Now, I'm sticking it in my document class. I thought it was easiest to call this function right at the very beginning (as my title screen shows up), EVEN THOUGH I don't want music to be playing right now at the title screen. So I thought I would call it, then just simply not play the music yet. So now I have it called from my document class. Now, I want to be able to click on the options button and then addChild the audio slider. I have actually accomplished this and it works. Finally....the hard part. I want to click the play game button and then have the music start playing WHILE BEING LINKED to the options slider which has the possibility of already being moved. Really...just a standard audio slider.
I'm really stuck on linking the options screen audio slider to the music actually playing in the game. I thought I would call the Volume() function (my audio slider), and then once my playscreen starts up, call it again and then just run a method of the Volume() class from within my playscreen class. This seems to lead to problems though...and this is where I lose it. By calling the Volume() class again, am I totally reinstating my audio? Like does this mean that I'm erasing everything I have done with my audio slider in the options panel? It seems to be...because it just starts playing at the default value.
So here's some detailed code of what I'm doing. I was thinking of using navigation events, but like I said, I'm not the greatest flash programmer yet and when I tried using them before I ran into trouble. Here are my main concerns in the code.
1) Calling new_Volume in my playScreen class (called SimpleMenuMain). Does this wipe out everything I've done in my document class when I called Volume()?
2) Towards the end of the Volume() class, I made a stopMusic function. Whenever I call it from my document class, it only works if I have not yet pressed play game, further leading me to believe I'm erasing whatever I do in the options panel before pressing play game.
3) Should I be using navigation events (custom events)? If so, how exactly do I do it? I understand how to use them in general (I've already used 5 in this game), but all of my navigation events go to my document class, so I'm guessing there's some subtlety with them I'm not quite understanding. I have a custom events class..I'll post that as well.
Thank you for all the help =) this has been killing me. I will wittle down these classes to the relavent info.
Document Class
package
{
import flash.display.MovieClip;
import flash.events.Event;
import flash.events.ProgressEvent;
public class SMGDocClass extends MovieClip
{
public var playScreen:SimpleMenuMain;
public var titleScreen:TitleScreen;
public var gameOver:GameOver;
public var loadingProgress:LoadingProgress;
public var optionScreen:OptionScreen;
public var new_Volume:Volume;
public function SMGDocClass()
{
loadingProgress = new LoadingProgress();
loadingProgress.x = 0;
loadingProgress.y = 0;
addChild( loadingProgress );
loaderInfo.addEventListener( Event.COMPLETE, onCompletelyDownloaded,false,0,true );
loaderInfo.addEventListener( ProgressEvent.PROGRESS, onProgressMade,false,0,true );
}
public function showTitleScreen():void
{
titleScreen = new TitleScreen();
titleScreen.addEventListener(NavigationEvent.START,onRequestStart,false,0,true);
titleScreen.addEventListener(NavigationEvent.OPTIONS,onRequestOptions,false,0,true);
titleScreen.x = 0;
titleScreen.y = 0;
addChild(titleScreen);
removeChild(loadingProgress);
new_Volume = new Volume();
new_Volume.stopMusic();
stage.focus = playScreen;
stage.stageFocusRect = false;
}
public function onStickman1Death(stickman1Event:Stickman1Event):void
{
var finalScore:Number = playScreen.getFinalScore();
var finalClockTime:Number = playScreen.getFinalClockTime();
gameOver = new GameOver();
gameOver.addEventListener(NavigationEvent.RESTART,onRequestRestart,false,0,true);
gameOver.addEventListener(NavigationEvent.MAINMENU,onRequestMainMenu,false,0,true);
gameOver.x = 5;
gameOver.y = 6;
gameOver.setFinalScore( finalScore );
gameOver.setFinalClockTime( finalClockTime );
addChild(gameOver);
new_Volume.stopMusic();
removeChild(playScreen);
playScreen = null;
}
public function onRequestStart( navigationEvent:NavigationEvent ):void
{
playScreen = new SimpleMenuMain();
playScreen.addEventListener( Stickman1Event.DEAD, onStickman1Death,false,0,true );
playScreen.x = 0;
playScreen.y = 0;
addChild( playScreen );
dispatchEvent(new Stickman1Event(Stickman1Event.DEAD));
removeChild(titleScreen);
titleScreen = null;
stage.focus = playScreen;
stage.stageFocusRect = false;
}
public function restartGame():void
{
playScreen = new SimpleMenuMain();
playScreen.addEventListener(Stickman1Event.DEAD, onStickman1Death,false,0,true);
playScreen.x = 0;
playScreen.y = 0;
addChild(playScreen);
removeChild(gameOver);
gameOver = null;
}
public function onRequestMainMenu( navigationEvent:NavigationEvent):void
{
titleScreen = new TitleScreen();
titleScreen.addEventListener(NavigationEvent.START,onRequestStart,false,0,true);
titleScreen.addEventListener(NavigationEvent.OPTIONS,onRequestOptions,false,0,true);
titleScreen.x = 0;
titleScreen.y = 0;
while(numChildren > 0)
{
removeChildAt(0);
}
addChild(titleScreen);
stage.focus = playScreen;
stage.stageFocusRect = false;
}
public function onCompletelyDownloaded( event:Event ):void
{
gotoAndStop(3);
showTitleScreen();
}
public function onProgressMade( progressEvent:ProgressEvent ):void
{
loadingProgress.setValue( Math.floor( 100 * loaderInfo.bytesLoaded / loaderInfo.bytesTotal ) );
}
public function onRequestOptions(navigationEvent:NavigationEvent):void
{
optionScreen = new OptionScreen();
optionScreen.addEventListener(NavigationEvent.MAINMENU,onRequestMainMenu,false,0,true);
optionScreen.x = 0;
optionScreen.y = 0;
addChild(optionScreen);
removeChild(titleScreen);
}
}
}
Play Screen Class
package {
import flash.display.MovieClip;
import flash.utils.Timer;
import flash.events.TimerEvent;
import flash.ui.Mouse;
import flash.events.KeyboardEvent;
import flash.ui.Keyboard;
import flash.events.Event;
import flash.media.SoundChannel;
public class SimpleMenuMain extends MovieClip {
public var gameTimer:Timer;
public var stickman1:Stickman1;
public var new_Volume:Volume;
public function SimpleMenuMain() {
currentLevelData = new LevelData( 1 );
setBackgroundImage();
new_Volume = new Volume();
gameTimer = new Timer(25);
gameTimer.addEventListener(TimerEvent.TIMER, onTick, false, 0, true);
gameTimer.start();
addEventListener( Event.ADDED_TO_STAGE, onAddToStage );
}
public function onAddToStage(event:Event):void
{
stage.addEventListener(KeyboardEvent.KEY_DOWN, onKeyPress,false,0,true);
stage.addEventListener(KeyboardEvent.KEY_UP, onKeyRelease,false,0,true);
}
public function onTick(timerEvent:TimerEvent):void
{
var stickman1HasBeenHit:Boolean = false;
var i:int = army1.length - 1;
var enemy1:Enemy1;
while ( i > -1 )
{
enemy1 = army1[i];
if (enemySpeed == 1)
{
enemy1.moveABit(3,-1.5);
}
if (enemySpeed == 2)
{
enemy1.moveABit(3.6,-1.8);
}
if ( PixelPerfectCollisionDetection.isColliding( stickman1, enemy1, this, true ) )
{
getFinalScore();
gameTimer.stop();
bgmSoundChannel.stop();
stickman1HasBeenHit = true;
}
if ( enemy1.y > 400 )
{
removeChild( enemy1 );
army1.splice( i, 1 );
}
i = i - 1;
}
var i:int = army2.length - 1;
var enemy2:Enemy2;
while ( i > -1 )
{
enemy2 = army2[i];
if (enemySpeed2 == 1)
{
enemy2.moveABit(6,-3);
}
if (PixelPerfectCollisionDetection.isColliding(stickman1, enemy2, this, true ) )
{
gameTimer.stop();
bgmSoundChannel.stop();
stickman1HasBeenHit = true;
}
if ( enemy2.y > 400 )
{
removeChild( enemy2 );
army2.splice( i, 1 );
}
i = i - 1;
}
if (stickman1HasBeenHit)
{
dispatchEvent(new Stickman1Event(Stickman1Event.DEAD));
}
}
}
Volume Class
package {
import flash.display.Sprite;
import flash.display.Graphics;
import flash.events.MouseEvent;
import flash.events.Event;
import flash.net.URLRequest;
import flash.media.Sound;
import flash.media.SoundChannel;
import flash.media.SoundTransform;
import flash.geom.Rectangle;
public class Volume extends Sprite {
public var snd:Sound = new Sound();
public var channel:SoundChannel = new SoundChannel();
//URLRequest=new URLRequest("solitude.wav");
//Make sure you pass URLRequest an audio file on your computer.
public var req:BackgroundMusic = new BackgroundMusic();
public var boundary:Rectangle;
public var sprite:Sprite;
public var slider:Sprite;
public var xPos:Number;
public var yPos:Number;
public var vol:Number;
/*
Our request is loaded into the sound object and plays through
our channel. Volume is initially set at 50% and passed as a
transformation to our our channels soundTransform property
(a fancy way of saying volume). The init() function is called.
*/
public function Volume() {
this.addEventListener(Event.ADDED_TO_STAGE, onStage,false,0,true);
startMusic();
}
/*
The init function creates and draws a rectangle and circle
to the stage and centers them based on the height and
width of the stage. In addition, a rectangle object is
created to 'contain' the sliding circle, like an imaginary box.
We pass -100 as the x value because it is added relative
to our sprite. If we set its x value at 0, or the sprites default x
value,the boundary would stop and start at the slider sprite. Change
-100 to 0 in the rectangle object to get a better idea of its use.
*/
public function onStage(e:Event):void
{
//We remove it immediately so that it doesn't get called multiple times
//As the instance is added to the display list tree
this.removeEventListener(Event.ADDED_TO_STAGE, onStage);
xPos = 320;
yPos = 132;
/* Now that we have a reference to the stage, let's go ahead and create our slider */
init();
}
public function init():void {
sprite = new Sprite();
channel.stop();
sprite.graphics.beginFill(0x999999);
sprite.graphics.drawRect(xPos,yPos,100,5);
sprite.graphics.endFill();
addChild(sprite);
sprite.x-=sprite.width/2;
slider = new Sprite();
slider.graphics.beginFill(0xFF0000);
slider.graphics.drawCircle(xPos+50,yPos, 15);
slider.graphics.endFill();
addChild(slider);
slider.addEventListener(MouseEvent.MOUSE_DOWN, dragSlider);
stage.addEventListener(MouseEvent.MOUSE_UP, stopSlider);
boundary=new Rectangle(-100,0,100,0);
}
/*
dragSlider runs when the use holds the mouse button down. A
startDrag method is used on our sprite where we specify boundary
as our dragging limits. A new event handler designed
to change the mouse volume is subsequenlty called per frame, where
the slider.x property determines volume.
*/
public function dragSlider(event:MouseEvent):void {
slider.startDrag(false,boundary);
slider.removeEventListener(MouseEvent.CLICK, dragSlider);
slider.addEventListener(Event.ENTER_FRAME, changeVolume);
}
/*
Stops dragging and removes the event listener to save on space. Again,
volume will be based on the sliders current x position, which is
constantly being recalculated per frame because we used an
ENTER_FRAME event.
*/
public function stopSlider(event:MouseEvent):void {
slider.stopDrag();
slider.removeEventListener(MouseEvent.MOUSE_UP, stopSlider);
}
/*
This function is constantly recalculating the vol variable
based on the sliders x position, relative to the length of
our rectangle. Creates a decimal range from 0 to 1, where 1
represents 100% volume and 0 represents mute. Anything exceeding
100% causes distortion.
*/
public function changeVolume(event:Event):void {
vol=1+Math.round(slider.x)/100;
channel.soundTransform=new SoundTransform(vol);
}
public function onBackgroundMusicFinished(event:Event):void
{
channel = req.play();
channel.addEventListener( Event.SOUND_COMPLETE, onBackgroundMusicFinished );
vol=1;
channel.soundTransform=new SoundTransform(vol);
}
public function startMusic():void
{
channel=req.play();
channel.addEventListener( Event.SOUND_COMPLETE, onBackgroundMusicFinished,false,0,true );
vol=1;
channel.soundTransform=new SoundTransform(vol);
}
public function playMusic():void
{
channel = req.play();
}
public function stopMusic():void
{
channel.stop();
}
}
}
Navigation Event Class
package
{
import flash.events.Event;
public class NavigationEvent extends Event
{
public static const RESTART:String = "restart";
public static const START:String = "start";
public static const MAINMENU:String = "mainmenu";
public static const OPTIONS:String = "options";
public static const STOPMUSIC:String = "stopmusic"
public function NavigationEvent( type:String, bubbles:Boolean = false, cancelable:Boolean = false )
{
super( type, bubbles, cancelable );
}
public override function clone():Event
{
return new NavigationEvent( type, bubbles, cancelable );
}
public override function toString():String
{
return formatToString( "NavigationEvent", "type", "bubbles", "cancelable", "eventPhase" );
}
}
}
Options Screen Class
package {
import flash.display.MovieClip;
import flash.display.SimpleButton;
import flash.events.MouseEvent;
import flash.text.TextField;
import flash.ui.Mouse;
import flash.net.SharedObject;
import flash.events.Event;
public class OptionScreen extends MovieClip {
public var mainMenuButton:SimpleButton;
private var new_Volume:Volume;
public var myEvent:Event;
public function OptionScreen() {
Mouse.show();
new_Volume = new Volume();
addChild(new_Volume);
mainMenuButtonOptions.addEventListener(MouseEvent.CLICK, onClickMainMenu,false,0,true);
}
public function onClickMainMenu(mouseEvent:MouseEvent):void
{
dispatchEvent( new NavigationEvent(NavigationEvent.MAINMENU));
}
}
}
Finally....Title Screen Class
package
{
import flash.display.MovieClip;
import flash.display.SimpleButton;
import flash.events.MouseEvent;
import flash.ui.Mouse;
public class TitleScreen extends MovieClip
{
public var startButton:SimpleButton;
public function TitleScreen()
{
Mouse.show();
startButton.addEventListener( MouseEvent.CLICK, onClickStart,false,0,true );
titleOptionsButton.addEventListener( MouseEvent.CLICK, onClickOptions,false,0,true );
}
public function onClickStart( event:MouseEvent ):void
{
dispatchEvent( new NavigationEvent( NavigationEvent.START ) );
}
public function onClickOptions( event:MouseEvent ):void
{
dispatchEvent( new NavigationEvent( NavigationEvent.OPTIONS ) );
}
}
}
You can change the volume on individual SoundChannels, or globally on the SoundMixer class. Your current implementation is the former, and defined every time you instantiate your class.
See Adobe's Controlling sound volume and panning.
You can also set global volume and pan values for all sounds at once
using the soundTransform property of the SoundMixer class, as the
following example shows:
SoundMixer.soundTransform = new SoundTransform(1, -1);
You could also store your volume and reference it as a global/static variable.
public static var soundLevel:Number = 100;
public function changeVolume(event:Event):void {
soundLevel = 1+Math.round(slider.x)/100;
channel.soundTransform=new SoundTransform(soundVolume);
}
This way, any slider object changes the same variable. Of course, then you're left with needing to update N number of soundObjects to the new soundLevel, which is why using the SoundMixer is a better solution.
No. The new instance of Volume class doesn't overwrite the SoundChannel previously created. However, because Volume is a DisplayObject, if its only reference was on the stage, and starting your game takes to you a different frame, the old Volume object would be removed from the DisplayList and could be garbage collected.
Definitely sounds like you're moving between frames, in which case the older Volume object would be removed from DisplayList and GC'ed. This comes up a lot, but I'll reiterate: don't use stage frames. There are better (less headache inducing) ways of programming what you need.
Your choice of Nav events is discretional. You could just as easily write a nav class which handles all input events as they come from the system, and appropriately call your methods. Personally, I don't like Flash style events. There's often other data I want to pass to my methods apart from the usual variable suspects, thereby leading to clutter induced by custom events. You could also clear that up with global state tracking, and simply reference your current state from your generic methods to determine their response to any given nav event.

How to improve this AS3 code structure to be more effective?

I have made an AS3 code to be a function. But I think my code is too lengthy. Could you help to improve it? Thank you!
I created test.fla first and added 5 grey block(external pictures from PSD) to stage. My function is to display different pictures when hovering mouse on corresponding grey block.
I converted those 5 grey blocks to Movie Clip and set instance name as sp1, sp2, sp3, sp4 and sp5. Then I created a document class, test.as and set 5 EventListener.
sp1.addEventListener(MouseEvent.MOUSE_OVER,clickmouse1);
sp2.addEventListener(MouseEvent.MOUSE_OVER,clickmouse2);
sp3.addEventListener(MouseEvent.MOUSE_OVER,clickmouse3);
sp4.addEventListener(MouseEvent.MOUSE_OVER,clickmouse4);
sp5.addEventListener(MouseEvent.MOUSE_OVER,clickmouse5);
So my first question is can I have any method to combine those 5 EventListener to be one? Because in my mind, so many EventListener will cost much more resource of PC.
My second question is I set 5 target pictures as 5 class.
In test.as I created code below:
public class EuroCup extends Sprite{
var arr:Array=new Array();
var Res1:Result609=new Result609();
var Res2:Result610=new Result610();
var Res3:Result611=new Result611();
var Res4:Result612=new Result612();
var Res5:Result613=new Result613();
var i:int=0;
public function EuroCup() {
arr[1]=Res1;
arr[2]=Res2;
arr[3]=Res3;
arr[4]=Res4;
arr[5]=Res5;
}
}
I think that is too lengthy. Is there any way to simplify it?
Here is the test.fla and test.as:Download
Whatever, thank u guys!
Restructuring:
public class EuroCup extends Sprite {
private var arr:Array;
public function EuroCup() {
arr = [ new Result609(), new Result610(),
new Result611(), new Result612(), new Result613()
];
}
}
Then use results as arr[0], arr[1] and so on. Also, if you have several sprites to listen clicks on, with similar listeners, you can connect all such sprites to single listeners and use event.target to distinguish them, where event is MouseEvent. Or place them into container and create one listener to that container - again, event.target will tell what sprite is clicked.
And yet two things - every time you see new Array(), replace it with [] - its faster and shorter. And place all code into constructor, not class body - it will be compiled to be executed faster.
You can/should use a Dictionary for associations between the grey rects and the images to display.
package {
public class EuroCup {
private var _children:Array, _current:Sprite, _map:Dictionary;
public function EuroCup() {
super();
initialize();
}
protected function initialize():void {
_children = [];
_map = new Dictonary();
// i don't know the image's symbol name.
// _map[_children[_children.length] = new Result609()] = new SYMBOL_NAME();
for each(var child:Sprite in _children) {
child.addEventListener(MouseEvent.CLICK, click_handler);
}
}
private function click_handler(event:MouseEvent):void
{
if (_current) {
_current.visible = false; // or use fading, etc
}
_current = _map[event.currentTarget] as Sprite;
if (_current) {
_current.visible = true; // or use fading, etc
}
}
}
}
One option for simplifying the code would be to associate the sp and Res instances with each other by identity, using a Dictionary. That allows you to avoid the work of tracking array indices, which is half of the reason you have separate event handler methods. Once the instances are associated by identity, then you can use the currentTarget property of a dispatched event to determine which element in the Dictionary you want to show on the stage.
package {
import flash.display.MovieClip;
import flash.display.Sprite;
import flash.events.MouseEvent;
import flash.events.Event;
import flash.utils.Dictionary;
public class test extends Sprite
{
var dict:Dictionary = new Dictionary();
var visibleResult:MovieClip;
public function test()
{
dict[sp1]=new Result609();
dict[sp2]=new Result610();
dict[sp3]=new Result611();
dict[sp4]=new Result612();
dict[sp5]=new Result613();
sp1.addEventListener(MouseEvent.MOUSE_OVER,clickmouse);
sp2.addEventListener(MouseEvent.MOUSE_OVER,clickmouse);
sp3.addEventListener(MouseEvent.MOUSE_OVER,clickmouse);
sp4.addEventListener(MouseEvent.MOUSE_OVER,clickmouse);
sp5.addEventListener(MouseEvent.MOUSE_OVER,clickmouse);
}
private function clickmouse(evt:MouseEvent):void
{
if(visibleResult)
{
removeChild(visibleResult);
}
var Res:MovieClip = dict[evt.currentTarget] as MovieClip;
addChild(Res);
Res.x=300;
Res.y=400;
visibleResult=Res;
}
}
}
If you expect to have more than 5 sp instances in the application, then you could use a loop to assign the event listeners. But for less than 10 instances, you probably don't gain much from a loop.
I would go for a more simple version; add only one event listener and use Event.target to determine on which item is clicked, using a switch-statement.
This is helpful if the buttons should do different things.
package
{
import flash.display.Sprite;
import flash.events.MouseEvent;
public class Test extends Sprite
{
public var sp1:Sprite;
public var sp2:Sprite;
public var sp3:Sprite;
public function Test()
{
this.addEventListener(MouseEvent.MOUSE_OVER, handleClick);
}
private function handleClick(event:MouseEvent):void
{
trace("Clicked on: " + event.target)
switch (event.target)
{
case this.sp1:
{
// do something here
break;
}
case this.sp2:
{
// do something here
break;
}
case this.sp3:
{
// do something here
break;
}
default
{
trace("No handler defined for: " + event.target)
}
}
}
}
}
However, you can also make smart use of it's type. Let's say all you buttons extend a custom class called CustomButton, and they all need to do the same (like call a function), but with a parameter based on it's id.
This is helpful if the buttons should basically do the same thing.
package
{
import flash.display.Sprite;
import flash.events.MouseEvent;
public class Test extends Sprite
{
public function Test()
{
this.addEventListener(MouseEvent.MOUSE_OVER, handleClick);
}
private function handleClick(event:MouseEvent):void
{
if (event.target is CustomButton)
{
var button:CustomButton = event.target as CustomButton; // you're now sure it's a CustomButton
this.showById(button.id); // let's say CustomButton has a public var 'id'
}
}
private function showById(id:int):void
{
// do something
}
}
}
Hope that helps.
Tip: Always start your class+filename with a capital. Variables start with capitals. This is very common in the actionscript world.