我想做一个飞镖游戏。我希望能够点击特定的馅饼给我点数。我通过连接饼图来制作飞镖靶。如何检测所选区域内是否有点击?这是我创建电路板的代码:
void draw(Color a, Color b, int j)
{
float start_angle = -9; // offset
float end_angle = 18;
for (int i = 0; i < 20; i++)
{
pie[j, i] = e.Graphics;
pie[j, i].DrawPie(pen, rectangle1, start_angle, end_angle);
if (i % 2 == 0)
{
brush.Color = a;
pie[j, i].FillPie(brush, rectangle1, start_angle, end_angle);
}
else
{
brush.Color = b;
pie[j, i].FillPie(brush, rectangle1, start_angle, end_angle);
}
start_angle = start_angle + end_angle;
}
}
答案 0 :(得分:0)
TaW关于使用GraphicsPath进行命中检测的答案很好,但这个特殊问题也可以用数学方法解决:
/// <summary>
/// Determine which wedge of the target a point is contained in, if any.
/// </summary>
/// <param name="p">The point to test.</param>
/// <param name="targetBoundingSquare">The bounding square of the circle target.</param>
/// <returns>
/// -1 if point is not in the circle target,
/// OR a number 0..19 which is the wedge that the point is in, in the same order
/// that they are drawn.
/// OR int.MaxValue if the point is a bullseye.
/// </returns>
int HitPieWedge(Point p, Rectangle targetBoundingSquare)
{
// precondition:
// the calculations below assume the target is a circle, not a general ellipse
if (targetBoundingSquare.Width != targetBoundingSquare.Height)
throw new ArgumentException("The width and height must be equal to form a circular target.", nameof(targetBoundingSquare));
// quick rejection test:
// If the point isn't within the overall target bounding rectangle,
// there's no hit
if (!targetBoundingSquare.Contains(p))
return -1;
// point is within the bounding rectangle of the target, so a
// hit is possible. now more carefully check to see if it's within
// the circle (instead of hitting a dead zone that's inside the
// bounding rectangle but outside of the circle)
// calculate the center point of the target
// could use double instead of float, but assume float is good enough precision for a game
float radius = targetBoundingSquare.Width / 2;
float centerX = targetBoundingSquare.X + radius;
float centerY = targetBoundingSquare.Y + radius;
// Use circle geometry to reject points outside the circle.
// This is done by calculating the distance from the center of the circle to the test point.
// If this distance is greater than the radius, we know the point
// is outside the circle. Otherwise it is inside or just on its boundary.
// Normally calculating the distance between two points requires a square root which is an
// relatively expensive operation, but since we only need to know if the distance
// exceeds the radius (and not the actual distance itself), comparing squared values
// is just as valid and is more efficient.
float dx = p.X - centerX;
float dy = p.Y - centerY;
// special case: Is point at the exact center ("bullseye")? If so,
// it is contained in the target but not in any wedge specifically, so
// just return an arbitrary special value.
if (dx == 0 && dy == 0)
return int.MaxValue;
// actual distance: not needed since we'll use an optimized form below just for
// relative distance comparison, which just squares both sizes of this equation.
// distance = Math.Sqr(Math.Pow(dx,2) + Math.Pow(dy,2));
// if point is further than radial distance from center, reject it
if (dx * dx + dy * dy > radius * radius)
return -1;
// at this point we know the point is on the target somewhere
// now determine which wedge it is in.
// note: this part can be done faster using a lookup table,
// but the math is kind of interesting :)
// the sweep angle of a single pie wedge, in radians
const double wedgeSweepAngle = 2 * Math.PI / 20;
// determine the direction from the center of the target to the hit point
// this will be an angle from the +X axis in radians in the range [-PI,PI]
double hitDirection = Math.Atan2(dy, dx);
// now the direction will be divided by the wedge angular size
// to identify which wedge it is in, but there are a couple of tricks needed:
// 1. Because the first wedge is centered on the positive X axis (rather than the
// leading edge being aligned on the axis), the direction needs to be offset by half a wedge.
hitDirection += wedgeSweepAngle * 0.5;
// 2. Change angle from the range [-PI,PI] to a nicer [0,2PI] range for wedge calculaton.
// This just maps the negative range [-PI,0] to [PI,2PI]; the positive range [0,PI] is fine as-is.
if (hitDirection < 0)
hitDirection += 2*Math.PI;
// now it's just a simple division to determine the wedge index.
// wedges will be numbered from 0 to 19 in the same order
// they're drawn in your code above
int wedgeIndex = (int)(hitDirection / wedgeSweepAngle);
return wedgeIndex;
}