分享Creator点击事件透明穿透问题

新手引导主要就是修改touchMask拦截事件的node的_hitTest方法.mask反向遮罩
cc.Class({
extends: cc.Component,

properties: {
    // foo: {
    //    default: null,      // The default value will be used only when the component attaching
    //                           to a node for the first time
    //    url: cc.Texture2D,  // optional, default is typeof default
    //    serializable: true, // optional, default is true
    //    visible: true,      // optional, default is true
    //    displayName: 'Foo', // optional
    //    readonly: false,    // optional, default is false
    // },
    // ...
},

onLoad: function () {
},

onEnable: function () {
            this.touchMask.on(cc.Node.EventType.TOUCH_START,this.xxxx,this);
	this.touchMask._hitTest = this.customHitTest;
},

onDisable: function () {
},

onDestroy: function () {
    
},

// called every frame, uncomment this function to activate update callback
// update: function (dt) {

// },

customHitTest : function(point, listener) {
    // console.log("---->customHitTest");
    var w = this.width,
        h = this.height;
    var rect = cc.rect(0, 0, w, h);
    
    var Camera = cc.Camera;
    if (Camera && Camera.main && Camera.main.containsNode(this)) {
        point = Camera.main.getCameraToWorldPoint(point);
    }
    
    var trans = this.getNodeToWorldTransform();
    cc._rectApplyAffineTransformIn(rect, trans);
    var left = point.x - rect.x,
        right = rect.x + rect.width - point.x,
        bottom = point.y - rect.y,
        top = rect.y + rect.height - point.y;
    if (left >= 0 && right >= 0 && top >= 0 && bottom >= 0) {
        if (listener && listener.mask) {
            var mask = listener.mask;
            var parent = this;
            for (var i = 0; parent && i < mask.index; ++i, parent = parent.parent) {
            }
            // find mask parent, should hit test it
            if (parent === mask.node) {
                var comp = parent.getComponent(cc.Mask);
                return (comp && comp.enabledInHierarchy) ? !comp._hitTest(point) : true;//反向mask判断hitTest修改
            }
            // mask parent no longer exists
            else {
                listener.mask = null;
                return true;
            }
        }
        else {
            return true;
        }
    }else{
        return false;
    }
}

});

1赞

太复杂了, 能简单点吗?

技术贴

经常看到有人说_hitTest,我到现在还搞不懂_hitTest是什么东西,在我看来这个_hitTest貌似就是写在案例教程里面某个脚本里的一个方法名称而已,也就是用一个polygonCollider组件来判断点击位置是否在不规则区域内而已,怎么经常被说的好像是一种好像shader那样有独立概念的专有名词一样,有点高大上的样子。有高手来解释下_hitTest这个概念究竟是什么吗?

应该往这段代码里加像素点判断吗?有像素点判断的代码吗兄弟 我不会太加

CCC如何获取像素点呢?

mark

不错哈

mark

let _textureIdMapRenderTexture = {}

cc.Class({
    extends: cc.Component,

    properties: {
    },

    // LIFE-CYCLE CALLBACKS:

    onLoad() {
        this.node._hitTest = this.hitTest.bind(this)
    },

    start() {

    },

    hitTest(location) {
               let spriteFrame = this.node.getComponent(cc.Sprite).spriteFrame
        if (spriteFrame == null) {
            return false
        }
        let posInNode = this.node.convertToNodeSpaceAR(location)
        let rect = spriteFrame.getRect()
        let offset = spriteFrame.getOffset()

        var type = this.node.getComponent("DecorateItemPrefab").m_type
        // cc.log(type + "--", "xxxxxxxxxxxxxx", type)
        // cc.log(type + "--", "posInNode", posInNode)
        // cc.log(type + "--", "rect", rect)
        // cc.log(type + "--", "offset", offset)
        if ((posInNode.x < offset.x - rect.width / 2) || (posInNode.y < offset.y - rect.height / 2)
            || (posInNode.x > (offset.x + rect.width / 2)) || (posInNode.y > (offset.y + rect.height / 2))) {
            return false
        }
        else {
            let posInRect = cc.v2(parseInt(posInNode.x - offset.x + rect.width / 2), parseInt(posInNode.y - offset.y + rect.height / 2))
            // cc.log(type + "--", "posInRect",  posInRect)
            // cc.log(type + "--", "isRotated", spriteFrame.isRotated())

            let tex = spriteFrame.getTexture()
            var rt = _textureIdMapRenderTexture[tex.getId()]
            if (!rt) {
                rt = new cc.RenderTexture()
                rt.initWithSize(tex.width, tex.height)
                rt.drawTextureAt(tex, 0, 0)
                _textureIdMapRenderTexture[tex.getId()] = rt
            }

            // data就是这个texture的rgba值数组
            let data
            if (spriteFrame.isRotated())
            {
                data = rt.readPixels(null, rect.x + posInRect.y, rect.y + posInRect.x, 1, 1)
                // cc.log(type + "--", "data", data, rect.x + posInRect.y, rect.y + posInRect.x)
            }
            else{
                data = rt.readPixels(null, rect.x + posInRect.x, rect.y + rect.height - posInRect.y, 1, 1)
                // cc.log(type + "--", "data", data, rect.x + posInRect.x, rect.y + rect.height - posInRect.y)
            }

            //if ((data[0] <= 0 && data[1] <= 0 && data[2] <= 0) || data[3] <= 0) {
            if (data[3] <= 0) {
                return false
            }
            else {
                return true
            }
        }
    },

    onDestroy(){
     let spriteFrame = this.node.getComponent(cc.Sprite).spriteFrame
        if (spriteFrame == null) {
            return false
        }

        let tex = spriteFrame.getTexture()
        let id = tex.getId()
        if (_textureIdMapRenderTexture[id]) {
            _textureIdMapRenderTexture[id].destroy()
            delete _textureIdMapRenderTexture[id]
        }
    }
    // update (dt) {},
});

目前仅支持cc.Sprite 如下模式, 其他模式其实也可以按需改代码适配, sprite的mode 和 type 主要是决定,我们如何使用触摸的location

按照panda 大大的写了一个组件,用RenderTexture实现,用于过滤透明区域。
我的经验是
1,游戏内存基本不会增加,游戏性能也基本没有影响,游戏GPU相关的图片对应的内存会翻倍

不知道是否是这样,底层不是很了解。

2赞

mark

@panda
在玩一玩平台,实测renderTexture readPixels接口读出来的全部是0。。。 求解

打印gl.readPixels之后发现是个空函数

@panda 可以看一看玩一玩这个问题么

mark

@panda 大神帮忙看一下可以不
https://forum.cocos.com/t/rendertexture-gl-readpixels/68802

@wangzhe 哲大大
https://forum.cocos.com/t/rendertexture-gl-readpixels/68802

试着使用这种方法去获取数据
https://forum.cocos.com/t/cocos-creator/60736/11

好的, 谢谢,我试一下,不过顺便说一下的是,我在web mobile 和 cocos creator 模拟器上跑,我上面使用的RenderTexure
gl.readPixels都是可以获取到像素来判断透明区域的哦,而且不会卡顿。而且就算你说的新方法可以,我还是想知道为什么我的方法在玩一玩平台不行, 玩一玩平台的gl.readPixels 为什么不工作

谢啦

let _textureIdMapRenderTexture = {}

cc.Class({
    extends: cc.Component,

    properties: {
    },

    // LIFE-CYCLE CALLBACKS:

    onLoad() {
        this.node._hitTest = this.hitTest.bind(this)
    },

    start() {

    },

    hitTest(location) {
        let spriteFrame = this.node.getComponent(cc.Sprite).spriteFrame
        if (spriteFrame == null) {
            return false
        }
        let posInNode = this.node.convertToNodeSpaceAR(location)
        let rect = spriteFrame.getRect()
        let offset = spriteFrame.getOffset()

        var type = "哈哈哈"
        cc.log(type + "--", "xxxxxxxxxxxxxx", type)
        cc.log(type + "--", "posInNode", posInNode)
        cc.log(type + "--", "rect", rect)
        cc.log(type + "--", "offset", offset)
        if ((posInNode.x < offset.x - rect.width / 2) || (posInNode.y < offset.y - rect.height / 2)
            || (posInNode.x > (offset.x + rect.width / 2)) || (posInNode.y > (offset.y + rect.height / 2))) {
            return false
        }
        else {
            let posInRect = cc.v2(parseInt(posInNode.x - offset.x + rect.width / 2), parseInt(posInNode.y - offset.y + rect.height / 2))
            cc.log(type + "--", "posInRect", posInRect)
            cc.log(type + "--", "isRotated", spriteFrame.isRotated())

            let tex = spriteFrame.getTexture()
            var rt = _textureIdMapRenderTexture[tex.getId()]

            /* RenderTexure方式
            if (!rt) {
                rt = new cc.RenderTexture()
                rt.initWithSize(tex.width, tex.height)
                rt.drawTextureAt(tex, 0, 0)
                _textureIdMapRenderTexture[tex.getId()] = rt
            }

            // data就是这个texture的rgba值数组
            let data
            if (spriteFrame.isRotated()) {
                data = rt.readPixels(null, rect.x + posInRect.y, rect.y + posInRect.x, 1, 1)
                //data = rt.getImageData(rect.x + posInRect.y, rect.y + posInRect.x, 1, 1)
                cc.log(type + "--", "data", data, rect.x + posInRect.y, rect.y + posInRect.x)
            }
            else {
                //data = rt.getImageData(rect.x + posInRect.x, rect.y + rect.height - posInRect.y, 1, 1)
                data = rt.readPixels(null, rect.x + posInRect.x, rect.y + rect.height - posInRect.y, 1, 1)
                cc.log(type + "--", "data", data, rect.x + posInRect.x, rect.y + rect.height - posInRect.y)
            }

            */

            //Web 方式
            if (!rt) {
                var cvs = document.createElement("canvas")
                var rt = cvs.getContext('2d')
                cvs.width = tex.width
                cvs.height = tex.height
                rt.drawImage(tex.getHtmlElementObj(), 0, 0, tex.width, tex.height, 0, 0, tex.width, tex.height)
                _textureIdMapRenderTexture[tex.getId()] = rt
            }

            let data
            if (spriteFrame.isRotated()) {
                data = rt.getImageData(rect.x + posInRect.y, rect.y + posInRect.x, 1, 1).data
                cc.log(type + "--", "data", data, rect.x + posInRect.y, rect.y + posInRect.x)
            }
            else {
                data = rt.getImageData(rect.x + posInRect.x, rect.y + rect.height - posInRect.y, 1, 1).data
                cc.log(type + "--", "data", data, rect.x + posInRect.x, rect.y + rect.height - posInRect.y)
            }

            if (data[3] <= 0) {
                return false
            }
            else {
                return true
            }
        }
    },

    onDestroy() {
        let spriteFrame = this.node.getComponent(cc.Sprite).spriteFrame
        if (spriteFrame == null) {
            return false
        }

        let tex = spriteFrame.getTexture()
        let id = tex.getId()
        if (_textureIdMapRenderTexture[id]) {
            _textureIdMapRenderTexture[id].destroy()
            delete _textureIdMapRenderTexture[id]
        }
    }
    // update (dt) {},
});

@huanxinyin ,两种方式我都试啦,qq玩一玩还是不行, 在浏览器里面调试都没问题,qq玩一玩就是不行,
1, Canvas方式在玩一玩平台读出来的都是1, 0, 1, 0
2, qq玩一玩 使用Rendtexture 方式我能确定的是, RenderTexture肯定是画上去的,因为我把他赋值给一个sprite, 这张纹理就能显示, 所以只是readPixels失败,Canvas 方式我就不清楚了

麻烦帮忙看看,刚好一个游戏要移植到qq玩一玩平台,之前都只在web上测试 :cry:

应该是玩玩没有实现 gl.readPixels 这个接口导致的。

@huanxinyin 能提供点思路么,我看他qqPlayCore里面这些接口都在用,不知道什么原因,在外部就没了,是不是CocosCreator适配的问题