How to get single pixel of Sprite with cocos2dJS and CocosBuilder?

Hello everybody!
I need to get info about single pixel of any cc.Sprite instance. I want to check its transparency after touch.

I want to do what is written here, but with cocos2d-JS and CocosBuilder: http://stackoverflow.com/questions/10889170/cocos2d-2-0-ignoring-touches-to-transparent-areas-of-layers-sprites

I was trying with sprite.getTexture() but I didn’t know how to go “deeper”…

Any advices?

1 Like

Maybe gl.ReadPixel could do that job.

Thank you very much for reply!
I managed to write following function which checks if point in sprite is transparent (regarding to the linked article on stackoverflow). I assume that sprite’s anchor point is cc.p(0.5, 0.5).

function isSpriteTransparentInPoint(sprite, point) {
    var oldPosition = sprite.getPosition();
    var pixel = new Uint8Array(4);

    sprite.setPosition(sprite.getParent().convertToWorldSpace(sprite.getPosition()));

    var renderTexture = cc.RenderTexture.create(winSize.width, winSize.height, sprite.getTexture().getPixelFormat());
    renderTexture.begin();
    sprite.visit();

    gl.readPixels(point.x, point.y, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, pixel);
    cc.log("RGBA: "+pixel[0]+", "+pixel[1]+", "+pixel[2]+", "+pixel[3]);

    renderTexture.end();
    //renderTexture.release(); <<< code fails here

    sprite.setPosition(oldPosition);

    if (pixel[3] < 255) {
        return true;
    }
    else {
        return false;
    }
}

Example of use:

Scene.prototype.onTouchesBegan = function(touches, event) {
    var touch = touches[0];

    if (cc.rectContainsPoint(sprite.getBoundingBox(), touch.getLocation()) && isSpriteTransparentInPoint(sprite, touch.getLocation()) == false) {
        //sprite touched
    }
}

If someone have suggestions, I will appreciate.

PS. Where I can find complete documentation for gl module in cocos2dx-JS ?

1 Like

I do everything as described in this article but have no success. I have solid background under my sprite. And each time I try to define sprite touch event with gl.readPixels(point.x, point.y, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, pixel); gives me color of background and spoils the whole procedure.

Between rt.begin() and rt. end() I call only mySprite.visit(), but it seems that gl.readPixels read pixels not from FBO but from the screen.

I am really stuck and need help.

Hi, I just wanted to give this a bump with code tested and working for latest’s cocos version.


Note1: this post is for cocos2d-js v3. Sadly I don’t even understand why some of it works, so I take no credit for it.

Note2: I have no clue wether this method will work outside of a browser (I mean, compiling into native app, because it relies in the gl object, which is the instance of the web rendering engine.

Note3: as said in Note2, this requires webGL, so if we are running the game in canvas renderer mode, instead of webgl renderer mode, it won’t work.

Note4: this proccess is extremely slow, so if you have several sprites overlapping and the event hast to bubble all across them finding transparent pixels you will have a nasty delay until the action is regained.


So, first we need a function that’ll take a sprite and a point and tell us if the point within that sprite is transparent:

function isSpriteTransparentInPoint(sprite, point) {
  var oldPosition = sprite.getPosition();
  var pixel = new Uint8Array(4);

  sprite.setPosition(sprite.getParent().convertToWorldSpace(sprite.getPosition()));

  var renderTexture = cc.RenderTexture.create(cc.view.getDesignResolutionSize().width, cc.view.getDesignResolutionSize().height, sprite.getTexture().getPixelFormat());
  renderTexture.begin();
  sprite.visit();

  gl.readPixels(point.x, point.y, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, pixel);
  cc.log("RGBA: " + pixel[0] + ", " + pixel[1] + ", " + pixel[2] + ", " + pixel[3]);

  renderTexture.end();

  sprite.setPosition(oldPosition);

  //return pixel[3] < 255; //This returns true if the pixel is not completely solid
  return pixel[3] === 0; //This returns true only if the pixel is completely transparent
}

And now, assuming we are modding the “Hello World” example that’s generated when you create a new cocos2d-js project, we’ll just have to add this event manager after the logo’s sprite’s been added to the layer:

    cc.eventManager.addListener(cc.EventListener.create({
      event: cc.EventListener.TOUCH_ALL_AT_ONCE,
      onTouchesEnded: function (touches, event) {
        var touch = touches[0];
        if(cc.rectContainsPoint(event.getCurrentTarget().getBoundingBox(), touch.getLocation())){
          if (isSpriteTransparentInPoint(event.getCurrentTarget(), touch.getLocation())) {
            console.log("transparent!");
          } else {
            console.log("solid!");
          }
        }
      }
    }), this.sprite);

I think there’s still room for improvement, and I bet there’s got to be a way to do this with canvas render and cross-platform compilation, so I’ll post whatever I find here.

Cheers!

PS: note that, despite the thread’s original title, there’s no use of CocosBuilder here.

this kind of operations is something you really don’t want to do if performance is priority.
readPixels is a synchronous operation which will halt the gpu until the operation ends. Taking into account that GPUs are asynchronous, and that the frame we see is probably between 1 and 3 frames before in time, each of this readpixels will be a performance killer.

my advice would be to extract the mask from image (not texture), keep it safe for later usage, and look it up with the result of transforming screen space coord to local node coord. If you just want to know whether it is masked or not, you can pack the mask with 1 bit per byte of mask saving some valuable space.

Nonetheless, you solution works, but just wanted to give another point of view.

1 Like

That’s pretty much what I had in mind. The problem is, no one seems to know how to actually implement that.

Hey @ZippoLag,

have added a demo to V4: input-mask.
you can get the code and try it from a local webserver . It is located here: https://github.com/hyperandroid/Cocos2d-html5/blob/Feature/Input_Refactor/src/test/engine/input-mask.html
Still have to PR and merge the code though.
will push all the demos live on github pages soon, but until then, you should starst a local webserver on the project root :frowning:
It will give you perfect input-mask when identifying sprites on-the-screen.
Sprites are rotated/scaled and randomly positioned, so the result is pretty neat.
It is free of gl.readpixels and i believe is quite fast.

Only web by now.

Best.

1 Like

That’s great @Ibon!

Do you think that, if I poke around with your v4 code, I may be able to implement this in v3? It’d be nice to have for a few projects I’ve got going :smiley:

I think so.
have a look here: http://hyperandroid.github.io/Cocos2d-html5/src/test/engine/input-mask.html and let me know how you find it.

  • ibob
1 Like

It’s in my backlog for after final-exams season ends :smiley:

Alright, so, after a bit of guessing and (horrible, horrible) copypasting, I’ve got a sample 2/3rds working here (event is registered for onTouchesMoved so click and drag for it to work): http://heartbitgames.com/proto/NewTransparencyTest/

Now, I’m missing piece of code on the eventInTransparentMask regarding a spriteFrame object, so I’m not sure this will work for all intended scenarios, and the code’s a bit jumbled up since I just copy pasted the generated code for the sample @Ibon posted above, but hey, it works (and fast!). Hopefully I’ll get polish up the code by the the weekend’s end and create a pull request or at least post the sample here :smiley:

awesome mate.
good work.

Thanks to you! :smiley:

Anyways, I’ve polished it up a bit, I’m not comftible enough with it as to try and make a pull request for v3.x, but I’m leaving that sample posted for anyone to find and I’ll use it in my own projects to see if I bump into any issues.

Hey @Ibon
can you please share the source code of this?

http://hyperandroid.github.io/Cocos2d-html5/src/test/engine/input-mask.html

Hey @ZippoLag
can you please share the source code of this test?

http://heartbitgames.com/proto/NewTransparencyTest/

Sure, here you go: http://pastebin.com/y77ZftdH

(you could’ve also just press F12 and looked at app.js :stuck_out_tongue:)

Thanks for sharing your code. Its working on web platform but not on Mobile platform. I am trying to run it on iOS platform. Can you plz help me out in running this on mobile platform

No luck there, sorry.

As far as I know it can’t be done.