2016-09-10 32 views
2

我有一个关于检测何时移动且可能旋转的矩形越过面板背景图像的黄色像素的最佳方法的查询。检测矩形在黄色像素上的传递

我有一个方法接受图像和点,并返回true,如果该点是黄色像素的点。我需要对我的游戏功能进行颜色检测,如果它在赛道的黄色边框上行驶,则会重置赛车(玩家)。该方法如下所示:

private Boolean isYellow(Image image, Point point) 
{ 
    Bitmap bitmap = new Bitmap(image); 
    Color color = bitmap.GetPixel(point.X, point.Y); 

    return (color.R > 220 && color.G > 220 && color.B < 200); 
} 

以前,为了如果玩家矩形越过黄色检测,我检查针对矩形的位置,如由对象的X和Y值中。与此相关的问题是,该位置是水平矩形的左上角,这意味着汽车几乎可以完全离开轨道而不会发生检测。

我想通过检查矩形覆盖的所有点来解决这个问题。这并不像看起来那么简单,因为矩形很可能会旋转。我的绘画和运动逻辑如下图所示:

public void draw(Graphics g) 
{ 
    int dx = rectangle.X + (rectangle.Height/2); 
    int dy = rectangle.Y + (rectangle.Width/2); 

    g.ScaleTransform(xScale, yScale); 
    g.TranslateTransform(dx, dy); 
    g.RotateTransform((float) ((180 * angle)/Math.PI)); 
    g.TranslateTransform(-dx, -dy); 
    g.DrawImage(image, rectangle.X, rectangle.Y); 
    g.ResetTransform(); 
} 

public void move(uRaceGame game, Panel panel) 
{ 
    double cos = Math.Cos(angle), sin = Math.Sin(angle); 
    int xLocation = 200; 
    int yLocation = 200; 

    xLocation = (int) Math.Floor(rectangle.X + (cos * game.moveDir * 60)); 
    yLocation = (int) Math.Floor(rectangle.Y + (sin * game.moveDir * 60)); 

    angle = (angle + (game.rotateDir * (Math.PI/128))) % (Math.PI * 2); 

    if (xLocation * xScale > panel.Width - (rectangle.Width * cos) || yLocation * yScale > panel.Height - (rectangle.Width * sin) - 5 || xLocation * xScale < 0 || yLocation * yScale < 5) return; 

    rectangle.Location = new Point(xLocation, yLocation); 
} 

我试过,但未能创造出翻译的角落COORDS和计算出的矩形中间的方法,但是,这并不工作,和黄色检测火灾在非常晦涩的地方:

public Point getCentre() 
{ 
    int cX = (int) (rectangle.X + ((rectangle.Width/2)/xScale)), cY = (int) (rectangle.Y + ((rectangle.Height/2)/yScale)); 
    float tempX = (rectangle.X - cX), tempY = (rectangle.Y - cY); 

    double rX = (tempX * Math.Cos(angle)) - (tempY * Math.Sin(angle)); 
    double rY = (tempX * Math.Sin(angle)) - (tempY * Math.Cos(angle)); 

    return new Point((int) ((rX + cX) * xScale), (int) ((rY + cY) * yScale)); 
} 

我真的很感激任何建议如何解决这个问题。我包括翻译和黄色检测代码,以防万一我尝试了几英里,而其他人有更好的主意。

非常感谢。

+0

速度使用LockBits! - 为了遍历矩形,你需要在外部边界(顶部和底部边界)和内部循环之间建立一个外部循环,这些内部循环通过累积浮动斜率来计算左侧和右侧的开始和结束点,直到碰到一个拐角,然后减去。在改变汽车方向时,您可以计算出2个斜坡。 – TaW

+0

很高兴你发现有用的anwer。你可能想看看更新后的版本.. – TaW

+0

@TaW我真的很感谢你为这个答案付出的努力!非常感谢你的帮助。 –

回答

3

有迹象表明,我想起两种方法:

  • 您可以创建沿车矩形的倾斜面走循环
  • 或者你也可以在汽车复制到非倾斜位图和环它通常。

以下是第二种方法的示例。

它采用了LockBits方法检测Yellow在一个Bitmap你的代码。

并且它通过从原始的BackgroundImage未复制的复制它来准备该位图。

下面是结果,包括控制Panel表示非倾斜矩形:


enter image description here


这里是黄色取景器功能。它采用Lockbits飞车:

using System.Runtime.InteropServices; 
using System.Drawing.Imaging; 

public bool testForYellowBitmap(Bitmap bmp) 
{ 
    Size s1 = bmp.Size; 
    PixelFormat fmt = new PixelFormat(); 
    fmt = bmp.PixelFormat; 
    Rectangle rect = new Rectangle(0, 0, s1.Width, s1.Height); 
    BitmapData bmp1Data = bmp.LockBits(rect, ImageLockMode.ReadOnly, fmt); 
    byte bpp1 = 4; 
    if (fmt == PixelFormat.Format24bppRgb) bpp1 = 3; 
    else if (fmt == PixelFormat.Format32bppArgb) bpp1 = 4; else return false; // throw!! 
    int size1 = bmp1Data.Stride * bmp1Data.Height; 
    byte[] data1 = new byte[size1]; 
    System.Runtime.InteropServices.Marshal.Copy(bmp1Data.Scan0, data1, 0, size1); 
    for (int y = 0; y < s1.Height; y++) 
    { 
     for (int x = 0; x < s1.Width; x++) 
     { 
      Color c1; 
      int index1 = y * bmp1Data.Stride + x * bpp1; 
      if (bpp1 == 4) 
       c1 = Color.FromArgb(data1[index1 + 3], data1[index1 + 2], 
            data1[index1 + 1], data1[index1 + 0]); 
      else c1 = Color.FromArgb(255, data1[index1 + 2], 
              data1[index1 + 1], data1[index1 + 0]); 
      if (c1.R > 220 && c1.G > 220 && c1.B < 200) 
       { bmp.UnlockBits(bmp1Data); return true; } 
     } 
    } 
    bmp.UnlockBits(bmp1Data); 
    return false; 
} 

我准备BitmapMouseMove比较。变量w, h, w2, h2保持汽车尺寸的宽度,高度和一半。源位图位于drawPanel1.BackgroundImage。当前角度在TrackBar tr_a.Value。为了进一步控制,我还以白色显示旋转的汽车矩形。

private void drawPanel1_MouseMove(object sender, MouseEventArgs e) 
{ 
    if (e.Button.HasFlag(MouseButtons.Left)) 
    { 
     Size sz = drawPanel1.BackgroundImage.Size; 
     Rectangle rectSrc = new Rectangle(e.X - w2, e.Y - h2, w, h); 
     Rectangle rectTgt = new Rectangle(e.X - w, e.Y - h, 2 * w, 2 * h); 

     using (Graphics g = drawPanel1.CreateGraphics()) // start optional 
     { 
      g.TranslateTransform(e.X, e.Y); 
      g.RotateTransform(trb_a.Value); 
      g.TranslateTransform(-e.X, -e.Y); 
      drawPanel1.Refresh(); 
      g.DrawRectangle(Pens.White, rectSrc); 
     } 

     using (Graphics g = drawPanel2.CreateGraphics()) 
     {              // end optional 
      using (Bitmap bmp = new Bitmap(sz.Width, sz.Height)) 
      using (Graphics g2 = Graphics.FromImage(bmp)) 
      { 
       g2.TranslateTransform(e.X, e.Y); 
       g2.RotateTransform(-trb_a.Value); 
       g2.TranslateTransform(-e.X, -e.Y); 
       g2.DrawImage(drawPanel1.BackgroundImage, rectTgt, rectTgt, 
          GraphicsUnit.Pixel); 
       drawPanel2.Refresh(); 
       g.DrawImage(bmp, rectSrc, rectSrc, GraphicsUnit.Pixel); 
       Text = testForYellowBitmap(bmp) ? "!!YELLOW!!" : ""; 
      } 
     } 
    } 

第一种方法将使用类似的方法LockBits,但内部的循环沿车矩形的旋转边走,使用floats问心无愧的循环变量来计算x坐标。这些数据应根据每次更改车辆尺寸或角度进行准备。代码有点长,但也应该快一点。

如果第二种方法的优点是通过在Graphics对象上使用ClippingRegion可以检查任意形状,而第一种方法可以很容易地修改为凹多边形,但不能用于弯曲形状。

下面是检查代码为第一版本的改编版:

public bool testForYellowBitmapTilt(Bitmap bmp, List<int> leftPts, 
            List<int> rightPts, Point topLeft) 
{ 
    Size s1 = bmp.Size; 
    PixelFormat fmt = new PixelFormat(); 
    fmt = bmp.PixelFormat; 
    Rectangle rect = new Rectangle(0, 0, s1.Width, s1.Height); 
    BitmapData bmp1Data = bmp.LockBits(rect, ImageLockMode.ReadOnly, fmt); 
    byte bpp1 = 4; 
    if (fmt == PixelFormat.Format24bppRgb) bpp1 = 3; 
    else if (fmt == PixelFormat.Format32bppArgb) bpp1 = 4; 
     else return false; // or throw!! 
    if (leftPts.Count != rightPts.Count) return false; // or throw!! 

    int size1 = bmp1Data.Stride * bmp1Data.Height; 
    byte[] data1 = new byte[size1]; 
    System.Runtime.InteropServices.Marshal.Copy(bmp1Data.Scan0, data1, 0, size1); 

    for (int y = 0; y < (leftPts.Count); y++) 
    { 
     for (int x = leftPts[y] + topLeft.X; x < rightPts[y] + topLeft.X; x++) 
     { 
      Color c1; 

      int index1 = (y + topLeft.Y) * bmp1Data.Stride + x * bpp1; 
      if (index1 > 0) 
      { 
       if (bpp1 == 4) 
        c1 = Color.FromArgb(data1[index1 + 3], data1[index1 + 2], 
             data1[index1 + 1], data1[index1 + 0]); 
       else c1 = Color.FromArgb(255, data1[index1 + 2], 
             data1[index1 + 1], data1[index1 + 0]); 

       if (c1.R > 220 && c1.G > 220 && c1.B < 200) 
        { bmp.UnlockBits(bmp1Data); return true; } 
      } 
     } 
    } 
    bmp.UnlockBits(bmp1Data); 
    return false; 
} 

左侧和rightside坐标都存储在这里:

List<int> leftPts = new List<int>(); 
List<int> rightPts = new List<int>(); 
Point top = Point.Empty; 


void getOuterPoints(List<PointF> corners, out List<int> leftPts, 
        out List<int> rightPts, out Point top) 
{ 
    leftPts = new List<int>(); 
    rightPts = new List<int>(); 

    PointF left = corners.Select(x => x).OrderBy(x => x.X).First(); 
    PointF right = corners.Select(x => x).OrderByDescending(x => x.X).First(); 
      top = Point.Round(corners.Select(x => x).OrderBy(x => x.Y).First()); 
    PointF bottom = corners.Select(x => x).OrderByDescending(x => x.Y).First(); 

    int w1 = -(int)(top.X - left.X); 
    int w2 = -(int)(left.X - bottom.X); 
    int h1 = (int)(left.Y - top.Y); 
    int h2 = (int)(bottom.Y - left.Y); 

    float d1 = 1f * w1/h1; 
    float d2 = 1f * w2/h2; 

    for (int y = 0; y < h1; y++) leftPts.Add((int)(y * d1)); 
    for (int y = 0; y < h2; y++) leftPts.Add((int)(y * d2 + w1)); 

    for (int y = 0; y < h2; y++) rightPts.Add((int)(y * d2)); 
    for (int y = 0; y < h1; y++) rightPts.Add( (int)(y * d1 + w2)); 
} 

您需要在养活以任何顺序作为List<PointF>四个角落; top可以是任何东西,它将在方法中设置。该coodinates是相对于汽车,所以他们不会改变当汽车移动..