Actionscript to play/pause audio on different buttons - actionscript-3

I've created a few buttons in Flash. I'm trying to make it so that if you click one button, the audio starts playing for that button. If you click another button, the active audio stops and the new audio of the button you clicked last start playing.
Any help please?

What you're describing is actually quite easy to do.
First things first, I recommend importing the audio into your Flash project. Alternatively, there is a way to play it directly from an external file. This is beyond the scope of my answer, so if you need help on that, you should post a question specifically covering it.
Assuming you have imported the audio file into your Flash project's library, make an as3 instance of it. (Right click the file in the library, click Properties --> ActionScript [tab] --> [Check] Export for ActionScript & [Enter name in] Class)
Now, create a definition of the sound in your code. (Assuming your two sounds were named "mySound1" and "mySound2" in the Class field of the previous step.)
var mySound1:Sound = new mySound1();
var mySound2:Sound = new mySound2();
Now, define your sound channel.
var mySoundChannel:SoundChannel = new SoundChannel();
There are two alternate ways of stopping one sound and playing another. The first is to create one function that does both every time. The second method is to create two formulas, one for "play" and one for "stop". You will need to decide which method works best for you. I'll use the two-function method below:
function stopSound():void
{
//This stops all sound in the sound channel.
//If there is nothing playing, nothing happens.
mySoundChannel.stop();
}
//In this function, we create an argument that allows us to tell the function
//what sound to we want it to play.
function playSound(soundname:String):void
{
mySoundChannel = this[soundname].play(0, 0);
}
[Note, you can tweak the play() properties to meet your needs, doing things like starting in the middle of the song, or looping it forever. 0,0 starts at the beginning, and doesn't loop. See the documentation for this.]
Now you hook up the event listeners for the buttons. (If you need help with event listeners, read the documentation.)
myButton1.addEventListener(Mouse.CLICK, btn1Click);
myButton2.addEventListener(Mouse.CLICK, btn2Click);
function btn1Click(evt:Event):void
{
stopSound();
playSound(mySound1);
}
function btn2Click(evt:Event):void
{
stopSound();
playSound(mySound2);
}
This should be enough information to get you started. In my game core, I actually have a custom class for dealing with sound playback that gives me the ability to repeat sounds, change volume, and keep sounds from conflicting with each other. I say that to emphasize that you can do quite a bit with the sound class. Do some digging in that documentation for ideas and help.
You may also consider putting a try-catch statement in the playSound function, since it will throw an reference error if you pass a name for a sound that doesn't exist.

Related

Play one of a set of audio clips randomly?

So I'm trying to setup a web-page where when you open it, it will play a random piece of music, and when that one finishes, it will play another directly after so you get a constant stream of music, but not in the same order every time. If this goes outside the bounds of HTML and I'm looking at for instance JavaScipt than that's fine.
I know this is probably a rather easy solution, but I'm new to HTML and trying to understand it better.
Thanks in advance if I don't get back to you soon!
This is best solved using JavaScript, which is used to add behavior to a website. HTML is primarily used to structure your site.
We can get a collection of all audio elements with the querySelectorAll function. Then we find a random element with the next line and call the play method.
var audio = document.querySelectorAll("audio");
function playRandom() {
audio[Math.floor(Math.random() * audio.length)].play()
}();
For the second part of your question, you would want to listen for the ended event. Inside the event listener function, you would then call the playRandom() function.
audio.addEventListener("ended", function() {
playRandom();
});

Animating from within classes in Actionscript 3 (Not on the timeline), what's the best way?

I've found some stuff online about how to animate in actionscript 3 from within a class, but haven't been able to find a really good tutorial. I want to control the animations from a class because at some point I intend to move from the flash IDE to using flash develop, where I won't have access to the Flash IDE's timeline.
I have to be able to control an initial animation (opening a bag) which joins onto an animation loop (searching through a bag).
The only way I have been able to do this so far is to add an event listener to listen for the initial animation's final frame. Then when initialAnimation.currentFrameLabel = "Last" then I gotoAndStop("animationLoop").
This has been working fine, if a bit time-consuming. I'm just wondering if there's a better, easier way to do it? Can anyone tell me or point me towards a tutorial that does it better? Thanks very much!
Romano
I recommend instead of using an event listener, you use the method addFrameScript. Essentially you can fire a method when a specific frame number is reached.
Read the following question for more information.
actionscript3 whats the point of addFrameScript
It depends on what it is you want to do:
Usually if you are working together with an artist or want to do animations that are non-code driven, the "best way" is usually to listen for something to happen, and then start animations and on last frame of animation (or when you want to return control to code) you create an event, or use a callback or something else to let code notify that animation is complete or reached a certain point.
If you want to do something from code, the easiest way is to use an external animation library.
Tweener (https://code.google.com/p/tweener/)
TweenLite (http://www.greensock.com/tweenlite/)
Using those libraries, you would write something similar to:
function fadeOut():void {
mc.alpha = 1;
Tweener.addTween(mc, {alpha:0, time:0.275, delay:1, onComplete:onDone});
}
function onDone():void {
trace("Animation finished");
}

Flash CS6 AS3, Resetting Sound Channel

I've had a problem for a while trying to reset an audio loop to the beginning when loaded into Actionscript with this code i got from the official help forum
var alreadyExecuted:Boolean;
if(!alreadyExecuted){
alreadyExecuted=true;
var s:Sound=new SkaianSpirit();
var sc:SoundChannel=s.play(0,1);
}
What i want it to do, is loop indefinitely while the animation is playing regardless of positioning and whether or not frames have been skipped with buttons etc, but when the end of the animation comes, the track needs to end. At which point, if a button is clicked to send it back to the beginning of the animation, i want it to start the same loop as before.
Currently, it will do the first part, but will not replay.
A couple caveats:
I am not loading it through a url.
I am not using buttons to control audio volume etc. It is to play automatically when the animation starts.
I am a complete and utter novice with Actionscript. I will not know what you're talking about if you lambast me with jargon i won't understand.
I'd highly appreciate the help!
I'm always use addFrameScript function for synchronizing external sounds with animations:
UPD: mc is the name of your animation. Place this code in the place (the first frame of the parent Sprite that holds the animation for instance) where your animation is created.
var s:Sound=new SkaianSpirit();
var sc:SoundChannel;
mc.addFrameScript(1, startSound);
mc.addFrameScript(mc.totalFrames - 1, stopSound);
function startSound():void
{
sc = s.play();
}
function stopSound():void
{
sc.stop();
}

Unloading external SWF files

I'm loading multiple swf files from the main menu which is never unloaded. I've done this with the following code... Only issue is that instead of unloading to the main menu I just see a white screen as if nothing is loaded.
function BackToMenu(i:MouseEvent):void
{
var BaseMovie:MovieClip = parent.parent as MovieClip;
BaseMovie.parent.removeChild(BaseMovie);
}
EDIT: I'll explain from the start I have a MainMenu.swf. The games are loaded from MainMenu.swf when the button relating to the game is clicked. When a game button is clicked on MainMenu.swf the game loads. When the player completes a game they are presented with the exit button which unloads the current game and shows the MainMenu.swf without having to re-load it.
First, you should remove one parent to make sure you are actually removing only the game:
function BackToMenu(i:MouseEvent):void
{
var BaseMovie:MovieClip = parent as MovieClip;
BaseMovie.parent.removeChild(BaseMovie);
}
This should take care of your most pressing problem, and allow you to return to the menu. You have, however, not really unloaded the game, but only removed it from the display list. This often means, that there are still sounds running, active key and/or mouse listeners, etc. - these must all be taken care of!
And, like I said, this will only fix your immediate problem. It is, however, neither a permanent solution, nor a good one: Since the main SWF is responsible for loading the games, it should also be responsible for disposing of them. You should put cleanup code into your game, but it should only be concerned with stopping any running scripts, sounds, etc. - simple rule: anything that is started within the game, should be stopped within the game. But it should not try to access objects further up in the display hierarchy, or try to unload itself.
The much better way to do this is by replacing all the above code, and letting the main SWF take care of removing the game, as well as unloading it from memory. For this, you have to do three things:
Instead of writing actual removeChild calls, etc., let your button dispatch a custom event to notify the main SWF that it should now be removed:
function onBackButtonClicked( event:MouseEvent ):void {
destroyGame(); // this would be the function that stops all the scripts
dispatchEvent( new Event( "FINISH_GAME", true ) );
}
Note that "FINISH_GAME" is now a "bubbling" event, i.e. it travels downward in the display hierarchy. We can now listen for this event in any ancestor display object containing the game.
In your main SWF, add an event listener to the Loader when the game was successfully loaded. This is done in the event listener that is called when the load process completes:
function onLoadComplete( event:Event ):void {
var loader:Loader = event.target.loader;
loader.addEventListener( "FINISH_GAME", onFinishGame, true );
}
Use the corresponding event handler to remove the game clip:
function onFinishGame( event:Event ):void {
var loader:loader = event.currentTarget;
loader.parent.removeChild( loader );
loader.unloadAndStop();
}
A few more things to consider:
The naming conventions in ActionScript advise us to use lower case names for methods and variables, and upper case only for types.
The same naming conventions suggest we use either "on" or "handle" as a prefix for event listeners, along with the name of the event. Thus, it should be onBackToMenu or rather, onBackButtonClicked, etc.
Since I don't know anything about the code you use for loading, I just assumed you have a complete listener, and you don't keep references to the loader. If you use a member variable, you can use that instead of event.target, resp. event.currentTarget.

EventDispatcher between an as and an fla?

I am making a fighting game in Flash and while I have everything running, I am missing something: a victory/loss screen. Logically, I know how to do it:
if character.hp < 0
{
character.dead = true;
dispatchevent("death", event)
}
My problem is that I have no idea as to how to code it. I know I will use two classes and my two .fla files (unless I am wrong).
I have two .fla files that are in play here: the Menu.fla file and the Arena.fla file. Menu.fla contains the entire navigation of the game, options, character selection screens, etc. and when it is time for the player to engage in battle, it loads the Arena.fla file, which contains only the backgrounds (depending on the selected stage) and for now is set to a length of one frame only. For Arena.fla, the real action happens in my classes, but logically, I would only need HP.as and Character.as.
In Character.as, I have declared the following variable:
var isDead:Boolean = false; //is character dead?
In HP.as, believe I should have the following:
if(currentHp<0)
{
currentHp = 0;
character.isDead = true; //declared as var `character:Object;`
EventDispatcher.dispatchEventListener("playerDead", playerDead);
}
And finally, in Arena.fla, I want to be able to detect the above-mentioned eventlistener and simply move on to a second frame which will display a message in the style of "PLAYER ONE HAS WON" or "PLAYER ONE HAS LOST" with a button that will allow me to go back to the character selection screen. This is the first part in which I am stuck: how do I detect the dispatched event listener in my main .fla file?
Secondly, if the player clicks on the "CONTINUE" button, which displays regardless if the player has won or lost, how can my Menu.fla (which loads the Arena.swf) detect this click event, unload the game, and go back to the character selection screen?
Thank you in advance for helping me out. I realize this is a lot of text but it's the most descriptive I can be. If you have any questions or need any clarification concerning my question, feel free to speak up.
-Christopher
I'm not sure about the code you have to read the HP but do you know that character.dead is actually becoming true?
You could always have the Arena.swf call a function in the HP.as that will end the game and declare a winner.You could add a second Frame to Arena.swf that contains a dimmed background and a WINNER or LOSER text.'
In general, the easiest way for a user-defined class to gain event dispatching capabilities is to extend EventDispatcher. If this is impossible (that is, if the class is already extending another class), you can instead implement the IEventDispatcher interface, create an EventDispatcher member, and write simple hooks to route calls into the aggregated EventDispatcher.
activate
Dispatched when Flash Player or an AIR application gains operating system focus and becomes active.
deactivate
Dispatched when Flash Player or an AIR application loses operating system focus and is becoming inactive.
Event dispatcher
Thank you all for your help, but I have figured it out. Turns out my method was far too complicated for what I wanted to do, and for the time I had left. I will explain how I did it.
Instead of using an EventDispatcher like I thought I would, I used a SharedObject, which simply made everything work like magic.
A SharedObject can be accessed from anywhere in the application/game, as long as it is referred to it correctly. So I simply created a SharedObject called "winLossData" set to "NO WINNERS" in my character selection screen. This cookie is never saved nor written to the disk, so there's no chance for the user to find it (generally speaking).
I have decided to use the Movement.as class which contains all of my controls and wrote an event listener of type Event.ENTER_FRAME that checks constantly my characters' health status. If one of them is below 100, my SharedObject immediately takes for value either "PLAYER ONE" or "PLAYER TWO", depending on who won (i.e. whose health points are not under 100). Afterward, just for precaution, I reset the losing character's health points to 100. Here's the code:
function whoWon(event:Event):void
{
if(playerSpriteBar.getPower() <= 0)
{
winner.data.winner = "Player Two";
playerSpriteBar.update(100);
}
if(playerAIBar.getPower() <= 0)
{
winner.data.winner = "Player One";
playerAIBar.update(100);
}
}
In my Menu.fla, I have another event listener of type Event.ENTER_FRAME that waits for the cookie to change value. As soon as the cookie changes values, Menu.fla automatically unloads the external swf (in our case, Arena.swf) and displays the results, accordingly to the received SharedObject. The rest of the actions happen inside the Menu.fla file, so no need for any extra coding.
Once again, thank you all for your help.