I've got a couple of classes.
My document class (Main.as) instantiates the class player and Soundsloader.
In the player class when the player picks up an item in the game, i want to play a short sound. I do this with the following code: MovieClip(this.main_object.sound_loader).playPickUp();
In my document class i also instantiate the SoundsLoader which basically should load all the sounds.
(just one in my sample code below)
package {
import flash.display.MovieClip;
public class SoundsLoader extends MovieClip{
private var pick_up_item:sound_pickup_item = new sound_pickup_item;
public function SoundsLoader() {
}
public function playPickUp(){
pick_up_item.play();
}
}
}
However when i use the methods my swf file freezes for a moment, plays the sound and then continues. So what's the best way of doing this ? (i'm using a .wav sound)
If this only happens the first time that you play the sound, maybe due to decompressing issues. I discard download latency because you told that you are already loading all the sounds, in a preloader or something like that.
Like I said, if this only happens the first time, a workaround would be to play all the sounds at the preload time, but muted.
Example:
public function SoundsLoader() {
var songController:SoundChannel = pick_up_item.play();
var volControl:SoundTransform = songController.soundTransform;
volControl.volume = 0;
songController.soundTransform = volControl;
}
Or
public function SoundsLoader() {
var songController:SoundChannel = pick_up_item.play();
songController.stop;
}
Related
I have an AS3 project that has all its assets embedded using the [Embed] metadata tag because I would like the resulting SWF to be completely self-contained for the sake of portability on the internet.
Problem:
The file size is rather large, and I would like a progress bar to be displayed as it loads itself instead of a blank screen until it's completely finished. I can already achieve this with Adobe Animate (Flash Professional) by having a timeline with a light frame 1 and a heavy frame 2 which has a MovieClip that embeds the bulk of the assets.
I'm trying to switch over to Adobe Flash Builder, which has no IDE timeline, but I'm at a loss on how to do the same thing as the Flash IDE. Does anyone have any ideas on how to accomplish this?
Option №1. The one I'd picked, because it's easier to comprehend: external loader. A lightweight SWF with the only purpose of displaying some preloading info like % or progress while loading the heavyweight main module.
Option №2. There's a certain metatag that allows you to emulate that frame 1 preloader behavior. Keep in mind that is is not supported by ASC2.0 compiler (AIR SDK, I assume) but only by ASC1.0 compiler (Flex SDK). Flash Builder is a descendant of Flex Builder, so it's fine, I guess, but if it does not work for you, the first thing you should check is the compiler version your Flash Builder is packed with.
So, your main (the one you set as the document class in the settings) class should have that one metatag:
package
{
import flash.events.Event;
import flash.display.Sprite;
// Brace for the magic impact.
[Frame(factoryClass="Preloader")]
public class Main extends Sprite
{
public function Main()
{
// This is important, because at the moment of creation
// the instance is not attached to the stage.
if (stage) onStage(null);
else addEventListener(flash.events.Event.ADDED_TO_STAGE, onStage);
}
private function onStage(e:Event):void
{
removeEventListener(flash.events.Event.ADDED_TO_STAGE, onStage);
// This is the entry point of your actual application.
// The rest of the class goes normally from this point on.
Then, the mentioned preloader class. Its name should qualify exactly as mentioned in the metatag above.
package
{
import flash.display.DisplayObject;
import flash.display.MovieClip;
import flash.events.Event;
import flash.events.IOErrorEvent;
import flash.events.ProgressEvent;
import flash.utils.getDefinitionByName;
// This class represents the multi-framed main timeline
// thus it should subclass the basic MovieClip.
public class Preloader extends MovieClip
{
public function Preloader()
{
// Subscribe to all necessary points to monitor the loading.
addEventListener(Event.ENTER_FRAME, onFrame);
loaderInfo.addEventListener(IOErrorEvent.IO_ERROR, ioError);
loaderInfo.addEventListener(ProgressEvent.PROGRESS, onProgress);
}
private function ioError(e:IOErrorEvent):void
{
// Handle loading errors here.
}
private function onProgress(e:ProgressEvent):void
{
// Display loading progress here.
// Use e.bytesLoaded and e.bytesTotal values
// to calculate the % loaded and the overall loading progress.
}
private function onFrame(e:Event):void
{
// When the loading is finished, the main timeline,
// represented by Preloader class moves to the frame 2.
if (currentFrame == totalFrames)
{
stop();
onComplete();
}
}
// This method concludes the loading,
// cleans up the preloader itself
// and instantiates the Main class.
private function onComplete():void
{
removeEventListener(Event.ENTER_FRAME, onFrame);
loaderInfo.removeEventListener(IOErrorEvent.IO_ERROR, ioError);
loaderInfo.removeEventListener(ProgressEvent.PROGRESS, onProgress);
// So, here's the thing. You don't import the Main class
// because if you import it, then it will be embedded into
// the Preloader, then it must be loaded before Preloader
// can be initialized, which kind of fails the whole idea.
// Thus, you don't import the Main class but obtain it
// via the other means, like the "getDefinitionByName" method.
// Again, the fully qualified class name is to be provided.
var aMain:Class = getDefinitionByName("Main") as Class;
stage.addChild(new aMain as DisplayObject);
// Remove this instance as it no longer needed for anything.
parent.removeChild(this);
}
}
I found a solution that works with ASC2 / AIR SDK. Although his example preloader extends Sprite, and I believe you need to extend MovieClip to make it work, since you need a frame 2. And you need a gotoAndStop(2) once it has finished loading itself. Additional information here. Man, it's not a good sign when all your reference links go through web.archive.org!
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());
I'm making a quiz type animation for work where on clicking an answer it plays a short animation FLV file relating to what you picked. As everything I read points towards AS3 being OOP I decided to make a MovieClip containing an FLV player and linked it to an AS3 file called FLV_Player.as. That way I can create a new instance of the FLV_Player everytime I need to play a video. Here is the code in that file which seems to work fine:
package
{
import fl.video.VideoEvent;
import flash.events.VideoEvent;
import flash.display.MovieClip;
public class FLV_Player extends MovieClip
{
public function FLV_Player(NextVideo:String)
{
animation_player.source=(NextVideo);
animation_player.addEventListener(VideoEvent.COMPLETE, vcompleted);
}
private function vcompleted(e:VideoEvent):void
{
nextFrame();
}
}
}
Now in the DocumentClass.as file I have this code:
private function NewVideo(videoname:String)
{
var nextvideo:FLV_Player = new FLV_Player(videoname);
addChild(nextvideo);
nextvideo.x = 0;
nextvideo.y = 0;
}
So when you click a button, go to the next frame or whatever the prompt is, it calls the NewVideo function and passes the name of whatever video is to be played next.
NewVideo("Introduction.flv");
Now I'm sure I'm going to run in to other issues later down the line as I really have no idea whether anything I've done is how it should be done, but the only issue I seem to be having at this point in time is removing the video and going to the next (or previous) frame to answer another question. I tried:
nextFrame();
removeChild(newVideo);
But it didn't work. Well, it may have gone to the next frame but with the video taking up the whole window it's hard to see if it did or not.
So how do I remove the video I've created? The main issue seems to be that because I had to create a new instance of the FLV_Player class in a private function the child is defined locally "var", rather than "public" or "private" var so I can't reference it again. It tells me that you can only create a "private var" from within the document class but if I make it there it will create the class on load rather than from the function when I'm ready to pass the video name parameter to it. At load I don't know what video I need it to play?
removeChild() must be called from the same object in which it was added. In this case, your DocumentClass. What you're trying to do now is telling an FLV_Player to remove itself, which won't work due to several reasons and bugs in your code.
The correct way to do things would be to have the FLV_Player object dispatch a custom event that your DocumentClass listens for. You need to create a new class which inherits from Event to create your custom event. I'd call it "PlayerEvent". In DisplayClass function you'd do this:
nextVideo.addEventListener(PlayerEvent.PLAYBACK_FINISHED, onPlaybackFinished);
addChild(nextVideo);
Then you need to create the onPlaybackFinished method:
private function onPlaybackFinished(event:PlayerEvent):void {
nextVideo.removeEventListener(PlayerEvent.PLAYBACK_FINISHED, onPlaybackFinished);
removeChild(nextVideo);
}
Inside the FLV_Player class, the vcomplete function should change to:
dispatchEvent(new Event(PlayerEvent.PLAYBACK_FINISHED));
Alternately, you could pass a pointer of the DocumentClass to the FLV_Player object, but this is very messy, can cause serious problems and not at all in the spirit of OOP. But it's a quick fix if you want to be lazy.
Events are an extremely important part of Actionscript 3 and I recommend you read up on them. Here's some good references:
http://www.adobe.com/devnet/actionscript/articles/event_handling_as3.html
http://help.adobe.com/en_US/ActionScript/3.0_ProgrammingAS3/WS5b3ccc516d4fbf351e63e3d118a9b90204-7fca.html
http://www.blog.mpcreation.pl/actionscript-3-0-basics-custom-events-part-1/
I think you're right that your first problem is simply how to reference the new video, so to expand on my comment a bit: You can declare a variable without also assigning a value, so you don't need to have var nextvideo within your NewVideo function. With a class level variable instead, you can then reference whatever you set nextvideo to when you want to remove the video:
public class DocumentClass {
private var nextvideo:FLV_Player;
private function NewVideo(videoname:String)
{
nextvideo = new FLV_Player(videoname);
addChild(nextvideo);
}
private function removeVideo():void
{
removeChild(nextvideo);
nextvideo = null;
}
}
Greatings!
I have yet another question concerning the loading of a .swf inside a existing one.
In my game. I have a introduction screen in which I load another .swf (which is a movie).
This all works fine and the URLRequest is this:
request = new URLRequest("Movie.swf");
As I said, this works fine. However when I copy my game.swf and movie.swf to a USB stick.
(I put them in the same directory to prevent other issues).
It doesn't seem to find the movie.swf.
Now I know that it has to do with the path given in the URLRequest and/or the publish settings. But I do not know how to make this so that it searches in the same directory
as the game.swf is in.
I hope you guys have an answer for this issue.
Thanks in advance,
Matti.
Matti, I believe Lukasz's comment is correct about it being a security error.
You can avoid this security error by embedding Movie.swf instead of using a Loader. If you do this, then at compile-time the Movie.swf needs to sit next to the Game.as file, and it will be included in the Game.swf (no need to deliver both files, just Game.swf).
The syntax is:
package
{
import flash.display.Sprite;
public class Game extends Sprite
{
[Embed(source="MyMovie.swf")]
private var myMovieClass:Class;
private var myMovie:DisplayObject;
public function Game():void
{
myMovie = new myMovieClass();
// Technically myMovie is now a Loader, and once
// it's loaded, it'll have .content that's a
// MovieClipLoaderAsset, and .content.getChildAt(0)
// will be your MyMovie.swf main timeline.
}
}
}
Alternately, if you embed it as mimeType="application/octet-stream", you can get the bytes of the SWF and use it in your existing Loader's .loadBytes() method:
package
{
import flash.display.Sprite;
import flash.utils.ByteArray;
public class Game extends Sprite
{
[Embed(source="MyMovie.swf", mimeType="application/octet-stream")]
private var movieBytes:Class;
private var myMovie:DisplayObject;
public function Game():void
{
// Treat this loader the same as your current loader,
// but don't call .load(url), call loadbytes():
var l:Loader = new Loader();
l.loadBytes(new movieBytes() as ByteArray);
}
}
}
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.