我正在使用Canvas在html5中编写2D游戏,需要鼠标点击和悬停事件才能被检测到。这有三个问题:检测必须是像素完美的,对象不是矩形(房屋,奇怪的UI按钮......),并且要求快速和响应。 (显然蛮力不是一种选择)
所以我想问的是如何找出鼠标所在的对象,以及可能的优化内容。
P.S:我做了一些调查,找到了一个使用QuadTree here的人。答案 0 :(得分:3)
我有一个(过时的)教程,解释了鬼画布的概念,这对于像素完美的命中检测是不错的。教程是here。忽略有关较新教程的警告,较新的教程不使用ghost canvas概念。
我们的想法是将有问题的图像绘制到内存中的画布上,然后使用getImageData
来获取鼠标单击的单个像素。然后你会看到那个像素是否完全透明。
如果它不完全透明,那么,你已经有了目标。
如果它是完全透明的,则将下一个对象绘制到内存中的画布并重复。
您只需要在最后清除内存中的画布。
getImageData
速度很慢,但如果你想要像素完美的命中检测并且没有预先计算任何东西,那么它是你唯一的选择。
或者,您可以预先计算路径或具有偏移量的像素数组。这将是很多工作,但可能会更快。例如,如果你有一个40x20的图像具有一定的透明度,你就会计算出一个数组[40] [20],这个数组的真或假对应于透明或不对应。然后你要针对鼠标位置进行测试,有一些偏移量,如果在(25,55)处绘制图像,你想从鼠标位置减去该值,然后在你看时测试新位置是否为真阵列[POSX] [POSY]。
这是我对你问题的回答。 我的建议?如果这是游戏,请忘记像素完美检测。
严重。
而是创建表示对象但不是像素完美的路径(不是在画布中,在普通的javascript代码中),例如房子可能是顶部有三角形的正方形,这是一个非常接近的图像但是在进行热门测试时,它被用来代替它。如果一个点在路径内部而不是像素完美检测,则计算相对非常快。在多边形匝数规则检测中查找点。老实说,这是你最好的选择。
答案 1 :(得分:2)
传统游戏开发中的常见解决方案是构建一个点击掩码。您可以将所有内容重新渲染到一个单独的离屏画布上,呈现纯色(渲染应该非常快)。当您想要弄清楚所点击的内容时,您只需在屏幕外画布上的x / y坐标处采样颜色。您最终会构建一个颜色 - > obj哈希,类似于:
var map = {
'#000000' : obj1
, '#000001' : obj2
, ...
};
您还可以优化渲染到辅助画布,只有在用户点击某些内容时才会发生。使用各种技术,您可以进一步优化它以仅绘制用户单击的画布部分(例如,您可以将画布分割为NxN网格,例如20x20像素正方形的网格,并标记所有那个方块中的对象 - 你只需要重新绘制少量的对象)
答案 2 :(得分:0)
HTML5 Canvas只是一个绘图平面,您可以在调用每个绘图API函数之前设置不同的变换。无法创建对象,也没有显示列表。因此,您必须自己构建这些功能,或者可以使用不同的库。
在我对此感兴趣之前的几个月,甚至为此目的写了一个图书馆。你可以在这里看到它:http://exsprite.com。结束了很多性能问题,但由于时间不够,我无法对其进行优化。这真的很有趣,所以等待一段时间才能完美。
答案 3 :(得分:0)
我认为评论应该足够了。这就是我在我的二维等距卷轴中确定用户意图的方法,目前位于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定位绝对值和x = - (宽度)以隐藏;