I have a query regarding the best approach to detect when a moving and potentially rotated rectangle passes over a yellow pixel of a Panel's background image.
I have a method which accepts an Image and a Point, and returns true if that point is that of a yellow pixel. I require this colour detection for the function of my game, which resets the car (player) if it drives over the yellow borders of the track. This method is shown below:
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);
}
Previously, to detect if the player rectangle passes over yellow, I checked against the location of the rectangle, as provided by the X and Y values of the object. The issue with this is that the location is the top left corner of a horizontal rectangle, meaning the car can drive almost entirely off the track without detection occurring.
I'd like to fix this by checking all points covered by the rectangle. This is not as simple as it may seem as the rectangle is likely to be rotated. My drawing and movement logic is shown below:
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);
}
I tried but failed to create a method which translates the coords of the corner and figures out the middle of the rectangle, but this does not work, and the yellow detection fires in very obscure places:
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));
}
I'd really appreciate any suggestions on how to tackle this. I included the translation and yellow detection code in case I'm miles off in my attempt and someone else has a better idea.
Thank you very much.
There are two approaches that come to my mind:
Here is an example of the second approach.
It uses a
LockBits
method that detectsYellow
with your code in aBitmap
.And it prepares that bitmap by copying it from the original
BackgroundImage
un-rotated.Here is the result, including a control
Panel
that shows the untilted Rectangle:Here is the yellow finder function. It uses Lockbits for speed:
I prepare the
Bitmap
to compare in theMouseMove
. The variablesw, h, w2, h2
hold the width, height and halves of that of the car's size. The source bitmap is indrawPanel1.BackgroundImage
. The current angle is in aTrackBar tr_a.Value
. For further control I also display the rotated car rectangle in White.The first approach would use a similar
LockBits
method, but with loops inside that go along the rotated sides of the car rectangle, usingfloats
wth the loop variables to calculate the x-coordinates. Those data should be prepared on each change of car size or angle. The code is a little longer but should be a bit faster, too.The advantage if the second approach is that by using a
ClippingRegion
on theGraphics
object one could check an arbitrary shape while the first method can be easily modified for concave polygons but not for curved shapes.Here is the adapted version of the checking code for the first version:
The left- and rightside coordinates are stored here:
You need to feed in the four corners as a
List<PointF>
in any order; thetop
can be anything, it will be set in the method. The coodinates are relative to the car, so they don't change when the car moves..