C#中的每像素碰撞问题

我正在用C#编写一个小的2d游戏引擎用于我自己的目的,除了sprite碰撞检测之外它工作得很好。 我决定让它成为每像素检测(对我来说最容易实现),但它没有像预期的那样工作。 代码在发生之前很久就会检测到它。 我已经检查了检测的每个组件,但我找不到问题。

碰撞检测方法:

public static bool CheckForCollision(Sprite s1, Sprite s2, bool perpixel) { if(!perpixel) { return s1.CollisionBox.IntersectsWith(s2.CollisionBox); } else { Rectangle rect; Image img1 = GraphicsHandler.ResizeImage(GraphicsHandler.RotateImagePoint(s1.Image, s1.Position, s1.Origin, s1.Rotation, out rect), s1.Scale); int posx1 = rect.X; int posy1 = rect.Y; Image img2 = GraphicsHandler.ResizeImage(GraphicsHandler.RotateImagePoint(s2.Image, s2.Position, s2.Origin, s2.Rotation, out rect), s2.Scale); int posx2 = rect.X; int posy2 = rect.Y; Rectangle abounds = new Rectangle(posx1, posy1, (int)img1.Width, (int)img1.Height); Rectangle bbounds = new Rectangle(posx2, posy2, (int)img2.Width, (int)img2.Height); if(Utilities.RectangleIntersects(abounds, bbounds)) { uint[] bitsA = s1.GetPixelData(false); uint[] bitsB = s2.GetPixelData(false); int x1 = Math.Max(abounds.X, bbounds.X); int x2 = Math.Min(abounds.X + abounds.Width, bbounds.X + bbounds.Width); int y1 = Math.Max(abounds.Y, bbounds.Y); int y2 = Math.Min(abounds.Y + abounds.Height, bbounds.Y + bbounds.Height); for(int y = y1; y < y2; ++y) { for(int x = x1; x > 24) > 20 && ((bitsB[(x - bbounds.X) + (y - bbounds.Y) * bbounds.Width] & 0xFF000000) >> 24) > 20) return true; } } } return false; } } 

图像旋转方法:

 internal static Image RotateImagePoint(Image img, Vector pos, Vector orig, double rotation, out Rectangle sz) { if(!(new Rectangle(new Point(0), img.Size).Contains(new Point((int)orig.X, (int)orig.Y)))) Console.WriteLine("Origin point is not present in image bound; unwanted cropping might occur"); rotation = (double)ra_de((double)rotation); sz = GetRotateDimensions((int)pos.X, (int)pos.Y, img.Width, img.Height, rotation, false); Bitmap bmp = new Bitmap(sz.Width, sz.Height); Graphics g = Graphics.FromImage(bmp); g.SmoothingMode = SmoothingMode.AntiAlias; g.InterpolationMode = InterpolationMode.HighQualityBicubic; g.PixelOffsetMode = PixelOffsetMode.HighQuality; g.RotateTransform((float)rotation); g.TranslateTransform(sz.Width / 2, sz.Height / 2, MatrixOrder.Append); g.DrawImage(img, (float)-orig.X, (float)-orig.Y); g.Dispose(); return bmp; } internal static Rectangle GetRotateDimensions(int imgx, int imgy, int imgwidth, int imgheight, double rotation, bool Crop) { Rectangle sz = new Rectangle(); if (Crop == true) { // absolute trig values goes for all angles double dera = de_ra(rotation); double sin = Math.Abs(Math.Sin(dera)); double cos = Math.Abs(Math.Cos(dera)); // general trig rules: // length(adjacent) = cos(theta) * length(hypotenuse) // length(opposite) = sin(theta) * length(hypotenuse) // applied width = lo(img height) + la(img width) sz.Width = (int)(sin * imgheight + cos * imgwidth); // applied height = lo(img width) + la(img height) sz.Height = (int)(sin * imgwidth + cos * imgheight); } else { // get image diagonal to fit any rotation (w & h =diagonal) sz.X = imgx - (int)Math.Sqrt(Math.Pow(imgwidth, 2.0) + Math.Pow(imgheight, 2.0)); sz.Y = imgy - (int)Math.Sqrt(Math.Pow(imgwidth, 2.0) + Math.Pow(imgheight, 2.0)); sz.Width = (int)Math.Sqrt(Math.Pow(imgwidth, 2.0) + Math.Pow(imgheight, 2.0)) * 2; sz.Height = sz.Width; } return sz; } 

像素获取方法:

 public uint[] GetPixelData(bool useBaseImage) { Rectangle rect; Image image; if (useBaseImage) image = Image; else image = GraphicsHandler.ResizeImage(GraphicsHandler.RotateImagePoint(Image, Position, Origin, Rotation, out rect), Scale); BitmapData data; try { data = ((Bitmap)image).LockBits(new Rectangle(0, 0, image.Width, image.Height), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb); } catch (ArgumentException) { data = ((Bitmap)image).LockBits(new Rectangle(0, 0, image.Width, image.Height), ImageLockMode.ReadOnly, image.PixelFormat); } byte[] rawdata = new byte[data.Stride * image.Height]; Marshal.Copy(data.Scan0, rawdata, 0, data.Stride * image.Height); ((Bitmap)image).UnlockBits(data); int pixelsize = 4; if (data.PixelFormat == PixelFormat.Format24bppRgb) pixelsize = 3; else if (data.PixelFormat == PixelFormat.Format32bppArgb || data.PixelFormat == PixelFormat.Format32bppRgb) pixelsize = 4; double intdatasize = Math.Ceiling((double)rawdata.Length / pixelsize); uint[] intdata = new uint[(int)intdatasize]; Buffer.BlockCopy(rawdata, 0, intdata, 0, rawdata.Length); return intdata; } 

像素检索方法有效,旋转方法也可以,因此代码可能出错的唯一地方是碰撞检测代码,但我真的不知道问题可能在哪里。

我不认为这里有很多人会费心仔细检查你的代码,找出究竟是什么问题。 但我可以提供一些提示,告诉你如何找到问题。

如果碰撞发生很久之前,我建议你的边界框检查不能正常工作。

我会更改代码以在碰撞时转储有关矩形的所有数据。 因此,您可以创建一些代码来显示碰撞时的情况。 这可能比查看数字更容易。

除此之外,我怀疑每个像素碰撞检测更容易实现。 当您允许旋转和缩放时,很快就会变得难以正确。 我会做基于多边形的碰撞检测。

我像你一样制作了自己的2D引擎,但我使用了基于多边形的碰撞检测,效果很好。

我想我找到了你的问题。

 internal static Rectangle GetRotateDimensions(int imgx, int imgy, int imgwidth, int imgheight, double rotation, bool Crop) { Rectangle sz = new Rectangle(); // <-- Default constructed rect here. if (Crop == true) { // absolute trig values goes for all angles double dera = de_ra(rotation); double sin = Math.Abs(Math.Sin(dera)); double cos = Math.Abs(Math.Cos(dera)); // general trig rules: // length(adjacent) = cos(theta) * length(hypotenuse) // length(opposite) = sin(theta) * length(hypotenuse) // applied width = lo(img height) + la(img width) sz.Width = (int)(sin * imgheight + cos * imgwidth); // applied height = lo(img width) + la(img height) sz.Height = (int)(sin * imgwidth + cos * imgheight); // <-- Never gets the X & Y assigned !!! } 

由于您从未将imgx和imgy分配给Rectangle的X和Y坐标,因此每次调用GetRotateDimensions都会生成具有相同位置的Rectangle。 它们可能具有不同的大小,但它们将始终处于默认的X,Y位置。 这会导致你看到真正的早期碰撞,因为任何时候你试图检测两个精灵上的碰撞,GetRotateDimensions会将它们的边界置于相同的位置,而不管它们实际位于何处。

一旦纠正了该问题,您可能会遇到另一个错误:

 Rectangle rect; Image img1 = GraphicsHandler.ResizeImage(GraphicsHandler.RotateImagePoint(s1.Image, s1.Position, s1.Origin, s1.Rotation, out rect), s1.Scale); // <-- Should resize rect here. int posx1 = rect.X; int posy1 = rect.Y; 

从RotateImagePoint函数获取边界矩形,然后调整图像大小。 矩形中的X和Y可能与图像的resize的边界的X和Y不完全相同。 我猜你的意思是图像的中心保持在原位,而所有点在resize时向中心收缩或从中心扩展。 如果是这种情况,那么您需要调整rect和图像的大小以获得正确的位置。

我怀疑这是实际问题,但LockBits并不保证位数据与图像的宽度对齐。

即,可能有一些填充。 您需要使用data[x + y * stride]而不是data[x + y * width]来访问图像。 Stride也是BitmapData一部分。