Я хочу обнаружить идеальные по пикселям коллизии между двумя спрайтами.
Я использую следующую функцию, которую я нашел в Интернете, но для меня это имеет смысл.
static bool PerPixelCollision(Sprite a, Sprite b)
{
// Get Color data of each Texture
Color[] bitsA = new Color[a.Width * a.Height];
a.Texture.GetData(0, a.CurrentFrameRectangle, bitsA, 0, a.Width * a.Height);
Color[] bitsB = new Color[b.Width * b.Height];
b.Texture.GetData(0, b.CurrentFrameRectangle, bitsB, 0, b.Width * b.Height);
// Calculate the intersecting rectangle
int x1 = (int)Math.Floor(Math.Max(a.Bounds.X, b.Bounds.X));
int x2 = (int)Math.Floor(Math.Min(a.Bounds.X + a.Bounds.Width, b.Bounds.X + b.Bounds.Width));
int y1 = (int)Math.Floor(Math.Max(a.Bounds.Y, b.Bounds.Y));
int y2 = (int)Math.Floor(Math.Min(a.Bounds.Y + a.Bounds.Height, b.Bounds.Y + b.Bounds.Height));
// For each single pixel in the intersecting rectangle
for (int y = y1; y < y2; ++y)
{
for (int x = x1; x < x2; ++x)
{
// Get the color from each texture
Color colorA = bitsA[(x - (int)Math.Floor(a.Bounds.X)) + (y - (int)Math.Floor(a.Bounds.Y)) * a.Texture.Width];
Color colorB = bitsB[(x - (int)Math.Floor(b.Bounds.X)) + (y - (int)Math.Floor(b.Bounds.Y)) * b.Texture.Width];
if (colorA.A != 0 && colorB.A != 0) // If both colors are not transparent (the alpha channel is not 0), then there is a collision
{
return true;
}
}
}
//If no collision occurred by now, we're clear.
return false;
}
(все Math.floor
бесполезны, я скопировал эту функцию из моего текущего кода, где я пытаюсь заставить ее работать с плавающей точкой).
Он считывает цвет спрайтов в прямоугольной части, которая является общей для обоих спрайтов.
Это на самом деле работает нормально, когда я отображаю спрайты в координатах x / y, где x и y являются целыми числами (.Bounds.X
и .Bounds.Y
):
Посмотреть пример
Проблема с отображением спрайтов в координатах int заключается в том, что это приводит к очень неровному движению по диагонали:
Посмотреть пример
Так что, в конечном счете, я бы не хотел приводить позицию спрайта к int при рисовании, что приводит к плавному (er) движению:
Посмотреть пример
Проблема в том, что PerPixelCollision работает с целыми числами, а не с плавающей точкой, поэтому я добавил все эти объекты Math.Floor. Как и раньше, он работает в большинстве случаев, но ему не хватает одной строки и одного ряда проверки снизу и справа (я думаю) общего прямоугольника из-за округления, вызванного Math.Floor:
Посмотреть пример
Когда я думаю об этом, я думаю, что это имеет смысл. Если x1 равен 80, а x2 фактически равен 81,5, но равен 81 из-за преобразования, то цикл будет работать только для x = 80 и, следовательно, пропустит последний столбец (в примере gif у фиксированного спрайта есть прозрачный столбец на слева от видимых пикселей).
Проблема в том, что, как бы я ни думал об этом или что бы я ни пытался (я много чего пробовал) - я не могу заставить это работать должным образом. Я почти убежден, что x2 и y2 должны иметь Math.Ceiling вместо Math.Floor, чтобы «включить» последний пиксель, который в противном случае был бы пропущен, но тогда он всегда выводит мне индекс из массивов bitA или bitsB.
Кто-нибудь сможет настроить эту функцию так, чтобы она работала, когда Bounds.X
и Bounds.Y
являются числами с плавающей запятой?
PS - возможно, проблема возникла в BoxingViewportAdapter? Я использую это (из MonoExtended), чтобы «улучшить» мою игру, которая на самом деле 144p.