我正在使用Canvas编写一个2D游戏,需要检测鼠标点击和悬停事件。这里有三个问题:检测必须精准到像素级别、物体不是矩形(例如房屋、奇形怪状的UI按钮...)且需要快速响应。(显然采用暴力算法不是一个好选择)
因此,我想问的是如何找出鼠标所在的物体,并进行可能的优化。
P.S:我进行了一些调查,发现有个人使用QuadTree 这里。
因此,我想问的是如何找出鼠标所在的物体,并进行可能的优化。
P.S:我进行了一些调查,发现有个人使用QuadTree 这里。
getImageData
来获取鼠标单击的单个像素。然后看看该单个像素是否完全透明。getImageData
很慢,但如果要实现像素完美的点击检测并且没有预计算任何东西,它是您唯一的选择。传统游戏开发中的常见解决方案是构建一个点击掩码。您可以在单独的屏幕外画布上重新渲染所有内容,以纯色显示(渲染应该非常快)。当您想要弄清楚点击了什么时,只需在屏幕外画布上的x/y坐标处取样颜色即可。最终您会构建一个颜色-对象哈希表,类似于:
var map = {
'#000000' : obj1
, '#000001' : obj2
, ...
};
您也可以将次要画布的渲染方式优化为仅在用户单击某些内容时才进行。并且使用各种技术,您可以进一步优化它以仅绘制用户点击的画布部分(例如,您可以将画布分割成一个NxN网格,例如20x20像素的正方形网格,并标记该网格中的所有对象-然后您只需要重新绘制少量对象)
HTML5 Canvas只是一个绘图平面,在调用每个绘图API函数之前,您可以设置不同的变换。无法创建对象,也没有显示列表。因此,您必须自己构建这些功能,或者可以使用可用的不同库。
几个月前,我对此产生了兴趣,甚至为此编写了一个库。你可以在这里看到它:http://exsprite.com。最终面临了许多性能问题,但因为时间不够,我无法进行优化。这真的很有趣,所以我在等待一些时间来完善它。我相信注释应该足够了。这是我在我的2D等距滚动游戏中确定用户意图的方法,目前位于http://untitled.servegame.com。
var lastUp = 0;
function mouseUp(){
mousedown = false; //one of my program globals.
var timeNow = new Date().getTime();
if(mouseX == xmouse && mouseY == ymouse && timeNow > lastUp + 100){//if it was a centralized click. (mouseX = click down point, xmouse = mouse's most recent x) and is at least 1/10th of a second after the previous click.
lastUp = new Date().getTime();
var elem = document.elementFromPoint(mouseX, mouseY); //get the element under the mouse.
var url = extractUrl($(elem).css('background-image')); // function I found here: http://webdevel.blogspot.com/2009/07/jquery-quick-tip-extract-css-background.html
imgW = $("#hiddenCanvas").width(); //EVERY art file is 88px wide. thus my canvas element is set to 88px wide.
imgH = $(elem).css('height').split('p')[0]; //But they vary in height. (currently up to 200);
hiddenCanvas.clearRect(0, 0, imgW, imgH); //so only clear what is necessary.
var img = new Image();
img.src = url;
img.onload = function(){
//draw this elements image to the canvas at 0,0
hiddenCanvas.drawImage(img,0,0);
///This computes where the mouse is clicking the element.
var left = $(elem).css('left').split('p')[0]; //get this element's css absolute left.
var top = $(elem).css('top').split('p')[0];
offX = left - offsetLeft; //left minus the game rendering element's absolute left. gives us the element's position relative of document 0,0
offY = top - offsetTop;
offX = mouseX - offX; //apply the difference of the click point's x and y
offY = mouseY - offY;
var imgPixel = hiddenCanvas.getImageData(offX, offY, 1, 1); //Grab that pixel. Start at it's relative X and it's relative Y and only grab one pixel.
var opacity = imgPixel.data[3]; //get the opacity value of this pixel.
if(opacity == 0){//if that pixel is fully transparent
$(elem).hide();
var temp = document.elementFromPoint(mouseX, mouseY); //set the element right under this one
$(elem).show();
elem = temp;
}
//draw a circle on our hiddenCanvas so when it's not hidden we can see it working!
hiddenCanvas.beginPath();
hiddenCanvas.arc(offX, offY, 10, 0, Math.PI*2, true);
hiddenCanvas.closePath();
hiddenCanvas.fill();
$(elem).css("top", "+=1"); //apply something to the final element.
}
}
}
关于此:
<canvas id="hiddenCanvas" width="88" height="200"></canvas>
将CSS的定位设置为absolute,并将x = -(width)以隐藏;