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

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());

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();

AS3 Setup Buttons for All Frames in Document Class

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.

How to convert a two-file preloader and game swf with Document class into single swf?

I have a game that I've made that runs in two files, one the preloader and one the swf. When I use these two files together they work fine, however some flash portals require only one SWF, so I'm trying to find the shortest-path way to convert it to use only one.
The game swf uses a Document Class that includes the package, class constructor, and tons of functions, and runs fine. I have lots of movieclips on the stage that I refer to in my code. The code is something like this (abridged):
package {
import flash.display.MovieClip;
import flash.events.MouseEvent;
...
public class Game extends MovieClip {
var EnemyArray:Array;
var HeroArray:Array;
...
public function Game() { // class constructor
EnemyArray = new Array();
addEventListener(Event.ENTER_FRAME,onEnterFrame);
mainRestartButton.addEventListener(MouseEvent.CLICK, RestartLevel);
...
}
public function KeyPressed(event:KeyboardEvent):void
{
if (event.keyCode == Keyboard.SPACE)
{
attack();
...
}
} // End of class
} // End of package
I also have another swf, my preloader, which has the following code in frame 1:
import flash.display.Loader;
import flash.net.URLRequest;
import flash.events.ProgressEvent;
import flash.events.Event;
var myLoader:Loader = new Loader();
myLoader.contentLoaderInfo.addEventListener(ProgressEvent.PROGRESS, onLoading);
var myURL:URLRequest = new URLRequest("Game.swf");
myLoader.load(myURL);
myLoader.contentLoaderInfo.addEventListener(Event.COMPLETE, onComplete);
function onLoading(event:ProgressEvent):void {
var loaded:Number = event.bytesLoaded / event.bytesTotal;
percent_txt.text = "Loading: " + (loaded*100).toFixed(0) + "%";
}
function onComplete(event:Event):void {
myLoader.contentLoaderInfo.removeEventListener(ProgressEvent.PROGRESS, onLoading);
myLoader.contentLoaderInfo.removeEventListener(Event.COMPLETE, onComplete);
this.addChild(myLoader);
}
I've been through the gamut of solutions online and none of them seem to work with the way that my code is set up, such as: How to create Preloader in AS3
I tried his option 1 by taking all the code in my Document Class other than the package and constructor and pasted it into frame 2. However it returns "Error #1009: Cannot access a property or method of a null object reference." for any references to items on the stage, such as:
mainRestartButton.addEventListener(MouseEvent.CLICK, RestartLevel);
As he mentions, "This works well if you organized your project in a way that the most of the assets (images, etc) are in the Flash IDE Library and are not loaded on the first frame (you can check that in each library item's properties)." My project is NOT organized in such a way.
What is the easiest way for me to reorganize my FLA/code so they can have a preloader in a single swf?
Thanks so much for your time and help!
I use FlashDevelop (no FLAs) so I'm not sure if it'll work for you but I use the solution described here.

Actionscript 3.0 sounds accounting

I have an Array of SoundChannels actively playing.
When new sound is going to play, I append its SoundChannel to this array.
I have to maintain such an array in order to be able to stop all sounds at once.
I would like to remove sound channel from array when it finishes to prevent inifinite growing of my array. But when I catch e=Event.SOUND_COMPLETE, I have no information on sound channel. It is only possible to get Sound as e.target.
Actually, I can maintain Array of pairs (Sound, SoundChannel).
But maybe there exists more light-weight solution?
you don't need that array :) you can just use SoundMixer.StopAll(); to stop every sound that is playing.
edit: since you want to stop all special sounds, i have a new solution.
first, you create a new actionscript class and you add this code to it.
package
{
import flash.display.DisplayObject;
import flash.events.Event;
import flash.media.SoundChannel;
public class SpecialSoundChannel extends SoundChannel
{
var _parent:DisplayObject;
public function SpecialSoundChannel(Parent:DisplayObject)
{
super();
_parent = Parent;
_parent.addEventListener("StopSpecialSound", stopChannel);
}
public function stopChannel(e:Event):void
{
//DO SOME OTHER STUFF YOU WANT DONE.
stop();
}
}
}
every time you want to have a special sound added that is not music, you just do it like this:
var _sound:SpecialSoundChannel = new SpecialSoundChannel(this);
"this" is the class where you play and stop your soundchannel, which i am assuming is the same as where you create your soundchannel and therefore can call it "this". You add the following function to that class.
public function stopSpecialSounds():void
{
var _e:Event = new Event("StopSpecialSound");
dispatchEvent(_e);
}
if you want to stop all special sounds, you just call for this last function.

change var initialized on root time line from class in as3

hi im new on as3 and its make me so frustated since so many changes from as2. i have problem when i want to change variable value from class file, here is the detail.
on the root timeline frame 2, i initialized var like below
stop();
var gameStat;
gameStat = "stop";
then i attached penguin movie clip to the stage,
and i make class file called "penguin.as"
//untuk hero penguin
package {
import flash.display.MovieClip;
import flash.events.Event;
public class penguin extends MovieClip {
var rootref:Object = root;//mendefinisikan root dengan rootref
public function penguin() {
rootref.gameStat = "play";//change var value to play
addEventListener(Event.ENTER_FRAME,on_enter_frame);//tmbhkn enterFrame function
}//eof penguin()
private function on_enter_frame(e:Event) {
trace(rootref.gameStat);//<- test the variable here
//trace(this.parent.parent.parent.gameStat);
}
}//eof class
}
i trace gameStat var in on_enter_frame function, but it still give me "stop" result not "play".
any help how to change the "gameStat" value??
Classes should start with the first letter capitalized.
Also why make a reference to root if it can be accessed anyway. You should also trace in the constructor whether you set the value.