Зацикливание всех пикселей может быть довольно дорогостоящим. Как правило, наличие игрока, проверяющего пиксели, лучше, когда это возможно. Вот мой подход:
function getTopMost(dpo:DisplayObject,global:Boolean = false):Point {
var bounds:Rectangle = dpo.getBounds(dpo);
var bmd:BitmapData = new BitmapData(bounds.width,1,true,0);
var mat:Matrix = new Matrix();
mat.translate(-bounds.x,-bounds.y);
bmd.draw(dpo,mat);
var colRect:Rectangle = bmd.getColorBoundsRect(0xff000000,0,false);
bmd.dispose();
var point:Point = new Point(bounds.x + colRect.x,bounds.y + colRect.y);
var transfMatrix:Matrix = global ? dpo.transform.concatenatedMatrix : dpo.transform.matrix;
return transfMatrix.transformPoint(point);
}
Сначала мы получаем ограничивающий прямоугольник экранного объекта. Таким образом, мы сразу отбрасываем любой прозрачный пиксель. Теперь этот прямоугольник уже дает вам «самый низкий» у спрайта (верхняя граница), так что теперь нам нужно только найти значение для соответствующего смещения x, чтобы получить искомую точку. На данный момент мы должны проверить пиксели, поэтому нам нужен объект BitampData. Нам нужно только нарисовать видимую часть экранного объекта (область, заключенная в bounds
). Но так как мы уже знаем значение для y, высоты 1 px будет достаточно. Эти BitmapData должны обеспечивать прозрачность, и мы будем заполнять их прозрачными черными пикселями перед рисованием (поэтому мы можем обнаружить непрозрачные пиксели после; здесь мы рассматриваем прозрачный пиксель, значение альфа которого равно 0, и непрозрачный все остальное; используйте порог вместо этого, если хотите, это потребует небольших изменений в коде). При рисовании мы применяем перевод, чтобы нарисовать только ту часть, которую мы хотим, воображаемую «линию» высотой 1 пиксель вдоль верхней границы, равную ширине объекта.
Как только мы нарисовали BitmapData, мы получили прямоугольник, который охватывает все непрозрачные пиксели, используя getColorBoundsRect
. Значение x этого прямоугольника - это то, что мы искали. И теперь у нас есть и х и у. Эти координаты относятся к видимой области объекта (bounds
), поэтому мы должны сместить их по x и y, равным bounds
. И это самая верхняя левая точка фигуры внутри спрайта.
Но это не преобразованная точка, поэтому, если вы вращаете или масштабируете спрайт, эта точка не будет отражать эти преобразования. Преобразовать этот пункт легко, хотя. Просто используйте матрицу преобразования спрайта, чтобы получить точку относительно его родителя. Или, если вам нужна глобальная точка, которая учитывает все преобразования от этого объекта до стадии, используйте вместо этого сцепленную матрицу спрайта.
Редактировать
Функция, которая делает то же самое, что и выше, но позволяет вам указать альфа-порог в диапазоне 0 - 255. Она использует ту же идею, которую я объяснил ранее, хотя ее реализация немного сложнее.
function getTopMostThreshold(dpo:DisplayObject,alphaVal:int,global:Boolean = false):Point {
if(alphaVal < 0) {
alphaVal = 0;
}
if(alphaVal > 0xff) {
alphaVal = 0xff;
}
var alphaThreshold:uint = alphaVal << 24;
var dpoBounds:Rectangle = dpo.getBounds(dpo);
// draw all pixel with alpha > 0
var boundsBmd:BitmapData = new BitmapData(dpoBounds.width,dpoBounds.height,true,0);
var mat:Matrix = new Matrix();
mat.translate(-dpoBounds.x,-dpoBounds.y);
boundsBmd.draw(dpo,mat);
// any pixel with alpha >= threshold will be set to alpha = 0xff
boundsBmd.threshold(boundsBmd,
new Rectangle(0,0,boundsBmd.width,boundsBmd.height),
new Point(0,0),
">=",
alphaThreshold,
0xff000000,
0xff000000);
// this rect encloses all pixels that passed the threshold (and hence were set to alpha = 0xff)
// With this, we have the value for y
var alphaBounds:Rectangle = boundsBmd.getColorBoundsRect(0xff000000,0xff000000,true);
// now, let's find the value for x; we know y, so 1 px height will be enough
var aux:BitmapData = new BitmapData(alphaBounds.width,1,true,0);
// again, any pixel with alpha >= threshold will be set to alpha = 0xff
aux.threshold(boundsBmd,
alphaBounds,
new Point(0,0),
">=",
alphaThreshold,
0xff000000,
0xff000000);
// this rect will gives us the x value
var colRect:Rectangle = aux.getColorBoundsRect(0xff000000,0xff000000,true);
boundsBmd.dispose();
aux.dispose();
// now, just add up the offsets:
// the bounds of the dpo (that is, every px with alpha > 0)
// the area that encloses every px with alpha >= threshold
// the color bounds rect of the aux bmd we used to find x
var point:Point = new Point(dpoBounds.x + alphaBounds.x + colRect.x,
dpoBounds.y + alphaBounds.y + colRect.y);
var transfMatrix:Matrix = global ? dpo.transform.concatenatedMatrix : dpo.transform.matrix;
return transfMatrix.transformPoint(point);
}