Why is touch event triggered even when I touch outside the sprite? - cocos2d-x

I have a ui::ScrollView containing a number of sprites.
I have created each sprite and added a touch listener to each sprite by doing something like:
for(int i=0; i < 5; i++){
Sprite* foo = Sprite::createWithSpriteFrameName("foo");
myScrollView->addChild(foo);
auto listener = EventListenerTouchOneByOne::create();
listener->onTouchBegan = [this,somestring](Touch* touch, Event* event){
......some code
};
listener->onTouchMoved = [foo,this,somestring](Touch* touch, Event* event){
......some code
};
listener->onTouchEnded = [foo,this,somestring](Touch* touch, Event* event){
......some code
};
foo->getEventDispatcher->addEventListenerWithSceneGraphPriority(listener1,foo);
}
The problem is, if I click ANYWHERE on screen, it seems to trigger the touch events of ALL the sprites created in the loop. Is there something incorrect in how I am creating the listener, or does it have to do with some conflict with touches in a ui::ScrollView ?
I am using v 3.10

Because that is how TouchListener works in cocos2d-x. All touch listener will be called unless someone has swallowed the touch event. Your code would be:
auto touchSwallower = EventListenerTouchOneByOne::create();
touchSwallower ->setSwallowed(true);
touchSwallower->onTouchBegan = [](){ return true;};
getEventDispatcher->addEventListenerWithSceneGraphPriority(touchSwallower ,scrollview);
for(int i=0; i < 5; i++){
Sprite* foo = Sprite::createWithSpriteFrameName("foo");
myScrollView->addChild(foo);
auto listener = EventListenerTouchOneByOne::create();
listener->setSwallowed(true);
listener->onTouchBegan = [this,somestring](Touch* touch, Event* event){
......some code
Vec2 touchPos = myScrollView->convertTouchToNodeSpace(touch);
return foo->getBoundingBox()->containsPoint(touchPos);
};
listener->onTouchMoved = [foo,this,somestring](Touch* touch, Event* event){
......some code
};
listener->onTouchEnded = [foo,this,somestring](Touch* touch, Event* event){
......some code
};
foo->getEventDispatcher->addEventListenerWithSceneGraphPriority(listener1,foo);
}

cocos2dx will dispatch the touch-event to every Node attached a touch-event, unless someone swallow the it.
But if you want the "node" default judge whether touch-location in the content, try to use "UIWidget" with "addTouchEventListener". It will calculate by itself.
bool Widget::onTouchBegan(Touch *touch, Event *unusedEvent)
{
_hitted = false;
if (isVisible() && isEnabled() && isAncestorsEnabled() && isAncestorsVisible(this) )
{
_touchBeganPosition = touch->getLocation();
auto camera = Camera::getVisitingCamera();
if(hitTest(_touchBeganPosition, camera, nullptr))
{
if (isClippingParentContainsPoint(_touchBeganPosition)) {
_hittedByCamera = camera;
_hitted = true;
}
}
}
if (!_hitted)
{
return false;
}
setHighlighted(true);
/*
* Propagate touch events to its parents
*/
if (_propagateTouchEvents)
{
this->propagateTouchEvent(TouchEventType::BEGAN, this, touch);
}
pushDownEvent();
return true;
}

Related

Restore dragged movieclips to their original position as they are dragged outside the stage in action script 3

I am creating a drag and drop the game for kids and I realized that when the kid drags a movie clip by mistake outside the stage the movie clip hangs where it is dragged onto and doesn't return back to its original position or it overlaps over other movie clips.
Thanks in advance
the code I am basically using is:
* square_mc.addEventListener(MouseEvent.MOUSE_DOWN, pickUp2);
square_mc.addEventListener(MouseEvent.MOUSE_UP, dropIt2);
function pickUp2(event: MouseEvent): void {
square_mc.startDrag(true);
event.target.parent.addChild(event.target);
startX = event.currentTarget.x;
startY = event.currentTarget.y;
}
function dropIt2(event: MouseEvent): void {
square_mc.stopDrag();
var myTargetName:String = "target" + event.target.name;
var myTarget:DisplayObject = getChildByName(myTargetName);
if (event.target.dropTarget != null && event.target.dropTarget.parent ==
myTarget){
event.target.removeEventListener(MouseEvent.MOUSE_DOWN, pickUp2);
event.target.removeEventListener(MouseEvent.MOUSE_UP, dropIt2);
event.target.buttonMode = false;
event.target.x = myTarget.x;
event.target.y = myTarget.y
} else {
event.target.x = startX;
event.target.y = startY;
}
}*
What you need to do is to preserve the initial position of the clip somewhere and then use it.
Basic algorithm I can think of can look as follows: 1 - player starts dragging a clip, init position is saved 2 - player releases the clip.
Two outcomes are possible: (a): the clip is on the right position or (b): it is misplaced. In case (b) you just need to assign initial coordinates back to the clip.
Very simple example:
private const initialCoords: Dictionary = new Dictionary();
private function saveInitialCoords(clip: DisplayObject): void {
initialCoords[clip] = new Point(clip.x, clip.y);
}
private funciton retreiveInitialCoords(clip: DisplayObject): Point {
return initialCoords[clip]; // it would return null if there is no coords
}
UPD: As it was pointed by #kaarto my answer might be irrelevant. If the issue is to keep the dragging movieclip
within some bounds I usually use something like this:
private var currentTarget: DisplayObject; // clip we're dragging currently
private var areaBounds: Rectangle = new Rectangle(0, 0, stage.stageWidth, stage.stageHeight); // you might want to ensure stage is not null.
private function startDrag(e: MouseEvent): void {
// I'll drop all checks here. Normally you want to ensure that currentTarget is null or return it to initial position otherwise
currentTarget = e.currentTarget as DisplayObject;
saveInitialCoords(currentTarget);
addEventListener(MouseEvent.MOUSE_MOVE, checkAreaBounds);
}
private funciton stopDrag(e: MouseEvent): void {
// some checks were dropped.
if (clipIsOnDesiredPlace(currentTarget)) {
// do something
} else {
const initialCoords: Point = retreiveInitialCoords(currentTarget);
currentTarget.x = initialCoords.x;
currentTarget.y = initialCoords.y;
}
currentTarget = null;
removeEventListener(MouseEvent.MOUSE_MOVE, checkAreaBounds);
}
private funciton checkAreaBounds(e: MouseEvent): void {
// you might need to convert your coords localToGlobal depending on your hierarchy
// this function gets called constantly while mouse is moving. So your clip won't leave areaBounds
var newX: Number = e.stageX;
var newY: Number = e.stageY;
if (currentTarget) {
if (newX < areaBounds.x) newX = areaBounds.x;
if (newY < areaBounds.y) newY = areaBounds.y;
if (newX + currentTarget.width > areaBounds.x + areaBounds.width) newX = areaBounds.x + areaBounds.width - currentTarget.width;
if (newY + currentTarget.height > areaBounds.y + areaBounds.heght) newY = areaBounds.y + areaBounds.height - currentTarget.height;
currentTarget.x = newX;
currentTarget.y = newY;
}
}
... somewhere ...
// for each clip you're going to interact with:
for each (var c: DisplayObject in myDraggableClips) {
c.addEventListener(MouseEvent.MOUSE_DOWN, startDrag);
c.addEventListener(MouseEvent.MOUSE_UP, stopDrag);
}
Another option (if you don't want to limit the area with some bounds) is to use MouseEvent.RELEASE_OUTSIDE event and return a clip to it's initial position once it gets released:
stage.addEventListener(MouseEvent.RELEASE_OUTSIDE, onMouseReleaseOutside);
private function onMouseReleaseOutside(e:MouseEvent):void {
// return clip to it's position.
}
More info you can find in documentation: https://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/events/MouseEvent.html#RELEASE_OUTSIDE

How to prevent a drag action to affect the childrens of a MovieClip

My problem is: I have a MovieClip (obj) that users can drag to both sides to navigate, the code I use for this:
import flash.events.MouseEvent;
import flash.geom.Point;
import flash.events.Event;
import flash.geom.Rectangle;
var destination: Point = new Point();
var dragging: Boolean = false;
var speed: Number = 10;
var offset: Point = new Point();
var bounds: Rectangle = new Rectangle(0, 0, stage.stageWidth, stage.stageHeight);
obj.addEventListener(MouseEvent.MOUSE_DOWN, startdrag);
stage.addEventListener(MouseEvent.MOUSE_UP, stopdrag);
obj.addEventListener(Event.ENTER_FRAME, followmouse);
function startdrag(e: MouseEvent): void {
offset.x = obj.mouseX * obj.scaleX;
dragging = true;
}
function stopdrag(e: MouseEvent): void {
dragging = false;
}
function followmouse(e: Event): void {
if (obj) {
if (dragging) {
destination.x = mouseX;
}
obj.x -= (obj.x - (destination.x - offset.x)) / speed;
if (obj.x > bounds.left) {
obj.x = bounds.left;
}
if (obj.x < -obj.width + bounds.right) {
obj.x = -obj.width + bounds.right;
}
}
}
So far so good, the problem comes up when I put some clickable elements inside that MovieClip (obj), here are the code for the clickable elements:
objA.addEventListener(MouseEvent.CLICK, objATrigger);
objB.addEventListener(MouseEvent.CLICK, objBTrigger);
objC.addEventListener(MouseEvent.CLICK, objCTrigger);
function objATrigger(event: MouseEvent): void {
MovieClip(this.parent).gotoAndPlay(1, "Main");
}
function objBTrigger(event: MouseEvent): void {
MovieClip(this.parent).gotoAndPlay(1, "Main");
}
function objCTrigger(event: MouseEvent): void {
MovieClip(this.parent).gotoAndPlay(1, "Main");
}
The problem is: When I drag the MovieClip (obj) there is a conflict with the event, when release the mouse after the drag, the event Click of MovieClips inside the MovieClip (obj) is fired, how can I fix this? They should only be triggered when there is no drag action.
This is how I handle dragging a parent that has clickable children. The benefit of this method, is that you don't need to do anything to the children (no extra conditions in their click handlers etc), the click event simply doesn't reach them.
You can also hopefully gleam some efficiency tips from the code/comments below:
var wasDragged:Boolean = false;
var dragThreshold:Point = new Point(10,10);
// ^ how many pixels does it need to move before it's considered a drag
//this is good especially on touchscreens as it's easy to accidentally drag the item a couple pixels when clicking.
var dragStartPos:Point = new Point(); //to store drag origin point to calculate whether a drag occured
var dragOffset:Point = new Point(); //to track the gap between the mouse down point and object's top left corner
obj.addEventListener(MouseEvent.MOUSE_DOWN, startdrag);
obj.addEventListener(MouseEvent.CLICK, dragClick, true); //listen on the capture phase of the event.
//the only reason we listen for click on the draggable object, is to cancel the click event so it's children don't get it
function dragClick(e:Event):void {
//if we deemed it a drag, stop the click event from reaching any children of obj
if(wasDragged) e.stopImmediatePropagation();
}
function startdrag(e: MouseEvent): void {
//reset all dragging vars
wasDragged = false;
dragStartPos.x = obj.x;
dragStartPos.y = obj.y;
//set the offset so the object doesn't jump when first clicked
dragOffset.x = stage.mouseX - obj.x;
dragOffset.y = stage.mouseY - obj.y;
//only add the mouse up listener AFTER the mouse down
stage.addEventListener(MouseEvent.MOUSE_UP, stopdrag);
//mouse_move is more efficient that enter_frame, and only listen for it when dragging
stage.addEventListener(MouseEvent.MOUSE_MOVE, followmouse);
}
function stopdrag(e:MouseEvent = null): void {
//remove the dragging specific listeners
stage.removeEventListener(MouseEvent.MOUSE_UP, stopdrag);
stage.removeEventListener(MouseEvent.MOUSE_MOVE, followmouse);
}
function followmouse(e:MouseEvent): void {
if (obj) {
//do what you need to move the object
obj.x = stage.mouseX - dragOffset.x;
obj.y = stage.mouseY - dragOffset.y;
//check if the obj moved far enough from the original position to be considered a drag
if(!wasDragged
&& (Math.abs(obj.x - dragStartPos.x) > dragThreshold.x
|| Math.abs(obj.y - dragStartPos.y) > dragThreshold.y)
){
wasDragged = true;
}
}
}
I don`t know if this is the best approach, but it was possible to check using the code below:
stage.addEventListener(MouseEvent.MOUSE_DOWN, setmousepos);
brose_trigger.addEventListener(MouseEvent.MOUSE_UP, broseTrigger);
denso_trigger.addEventListener(MouseEvent.MOUSE_UP, densoTrigger);
honda_trigger.addEventListener(MouseEvent.MOUSE_UP, hondaTrigger);
var mousePos: Point = new Point();
function setmousepos(e:MouseEvent): void {
mousePos.x = mouseX;
}
function broseTrigger(e:MouseEvent): void {
if(mousePos.x == mouseX){
MovieClip(this.parent).gotoAndPlay(1, "Main");
}
}
function densoTrigger(event:MouseEvent): void {
if(mousePos.x == mouseX){
MovieClip(this.parent).gotoAndPlay(1, "Main");
}
}
function hondaTrigger(event:MouseEvent): void {
if(mousePos.x == mouseX){
MovieClip(this.parent).gotoAndPlay(1, "Main");
}
}
When MOUSE_DOWN event is triggered, I store the mouse.x position in a variable, after that in the MOUSE_UP event, I compare the stored position with the actual position, if equals, TÃDÃ!
Do add condition in objATrigger function to check if dragging is false
function objATrigger(event: MouseEvent): void {
if(!MovieClip(root).dragging){
MovieClip(this.parent).gotoAndPlay(1, "Main");
}
}

Does fullscreen event trigger resize in as3?

I'm trying to set up a fullscreen toggle and implement elements of fluidity however am uncertain if I have to include a dispatch event in the toggle for screen resize or will this be something that will be detected by the previous eventListener when the fullscreen toggle is activated?
Furthermore having looked online for several days on the subject, I'm still uncertain where it's optimal to place the resize control of my stage elements.
public function Main():void {
addEventListener(Event.ADDED_TO_STAGE, init);
stage.addEventListener(Event.RESIZE, resizeListener);
stage.addEventListener(FullScreenEvent.FULL_SCREEN_INTERACTIVE_ACCEPTED, fullScreenRedraw);
}
private function resizeListener (e:Event):void {
// - Do I put my resize control options here to cater for general resize or see below?
myMovie.width = stage.stageWidth; // etc
}
private function fullScreen(e:MouseEvent):void {
try {
switch (stage.displayState) {
case StageDisplayState.FULL_SCREEN_INTERACTIVE:
/* If already in full screen mode, switch to normal mode. */
stage.displayState = StageDisplayState.NORMAL;
break;
default:
stage.fullScreenSourceRect = null;
// If not in full screen mode, switch to full screen mode.
stage.dispatchEvent(new Event(Event.RESIZE));
stage.displayState = StageDisplayState.NORMAL;
stage.displayState = StageDisplayState.FULL_SCREEN_INTERACTIVE;
break;
}
} catch (err:SecurityError) {
// ignore
}
}
private function fullScreenRedraw(event:FullScreenEvent):void {
if ( event.fullScreen ) {
// FULLSCREEN TRUE
// - Or do I put my resize control options here to cater for the fullscreen as well?
myMovie.width = stage.stageWidth; // etc
var fScrField:TextField = new TextField();
fScrField.y = 480;
fScrField.text = "Redraw : True";
addChild(fScrField);
} else {
// NON FULLSCREEN
fScrField.text = "Redraw : False";
addChild(fScrField);
}
}
** Amended handlers as mentioned on adobe. But conflicting info on what goes where and why?!
private function activateHandler(event:Event):void {
trace("activateHandler: " + event);
}
private function fullScreenRedraw(event:FullScreenEvent):void {
if ( event.fullScreen ) {
// Add additional panels if set/sizes
var fScrField:TextField = new TextField();
fScrField.y = 480;
fScrField.text = "Redraw : True";
addChild(fScrField);
} else {
// Remove additional panels etc
fScrField.text = "";
fScrField.text = "Redraw : False";
addChild(fScrField);
}
}
Now I've been able to get it working in all manner of ways using the code above with variations but having spent days online googling there seems to be no efficiant or clear explanation which is best.
Any help on where this could be better optimised and made more efficient as I've kind of just crowbar'd it together from what I can work out.
Thanks in advance.
I think you're over-complicating it. Try that way:
// in your constructor:
fullScreenBtn.addEventListener(MouseEvent.CLICK, toggleFullScreen);
stage.addEventListener(FullScreenEvent.FULL_SCREEN, refreshStage);
stage.addEventListener(Event.RESIZE, refreshStage);
// then handle stage resize:
private function refreshStage(event:Event = null):void
{
if ( stage.displayState == StageDisplayState.NORMAL ) {
// handle stage in normal mode
} else {
// handle stage in full screen mode
}
}
// handle stage state toggle:
private function toggleFullScreen(event:Event = null):void
{
if ( stage.displayState == StageDisplayState.NORMAL )
stage.displayState = StageDisplayState.FULL_SCREEN;
else
stage.displayState = StageDisplayState.NORMAL;
}

swap depthes when movie clips loaded from library

I have two movie clips in the library with linkage.
on the stage I have two buttons - each load a movie clip to a specific mc target on the stage.
I also have a third button that removes the mc target, to clear the stage.
I want to know how can I change the code in AS3 so the loaded movie clips will not show at the same time, but swap each other, like I used to use depth in AS2.
This is the code:
var myIgool = new igool ();
var myRibooa = new ribooa ();
loadigool.addEventListener(MouseEvent.CLICK, fl_MouseClickHandler_3);
function fl_MouseClickHandler_3(event:MouseEvent):void
{
mc_all.addChild (myIgool);
}
loadribooa.addEventListener(MouseEvent.CLICK, fl_MouseClickHandler_4);
function fl_MouseClickHandler_4(event:MouseEvent):void
{
mc_all.addChild (myRibooa);
}
unloadall.addEventListener(MouseEvent.CLICK, fl_MouseClickHandler_6);
function fl_MouseClickHandler_6(event:MouseEvent):void
{
removeChild(mc_all);
;
}
I would recommend something like this:
var myIgool = new igool ();
var myRibooa = new ribooa ();
mc_all.addChild(myIgool);
mc_all.addChild(myRibooa);
myIgool.visible = false;
myRibooa.visible = false;
loadigool.addEventListener(MouseEvent.CLICK, fl_MouseClickHandler_3);
function fl_MouseClickHandler_3(event:MouseEvent):void
{
myIgool.visible = true;
myRibooa.visible = false;
}
loadribooa.addEventListener(MouseEvent.CLICK, fl_MouseClickHandler_4);
function fl_MouseClickHandler_4(event:MouseEvent):void
{
myIgool.visible = false;
myRibooa.visible = true;
}
unloadall.addEventListener(MouseEvent.CLICK, fl_MouseClickHandler_6);
function fl_MouseClickHandler_6(event:MouseEvent):void
{
myIgool.visible = false;
myRibooa.visible = false;
}
But if you really want to swap, you could also do the following, however I recommend setting the visible flag as it's more efficient rather than covering something up that wouldn't need to draw.
loadigool.addEventListener(MouseEvent.CLICK, fl_MouseClickHandler_3);
function fl_MouseClickHandler_3(event:MouseEvent):void
{
if (myIgool.parent != mc_all)
{
mc_all.addChild(myIgool);
}
else
{
mc_all.setChildIndex(myIgool, mc_all.numChildren - 1);
}
}
loadribooa.addEventListener(MouseEvent.CLICK, fl_MouseClickHandler_4);
function fl_MouseClickHandler_4(event:MouseEvent):void
{
if (myRibooa.parent != mc_all)
{
mc_all.addChild(myRibooa);
}
else
{
mc_all.setChildIndex(myRibooa, mc_all.numChildren - 1);
}
}

ActionScript 3 Mouse Move event not dispatching

Script problem is that every movieclip dispatch down and up mouse event but mouse move event is not dispatching by some movieclips, which is an unexpected behaviour while I have traced the down event and it trace successfully on every object
also recommend your feedback on my code, thanks.
private function loadPurchasedClip(){
var decorationItem:String;
var lastItemIndex:uint = this.getChildIndex(tree1);
var item:Sprite;
for(var a in purchasedItems){
for(var b in purchasedItems[a]){
if(purchasedItems[a][b].item=='shed'){
item = new shed();
} else {
var ClassDefinition:Class = loadedDecorationItem.purchaseItem(purchasedItems[a][b].item) as Class;
item = new ClassDefinition();
}
item.x = purchasedItems[a][b].posX;
item.y = purchasedItems[a][b].posY;
item.addEventListener(MouseEvent.MOUSE_DOWN,function(e:MouseEvent){
Mouse.cursor = "hand";
e.target.startDrag(false);
dusbin.visible = true;
item.addEventListener(MouseEvent.MOUSE_MOVE,trashMe);
});
item.addEventListener(MouseEvent.MOUSE_UP,function(e:MouseEvent){
Mouse.cursor = "auto";
e.target.stopDrag();
externalPhpCall(e);
dusbin.visible = false;
if(trashClip){
removeChild(trashClip);
trashClip = null;
}
});
item.mouseChildren = false;
// if item is fence or flowers then move them behind the tree
if(
String(purchasedItems[a][b].item).indexOf('fence')!=-1
||
String(purchasedItems[a][b].item).indexOf('flower')!=-1
){
addChildAt(item,lastItemIndex);
lastItemIndex++;
} else {
addChildAt(item,this.numChildren-2);
}
purchasedNameAr[getChildIndex(item)] = purchasedItems[a][b].item;
}
}
Can't be sure, but I think it's probably that you're expecting a clip to continue to dispatch MouseEvent.MOUSE_MOVE events even once the mouse has left the clip - this won't happen, it's only whilst the local mouse pointer co-ordinates (ie yourClip.mouseX/mouseY) intersect the graphics of the clip itself that it will fire - even when dragging a clip, it can't be guaranteed that it will dispatch a MOVE event.
Let's suppose your clips are all on the root, which means you have access to 'stage' - you could do this:
replace:
item.addEventListener(MouseEvent.MOUSE_MOVE,mouseMove);
with:
stage.addEventListener(MouseEvent.MOUSE_MOVE,mouseMove);
...but you should remember to remove that event when necessary (use stage again, in case mouse is not released over the clip):
stage.addEventListener(MouseEvent.MOUSE_UP,endMove);
//Don't use anon function as won't have stage reference:
function endMove(e:MouseEvent):void {
//The rest of your code, then:
stage.removeEventListener(MouseEvent.MOUSE_MOVE,mouseMove);
}
private function loadPurchasedClip(){
var decorationItem:String;
var lastItemIndex:uint = this.getChildIndex(tree1);
var item:Sprite;
var Move:Boolean
for(var a in purchasedItems){
for(var b in purchasedItems[a]){
if(purchasedItems[a][b].item=='shed'){
item = new shed();
} else {
var ClassDefinition:Class = loadedDecorationItem.purchaseItem(purchasedItems[a][b].item) as Class;
item = new ClassDefinition();
}
item.x = purchasedItems[a][b].posX;
item.y = purchasedItems[a][b].posY;
item.addEventListener(e:Event.ENTER_FRAME, onEnterFrame);
item.addEventListener(MouseEvent.MOUSE_DOWN,function(e:MouseEvent){
Mouse.cursor = "hand";
e.target.startDrag(false);
Move = true
dusbin.visible = true;
});
item.addEventListener(MouseEvent.MOUSE_UP,function(e:MouseEvent){
Mouse.cursor = "auto";
e.target.stopDrag();
externalPhpCall(e);
dusbin.visible = false;
if(trashClip){
removeChild(trashClip);
trashClip = null;
}
});
item.mouseChildren = false;
// if item is fence or flowers then move them behind the tree
if(
String(purchasedItems[a][b].item).indexOf('fence')!=-1
||
String(purchasedItems[a][b].item).indexOf('flower')!=-1
){
addChildAt(item,lastItemIndex);
lastItemIndex++;
} else {
addChildAt(item,this.numChildren-2);
}
purchasedNameAr[getChildIndex(item)] = purchasedItems[a][b].item;
}
function onEnterFrame(e:Event):void{
if(Move){
// what ever here
{
}