Pixel perfect 2D коврик для мыши с помощью Canvas - PullRequest
2 голосов
/ 20 декабря 2011

Я пишу 2D-игру на html5 с использованием Canvas, для которой требуется обнаружение событий щелчка мыши и наведения мыши. С этим есть 3 проблемы: обнаружения должны быть идеальными по пикселям, объекты не прямоугольными (дома, странные кнопки пользовательского интерфейса ...), и это должно быть быстрым и отзывчивым. (Очевидно, грубая сила не вариант)

Итак, я хочу спросить, как мне определить, на каком объекте находится мышь, и каковы возможные оптимизации.

П.С .: Я провел некоторое расследование и обнаружил парня, который использовал QuadTree здесь .

Ответы [ 4 ]

3 голосов
/ 20 декабря 2011

У меня есть (устаревшее) учебное пособие, которое объясняет концепцию призрачного холста, подходящего для идеального обнаружения пикселей.Учебное пособие здесь .Не обращайте внимания на предупреждение о новом учебнике, новый не использует концепцию холста-призрака.

Идея состоит в том, чтобы нарисовать нужное изображение на холсте в памяти, а затем использовать getImageData, чтобы получить единственныйпиксель щелчка мыши.Затем вы видите, является ли этот единственный пиксель полностью прозрачным или нет.

Если он не полностью прозрачный, значит, у вас есть цель.

Если он полностью прозрачный, нарисуйте следующий объектна холст в памяти и повторите.

Вам нужно только очистить холст в памяти в конце.

getImageData медленно, но это единственный вариант, если вам нужен пиксель-совершенное обнаружение попадания и ничего не вычисляет заранее.

В качестве альтернативы вы можете предварительно вычислить путь или массив пикселей со смещением.Это будет много работы, но может быть быстрее.Например, если у вас есть изображение 40x20 с некоторой прозрачностью, вы бы вычислили массив [40] [20], который бы имел значение true или false, соответствующее прозрачности или нет.Затем вы проверите это по отношению к положению мыши с некоторым смещением, если изображение нарисовано в (25, 55), вы хотите вычесть это из положения мыши, а затем проверить, является ли новое положение истинным, когда вы смотрите намассив [posx] [posy].

Это мой ответ на ваш вопрос. Мое предложение?Забудьте о точном обнаружении пикселей, если это игра.

Серьезно.

Вместо этого создайте пути (не на холсте, в простом коде JavaScript), которые представляют объекты, но не являются идеальными для пикселейНапример, дом может быть квадратом с треугольником на вершине, который является очень близким приближением изображения, но используется вместо него, когда дело доходит до тестирования удара.Сравнительно чрезвычайно быстро вычислить, если точка находится внутри траектории, чем для точного обнаружения пикселей.Посмотрите на точку в обнаружении правила числа обмоток многоугольника.Честно говоря, это ваш лучший выбор.

2 голосов
/ 20 декабря 2011

Распространенным решением в традиционной разработке игр является создание маски клика.Вы можете перерисовать все на отдельном закадровом холсте сплошным цветом (рендеринг должен быть очень быстрым).Когда вы хотите выяснить, на что нажимали, вы просто сэмплируете цвет в координате x / y на экране за пределами экрана.Вы в конечном итоге создаете цвет -> obj hash, похожий на:

var map = {
      '#000000' : obj1
    , '#000001' : obj2
    , ...
};

Вы также можете оптимизировать рендеринг на вторичный холст, чтобы он происходил только тогда, когда пользователь нажимает на что-то.А используя различные методы, вы можете дополнительно оптимизировать его, чтобы рисовать только ту часть холста, на которой щелкнул пользователь (например, вы можете разделить ваш холст на сетку NxN, например сетку из квадратов 20x20 пикселей, и пометить всеобъекты в этом квадрате - тогда вам нужно будет только перерисовать небольшое количество объектов)

0 голосов
/ 16 января 2012

Я считаю, что комментариев должно быть достаточно.Вот как я определяю намерение пользователя в своем 2-мерном изометрическом скроллере, который в настоящее время находится по адресу 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 =- (ширина), чтобы скрыть;

0 голосов
/ 20 декабря 2011

HTML5 Canvas - это просто плоскость рисования, где вы можете установить различные преобразования перед вызовом каждой функции API рисования. Объекты не могут быть созданы, и нет списка отображения. Таким образом, вы должны создать эти функции самостоятельно или использовать для этого разные библиотеки.

http://www.kineticjs.com/

http://easeljs.com/

За несколько месяцев до того, как я заинтересовался этим и даже написал библиотеку для этой цели. Вы можете увидеть это здесь: http://exsprite.com. Закончилось много проблем с производительностью, но из-за нехватки времени я не смог оптимизировать его. Это было действительно интересно, так что ждали некоторое время, чтобы сделать его идеальным.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...