需求:实现点击穿透判定 | 实现像素级别点击触摸判定 | 实现透明像素不算点击
参考
cocos creator实现触摸特效,触摸穿透
creator2.x 获取图片的像素数据
点击获取图片像素-示例
RenderUtil (注意!放大后不准确)
解决方案
以下是我借鉴整合并解决了放大问题
const {ccclass, property} = cc._decorator;
@ccclass
export default class touch2 extends cc.Component {
protected onLoad(): void {
this.InitTouch();
}
private _eventManager = cc["internal"]["eventManager"];
private _touchListener: any;
InitTouch() {
const EventListener = cc["EventListener"];
this._touchListener = EventListener.create({
event: EventListener.TOUCH_ONE_BY_ONE,
swallowTouches: false,//是否吞噬touch事件
owner: this.node,
mask: null,
onTouchBegan: this.onTouchStart.bind(this),
onTouchMoved: null,
onTouchEnded: this.onTouchEnded.bind(this),
onTouchCancelled: null,
});
this._eventManager.addListener(this._touchListener, this.node);
}
private static pool: Set<string> = new Set;
private onTouchStart(touch: cc.Touch, event: cc.Event.EventTouch): boolean {
// cc.log("touch start = " + this.node.name);
//此处必须返回true(表示接触到了节点),否则TOUCH_MOVE,TOUCH_END,TOUCH_CANCEL不触发。
if (this.IsTouchSuccess(touch)) {
touch2.pool.add(this.node.name)
}
return true;
}
private onTouchEnded(touch: cc.Touch, event: cc.Event.EventTouch): void {
// cc.log("touch end = " + this.node.name);
if (!touch2.pool.has(this.node.name)) {
return
}
touch2.pool.clear()
this.onTargetClick();
}
protected onDestroy(): void {
// super.onDestroy();
this._eventManager.removeListener(this._touchListener, this.node);
}
protected pixelsData: Uint8Array = null;
private IsTouchSuccess(touch: cc.Touch): boolean {
// 点击位置
const touchPos = touch.getLocation()
const localPos = this.node.convertToNodeSpaceAR(touchPos);
if (!this.node.getBoundingBoxToWorld().contains(touchPos)) {
return false; // 不在节点内
}
// 获取像素数据
if (!this.pixelsData) {
this.pixelsData = this.getPixelsData(this.node);
// 将收集到的像素数据存储到全局变量或其他需要共享的位置
cc.game.emit("pixelsDataCollected", this.pixelsData);
}
// 截取像素颜色
let x = localPos.x + this.node.anchorX * this.node.width,
y = -(localPos.y - this.node.anchorY * this.node.height);
// 图片放大的话,像素数据也相应变多
let cellSize = this.node.scaleX * 4
let index = (this.node.width * cellSize * (Math.floor(y) * this.node.scaleY)) + (cellSize * Math.floor(x))
const colors = this.pixelsData.slice(index, index + cellSize);
if (colors[3] == 0) {
return false; // 点在透明像素上
}
return true;
}
private onTargetClick() {
console.log(this.node.name);
}
/**
* 获取像素数据
* @param node 节点
* @param flipY 垂直翻转数据
*/
public getPixelsData(node: cc.Node, flipY: boolean = true) {
if (!cc.isValid(node)) {
return null;
}
// 节点宽高
const width = node.width * node.scaleX,
height = node.height * node.scaleY;
// 创建临时摄像机用于渲染目标节点
const cameraNode = new cc.Node();
cameraNode.parent = node;
const camera = cameraNode.addComponent(cc.Camera);
camera.clearFlags |= cc.Camera.ClearFlags.COLOR;
camera.backgroundColor = cc.color(0, 0, 0, 0);
camera.zoomRatio = cc.winSize.height / height;
// 将节点渲染到 RenderTexture 中
const renderTexture = new cc.RenderTexture();
renderTexture.initWithSize(width, height, cc.RenderTexture.DepthStencilFormat.RB_FMT_S8);
camera.targetTexture = renderTexture;
camera.render(node);
// 获取像素数据
const pixelsData = renderTexture.readPixels();
// 销毁临时对象并返回数据
renderTexture.destroy();
cameraNode.destroy();
// 垂直翻转数据
if (flipY) {
const length = pixelsData.length,
lineWidth = width * 4,
data = new Uint8Array(length);
for (let i = 0, j = length - lineWidth; i < length; i += lineWidth, j -= lineWidth) {
for (let k = 0; k < lineWidth; k++) {
data[i + k] = pixelsData[j + k];
}
}
return data;
}
return pixelsData;
}
}