Stop MouseEvent-propagation of displayList views to starling views - actionscript-3

I'm creating a game that uses the starling-layer (the game itself) and the classic display list which contains several Popups and Stuff like that.
I have one thing that troubles me:
If MouseEvents are generated on displayList-elements they always go through to the starling layer and produce TouchEvents etc. which is quite annoying.
I was wondering there is some general (and easy to use) approach to handle that.
One possibility was to listen on all displayList-Elements for the following Events:
interfaceElement.addEventListener(MouseEvent.MOUSE_MOVE, stopPropagationHandler);
interfaceElement.addEventListener(MouseEvent.MOUSE_DOWN, stopPropagationHandler);
interfaceElement.addEventListener(MouseEvent.MOUSE_UP, stopPropagationHandler);
private function stopPropagationHandler(e:MouseEvent):void {
e.stopPropagation();
}
But this looks quite nasty to me.
And even if I did it like that, I have one more issue:
If a starling-element is below that display-list-element and if it has a TouchEvent.TOUCH for rollover-behavior >> the rollover-appearance will not be removed from the starling if you hover over the display-list-element.
I also thought about putting a dummy-starling element behind every display-list-element,... to stop the events.. but that all sounds a bit "over-complicated" for such a "simple" task.
Or am I missing something?
A hint would be much appreciated.
Thanks.

You could create 1 main container in the displaylist (not the stage) and listen for ROLL_OVER and ROLL_OUT, and set somekind of global flag there, that your mouse is over the display-list container. Then in your starling events, check for this flag. This isn't the nicest solution out there i guess, but it should work
var isOverDisplayList:Boolean = false;
container.addEventListener(MouseEvent.ROLL_OVER, onRollOver);
container.addEventListener(MouseEvent.ROLL_OUT, onRollOut);
function onRollOver(e:MouseEvent) {
isOverDisplayList = true;
}
function onRollOut(e:MouseEvent) {
isOverDisplayList = false;
}

Related

Checking a Boolean variable in another class

Let me set the stage because it's too much code to post everything:
I have a Main.as that is setting up my SoundController.as so I can trigger sounds from any other class (SoundController.as has all the functions needed to call all my sounds as needed)
I have a ControlPanel.as that can access these sounds by using docRef.soundControl.laserFire or whatever other function name I want, and in this case the laserFire sound would trigger once.
So here is my question. I want to let this laser sound effect finish playing before you can fire another laser. So in SoundController.as I've set up the following pieces of code:
private var _laserPlaying:Boolean = false;
internal function laserFire():void {
_sfxChannel = _laser.play(25);
_laserPlaying=true;
_sfxChannel.addEventListener(Event.SOUND_COMPLETE, laserFinished);
}
internal function laserFinished(event:Event):void {
_sfxChannel.removeEventListener(Event.SOUND_COMPLETE, laserFinished);
_laserPlaying=false;
}
public function get laserPlaying():Boolean {
return _laserPlaying;
}
public function set laserPlaying(value:Boolean):void {
_laserPlaying = value;
}
Now in my ControlPanel class in the enterFrameHandler function I want to do an
if (docRef.soundControl.laserPlaying()==false)
or something to that effect so I can check when the sound is done and allow the player to once again press the trigger to fire the laser. So far any variant I've tried on this either gives me an error (in this case 1195; Attempted access of inaccessible method laserPlaying through a reference with static type SoundController) or it actually compiles but after firing the first laser shot it never allows the trigger to be pressed again. So I'm obviously doing something wrong and am hoping someone can help.
Let me just state that the laser sound is playing just fine that first time, so don't worry about all the code I'm not bothering to show to make that portion of the code work. However, if more info is needed to understand how I'm making anything work just let me know. And Thanks in advance!
If your soundController AND ControlPanel are instantiated in Main:
handle the firing event, in ControlPanel, like this:
if(MovieClip(parent).soundController.soundChannel.position==0)
{
MovieClip(parent).soundController.laserfire();
}else{do nothing;}
Of course use proper instance names.
If this doesn't work you'll have to make your code a little easier to understand.
Sorry for wasting everyone's time. I ended up figuring out what I needed to do to make this work. This probably won't be of much use to anyone else since my setup was probably unique to my layout and not something that anyone else will try, but here is what I did anyway.
In my ControlPanel.as I added the following code:
private var _soundController:SoundController;
And then I have a function that waits for the ControlPanel to be added to the stage and once that occurs I fired:
_soundController = new SoundController(docRef);
Now by adding those I was able to simply call:
if(!_soundController.laserPlaying) { do stuff; }
It now seems to wait for laserPlaying to be false and then moves on as intended.

Check the existence of an object instance

I'm surprised I don't know how to do this, but as it turns out I really don't; simply put, I'm trying to make a side-scrolling shooter game, a basic one and in it, I have 50 stars spawned on-screen through a "for" loop upon the game starting. There is a function which does this and a listener is at the beginning. Problem is, when you lose the game and go back to main menu, 50 more stars would be spawned, which isn't what I want. So, I'm trying to make an "if" statement check at the beginning, so that the game checks whether there is an instance/movie clip of the star object/symbol before determining whether the function that spawns stars should be called out with a listener. So, how do I do this? I looked through some other checks and they didn't help as the codes presented were vastly different there and so I'm just getting errors.
Let me know if a better explanation is needed or if you would like to see some of the code. Note that the game overall already has a lot of code, so just giving all of it would probably not be helpful.
I suggest you rethink your approach. You're focusing on whether stars have been instantiated. That's ok but not the most basic way to think about it.
I would do this instead
private function setup():void{
loadLevel(1);
addListeners();
loadMusic();
// etc...
// call all functions that are needed to just get the app up and running
}
private function loadLevel(lev:int):void{
addStars();
// call all functions that are needed each time a new level is loaded
}
private function restartLevel():void{
// logic for restarting level,
// but this *won't* include adding star
// because they are already added
}
There are other ways to do this but this makes more sense to me than your approach. I always break my game functions into smaller bits of logic so they can be reused more easily. Your main workhorse functions should (IMHO) primarily (if not exclusively) just call other functions. Then those functions do the work. By doing it this way, you can make a function like resetLevel by assembling all the smaller functions that apply, while excluding the part about adding stars.
Here's what I did to solve my problem... Here's what I had before:
function startGame():void
{
starsSpawn();
//other code here
}
This is what I changed it to:
starsSpawn();
function startGame():void
{
//other code here
}
when you said existance, so there is a container, i named this container, (which contain stars , and stars was added to it) as starsRoot, which absolutely is a DisplayObject (right?)
now, to checking whole childrens of a DisplayObject, we have to do this :
for (var i:int=0; i<starsRoot.numChildren; i++) {
var child = starsRoot.getChildAt[i];
}
then, how to check if that child is really star!?
as you said
whether there is an instance/movie clip of the star
so your stars's type is MovieClip, and they don't have any identifier (name), so how to find them and make them clear from other existing movieclips. my suggestion :
define a Linkage name for stars from library, thats a Class name and should be started with a capital letter, for example Stars
now, back to the code, this time we can check if child is an instance of Stars
for (var i:int=0; i<starsRoot.numChildren; i++) {
var child = starsRoot.getChildAt[i];
if (child is Stars) {
// test passed, star exist
break;
}
}

Possible to have multiple timelines? (separate part without affecting the keyframes of other layers) AS3

http://i.snag.gy/eu7iz.jpg
So im doing this generator/designer on flash. It has different features on it so the keyframes clashes with other features considering a lot of the action scripts deal with nextframes and gotos. Its getting confusing once i add little features.
Like right now i wanna add next buttons for the design part. I can do it easily with a blank stage, i can easily click next and back, but when applied to my project, its getting a little dizzying.
This is the script for the first frame:
stop();
small.addEventListener(MouseEvent.CLICK,play1);
function play1(event:MouseEvent):void{
gotoAndStop("3");
}
medium.addEventListener(MouseEvent.CLICK,play2);
function play2(event:MouseEvent):void{
gotoAndStop("6");
}
large.addEventListener(MouseEvent.CLICK,play3);
function play3(event:MouseEvent):void{
gotoAndStop("8");
}
item_mc.addEventListener(MouseEvent.MOUSE_DOWN, dragTheObject);
item_mc.addEventListener (MouseEvent.MOUSE_UP, itemRelease);
item_mc1.addEventListener(MouseEvent.MOUSE_DOWN, dragTheObject);
item_mc1.addEventListener (MouseEvent.MOUSE_UP, itemRelease);
item_mc2.addEventListener(MouseEvent.MOUSE_DOWN, dragTheObject);
item_mc2.addEventListener (MouseEvent.MOUSE_UP, itemRelease);
function dragTheObject(event:MouseEvent):void {
var item:MovieClip=MovieClip(event.target);
item.startDrag();
var topPos:uint=this.numChildren-1;
this.setChildIndex(item, topPos);
}
function itemRelease(event:MouseEvent):void {
var thisItem:MovieClip=MovieClip(event.target);
thisItem.stopDrag();
};
This is the fla file: https://www.dropbox.com/s/77euop1luqjreos/FINAL.fla
MovieClips have their own timeline.You may want to modularize your program into Movieclip components and export for Actionscript manipulation that you can instantiate at run time as necessary. Now that is one way to do it to avoid code spread across one single timeline.But If you still want to stick to your way (use of single timeline), You still could achieve your next/previous implementation without affecting frame logic at any rate.A simple way to do this goes like this:
Encapsulate all logic into functions and put these functions solely on frame 1 and nothing else.
This keeps logic clean and separate from the components.Also, the logic layer in principal should not have nothing else.Why on Frame 1?. Well, we want to expose and keep in memory first the logic, so that whatever component related code that follows on the subsequent frames should be aware of the previous logic and hence throw no run time errors when interacting frame 1 logic.
Spread your components and related code across the subsequent frames respectively.
Put only component related code on a frame that has the component in question. registering event listeners could have targets as their dependency. define event listeners and put them on frame 1 as part of the logic and simply put code for registering listeners on the component frames as per demand.
Example:
//On Frame 1
function onAMouseClick(event:Event):void
{
//implement logic
}
function onBMouseClick(event:Event):void
{
//implement logic
}
//Implemented function for next/back buttons
//Also on Frame 1
function navigate(event:Event):void
{
var frame:int;
switch(event.target.name)
{
case "nextBtn":
frame=currenFrame<numFrames?+1:numFrames;
gotoAndStop(frame);
break;
case "backBtn":
frame=currenFrame>2?-1:currentFrame;
gotoAndStop(frame);
break;
}
}
//On Frame 2 for A component
A.addEventListener(MouseEvent.CLICK, onAMouseClick)
//On Frame 3 for B component
B.addEventListener(MouseEvent.CLICK, onAMouseClick)
Put Next/Back button components on a single layer spreading from frame 2 all the way to the end of the last frame where you want the buttons to be visible. Then implement related code having its visibility spanning across between frame 2 and the last frame as the following shows:
//navigate handler is declared and implemented on **frame 1**
nextBtn.addEventListener(MouseEvent.Click, navigate)
nextBtn.addEventListener(MouseEvent.Click, navigate)
Well, that is your way of doing things (Single timeline scripting). Not bad for simple timeline scripting.You may try the other way also of instantiating Movieclips (exported for action scripiting)
at run time and the add these to the display list as per demand as on clicks next/back buttons.In doing so you will not only have one single point of logic but will have MC code encapsulated in each individual component.
Hope the foregoing helps. Thanks.
Do not be scared to change code! Let alone afraid to run into errors! That's way forward you want to learn to fix things that's a beauty of it!
.....cheers!.

Tweening with actionscript 3

I have been working on this one a while. I have an object in this case a movieClip called "mcBall". I set up two buttons "btnLeft" and "btnRight"with a tween so that the mcBall will ease between the two points smoothly.
Works fine, but where it gets glitchy is the the two buttons are still active an if the user clicks on either button of course the ball goes back to the starting point like it's supposed to.
My question is this ... What is the best way to have the buttons be de-activated while the "mcBall" object is moving. Would it be best to use a removeEventListener for the buttons and then have it added again. Would it be better to use an if statement like "If (mcBall.x = >=81 || <=469) removeEventListener"? Maybe use the tweenEvent.MOTION_FINISH to set up the eventListener again.
Any help would be greatly appreciated. Thanks
using Flash cs3
In this code I managed to turn off one button so that while the ball is moving it remains inactive but the other is still active. I'm not sure of the placement of the removeEventListener.
import fl.transitions.Tween;
import fl.transitions.easing.*;
function moveBallRight(evt:MouseEvent):void {
var moveBall:Tween=new Tween(mcBall,"x",Regular.easeOut,80,470,4,true);
btnRight.removeEventListener(MouseEvent.CLICK,moveBallRight);
btnLeft.addEventListener(MouseEvent.CLICK,moveBallLeft);
}
btnRight.addEventListener(MouseEvent.CLICK,moveBallRight);
function moveBallLeft(evt:MouseEvent) {
var moveBall:Tween=new Tween(mcBall,"x",Regular.easeOut,470,80,4,true);
btnRight.addEventListener(MouseEvent.CLICK,moveBallRight);
btnLeft.removeEventListener(MouseEvent.CLICK,moveBallLeft);
}
btnLeft.addEventListener(MouseEvent.CLICK,moveBallLeft);
Personally, I would recommend using the Actuate tween library. Unlike TweenLite/Max, it is fully opensource, and has most of the features, and is faster, with no pro/pay version.
I also like Actuate's interface much better than TweenLite. It is very similar so easy for people to start using it, but I like how tween modifiers are added in a more explicit way.
Simple example:
Actuate.tween(mySprite, 1, { alpha:1 });
Then is you want to specify an easing equation, just chain it on the end:
Actuate.tween(mySprite, 1, { alpha:1 }).ease(Quad.easeOut);
Want a delay as well? Add that to the chain:
Actuate.tween(mySprite, 1, { alpha:1 }).delay(1).ease(Quad.easeOut);
Of course you can also call a function onComplete, even with parameters:
Actuate.tween(mySprite, 1, { alpha:1 }).onComplete(trace, 'Tween finished');
Check out the Actuate Google Code page linked above for the full list of methods with examples.
I would recommend not using the native Tween class from fl.transitions.Tween. Its not very good. The industry standard is TweenMax from greensock.
Using TweenMax it is trivially easy to respond to end-of-tween events. You simply add an onComplete:myhandlerfunction to your tween.
An example from your above code would look like this:
Instead of
var moveBall:Tween=new Tween(mcBall,"x",Regular.easeOut,80,470,4,true);
You would have:
TweenMax.to(mcBall, 4, {x:470, ease:Expo.easeOut, onComplete:onBallMovedLeftComplete};
Hope that helps. And I hope you never have to use those native tween classes again. They are the pits.
I agree that you should use TweenLite or TweenMax as the other answers suggests.
From what I gather though, the question is the best approach in activating/deactivating your event listeners for the buttons.
I'd say the best approach is to have a function for adding your button listeners and another function for removing them.
Then, whenever you call a tween, you first call the removal function before executing the tween.
Then upon completion, you call the function to add them again. You can use the onComplete parameter with Tweenlite to specify the function to add the button listeners.
for example :
function moveBallRight(evt:MouseEvent):void
{
removeButtonListeners();
TweenLite.to(mcBall, 4, {x:470, ease:Expo.easeOut, onComplete:addButtonListeners};
}
function moveBallLeft(evt:MouseEvent):void
{
removeButtonListeners();
// do your tween
TweenLite.to(mcBall, 4, {x:80, ease:Expo.easeOut, onComplete:addButtonListeners};
}
function addButtonListeners():void
{
// add both listeners here
}
function removebuttonListeners():void
{
// remove both listeners here
}
Also, you'd obviously want to call addButtonListeners at the beginning of your program as well, so that the listeners are initially active when the program runs.

Flex Spark Textinput prevents component to be collected by GC

I've got a custom component (quite complex so I can't post any code here, although that shouldn't matter), that I can add to a view. When the component is deleted from the view or the view is switched I call my own dispose method which removes remaining eventListeners and kills some references so that the component can eventually be nulled and collected by the GC.
All that works perfectly fine until I add a Spark TextInput to the MXML part of the component (it took me hours to find out what is preventing the component to be collected!), so I recon that the TextInput somehow automatically adds some eventListeners.
My question is what are these listeners, or is there anything else I haven't thought of?
Any help would be greatly appreciated!
I'll summarize our discussion for the pleasure of future readers.
Find the culprit
You could can have a look at the code of SkinnableTextBase to see what event listeners are attached internally. Now that you know that, you can use hasEventListener() to test which ones weren't removed. Using this technique we found that these listeners were still lingering:
MouseEvent.MOUSE_DOWN
TouchInteractionEvent.TOUCH_INTERACTION_START
Removing them (preferably without subclassing TextInput)
Have a look at the code of SkinnableTextBase where these listeners are registered:
override public function styleChanged(styleProp:String):void
{
super.styleChanged(styleProp);
if (!styleProp ||
styleProp == "styleName" || styleProp == "interactionMode")
{
if (getStyle("interactionMode") == InteractionMode.TOUCH && !touchHandlersAdded)
{
addEventListener(MouseEvent.MOUSE_DOWN, touchMouseDownHandler);
addEventListener(TouchInteractionEvent.TOUCH_INTERACTION_START,
touchInteractionStartHandler);
touchHandlersAdded = true;
}
else if (getStyle("interactionMode") == InteractionMode.MOUSE && touchHandlersAdded)
{
removeEventListener(MouseEvent.MOUSE_DOWN, touchMouseDownHandler);
removeEventListener(TouchInteractionEvent.TOUCH_INTERACTION_START,
touchInteractionStartHandler);
touchHandlersAdded = false;
}
}
}
This means that if you set the TextInput's interactionMode style to InteractionMode.MOUSE, that should remove the listeners.
Note: you might want to take a look at the JIRA bug base and file a bug if noone already has. Though I must say I'm not sure if this JIRA is still maintained now that Flex is moving to Apache.