Я хочу обнаружить пиксельные столкновения между двумя спрайтами.
Я использую следующую функцию, которую я нашел в Интернете, но она имеет для меня смысл.
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, чтобы «включить» последний пиксель, который в противном случае не учитывался, но тогда он всегда дает мне индекс из массивов bitsA или bitsB.
Кто-нибудь сможет настроить эту функцию так, чтобы она работала, когда Bounds.X
и Bounds.Y
являются числами с плавающей запятой?
PS - может ли проблема возникнуть из-за BoxingViewportAdapter? Я использую это (из MonoExtended) для «увеличения» моей игры, которая на самом деле 144p.