Controlling FPS of a loaded swf - actionscript-3

I'm working on a flash app where I load multiple swf's. But the problem is that they have different framerates (12/25/30). If I add 2 swf's they both play at 25fps. I found numerous topic about this but I can't get it to work (in AS3). Does anyone know why it doesn't work and how to make it working?
public class MainClass extends MovieClip
{
var loader:Loader = new Loader();
var request:URLRequest;
var mcMedia:MovieClip = new MovieClip();
MovieClip.prototype.setFrameRate = function(frameRate:Number)
{
var mc:MovieClip = this;
if (mc.tweenFaster != null)
{
Timer(mc.tweenFaster).stop();
}
mc.tweenFaster = new Timer(1000/frameRate);
mc.tweenFaster.addEventListener(TimerEvent.TIMER, timelineFaster);
mc.tweenFaster.start();
function timelineFaster(event:TimerEvent = null)
{
if (mc.currentFrame == mc.totalFrames)
{
mc.tweenFaster.stop();
mc.gotoAndStop(1);
}
else
{
trace(mc.currentFrame);
mc.nextFrame();
}
event.updateAfterEvent();
}
}
public function MainClass()
{
configureListeners();
request = new URLRequest("data/7/7.swf");
try
{
loader.load(request);
}
catch (error:Error)
{
trace("Unable to load requested document.");
}
}
private function configureListeners():void
{
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, completeHandler);
loader.contentLoaderInfo.addEventListener(Event.OPEN, openHandler);
loader.contentLoaderInfo.addEventListener(ProgressEvent.PROGRESS, progressHandler);
loader.contentLoaderInfo.addEventListener(SecurityErrorEvent.SECURITY_ERROR, securityErrorHandler);
loader.contentLoaderInfo.addEventListener(HTTPStatusEvent.HTTP_STATUS, httpStatusHandler);
loader.contentLoaderInfo.addEventListener(IOErrorEvent.IO_ERROR, ioErrorHandler);
}
private function completeHandler(event:Event):void
{
loader.content.scaleX = 550/event.target.width;
loader.content.scaleY = 400/event.target.height;
mcMedia.addChild(loader);
mcMedia.setFrameRate(12);
addChild(mcMedia);
}

In as3, if you're just looking to change the framerate, use stage.frameRate = 12; or whatever;
http://livedocs.adobe.com/flash/9.0/ActionScriptLangRefV3/flash/display/Stage.html#frameRate
In AS3, while you can use prototypes, you generally don't. I'd rewrite your setFrameRate function (which is badly named, shouldn't it be more.. tweenFaster, or matchFrameRate or something?)
I'd make a helper function like this:
package util{
//imports
public class TweenFasterMC extends MovieClip{
public var mc:MovieClip;
public function matchFrameRate(frameRate:Number):void
{
if (tweenFaster != null)
{
Timer(mc.tweenFaster).stop();
}
tweenFaster = new Timer(1000/frameRate);
tweenFaster.addEventListener(TimerEvent.TIMER, timelineFaster);
tweenFaster.start();
}
function timelineFaster(event:TimerEvent = null):void
{
if (currentFrame == totalFrames)
{
tweenFaster.stop();
gotoAndStop(1);
}
else
{
trace(currentFrame);
nextFrame();
}
event.updateAfterEvent();
}
}
Also, clean up your event listeners, that strong timer event listener will cause a lot of problems if you have a lot of mc's your applying this functionality to.

As far as I know all MovieClips in one flash player instance (sharing the same 'stage') will run at the same speed - there is no way to have two clips running at different speeds. So to adjust the speed you have to resort to calling gotoAndStop() on all MovieClips in the loaded clip at the right time - that won't be fun.
Code along the lines that quoo is showing will only work if the loaded swf contains just 1 MovieClip (no nesting) as far as I can see.

It seems to me that the most likely reason why this wouldn't work is that it requires every clip you load to be a simple, completely non-dynamic animation that loops for ever. If the loaded content is that simple, why not just adjust it to look better at 30fps? (If it's extremely long, a JSFL script could automate the process of adding extra frames.) Alternately, if the content isn't that simple, then attempting to change its timing by calling nextFrame from elsewhere is not going to give you what you want.
With all that said, if you're sure this is what you want to do but you're getting 0 as a return for currentFrame in your loaded content, are you sure they are AS3 SWFs? If they aren't, AS3/AS2 interoperation is a hairy subject that will warrant reading up on.

This is a real hassle, I've been scouring the net for an answer. I have particles following a path, and I want to change the speed that these particles follow the path dynamically, without changing the whole movie. just the particles movie clip.
I've tried greensock, but, that doesn't really work like i need.. i'd think there would be something that you can change dynamically for each mc, but, no dice.
the stage.frameset is only for the whole movie... argggggggg..... sucks..

Related

How to loop SWF file loaded with Loader?

I want to loop a swf file that has been loaded with via the Loader class in AS3.
My code looks as following:
public class MyLoader extends MovieClip {
public function MyLoader() {
var myLoader:Loader = new Loader();
var url:URLRequest = new URLRequest("external-movie.swf");
myLoader.load(url);
myLoader.contentLoaderInfo.addEventListener("complete", function() {
});
addChild(myLoader);
}
}
From what I understand the Loader has no event for when the embedded movie is finished? Is there a way to figure that out? It must not be a AS3 implementation. I just want a movie that has been exported from Indesign to run in a loop. Thanks in advance
Especially when you have little experience programming you should avoid dirty shortcuts as they'll only get you a lot of trouble. So avoid anonymous function and avoid using string in place of static event variables.
This being said, if your loaded movie has its own timeline then it will be converted into a MovieClip. Also that movie is not embedded but loaded and that's a big difference.
Keep a reference of that movie and the loader:
private var theLoadedMovie:MovieClip;
private var myLoader:Loader;
Listen for the INIT event instead of the COMPLETE event (movies with timeline start to play when their first frame is loaded "INIT", the COMPLETE event fires when the whole movie is loaded).
myLoader = new Loader();
var url:URLRequest = new URLRequest("external-movie.swf");
myLoader.load(url);
myLoader.contentLoaderInfo.addEventListener(Event.INIT, handleInit);
In your handleInit method keep a reference of that movie:
theLoadedMovie = myLoader.content as MovieClip;
addChild(theLoadedMovie);
theLoadedMovie.addEventListener(Event.ENTERFRAME, handleEnterFrame);
in your handleEnterFrame method check the movie progress to know when it has ended:
if(theLoadedMovie.currentFrame == theLoadedMovie.totalFrames)
{
//movie has reached then end
}

How to apply removeChild to multiple swf's

I have a project in which the user has the option of pressing any one of 5 keyboard keys that each loads a different .swf file. I would like whatever key the user presses to unload whatever swf is currently playing and load whichever one is associated with the key pressed.
I have no problem coding this with two .swf's --each one loads and simultaneously unloads the other-- however when I add a third, it becomes erratic and the third removeChild function does not work properly.
It would seem that the problem is that only one removeChild can be applied at a time. Is there a way around this?
The code I am using is on a frame within the main timeline and it is not associated with a container other than the stage onto which the the swf's are directly loaded:
var myLoader:Loader = new Loader();
var myRequest:URLRequest=new URLRequest("swf/darker.swf");
var Loader2:Loader = new Loader();
var Request2:URLRequest=new URLRequest("swf/animalinside.swf");
var Loader3:Loader = new Loader();
var Request3:URLRequest=new URLRequest("swf/berlin1.swf");
stage.addEventListener(KeyboardEvent.KEY_DOWN, Darker);
function Darker(e:KeyboardEvent):void {
if (e.charCode == 51) { //51=3
myLoader.load(myRequest);
addChild(myLoader);
removeChild(Loader2);
removeChild(Loader3);
}
}
stage.addEventListener(KeyboardEvent.KEY_DOWN, animalInside);
function animalInside(e:KeyboardEvent):void {
if (e.charCode == 52) { //52=4
Loader2.load(Request2);
addChild(Loader2);
removeChild(myLoader);
removeChild(Loader3);
}
}
stage.addEventListener(KeyboardEvent.KEY_DOWN, Berlin1);
function Berlin1(e:KeyboardEvent):void {
if (e.charCode == 53) { //53=5
Loader3.load(Request3);
addChild(Loader3);
removeChild(myLoader);
removeChild(Loader2);
}
}
I have tried adding multiple instances within the removeChild() parens like this:
removeChild(Loader3, Loader2, Loader4, ...); however it doesn't work.
Stacking the removeChild also doesn't seem to work:
removeChild(myLoader);
removeChild(Loader2);
removeChild(Loader3);
//etc.
Not sure how to proceed, any help would be much appreciated!
Thanks in advance,
Llyfre
I'd go about this a different route. A much easier route in fact. Take a look at the code I wrote below. I haven't tested it, but I am pretty sure this will work
var swfs:Array = ["swf/darker.swf","swf/animalinside.swf","swf/berlin1.swf"];
var swfHolder:Sprite = new Sprite();
stage.addEventListener(KeyboardEvent.KEY_DOWN, keyDownHandler);
function keyDownHandler(e:KeyboardEvent):void
{
//remove any previous children in swfHolder
while (swfHolder.numChildren > 0) swfHolder.removeChildAt(0);
//-- new loader and load the swf
var loader = new Loader();
switch (e.charCode)
{
case 51:
loader.load(new URLRequest(swfs[0]));
break;
case 52:
loader.load(new URLRequest(swfs[1]));
break;
case 53:
loader.load(new URLRequest(swfs[2]));
break;
}
swfHolder.addChild(loader);
}
addChild(swfHolder);

How to stop sound in the middle of playing

I'm trying to write a bit of code that plays a sound while a buttons pressed however if the button has been pressed and the sound is playing then the sound is paused and played again rather then just playing and overlapping.
this is what I have
var sound:alarm = new alarm();
var isPlaying:Boolean = false;
public function Main()
{
button.addEventListener(MouseEvent.CLICK,playSound);
}
public function playSound(e:Event):void
{
if(isPlaying)sound.stop();
sound.play();
isPlaying=true;
}
at first glance It seemed to have worked but then I saw the following in my output
TypeError: Error #1006: stop is not a function.
at Main/playSound()
TypeError: Error #1006: stop is not a function.
at Main/playSound()
so apparently it works although stop is not a method of the Sound class. what would be the proper way of implementing this? Also I've been wondering if there is a more proper condition I can use, because with this code sound.stop() is called every time the function is entered after the first button click, is there a method that allows me to check in real time whether or not a sound is playing?
In your code, the function playSound(e:Event) should be playSound(e:MouseEvent);Also your right stop() is not a method of the Sound class, however your not using the Sound class, your using the alarm class (unless the alarm class extends the Sound class).On another note, I searched google and this popped up, Flash Play/Pause Sound
Update:
import flash.media.SoundChannel;
// Make sure to import the SoundChannel class
var sc:SoundChannel = new SoundChannel();
var sound:Sound = new alarm();
var isPlaying:Boolean = false;
var pausePos:Number = 0;
public function Main()
{
button.addEventListener(MouseEvent.CLICK,playSound);
}
public function playSound(e:MouseEvent):void
{
if(isPlaying) {
pausePos = sc.position;
sc.stop();
isPlaying = false;
} else {
sc = sound.play(pausePos);
isPlaying = true;
}
}
This code should work, however I have not tested it so if any errors are given or the desired result is not met just let me know and I'll see what I can do.
Short answer...okay, entire answer from me :). Instead of using the sound object, try the SoundChannel object. It offers more options, including volume and balance control, and most prominently, stop.
Documentation should provide enough info for using it. It's relatively common.
http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/media/SoundChannel.html

Embed vs Dynamically Loading sounds

Just wondering if anyone could help me with this. I'm new to actionscript, and am building an application that plays some sounds when buttons are clicked.
It's got 5 tabs, and will give the user the options to play about 10 sounds per tab.
I have initially been loading the sounds on runtime, so whenever the user clicked a button to play that sound, I would do something like:
var sound:Sound = new Sound(new URLRequest("assets/hello.mp3"));
sound.play();
I'm not sure, but I don't think this is very good, since I would be loading that sound over and over again if the user pressed the button too many times.
I then thought about embedding the sound in each of the views (I have one view per tab), so would embed the sounds whenever the view was loaded. I think this is a better options, but still am a bit unsure about how the embed works exactly.
[Embed('assets/hello.mp3')] private var hello_mp3:Class;
I suppose it simply embeds the mp3 files when the swf is compiled (making it bigger), but they would not be loaded anymore once the app starts, or once that view is initialized again.
My question is: Is this the right approach to take? Is there any better way I can accomplish this? Is embedding the right solution for my problem?
Thanks in advance
The first solution works fine, it won't make your SWf bigger and once the sound has been loaded once, it will be cached by Flash Player, so the sound is not loaded again and again.
This is a more efficient approach, sounds that are not played will not be loaded.
Bear in mind that when the button is pressed for the first time , you may experience a slight delay due to the sound being loaded.
In order to avoid this, you can load some sounds as external assets, meaning that after your SWF has loaded, you can call a function that will load some or all of the sounds, depending on the needs of the app. Your SWF will not be bloated and the button click will be more responsive.
Sounds like you need a SoundManager class, or something similar, that caches the sounds you need so that you can play them whenever you want and they only ever get loaded once. Classes like these, where you only ever need one instance of the class, are good candidates to be singletons. There are lots of ways to implement singleton design. In actionscript, my favorite way is like this:
//in SoundManager.as
public static var instance:SoundManager = new SoundManager();
Simple, and it works. Since the plan is that you'll only ever need one SoundManager, you can now get that one instance anywhere else in your code like this:
var soundManager:SoundManager = SoundManager.instance;
//or, more likely you can just use it in-line like this
SoundManager.instance.myMethod();
So now you've got your SoundManager. Let's set it up to keep a cache of sounds so that they only ever get loaded once:
// in SoundManager.as
private var _soundCache:Array = [];
public function getSound(soundName:String):Sound {
var testSound:Sound = _soundCache[soundName] as Sound;
if(!testSound) { //if the sound isn't loaded yet, testSound will be null
//the sound isn't there, so lets load it
var newSound:Sound = new Sound(new URLRequest(soundName));
_soundCache[soundName] = newSound;
return newSound;
}
//if we made it this far it means the sound was in the cache, so we return it
return testSound;
}
And presto, you only ever have to load the sound once! When you want to get a sound, it's as easy as:
var mySound:Sound = SoundManager.instance.getSound("mySound.mp3");
Let me know if you run into trouble with any of this code, but hopefully that's enough to get you on the right track.
In my opinion you should load all the sounds at the beggining and store references to each sound in a place you can access at anytime. A simple example would be storing each sound in an array. I've created a similar yet more robust application of this example:
sounds.xml
<?xml version="1.0" encoding="utf-8" ?>
<sounds>
<sound name="sound1" url="sound/sound1.mp3" />
<sound name="sound2" url="sound/sound2.mp3" />
<sound name="sound3" url="sound/sound3.mp3" />
<sound name="sound4" url="sound/sound4.mp3" />
<sound name="sound5" url="sound/sound5.mp3" />
</sounds>
Main.as(document class):
package
{
import flash.display.Sprite;
import flash.events.Event;
import flash.media.SoundChannel;
import flash.net.URLLoader;
import flash.net.URLRequest;
public class Main extends Sprite
{
private var _soundLibrary:SoundLibrary;
public function Main():void
{
if (stage) init() else addEventListener(Event.ADDED_TO_STAGE, init);
}// end function
private function init(e:Event = null):void
{
removeEventListener(Event.ADDED_TO_STAGE, init);
var urlLoader:URLLoader = new URLLoader(new URLRequest("xml/sounds.xml"));
urlLoader.addEventListener(Event.COMPLETE, onUrlLoaderComplete);
}// end function
private function onUrlLoaderComplete(e:Event):void
{
_soundLibrary = new SoundLibrary(XML(URLLoader(e.target).data));
_soundLibrary.addEventListener(Event.COMPLETE, onSoundLibraryComplete);
}// end function
private function onSoundLibraryComplete(e:Event):void
{
var soundChannel:SoundChannel = _soundLibrary.getSound("sound3").play();
}// end function
}// end class
}// end package
import flash.events.Event;
import flash.events.EventDispatcher;
import flash.media.Sound;
import flash.net.URLRequest;
internal class SoundLibrary extends EventDispatcher
{
private var _xml:XML;
private var _length:int;
private var _counter:int;
private var _soundLibraryItems:Vector.<SoundLibraryItem>;
public function SoundLibrary(xml:XML):void
{
_xml = xml;
_length = _xml.children().length();
_soundLibraryItems = new Vector.<SoundLibraryItem>();
loadSounds();
}// end function
public function getSound(name:String):Sound
{
var sound:Sound;
for (var i:int = 0; i < _soundLibraryItems.length; i++)
{
if (_soundLibraryItems[i].name == name)
sound = _soundLibraryItems[i].sound;
}// end for
if (!sound) throw new ArgumentError("No sound object matches specified name");
return sound;
}// end function
private function loadSounds():void
{
for (var i:int = 0; i < _length; i++)
{
var sound:Sound = new Sound(new URLRequest(_xml.children()[i].#url));
sound.addEventListener(Event.COMPLETE, onSoundComplete);
}// end for
}// end function
private function onSoundComplete(e:Event):void
{
_soundLibraryItems.push(new SoundLibraryItem(_xml.children()[_counter].#name, Sound(e.target)));
if (++_counter == _length) dispatchEvent(new Event(Event.COMPLETE));
}// end function
}// end class
internal class SoundLibraryItem
{
private var _name:String;
private var _sound:Sound;
public function get name():String { return _name }
public function get sound():Sound { return _sound }
public function SoundLibraryItem(name:String, sound:Sound)
{
_name = name; _sound = sound;
}// end function
}// class
[UPDATE]
Summary
First the the sounds.xml is loaded and then on its completion it is parsed to the new instance of SoundLibrary.
SoundLibrary handles loading the sounds from the xml and then parses each loaded Sound object to a new instance of SoundLibraryItem.
SoundLibraryItem simply stores the name of the Sound object as well as the Sound object itself. When all the SoundLibraryItem objects are created the SoundLibrary object dispatches an Event object with an Event.COMPLETE type.
When the event is dispatched the event listener on the SoundLibrary object calls the onSoundLibraryComplete() event handler.
Finally the SoundLibrary object's getSound() method is used to get one of the previously loaded Sound objects via the name argument. Then with a SoundChannel object the Sound object's play() method is called.
In my opinion
Embed things is never an approuch
Pros:
You have anything in the same file = 1 load (
well, this is not really a Pro... )
Cons:
It takes longer to load the whole
application ( users don't like to
wait )
If you want to change the sound you must compile everything again
Scalability benefits
I'm sure people can help me with a lot more Cons ;)
Hope it helps

preloading external .swf with class file as3

so i'm trying to make an external preloader to load my main .swf (loading.swf) file that has a class file named mainLoading.as using this code:
var l:Loader = new Loader();
l.contentLoaderInfo.addEventListener(ProgressEvent.PROGRESS, loop);
l.contentLoaderInfo.addEventListener(Event.COMPLETE, done);
l.load(new URLRequest("loading.swf"));
var loadingPage:loading = new loading;
function loop (e:ProgressEvent):void{
addChild(loadingPage);
loadingPage.x = stage.stageWidth/2;
loadingPage.y = stage.stageHeight/2;
}
function done (e:Event):void{
removeChild(loadingPage);
addChild(l);
}
so I'm getting an error message saying:
TypeError: Error #1009: Cannot access a property or method of a null object reference.
at mainLoading()
I think i'm getting the error message because i am accessing the stage in my mainLoading() class file. I tried adding this to the constructor in my class file but it didn't work:
public function mainLoading () {
addEventListener(Event.ADDED_TO_STAGE, init);
}
private function init(e:Event): void {
initStartUpScene ();
}
my initStartUpScene function just throws the intro scene to the loading.swf
any suggestions?
thanks for your help.
(question) is your mainLoading extends either Sprite or Movieclip ?
EDIT
After reading your comment, I would suggest trying this :
Add a function call inside the swf content in your complete progress handler :
function done (e:Event):void{
removeChild(loadingPage);
addChild(l);
Object(l.content).initMethod();
}
content let you access the methods in your loaded .swf main class (e.g. mainLoading)
And replace the event handling in your mainLoading by :
public function mainLoading () {
//no event handling in constructor
}
public function initMethod():void {
//here you go
init();
}
public function init():void { ... //No more event here
Btw it's not the cleanest way to solve your problem.
If that's the exact message you are getting, then yes, adding the ADDED_TO_STAGE listener should have fixed it. Remember to recompile the "loading.swf" if you make any changes to it (a step I always seem to forget)
Does "loading.swf" run just fine without any errors when running it on it's own (not loading it into the "container" SWF)?
This may be unrelated to the question you asked, but you might get better results and avoid some errors by structuring your code like this:
var loadingPage:loading = new loading;
addChild(loadingPage);
loadingPage.x = stage.stageWidth/2;
loadingPage.y = stage.stageHeight/2;
var l:Loader = new Loader();
l.contentLoaderInfo.addEventListener(ProgressEvent.PROGRESS, onProgress);
l.contentLoaderInfo.addEventListener(Event.COMPLETE, onComplete);
l.load(new URLRequest("loading.swf"));
//I renamed this function since I believe "loop" is a reserved keyword.
function onProgress (e:ProgressEvent):void{
//No code needed
}
function onComplete (e:Event):void{
removeChild(loadingPage);
addChild(l);
}
You can remove the "onProgress" function if you won't be needing it as well.
OOOOOOKKKKK, so after about a week of admitting defeat, trying it again, rewriting almost half of my code, trying to convince myself that preloaders are overrated, i finally figured it out (drum roll please):
I had a variable that was referencing the stage being called before my constructor method was called. like this:
private var xScrollPosRight:Number = stage.stageWidth - xScrollPosLeft;
I just changed stage.stageWidth to 750 and it worked.
live and learn.