[Solved] onTouchsBegan etc for cc.Sprite or cc.Node

Hi,

I’d like to add a “Start” button (cc.Sprite or cc.Node) on top of my game (layer) which when clicked would start the game.

I know that technically cc.Sprite can’t receive Touch events, however I’ve seen some code that registers the Sprite or Node etc directly with the TouchDispatcher and hence the class that registers for touches can then receive them

Unfortunately I can’t track down the code where I saw this written.

I’ve tried adding cc.Director.getInstance().getTouchDispatcher().addTargetedDelegate(this, 0,true); to the constructor of my class, but it doesn’t receive touch events, so I must be missing something.
I also tried cc.Director.getInstance().getTouchDispatcher().addTargetedDelegate(this, 0); but that didnt work either :frowning:

If I change my class you extend cc.Layer I can use the normal touch handling without registering etc, but just using setTouchEnabled(true), however I’m not too sure how to add another layer in front of my existing layer (from inside the layer).
I tried adding new Start button Layer as a child of my game layer, and my Start button layer, then received touch events, but the game layer also received events, and I wanted the game layer to block the events getting though to the game.

Does anyone know what I need to do to make this happen ??

Thanks

Roger

As long as the sprite, node etc has a tag, you can do the following:

`getItemForTouch:function (touch) {
var touchLocation = touch.getLocation();

    if (this._children && this._children.length > 0) {
        for (var i = 1; i < this._children.length; i++) {
                var local = this._children[i].convertToNodeSpace(touchLocation);
                var r = cc.RectMake(local.x, local.y, this._children[i].getBoundingBox().size.width, this._children[i].getBoundingBox().size.height);
                r.origin = cc.p(0,0);
                if (cc.Rect.CCRectContainsPoint(r, local)) {
                    return this._children[i];
                }
           // }
        }
    }

    return null;
},

`

Then in your onTouchesEnded:

`onTouchesEnded:function (touches, event) {
if (touches) {

        touched = this.getItemForTouch(touches[0]);

        if (touched == null) return;
        switch(touched.tag)
        {
           case START_BUTTON:
             // do stuff here
           break;
        }
    }`

s. not sure why it’s not displaying my code chunks properly.

Thanks Farmer Joe.

I’ll try that idea.

But I’m sure there was a nice hack to use the TouchDispatcher somehow, I just can’t remember where I saw it - I know it was code pasted via this forum (indirectly I think), but I cant recall what it was. And like most people, I have several computers, so I’m not sure which one I was using when I looked at the other code :frowning:

In case its of use to anyone, I found the code in the end (see the bottom of this posting)

The code is derived from an example game posted by Alberto SM (on this forum)

Note. The code doesn’t check if the ClickableSprite is has anything in front of it, so it would only be usable if you have non-overlapping sprites.
Also the code is pretty basic, to use it you’ll need to load a texture into the sprite after you create it e.g.

var p = new ClickableSprite();
p.initWithSpriteFrameName("mySprite"));
this.addChild(p);

Cheers

Roger

var ClickableSprite = cc.Sprite.extend({
    _selected:null,
    ctor:function()
    {
        this._super();
        this._selected = false;
        cc.Director.getInstance().getTouchDispatcher().addTargetedDelegate(this, 1, true);
    },
    setClickable:function()
    {
        cc.Director.getInstance().getTouchDispatcher().addTargetedDelegate(this, 0, true);
    },
    setUNClickable:function()
    {
        cc.Director.getInstance().getTouchDispatcher().removeDelegate(this);
    },
    getRect:function()
    {
        return cc.rect(-this._rect.size.width / 2, -this._rect.size.height / 2, this._rect.size.width, this._rect.size.height);
    },
    containsTouchLocation:function (touch) {    // function for locating a touch pos ONLY
        var getPoint = touch.getLocation();
        var myRect = this.getRect();

        myRect.origin.x += this.getPosition().x;
        myRect.origin.y += this.getPosition().y;

        return cc.Rect.CCRectContainsPoint(myRect, getPoint);
    },
    onTouchBegan:function (touch, event) {
        if (!this.containsTouchLocation(touch))
        {
            return false;
        }
        else
        {
            this._selected=true;
            cc.log("onTouchBegan");
            return true;
        }
    },
    onTouchMoved:function (touch, event)
    {
        if (this.containsTouchLocation(touch) && this._selected)
        {
            cc.log("onTouchMoved")
        }
    },
    onTouchEnded:function (touch, event)
    {
        this._selected=false;
        cc.log("onTouchEnded")
    },
    onTouchesCancelled:function (touches, event)
    {
        this._selected=false;
        console.log("onTouchesCancelled");
    }
});

Roger, this is great, but I’m stuck with an error because getTouchDispatcher isn’t bound yet. http://www.cocos2d-x.org/boards/20/topics/24687?r=25182

Ideas on a workaround?

(obviously @Famer Joe’s answer could work) - however I’m trying it and getting stuck on this.children - doesn’t seem to exist. However I should add that I’m building the scene & layer from cocosbuilder 3, so that might be part of the issue. I do cc.associateWithNative when creating my layer- do I need to do more than that if I want to reachchildren?

With my solution, if getTouchDispatcher is the problem when the class is instantiated, could you comment out that line in the ctor and just call setClickable on the instance later when everything has been initialsed?

Resolved in a workaround. Here’s my current setup:

~~cocosbuilder 3.0 alpha
~~all cocos2d-javascript (using jsbindings from 2.1 - v.6 I think)
-prototyping exclusively in cocosbuilder and cocosplayer, but periodically moving everything into an xcode project to confirm that it works. and hey, can also move over to android and web. yay so far. i can really get behind this direction as future of the framework.

Now, because touchdispatcher isn’t bound yet (but it is targeted for next release) making touchable sprites from cocosbuilder actually was a bit of a headache. A lot of the samples (including TouchesTest) didn’t bear fruit as they are based on cocos2d-html5, and if I understand correctly cocos2d-javascript (and the bindings which will ultimately produce native code) represents a subset of the functionality in html5 at the moment. Ergo the touchdispatcher binding issue. So, to make a touchable sprite in this edge usecase here’s how I do it, please let me know if you’ve got a better idea while waiting on bindings to catch up.

  1. Layout cocosbuilder scene as you would. To add touchable sprite, create a new ccb file with cclayer as its rootnode. Do not make fullscreen.

  2. Go into that layer, and set javascript controller to something that looks like Actor.js below.

  3. Add a sprite to the layer. Set doc var to “mysprite.”

  4. Go ahead and add a timeline, for fun, in ccb and call it “touchedAnimation.” Add some keyframes to the sprite and do things with it.

  5. Go back to your main scene, add the ccb you just created to the scene, run and it should work.

Some notes: I had to create my own function for cc.RectMake in the file below, as it was not working for some reason. Perhaps another bindings thing, i dunno. Also this makes for a nice set of functionality that I can then extend into other js controllers and then override the vars with other soundeffects, animations, behaviors, etc.

@
//Actor.js
require(“js/resource.js”);
var Actor = cc.Layer.extend({
ctor:function() {
this.*super;
cc.associateWithNative;
var soundeffect = ‘Rainbow.caf’;
},
onEnter:function {
this.*super();
},
});
Actor.prototype.onDidLoadFromCCB = function()
{
// Forward relevant touch events to controller (this)
this.rootNode.onTouchesBegan = function( touches, event) {
this.controller.onTouchesBegan(touches, event);
return true;
};
// this.rootNode.onTouchesMoved = function( touches, event) {
// this.controller.onTouchesMoved(touches, event);
// return true;
// };
this.rootNode.onTouchesEnded = function( touches, event) {
this.controller.onTouchesEnded(touches, event);
return true;
};
}
Actor.prototype.onTouchesBegan = function(touches, event)
{
this.startTouch = touches[0].getLocation();
};
//Actor.prototype.onTouchesMoved = function(touches, event)
//{
//// if (gSettingControlType != CD_CONTROLTYPE_TOUCH) return;
//
// this.touchPoint = touches[0].getLocation();
// // cc.log(‘moved’);
// // cc.log(‘touchPoint x:’touchPoint.x ‘y:’touchPoint.y);
//
// if > swipedistance )
//
//
//};
Actor.prototype.onTouchesEnded = function
{
this.endTouch = touches[0].getLocation;
this.myspriterect = ttRectMake.x
this.rootNode.getPosition().x, this.mysprite.getBoundingBox().y + this.rootNode.getPosition().y, this.mysprite.getBoundingBox().width,this.mysprite.getBoundingBox().height);
if (cc.rectContainsPoint(this.myspriterect, this.endTouch)){
audioEngine.playEffect(this.soundeffect);
this.rootNode.animationManager.runAnimationsForSequenceNamed(“touchedAnimation”);
}
};
ttRectMake = function (x, y, width, height) {
return cc.rect(x, y, width, height);
};
@

convertToNodeSpace(touchLocation) does not work. I had to find a work around for that.

the function works on cocos2d-x

Famer Joe wrote:

As long as the sprite, node etc has a tag, you can do the following:

`getItemForTouch:function (touch) {
var touchLocation = touch.getLocation();

    if (this._children && this._children.length > 0) {
        for (var i = 1; i < this._children.length; i++) {
                var local = this._children[i].convertToNodeSpace(touchLocation);
                var r = cc.RectMake(local.x, local.y, this._children[i].getBoundingBox().size.width, this._children[i].getBoundingBox().size.height);
                r.origin = cc.p(0,0);
                if (cc.Rect.CCRectContainsPoint(r, local)) {
                    return this._children[i];
                }
           // }
        }
    }

    return null;
},

`

Then in your onTouchesEnded:

`onTouchesEnded:function (touches, event) {
if (touches) {

        touched = this.getItemForTouch(touches[0]);

        if (touched == null) return;
        switch(touched.tag)
        {
           case START_BUTTON:
             // do stuff here
           break;
        }
    }`

s. not sure why it’s not displaying my code chunks properly.