AS3: Custom event propagation - actionscript-3

I'm trying to put together a projector of a sequence of externally loaded swfs and my general question will be as short as it can be.
If an external swf loaded into ctrl (an instance of MovieClip placed on stage during authoring) has in its first and last frames:
dispatchEvent(new Event("FIRST_FRAME")); // in the first frame, and:
dispatchEvent(new Event("LAST_FRAME")); // in the last frame
then - should those events be "heard" within the ctrl container?
At present I only seem to be able to listen to those events within the loaded content, not "higher", I mean - if I say in the loader complete listener:
mc:MovieClip = MovieClip(e.currentTarget.content);
mc.addEventListener("LAST_FRAME", function(){ // something });
then the events are heard, but not when I say:
ctrl.addEventListener("LAST_FRAME", function(){ // something });
The latter seems to be more robust, therefore I'm struggling to have it work, but I guess I've been missing out some lessons ;-)
Has anyone been through this? Is my approach correct or should I take another path?
Cheers everyone.

You can get your crtl display object to recieve the event dispatched from your external swf by setting the bubbles option to true when dispatching your event. Look at the following example where SWFB.swf is loaded into SWFA.swf:
SWFB:
package swfb
{
import flash.display.Sprite;
import flash.events.Event;
import flash.events.TimerEvent;
import flash.utils.Timer;
public class Main extends Sprite
{
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);
const SECOND:int = 1000;
var timer:Timer = new Timer(5 * SECOND, 1);
timer.addEventListener(TimerEvent.TIMER_COMPLETE, onTimerComplete);
timer.start();
}// end function
private function onTimerComplete(e:TimerEvent):void
{
dispatchEvent(new TimerEvent(TimerEvent.TIMER_COMPLETE, true, true));
}// end function
}// end class
}// end package
SWFA:
package swfa
{
import flash.display.Loader;
import flash.display.LoaderInfo;
import flash.display.Sprite;
import flash.events.Event;
import flash.events.TimerEvent;
import flash.net.URLRequest;
public class Main extends Sprite
{
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 loader:Loader = new Loader();
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, onLoaderComplete);
loader.load(new URLRequest("swf/SWFB.swf"));
}// end function
private function onLoaderComplete(e:Event):void
{
var loaderInfo:LoaderInfo = LoaderInfo(e.target);
var container:Sprite = new Sprite();
container.addChild(loaderInfo.content);
container.addEventListener(TimerEvent.TIMER_COMPLETE, onContainerTimerComplete);
addChild(container);
}// end function
private function onContainerTimerComplete(e:TimerEvent):void
{
trace("TIMER COMPLETE!");
e.stopPropagation();
}// end function
}// end class
}// end package
SWFB.swf dispatches a Timer event 5 seconds after it's added to the stage. When it dispatches the event the bubbles and cancelable options are set to true.
In SWFA.swf the SWFB.swf is loaded into it and then added to a display object container called container. Then an event listener is added to container that listens for the Timer event from SWFB.swf to be dispatched. When it's dispatched the onContainerTimerComplete() event handler invokes the Timer event's stopPropagation() method to(as its name suggests) stop the propagation of the event.

Related

How to loop background music while end game conditions are not met in ActionScript 3?

I am fairly new to action script and need to know how to loop BG music. I know other languages you could do something like
while(!endGame){
sound.play();
}
but I can't seem to find the syntax for how to do this in AS, or maybe there is a better way? It's a 7 second clip so I need it to continue to loop until end conditions are met.
AS3 provides two methods to deal with this. The most straightforward is telling the play command the number of times you want a sound to repeat using it's loops parameter.
https://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/media/Sound.html#play()
The second method - which gives you more control - is a bit more complicated. Basically you're starting the sound and add a listener which monitors playback of the sound. If it finished playing a SOUND_COMPLETE event will fire and it's callback function can restart the playback.
To give you an example:
package
{
import flash.display.Sprite;
import flash.events.Event;
import flash.media.Sound;
import flash.media.SoundChannel;
import flash.net.URLRequest;
public class Main extends Sprite
{
private var sound:Sound;
private var soundChannel:SoundChannel;
public function Main():void
{
if (stage)
init();
else
addEventListener(Event.ADDED_TO_STAGE, init);
}
private function init(e:Event = null):void
{
removeEventListener(Event.ADDED_TO_STAGE, init);
sound = new Sound();
var urlRequest:URLRequest = new URLRequest("sound.mp3");
sound.load(urlRequest);
soundChannel = sound.play();
soundChannel.addEventListener(Event.SOUND_COMPLETE, soundFinished);
}
public function soundFinished(event:Event):void
{
soundChannel.removeEventListener(Event.SOUND_COMPLETE, soundFinished);
soundChannel = sound.play();
soundChannel.addEventListener(Event.SOUND_COMPLETE, soundFinished);
}
}
}

as3 - dispatch mouse event from external class

I am having problems understanding correctly how to dispatch events and capture them in another class.
In this case, I am trying to emulate a mouse click dispatched from an "clickM" class.
On stage I have 2 movieclips to test, a custom cursor and the listeners to capture the click event.
clickM:
package {
import flash.display.MovieClip;
import flash.utils.Timer;
import flash.events.TimerEvent;
import flash.events.Event; //dispatcher
import flash.events.MouseEvent;// mouse event
public class clickM extends MovieClip {
private var delay: uint = 3000;
private var repeat: uint = 0; //se va por todo el tiempo
private var myTimer: Timer = new Timer(delay, repeat);
public function clickM() {
myTimer.start();
myTimer.addEventListener(TimerEvent.TIMER, timerHandler);
}
private function timerHandler(e: TimerEvent): void {
//repeat--;
//statusTextField.text = ((delay * repeat) / 1000) + " seconds left.";
trace ( "simulate click...");
//dispatchEvent(new MouseEvent(MouseEvent.CLICK));
this.dispatchEvent(new MouseEvent(MouseEvent.CLICK, true, false));
}
}
}
Stage code, rojo & morado are movieclips:
import flash.events.MouseEvent;
stage.addEventListener(Event.ENTER_FRAME, myFunction);
var mano: clickM = new clickM();
mano.name = "mano";
addChild (mano);
morado.addEventListener(MouseEvent.CLICK, cl);
rojo.addEventListener(MouseEvent.CLICK, cl);
stage.addEventListener(MouseEvent.CLICK, cl);
function myFunction(event: Event) {
mano.x = mouseX;
mano.y = mouseY;
}
function cl(e: MouseEvent) {
trace("click over " + e.target.name);
}
If I click over morado or rojo, there's no problem - I can get their names. If I just let the code run, I can't get their names, I just get "mano", which is the custom cursor I'm using.
How can I get the desired behavior?
Regards.
Add mouseEnabled=false; inside clickM constructor. This should make Flash to ignore your mano in the event dispatch phase, so the underlying object should be the primary target if there's any, otherwise the target will be the stage. If your custom cursor contains more movie clips, you should also add mouseChildren=false;.
public function clickM() {
mouseEnabled=false;
// possibly add this too
mouseChildren=false;
myTimer.start();
myTimer.addEventListener(TimerEvent.TIMER, timerHandler);
}

Movieclip attraction/repulsion to mouse

I'm really new to flash and as3...
I'm trying to create 2 scenes in flash - one where my movieclip moves away from the mouse whenever it goes near it, and the other where the movie clip is attracted to the mouse. I have found an answer on actionscript 2 but i cannot use this in my as3 file...
Any help or ideas?
Cheers!
Here is an example I made of a display object being "pushed" and "pulled" in relation to the mouse's position.
Main.as(Document class):
package
{
import flash.display.Sprite;
import flash.events.Event;
import flash.events.MouseEvent;
import flash.geom.Point;
public class Main extends Sprite
{
public static var PULL:String = "pull";
public static var PUSH:String = "push";
private var _circle:Circle;
private var _force:String;
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);
_circle = new Circle();
addChild(_circle);
_force = PULL;
stage.addEventListener(MouseEvent.MOUSE_DOWN, onStageMouseDown);
}// end function
private function onStageMouseDown(e:MouseEvent):void
{
stage.addEventListener(MouseEvent.MOUSE_UP, onStageMouseUp);
stage.addEventListener(Event.ENTER_FRAME, onStageEnterFrame);
}// end function
private function onStageMouseUp(e:MouseEvent):void
{
stage.removeEventListener(MouseEvent.MOUSE_UP, onStageMouseUp);
stage.removeEventListener(Event.ENTER_FRAME, onStageEnterFrame);
_force = (_force == PULL) ? PUSH : PULL;
}// end function
private function onStageEnterFrame(e:Event):void
{
var point1:Point = new Point(_circle.x, _circle.y);
var point2:Point = new Point(stage.mouseX, stage.mouseY);
var point3:Point = point2.subtract(point1);
point3.normalize(10);
if (_force == PULL) {
_circle.x += point3.x;
_circle.y += point3.y;
} else if (_force == PUSH) {
_circle.x -= point3.x;
_circle.y -= point3.y;
}// end else if
}// end function
}// end class
}// end package
import flash.display.Sprite;
import flash.events.Event;
class Circle extends Sprite {
public function Circle() {
draw();
}// end function
private function draw():void {
this.graphics.lineStyle(1);
this.graphics.beginFill(0xFFFFFF);
this.graphics.drawCircle( 0, 0, 20);
this.graphics.endFill();
}// end function
}// end class
In the init() method we add a new display object to the stage, this is the display object we will be "pulling" and "pushing" in relation to the mouse's position.
_circle = new Circle();
addChild(_circle);
Then we set the _force property to our PULL constant. The _force property will determine whether the display object is "pulled" or "pushed".
_force = PULL;
Next we add our mouse event listeners to the stage. On MouseEvent.MOUSE_DOWN we call the onStageMouseDown() event handler. When the handler is called, we add an Event.ENTER_FRAME and MouseEvent.MOUSE_UP event listeners to the stage.
private function onStageMouseDown(e:MouseEvent):void
{
stage.addEventListener(MouseEvent.MOUSE_UP, onStageMouseUp);
stage.addEventListener(Event.ENTER_FRAME, onStageEnterFrame);
}// end function
When the MouseEvent.MOUSE_UP event handler is called, the previous Event.ENTER_FRAME and MouseEvent.MOUSE_UP event listeners are removed from the stage. Then depending on the _force property's value, its value is alternated between PUSH and PULL.
private function onStageMouseUp(e:MouseEvent):void
{
stage.removeEventListener(MouseEvent.MOUSE_UP, onStageMouseUp);
stage.removeEventListener(Event.ENTER_FRAME, onStageEnterFrame);
_force = (_force == PULL) ? PUSH : PULL;
}// end function
Lastly, the onStageEnterFrame event handler. This is where we calculate the new position of the display object in relation to the mouse.
There are different ways to go about this, but I decided to use the Point class to simplify things. First we have to get a Point object for the display object's position and another Point object for mouse's position.
var point1:Point = new Point(_circle.x, _circle.y);
var point2:Point = new Point(stage.mouseX, stage.mouseY);
Next we have to get the difference between the points using the Point object's subtract() method.
var point3:Point = point2.subtract(point1);
With this new point, we can use the Point object's normalize() method to scale the line segment between the display object's position and the mouse's position to a set length.
point3.normalize(10);
Finally depending on the _force property's value we either subtract or add the point's x and y properties from the display object's x and y properties.

Dragging a movieclip as3

I have a Movieclip which is a child of another movieclip. I use startDrag() and stopDrag() with first (parent) Movieclip but the nested one doesnt move. Why?
relevant code on stage:
var main:rt = new rt(); // rt being a class in my library, which extends MovieClip object.
addChild(main);
stage.addEventListener(MouseEvent.MOUSE_DOWN, stage_mousedownHandler);
stage.addEventListener(MouseEvent.MOUSE_UP, stage_mouseupHandler);
function stage_mousedownHandler(event_object:Event) {
main.startDrag();
}
function stage_mouseupHandler(event_object:Event) {
main.stopDrag();
}
rt's constructor code:
public function rt() {
var bmp_bar:Bitmap;
var br_male:Bar_male; // Bar_male is a Bitmap in my library. (AS Linkage)
bmp_bar = new Bitmap(br_male);
this.addChild(bmp_bar);
}
Made this simple program to test and it works as expected in Flash Develop, there is a main sprite and a child bitmap when I mouse down any where on stage the main sprite is dragged and the child bitmap is moved.
So I am guessing there is something going on in your workflow in flash professional and linkage. Make sure the mouse event handlers are triggered put some breakpoints and debug.
package
{
import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.display.Sprite;
import flash.events.Event;
import flash.events.MouseEvent;
import flash.geom.Rectangle;
public class Test extends Sprite
{
private var sp:Sprite = new Sprite();
public function Test()
{
addEventListener(Event.ADDED_TO_STAGE, onAddedToStage);
}
private function onAddedToStage(e:Event):void
{
removeEventListener(Event.ADDED_TO_STAGE, onAddedToStage);
var bmpData:BitmapData = new BitmapData(100, 100,false,0x000000);
bmpData.fillRect(new Rectangle(0, 0, 100, 100), 0xff0000);
var bmp:Bitmap = new Bitmap(bmpData);
sp.addChild(bmp);
addChild(sp);
stage.addEventListener(MouseEvent.MOUSE_DOWN, onMouseDown);
stage.addEventListener(MouseEvent.MOUSE_UP, onMouseUp);
}
private function onMouseUp(e:MouseEvent):void
{
sp.stopDrag();
}
private function onMouseDown(e:MouseEvent):void
{
sp.startDrag();
}
}
}

AS3 - Move MovieClip across screen

I've 3 different MovieClips that I need to move at the same time across the screen. (From bottom to top)
What is the best way of doing this without using a tweening class like Caurina?
Thank you for tips.
You could add an event listener to the parent container of the display objects which listens for the Event.ENTER_FRAME event. On each Event.ENTER_FRAME event you simply decrement the y property of the display objects like in the following example.
package
{
import flash.display.Sprite;
import flash.events.Event;
[SWF(width="600", height="500")]
public class Main extends Sprite
{
private var _squares:Vector.<Square>;
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);
_squares = new Vector.<Square>();
var redSquare:Square = new Square(0xFF0000, 100);
redSquare.x = 0;
redSquare.y = 400;
addChild(redSquare);
var greenSquare:Square = new Square(0x00FF00, 100);
greenSquare.x = 300;
greenSquare.y = 300;
addChild(greenSquare);
var blueSquare:Square = new Square(0x0000FF, 100);
blueSquare.x = 500;
blueSquare.y = 100;
addChild(blueSquare);
_squares.push(redSquare, greenSquare, blueSquare);
addEventListener(Event.ENTER_FRAME, onEnterFrame);
}// end function
private function onEnterFrame(e:Event):void
{
for each(var square:Square in _squares)
{
if (square.y > 0) square.y -= 5;
}// end for
}// end function
}// end class
}// end package
import flash.display.Sprite;
internal class Square extends Sprite
{
public function Square(color:uint, size:Number)
{
graphics.beginFill(color);
graphics.drawRect(0, 0, size, size);
graphics.endFill();
}// end function
}// end function
I think you'd be making life easier for yourself though if you simply used Greensock's Tweening platform instead.
You can animate them in Flash IDE with frames and tweenings.
Also you can animate them programmatically yourself. Place every movie clip at the bottom of the screen, write some code that moves your movieClips a little to the top of the screen and gets called periodically (using Timer, EnterFrame event listener or setInterval), stop calling this code when all movieClips reached the top (using Timer.stop(), removeEventListener or clearInterval).
I don't see why you might need to do that because there are many tweening libraries that do all this for you.