Set object's `visible` to `true` when a `tap` event is performed then set it back to `false` - actionscript-3

I have an object as MovieClip and I have a button as Button on my flash timeline.
When the button is tapped, I want to set the object.visible to true then when the button is not tapped, I want to set it back to false.
How can I do that?
I have tried this code, but it won't works as I want. I only can show the object but cannot hide it back.
button1.addEventListener(TouchEvent.TOUCH_TAP, touchTap);
function touchTap(e:TouchEvent): void {
mcObj.visible = true;
stage.addEventListener(TouchEvent.TOUCH_END, touchEnd);
}
function touchEnd(e:TouchEvent): void {
mcObj.visible = false;
stage.removeEventListener(TouchEvent.TOUCH_END, touchEnd);
}

I think this code could work.
button1.addEventListener(TouchEvent.TOUCH_BEGIN, touchTap);
function touchTap(e:TouchEvent): void {
mcObj.visible = true;
button1.addEventListener(TouchEvent.TOUCH_END, touchEnd);
}
function touchEnd(e:TouchEvent): void {
mcObj.visible = false;
button1.removeEventListener(TouchEvent.TOUCH_END, touchEnd);
}
I changed
1: TouchEvent.TOUCH_TAP to TouchEvent.TOUCH_BEGIN
2: stage.addEventListener to button1.addEventListener

Before saying anything about your problem, let's take a look on the definitions of the TouchEvent.TOUCH_BEGIN, TouchEvent.TOUCH_END and TouchEvent.TOUCH_TAP events :
The TouchEvent.TOUCH_BEGIN is :
Dispatched when the user first contacts a touch-enabled device ...
The TouchEvent.TOUCH_END is :
Dispatched when the user removes contact with a touch-enabled device ...
The TouchEvent.TOUCH_TAP is :
Dispatched when the user lifts the point of contact over the same InteractiveObject instance on which the contact was initiated on a touch-enabled device ...
And with some tests, we can see that the TouchEvent.TOUCH_END event is, in almost cases, fired before the TouchEvent.TOUCH_TAP one (by 1 or 2 milliseconds), so we can understand the we are able to detect if the user has already removed contact with the device (TouchEvent.TOUCH_END is fired) then if that was on the same InteractiveObject object on which the contact was initiated (TouchEvent.TOUCH_TAP is fired).
And that's why your code is not working.
Now, let's see your problem : you want to show a MovieClip just when your user tap a button and hide it when he releases that button but only for a very short time (the time of a tap ~= 300 milliseconds).
In this case, I recommend you to use a TouchEvent.TOUCH_BEGIN event listener with a timeout to hide that object even if your user didn't release the button.
For that, take this example :
Multitouch.inputMode = MultitouchInputMode.TOUCH_POINT;
btn.addEventListener(TouchEvent.TOUCH_BEGIN, on_touchBegin);
function on_touchBegin(e:TouchEvent): void
{
obj.visible = true;
hide_obj();
}
function hide_obj(): void
{
// you can use a Timer object instead of setTimeout()
var timeout:int = setTimeout(function(){
clearTimeout(timeout);
obj.visible = false;
}, 300);
}
Hope that can help.

Related

Why isn't touch listener removed when Sprite is removed?

I have the following code for checking for touches on a sprite:
void SpriteBlock::addEvents()
{
auto listener = cocos2d::EventListenerTouchOneByOne::create();
listener->setSwallowTouches(true);
listener->onTouchBegan = [&](cocos2d::Touch* touch, cocos2d::Event* event)
{
Vec2 p = touch->getLocation();
Rect rect = this->getBoundingBox();
if(rect.containsPoint(p))
{
return true; // to indicate that we have consumed it.
}
return false; // we did not consume this event, pass thru.
};
listener->onTouchEnded = [=](cocos2d::Touch* touch, cocos2d::Event* event)
{
SpriteBlock::touchEvent(touch);
};
cocos2d::Director::getInstance()->getEventDispatcher()->addEventListenerWithFixedPriority(listener, 30);
}
void SpriteBlock::touchEvent(cocos2d::Touch* touch)
{
}
This seems to work fine, but even after the sprite is destroyed, it still gets triggered (if I click on the last place the sprite existed), and It crashes with:
"
Thread1: EXC_BAD_ACCESS (code=2, address=0x7....)
"
at the following line:
Rect rect = this->getBoundingBox();
Now it seems clear to me that the sprite is destroyed because my destructor is set to display a log message when it is triggered (which it does):
SpriteBlock::~SpriteBlock() {
CCLOG("Block destroyed");
}
So what is the problem here ? Why isn't the listener destroyed with my sprite ? I destroy my sprite by doing the following:
mysprite->removeFromParent();
When I create the sprite, I do not store any references. I just add it to the main layer of my scene, so it shouldn't remain. I create it using:
SpriteBlock *block = SpriteBlock::create();
How do I make sure the touch listener is also removed when the sprite is removed ?
auto listener = cocos2d::EventListenerTouchOneByOne::create();
store it as a variable in SpriteBlock class.
Then in SpriteBlock destructor remove a listener.

Flex topLevelApplication and SuperTabNavigator mouse event handler

I am using the flex SuperTabNavigator and want on closing the tab check if the control button was pressed. I tried:
public static var CONTROL_PRESSED:Boolean = false;
public function init():void {
var clickListener:Function = function _clickListener(event:MouseEvent):void{
trace(event.ctrlKey);
if(event.ctrlKey){
CONTROL_PRESSED = true;
}else{
CONTROL_PRESSED = false;
}
};
FlexGlobals.topLevelApplication.addEventListener(MouseEvent.CLICK, clickListener);
}
The problem with this is that the mouse click is called everywhere in the application except on the tab. I also tried the same code but addEventListener(MouseEvent.CLICK, clickListener); to add the listener to the SuperTabNavigator and it did not work at all. Is there another way to catch the mouse click?
This is because the SuperTabNavigator has a private mouse click handler function that hides the MouseEvent:
private function closeClickHandler(event:MouseEvent):void {
if(this.enabled) {
dispatchEvent(new Event(CLOSE_TAB_EVENT));
}
event.stopImmediatePropagation();
event.stopPropagation();
}
You'll need to modify the SuperTab class in the SuperTabNavigator source to dispatch some CustomEvent with the data you want instead of a plain new Event(CLOSE_TAB_EVENT).
Then change the onCloseTabClicked function in SuperTabBar to know about your CustomEvent. Then you can pass that to your application code (by adding it to the SuperTabEvent, perhaps).

referencing objects in different functions as3

I have an object in my mouse event functions that I want to reference in my time function.
Example, I basically created tiles and have mouse events:
var cell:MovieClip = new Tile();
cell.gotoAndStop(floor1[i][u]);
cell.x = ((u-i)*tileh)+365;
cell.y = ((u+i)*tileh/2)+70;
addChild(cell);
cell.addEventListener(MouseEvent.ROLL_OVER, mouseover);
cell.addEventListener(MouseEvent.ROLL_OUT, mouseout);
cell.addEventListener(MouseEvent.CLICK, mouseclick);
enemyMoveTimer.addEventListener(TimerEvent.TIMER, timerListener);
In the mouse events, I have something called event.currentTarget. Since I have tiles lined up with each other, I wanted to differentiate each individual tile. Thus creating that event.currentTarget. I wanted to use this in my time event, but it isn't recognizing event.currentTarget as an object, rather it's own timer. Is there any way to have the time function recognize the event.currentTarget from the mouse events?
Event.currentTarget is the last object to dispatch that specific event (and Event.target is the original object to dispatch the event). It can be absolutely anything that extends EventDispatcher.
The only way to do what you want is like this:
var currentTile:Tile;
cell.addEventListener(MouseEvent.ROLL_OVER, mouseEventsHandler);
cell.addEventListener(MouseEvent.ROLL_OUT, mouseEventsHandler);
cell.addEventListener(MouseEvent.CLICK, mouseEventsHandler);
enemyMoveTimer.addEventListener(TimerEvent.TIMER, timerListener);
function mouseEventsHandler( e:MouseEvent ):void {
this.currentTile = e.currentTarget as Tile;
}
function timeListener( e:TimerEvent ):void {
this.currentTile.blah.blah();
}
Basically you save the tile that most recently was interacted with into currentTile and then that is what you access in your timeListener.
You should really look through the LiveDocs to get a basic understanding of how events work and possibly look into how scope works as well.
Some explanation:
Your Event-Listener receives an Event-Object. Always. What kind of Event-Object it is depends on the Event. In your MouseListener you receive a MouseEvent, in you TimerListener a TimerEvent and so on.
EVERY Event-Object has two specific attributes.
event.currentTarget
is the Object, which binds the event listener (in your case "cell")
event.target
is the object, which "caused" the Event. If e.g. in you "cell"-MovieClip is another MovieClip called "nucleus" and you click on the the it, event.currentTarget would be "cell", but even.target would be "nucleus".
Since the event-object is a passed as a function parameter, it is destroyed, once the function is finished. If you wand to use parts of it in another function, you need to do it like this
var myCell:MovieClip;
function mouseClick(event:MouseEvent):void{
myCell = event.currentTarget as MovieClip;
}
function timeListener(event:TimerEvent):void{
if(myCell){
///what ever you want to do with it
myCell = null;
}
}
I hope I explained it clearly.
Here's one way you could do this :
enemyMoveTimer.addEventListener(TimerEvent.TIMER, timerListener(cell));
enemyMoveTimer.start();
public function timerListener(cell:MovieClip):Function
{
var doStuffToCell:Function = new Function(e:TimerEvent)
{
trace (cell.x + " : " + cell.y);
// do whatever you want with cell
}
return doStuffToCell;
}
I should note that I don't think it's a good idea to call your handler timerListener, as it's a handler.
Listeners do just that, they listen for an event. A handler, handles an event. The second parameter in the addEventListener method specifies a function to handle the event. Naming your handler timerListener is not a good idea in my opinion, as it's really not that.

Anonymous functions as event handlers in Action Script - good or bad?

I came to AS3 from JS world, and I should confess that anonymous functions are my weakness. I tend to use them everywhere. Now, coming to AS3 I've heard and read in lots of places, that AS and Flash are enormously bad at handling garbage collection, that one should empty, dispose and remove all event handlers and objects manually to avoid weird and unexplainable memory leaks and crashes. Not sure what part of this is true, but I would like to follow best practices right from the beginning.
So my question would be - how bad is idea of using anonymous functions as event handlers? Consider for example a code like this:
addEventListener(Event.ENTER_FRAME, function() : void {
controls.elapsed = stream.time;
});
contorls.elapsed is the setter, which apart from setting current play time for video player, updates the whole UI, and stream is NetStream object, which streams the actual video.
There are lot's of other places where anonymous function may make code cleaner and more intuitive. Check the following code for simple fade-in effect for the control bar:
public function showControls() : void
{
var self:Controls = this;
if (!visible) {
visible = true;
fadeTimer = new Timer(30, 10);
fadeTimer.addEventListener(TimerEvent.TIMER, function() : void {
self.alpha += 0.1;
});
fadeTimer.addEventListener(TimerEvent.TIMER_COMPLETE, function() : void {
self.alpha = 1;
});
fadeTimer.start();
}
}
I totally like how it looks and fits into the code, but I'm concerned about leaks. While Event.ENTER_FRAME handler probably would never become harmful in this form, what about timer listeners. Should I remove those listeners manually, or they will be removed automatically, as soon as I set fadeTimer = null ? Is it possible to remove listeners with anonymous functions properly at all?
Just noticed this post -- there are a couple things that might be of use to you. One is arguments.callee (which is a reference to the current function you're in). This is useful for removing references in anonymous functions. Also, it could be noted that you could use weak references in your addEventListener code -- however, this won't work for variables that are anonymous, as they'd get GC'd pretty much immediately. For simplicity sake I rewrote your code like this: (should work -- haven't tested)
private function showControls() : void {
if (visible) {
return;
}
var self:DisplayObject = this;
var fadeTimer= new Timer(30,10);
var handler = function(e:Event) {
switch (e.type) {
// timer complete
case TimerEvent.TIMER_COMPLETE:
// remove references to this anonymous function -- for garbage collection
fadeTimer.removeEventListener(TimerEvent.TIMER_COMPLETE, arguments.callee);
fadeTimer.removeEventListener(TimerEvent.TIMER, arguments.callee);
// break out
return self.alpha = 1;
// timer
case TimerEvent.TIMER:
return self.alpha += 0.1;
}
}
fadeTimer.addEventListener(TimerEvent.TIMER, handler);
fadeTimer.addEventListener(TimerEvent.TIMER_COMPLETE, handler);
fadeTimer.start();
}
I would do it something like this. And, be sure to use dispose() when you want to make sure to clear the timer if interrupting.
private function showControls() : void
{
if(_isVisible)
return;
// start you control here
_fadeTimer = new Timer(30, 10);
_fadeTimer.removeEventListener(TimerEvent.TIMER, updateFade);
_fadeTimer.removeEventListener(TimerEvent.TIMER_COMPLETE, updateFadeComplete);
_fadeTimer.start();
}
private function updateFade(event : TimerEvent) : void
{
// update fade here
}
private function updateFadeComplete(event : TimerEvent) : void
{
dispose();
}
private function dispose() : void
{
if(_fadeTimer)
{
_fadeTimer.stop();
_fadeTimer.removeEventListener(TimerEvent.TIMER, updateFade);
_fadeTimer.removeEventListener(TimerEvent.TIMER_COMPLETE, updateFadeComplete);
_fadeTimer = null;
}
}
There's nothing wrong with using function methods where it works. As far as memory leaks go, you need to track the object to the stage to see if it can be removed.
Adding an ENTER_FRAME event handler to the control ensures that the control has a reference to the anonymous function. As the code is part of the control (or so it appears), this is fine as the anonymous function will be removed when the control is.
Adding an event handler to the timer ensures that the timer has a reference to the anonymous function. If the timer is running, it will keep the anonymous function reference alive and, by association, the enture control. Once the timer has stopped, however, both it and the function should be collected.
If all else fails, use the profiler and see! ;)

ActionScript MouseEvent's CLICK vs. DOUBLE_CLICK

is it not possible to have both CLICK and DOUBLE_CLICK on the same display object? i'm trying to have both for the stage where double clicking the stage adds a new object and clicking once on the stage deselects a selected object.
it appears that DOUBLE_CLICK will execute both itself as well as the first CLICK functions in the path toward DOUBLE CLICK (mouse down, mouse up, click, mouse down, mouse up, double click).
in other languages i've programmed with there was a built-in timers that set the two apart. is this not available in AS3?
UPDATE
here's some code. essentially what i would like is have one or the other, not both with double click
stage.doubleClickEnabled = true;
stage.addEventListener(MouseEvent.DOUBLE_CLICK, twoClicks, false, 0, true);
stage.addEventListener(MouseEvent.CLICK, oneClick, false, 0, true);
function oneClick(evt:MouseEvent):void
{
trace("One CLICK");
}
function twoClicks(evt:MouseEvent):void
{
trace("Two CLICKS");
}
//oneClick trace = "One CLICK"
//twoClicks trace = "One CLICK Two CLICKS" (instead of just Two CLICKS)
Well, you could use setTimeout and clearTimeout.
It'd look something like this:
const var DOUBLE_CLICK_SPEED:int = 10;
var mouseTimeout;
function handleClick(evt:MouseEvent):void {
if (mouseTimeout != undefined) {
twoClicks();
clearTimeout(mouseTimeout);
mouseTimeout = undefined;
} else {
function handleSingleClick():void {
oneClick();
mouseTimeout = undefined;
}
mouseTimeout = setTimeout(handleSingleClick, DOUBLE_CLICK_SPEED);
}
}
function oneClick(evt:MouseEvent):void {
trace("One CLICK");
}
function twoClicks(evt:MouseEvent):void {
trace("Two CLICKS");
}
stage.addEventListener(MouseEvent.CLICK, handleClick, false, 0, true);
Did you set .doubleClickEnabled to true?
You should also take a look here.
Great answer Wallacoloo - thanks for that. I have just implemented your solution and refined a few points, so I'd thought I'd put it here for future reference (and of course the benefit of the overflow community!). Firstly, I couldn't test for undefined on the uint returned by setTimeout, so I replaced the undefined conditional with an == 0 conditional. Secondly, I wanted to commit the logic of a single click instantaneously (just makes for a more pleasant user interface), so I've done a bit of reshuffling:
if (mouseTimeout != 0) {
// clicked within the timeout, handle as double click
// rollback single click logic
rollbackSingleClickHandler(e);
// commit double click logic
dblClickHandler(e);
clearTimeOut(mouseTimeout);
mouseTimeout = 0;
} else {
// first click of a click sequence
// commit single click logic
singleClickHandler(e);
function clearTime():void {
mouseTimeout = 0;
}
// register a timeout for a potential double click
mouseTimeout = setTimeout(clearTime, DOUBLE_CLICK_SPEED);
}