AS3 Setup Buttons for All Frames in Document Class - actionscript-3

I'm making a simple interface with Flash. Let's say we've got:
frame 1: 1 button that advances to frame 10 (goto10)
frame 10: 2 buttons, one advances to frame 20 (goto20), one opens a URL (openURL)
frame 20: 3 buttons, one goes back to frame 1 (goto1), one goes to frame 10 (goto10), and one opens a URL (openURL)
package {
import flash.display.MovieClip
import flash.events.Event;
import flash.net.URLRequest;
import flash.net.URLLoader;
import flash.display.SimpleButton;
public class NKE_DocumentClass extends MovieClip {
public var goto1:SimpleButton = new SimpleButton();
public var goto10:SimpleButton = new SimpleButton();
public var goto20:SimpleButton = new SimpleButton();
public var openURL:SimpleButton = new SimpleButton();
public function NKE_DocumentClass() {
goto1.addEventListener(MouseEvent.CLICK,function(self:MouseEvent):void{clickGo(self,1)});
goto10.addEventListener(MouseEvent.CLICK,function(self:MouseEvent):void{clickGo(self,10)});
goto20.addEventListener(MouseEvent.CLICK,function(self:MouseEvent):void{clickGo(self,20)});
openURL.addEventListener(MouseEvent.CLICK,function(self:MouseEvent):void{urlGo(self,"http://google.com")});
}
public function clickGo(event:MouseEvent, nextCue:int):void {
gotoAndStop(nextCue);
trace("Advanced to: " + nextCue);
}
public function urlGo(event:MouseEvent, goURL:String):void {
var request:URLRequest = new URLRequest(goURL);
new URLLoader(request);
trace("Executed URL: " + goURL);
}
}
}
Problem is, once I leave frame 1, none of the buttons work... they're simply unresponsive. It seems like the code doesn't stay loaded once it leaves frame 1.
Thoughts?

I'm pressuming the problem is because when this code is first executed (as a Document Class) the only button that exists is the button on frame 1? This is under the assumption than you've created buttons in the Flash IDE then added them to the stage from the library on the specific keyframes.
I see you've created the SimpleButtons programmatically but since they haven't been added to the stage in the code you've shown, the presumption is that you've just called them the same names as the buttons you've placed on stage? Correct me if I'm wrong and I'll try to offer some other advice if the below doesn't help.
One solution would be to create them all on the first frame then switch their visibility on and off depending on when you need them.
If you're not sure how:
goto10.visible = false;
etc etc
I can't remember now without testing but if you have placed them all on the stage on different keyframes this may cause a problem.
Back in the days of putting code on the timeline if you put code on frame 1 but it referenced objects that weren't on frame 1 then the code would fail (this is probably what's happening with your document class - it's running when not all objects exist).
I would make sure they're all on one layer without any keyframes, from frame 1, and you just switch their visibility on and off. Alternatively, let your classes add and remove the buttons and other interface elements and don't use the timeline at all.

Related

AS3 || Using same function with different variables

I'm very new to AS3 and I'm trying to learn by experimenting in flash, by making a simple 2D farming game with very simple code.
I've made one crop field out of 6 that works, which is a movieclip with different frames for each fruit growing. For example, frame 1-5 is a strawberry growing where frame 5 is when it's ready to be picked, and then 6-10 is of carrots, etc
Is there a way for me to make it so that I don't have to write the exact same code for the next crop field, and instead change the variables in this code depending on which crop field you click on?
Here's an example of the code
if (field1.currentFrame == 1)
{
field1.nextFrame();
infoText.text = "You've watered the crop. Let's wait and see how it turns out!";
function plantStrawberry():void
{
field1.nextFrame();
if (field1.currentFrame == 5)
{
clearInterval(strawberryInterval);
}
}
var strawberryInterval = setInterval(plantStrawberry,5000);
}
pls no judgerino, as said, I'm very new to AS3, lol.
There are a few ways to go about being DRY (don't repeat yourself) with your code in this scenario. The best way, would be to learn to use classes. Classes are blueprints, and are made for these very scenarios.
Here is an example of a simple class to do what you'd like. In Flash/Animate, go to file, then new, then 'ActionScript 3.0 Class' - give it a name of Crop.
In the document that comes up, there should be some basic boilerplate code. Everything should wrapped in a package. The package tells flash where to find this class - so this example, leave it as is (just package {) and save this file in the same folder as your .fla. All functions need to be wrapped in a class declaration, this should be generated for you based off the name you put in (Crop). Next you'll see one function, that has the same name as the class. This is called a constructor, and this function runs whenever you create a new instance of this class. Since classes are blueprints, you create instances of them that are objects - those objects then get all the code you put in this class.
So, to start, you should have this:
package {
public class Crop {
public function Crop() {
// constructor code
}
}
}
Let's go ahead and put your code in. See the code comments for details:
package {
//imports should go here
import flash.display.MovieClip;
import flash.events.Event;
import flash.events.TimerEvent;
import flash.utils.Timer;
//lets make this class extend MovieClip - that means it will be a MovieClip in addition to everything else you add below
public class Crop extends MovieClip {
//instead of setInterval, use a timer - it's easier to manage and cleanup
//in class files, variables and functions have access modifiers, that's what the public and private words are about
//private means only this class can ever use the var/function
private var timer:Timer;
public function Crop() {
//initialize the timer - have it tick every 5 seconds, and repeat 4 times (to move you from frame 1 - 5)
timer = new Timer(5000, 4);
//listen for the TIMER event (which is the tick) and call the function 'grow' when the timer ticks
timer.addEventListener(TimerEvent.TIMER, grow);
}
//a function that starts the timer ticking
public function startGrowing():void {
timer.start();
}
//this function is called every timer tick.
private function grow(e:Event):void {
this.nextFrame(); //go to the next frame of your crop
}
}
}
Save the file. Now that you have this class, you need to attach it to your library assets so they all get this functionality.
In the library panel, for each of your crop objects, right click (or ctrl+click on Mac) and go to properties. In the properties, click advanced, and give it a unique class name (for instance Strawberry). Then in the base class field, put Crop (the class we just made). Repeat for the others.
Now on your timeline, when you want a crop to start growing, you can do:
field1.startGrowing(); //assuming your instance `field1` is one of the crops that you assigned the base class `Crop` to
Hopefully this gives an entry point into the power of classes. You can add more functionality into this one and it automatically apply to all the crops you attached it to.
Although BFAT's tutorial is absolutely correct, it is not the only way to do things, moreover, if you ever move from Flash and AS3 to something else, or even try Starling (a framework that allows to build fast and non-laggy mobile applications in Flash/AS3), you'll find that concept not applicable. It is very Flash-y and I applause to it though.
Instead of making each field subclass the abstract (means, it is never instantiated by itself) Crop class, you can make the Crop class take 1 of these 6 fields as an argument on creation (or later). Basically, you tell "I want to make crop field with wheat graphics". So, let me redo that class a bit.
package
{
// Imports.
import flash.display.Sprite;
import flash.display.MovieClip;
import flash.utils.Timer;
import flash.events.Event;
import flash.events.TimerEvent;
public class Crop extends Sprite
{
// I agree with the use of Timer.
private var timer:Timer;
// Visuals go here.
private var field:MovieClip;
// Class constructor.
public function Crop(FieldClass:Class)
{
// With "new" keyword you can omit ()
// if there are no mandatory arguments.
field = new FieldClass;
field.stop();
addChild(field);
}
// A function that starts the timer ticking.
public function startGrowing():void
{
timer = new Timer(5000, 4);
timer.addEventListener(TimerEvent.TIMER, grow);
timer.start();
}
// This function is called every timer tick.
private function grow(e:Event):void
{
// Command the graphics to go to the next frame.
field.nextFrame();
}
}
}
Then, the usage. When you create fields, you need to set AS3 classes to them to have access, leaving base class as is, Flash will automatically set it to non-specific MovieClip. Lessay, you have crops.Wheat field and crops.Barley field.
import Crop;
import crops.Wheat;
import crops.Barley;
var W:Crop = new Crop(Wheat);
var B:Crop = new Crop(Barley);
addChild(W);
addChild(B);
B.x = 100;
W.startGrowing();
B.startGrowing();

Actionscript 3 Multiple sounds playing at the same time when accessing different movieclips

I am at the moment trying to create an interactive movie, structured so that each keyframe in the timeline contains a movieclip navigated to using buttons inside the movieclips, with the appropriate code inside the main timeline.
The problem right now is that when you access the movieclip inside frame 3, the sound from frame 2 also plays simultaneously. After doing some research i found that this appears to be a bug with flash itself, and most of the time is dealt with using SoundMixer.stopAll();. Sadly, i have no idea how to use it to kill the sound from frame 2 when only frame 3 is accessed.
I know that when accessing frame 2 instead, only frame 2 is played, which should mean that flash basically goes through all frames on the way to the frame you are supposed to go to.
This is the limited code i am using at the moment:
Frame 1:
import flash.events.MouseEvent;
import flash.display.SimpleButton;
import flash.media.SoundMixer;
stop();
var soundVar:int = 0;
var Choice1F:SimpleButton;
var Choice1R:SimpleButton;
this.Val1.Choice1F.addEventListener(MouseEvent.CLICK, function(me:MouseEvent):void{buttonHandler(me, 2)});
this.Val1.Choice1R.addEventListener(MouseEvent.CLICK, function(me:MouseEvent):void{buttonHandler(me, 3)});
function buttonHandler(e:MouseEvent, Value:int): void{
SoundMixer.stopAll();
soundVar = Value;
this.gotoAndPlay(Value);
}
Frame 2:
import flash.media.SoundMixer;
stop();
if(soundVar == 3){
SoundMixer.stopAll();
}
Frame 3 simply contains a stop(); statement. The code in frame 2 was a futile attempt from me to make it kill the sound on its way to frame 3. Hopefully, you guys can think of a better solution, if one even exists.
The correct structure of the project assumes that you control the playback of music and sounds with a special instance of custom class. And timeline you use only gave him command when and what to do.
One SoundChannel and couple of Sound's will do the trick.
You could use this one
package src.utils{
import flash.media.Sound;
import flash.media.SoundChannel;
import flash.net.URLRequest;
public dynamic class BackgroundMusicPlayer extends Object{
public var playlist;
public var sndChannel;
private var ID;
public function BackgroundMusicPlayer(srcList:Array){
playlist=[];
for(var i=0;i<srcList.length;i++){
var src= new URLRequest(srcList[i]);
var newSound = new Sound(src);
playlist.push(newSound);
}
}
public function playMusic(id){
if (sndChannel!=undefined) {
sndChannel.stop();
}
sndChannel = playlist[id].play();
ID=id;
sndChannel.addEventListener("soundComplete",replayListener);
}
public function replayListener(e){
sndChannel = playlist[ID].play();
}
}
}
import class to you timeline, create instance passing him files list
var musicPlayer = new BackgroundMusicPlayer("music1.mp3","music2.mp3");
And then you want start some sound, call
musicPlayer.playMusic(0);
If you want use imported to project sounds, just share them to actionscript, give them class names and slightly modify given class constructor
public function BackgroundMusicPlayer(srcList:Array){
playlist=[];
for(var i=0;i<srcList.length;i++){
playlist.push(srcList[i]);
}
}
So your instance creation now should be
var musicPlayer = new BackgroundMusicPlayer(new MySound1(),new MySound2());

Actionscript 3.0: Help Linking Document Class To And Audio Slider Class

so I've been going at actionscript 3 for a couple weeks now but I'm still a complete newb. The most difficulty I've had is linking classes to my document class. For example, I'll have a nice great class that does things wonderfully (I could just insert it as the document class of another FLA and it would provide all the functionality I need for that specific function), but now when I have to insert it as a regular class...I guess "subclassing" the document class, all goes to hell.
I know you have to change variables and instantiate things to get it to work and I sort of understand that, but it sometimes it just gets way over my head and I feel like their should be a simple solution if I ALREADY HAVE a full working class. Seems that all too often there's a billion things I need to switch around.
Anyways, I have a specific example I'm hoping someone could help explain and walk me through a bit. I went online and found some code for a slider, then spent the last few hours editing it to contain the mp3 I want, loop it, etc. etc. Now it works great on a designated FLA...I just run it as the document class and up pops a designed audio slider that changes the volume, loops and everything. Now I want to add this slider into a simple game I've been working on, but just have NO idea where to start or what to do. For now I'll keep it simple though.
Say I just have my blank document class and my audio slider class. Now when I run my game, it runs the document class of course, and from there, I want it to run my audio slider class directly. I think if I just solve this I will be able to implement it into my game. So here is my blank document class and my audio slider class! Thanks for the help!
WHAT I'VE TRIED
I attempted to create public variables in the document class for the sprite and the slider, then create a new sprite/slider once the document class runs. I thought that to be on the right track, but then it started looking like I was going to have to do that for almost all the variables in the audio slider class. I also thought...well why can't I just run Volume() in the Document Class? Still confusing me a little why that doesn't work, but it doesn't.
Blank Document Class
package {
import flash.display.MovieClip;
import flash.display.Sprite;
public class ASDocumentClass extends MovieClip {
public function ASDocumentClass() {
}
}
}
and here is the audio slider 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=stage.stageWidth/2;
public var yPos:Number=stage.stageHeight/2;
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() {
channel=req.play();
channel.addEventListener( Event.SOUND_COMPLETE, onBackgroundMusicFinished,false,0,true );
vol=.5;
channel.soundTransform=new SoundTransform(vol);
init();
}
/*
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 init():void {
sprite = new Sprite();
sprite.graphics.beginFill(0x999999);
sprite.graphics.drawRect(xPos,yPos,200,5);
sprite.graphics.endFill();
addChild(sprite);
sprite.x-=sprite.width/2;
slider = new Sprite();
slider.graphics.beginFill(0xFF0000);
slider.graphics.drawCircle(xPos,yPos, 20);
slider.graphics.endFill();
addChild(slider);
slider.addEventListener(MouseEvent.MOUSE_DOWN, dragSlider);
stage.addEventListener(MouseEvent.MOUSE_UP, stopSlider);
boundary=new Rectangle(-100,0,200,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=.5+Math.round(slider.x)/200;
channel.soundTransform=new SoundTransform(vol);
}
public function onBackgroundMusicFinished(event:Event):void
{
channel = req.play();
channel.addEventListener( Event.SOUND_COMPLETE, onBackgroundMusicFinished );
}
}
}
It looks as though your Volume class is as you said, mostly complete and self-contained. This is good, as it will make instantiating a new instance of it within your document class easier.
Within the document, class, to instantiate a new class, you can do the following:
var new_volume:Volume = new Volume();
addChild(new_volume);
It's important to note that the stage does not come into scope within your Volume class until you have added it to the stage from within it's parent class (in this case, it's parent class is the document class).
So these two lines:
public var xPos:Number=stage.stageWidth/2;
public var yPos:Number=stage.stageHeight/2;
don't work, as the stage is undefined there. To wait until you get know stage is defined, you can use an Event.ADDED_TO_STAGE event listener. So you can re-write your Volume class a bit to look more like this:
package {
/* Imports here */
public class Volume extends Sprite {
/* Other vars here */
public var xPos:Number;
public var yPos:Number;
public function Volume(){
/* Other assignments that are not stage-dependant can go here */
this.addEventListener(Event.ADDED_TO_STAGE, onStage);
}
private 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 = stage.stageWidth/2;
yPos = stage.stageHeight/2;
/* Now that we have a reference to the stage, let's go ahead and create our slider */
init();
}
from there you can go on with business as usual, and just alter your variable values as needed to get the class to work within the confines of your player environment/document class.

How to run an animation from within another class in Actionscript 3.0

I'm new to ActionScript 3.0 and I am trying to work my way around Object Orientated Programming.
I've designed an application where there are 3 buttons on the stage and a logo. What I want to happen is when a button is clicked, the logo will fade out. Now I understand I need to manipulate the alpha properties of the logo but not sure how I would run the code once the button is clicked. I have my Main.as file loading the properties of the application. So once the FLA file runs, Main.as sets up the buttons and the logo. Now the buttons are put into a display holder and run from menuHolder.as which in turn gets the design of the buttons from button.as - so far with me?
To keep my code clean I have seperated my code into their own files (the design of the buttons into its own .as file, the arrangement of the menu buttons into its own .as file, the logo creation into its own .as file) so that the Main.as file just acts as a loader for placing everything into location on the stage.
So my question is that I want to create an animation for the logo (a fade out animation) where the code would be stored in say animationFade.as for example. Then when a button is clicked, the animation plays and the corresponding page is navigated to.
Here is my code for the setup of the menuBottons.as:
package
{
import flash.display.Sprite;
import flash.text.TextField;
import flash.events.MouseEvent;
import flash.text.TextFormat;
public class menuButtons extends Sprite
{
private var aboutMeBtn:button;
private var galleryBtn:button;
private var contactMeBtn:button;
public function menuButtons()
{
aboutMeBtn = new button();
aboutMeBtn.y = 0;
aboutMeBtn.label = "About Me";
addChild(aboutMeBtn);
aboutMeBtn.addEventListener(MouseEvent.CLICK, onButtonClick);
galleryBtn = new button();
galleryBtn.y = 75;
galleryBtn.label = "Gallery";
addChild(galleryBtn);
galleryBtn.addEventListener(MouseEvent.CLICK, onButtonClick);
contactMeBtn = new button();
contactMeBtn.y = 150;
contactMeBtn.label = "Contact Me";
addChild(contactMeBtn);
contactMeBtn.addEventListener(MouseEvent.CLICK, onButtonClick);
}
public function onButtonClick(event:MouseEvent):void
{
//code to initiate the animationFade.as file
}
}
}
it's this //code to initiate the animationFade.as file that i cannot grasp. It may be something very simple but my head seems to be spinning!
Hopefully I have provided enough information. Like I say I am just learning at the minute and trying to build up slowly.
Thank you
The best way to do this would be to dispatch an event from your button click function. Then, create a listener in your Main.as file that handles the event and tells the logo to fade out.
Maybe something like:
From menuButtons.as
public function onButtonClick(event:MouseEvent):void
{
dispatchEvent(new Event("hide_logo", true));
}
You can read up on the Event Class documentation to learn more about the parameters.
From Main.as (after you create your menuButtons instance)
menuButtonsInstance.addEventListener("hide_logo", hideLogoHandler);
function hideLogoHandler(e:Event):void {
// Fade out logo
TweenLite.to(logo, 1, {alpha:0}); // requires TweenLite
}
This example requires TweenLite from Greensock.
On a side note, for best practice your class names should start with an uppercase character. So "menuButtons" class would be "MenuButtons". Then, instance names would start with a lowercase character. Makes it easier to read if you do something like:
var menuButtons:MenuButtons = new MenuButtons();

How do I access the Document Class' properties from the time line in AS3?

I am building your standard slideshow flash header for a web page.
There are three main parts:
The Slideshow class
A controller class that is used as the projects Document Class
Some linking timeline code.
The slideshow class has all the functionality, so I used the Document class to create a new instance of the slideshow and keep a property variable called slideshow that keeps a reference the Slideshow instance.
import flash.display.MovieClip;
import flash.events.Event;
public class Header extends MovieClip
{
public var slideshow:Slideshow;
public function CSYC_Header()
{
var picturesURL:String = "images/pictures.xml";
var picturesURLFVar:String = root.loaderInfo.parameters.pictures;
picturesURL = picturesURLFVar ? picturesURLFVar : picturesURL;
slideshow = new Slideshow(picturesURL, Slideshow.FADE);
slideshow.init();
addChild(slideshow);
}
public function hello():void{trace("Hello");}
}
My next step now is to use Adobe Flash Professional to draw some play and stop buttons, and then link thier click events to call slidshow.play()/.pause(). This code is just place in the timeline:
import flash.events.MouseEvent;
pause_control_btn.addEventListener(MouseEvent.CLICK, pauseClicked);
play_control_btn.addEventListener(MouseEvent.CLICK, playClicked);
addChild(pause_control_btn);
addChild(play_control_btn);
function pauseClicked(e:MouseEvent):void
{
//the play and pause buttons are on the stage and have the following names as
// thier instance names: pause_control_btn, play_control_btn
pause_control_btn.alpha = 0;
play_control_btn.alpha = 0.37;
slideshow.pause();
}
function playClicked(e:MouseEvent):void
{
pause_control_btn.alpha = 0.37;
play_control_btn.alpha = 0;
slideshow.play();
}
Despite me being able to call regular methods that are in the Doc Class from the timeline, I can not call properties without the following error, for example when i say slideshow.play():
1061: Call to a possibly undefined method play through a reference with static type com.example.test:Slideshow.
So am I missing something obvious, or will I have to make a method on my document class every time I want to wire an event to call an object in my Document Class?
There's no need to put that code for the buttons on the timeline; you can reference those objects by their instance names from within the Document Class. That'd be the easiest solution, avoiding the timeline altogether, I think.
Otherwise possibly a call to parent.slideshow, or root.slideshow (though I think root is AS2, I don't quite remember) would give you access to that instance from the timeline. The former option is probably still the better option, and keeps your code in one place.
Hope that helps.