C# Raycasting / 从方向查找最近的矩形

所以,我试图找到一种方法来找到我的点将击中的第一个矩形(从矩形列表中,所有 2D),沿着特定方向(在 C# 中),但是我似乎无法正确描述术语(如果有这样的东西,我发现最接近的东西是光线投射)。


我的目标是从一个特定的“点”开始(在这种情况下,从下面的示例中可以看到星号-*),然后从那里选择一个特定的方向(左、右、下、上)(无角度)。所以假设我们选择 Down,那么它会命中的第一个矩形是“I'M A RECT 2”,因此应该返回这个。


我已经知道矩形的所有位置和大小,所以我得到了这些信息。


我该怎么做呢?


     *       [I'M A RECT 1]



[I'M A RECT 2]  

               [I'M A RECT 3]

[I'M A RECT 4]


动漫人物
浏览 53回答 1
1回答

慕码人2483693

您可以检查矩形是否可以与源自该点的射线相交,然后计算与 的距离point:var point = new PointF(1.2f, 2.5f);var rectangles = new RectangleF[]{&nbsp; &nbsp; new RectangleF(1, 1, 1, 1),&nbsp; &nbsp; new RectangleF(3, 1, 1, 1),&nbsp; &nbsp; new RectangleF(5, 2, 1, 1),};var hit = rectangles&nbsp; &nbsp; .Select(x =>&nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; if (IsBetween(point.X, x.Left, x.Left + x.Width))&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return new { Rectangle = x, Distance = GetClosestDistance(point.Y, x.Top - x.Height, x.Top) as float? };&nbsp; &nbsp; &nbsp; &nbsp; else if (IsBetween(point.X, x.Top - x.Height, x.Top))&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return new { Rectangle = x, Distance = GetClosestDistance(point.Y, x.Left, x.Left + x.Width) as float? };&nbsp; &nbsp; &nbsp; &nbsp; else return new { Rectangle = x, Distance = default(float?) };&nbsp; &nbsp; })&nbsp; &nbsp; .Where(x => x.Distance != null)&nbsp; &nbsp; .OrderBy(x => x.Distance)&nbsp; &nbsp; .FirstOrDefault()?.Rectangle;bool IsBetween(float value, float lBound, float uBound) => lBound <= value && value <= uBound;float GetClosestDistance(float value, float p0, float p1) => Math.Min(Math.Abs(p0 - value), Math.Abs(p1 - value));编辑:var hit = RayTest(point, rectangles, RayDirections.Right | RayDirections.Down) // or, try just `Down`&nbsp; &nbsp; .Where(x => x.Success)&nbsp; &nbsp; .OrderBy(x => x.Distance)&nbsp; &nbsp; .FirstOrDefault()?.Target;[Flags]public enum RayDirections { None = 0, Left = 1 << 0, Up = 1 << 1, Right = 1 << 2, Down = 1 << 3, All = (1 << 4) - 1 }public class RayHit<T>{&nbsp; &nbsp; public T Target { get; }&nbsp; &nbsp; public float? Distance { get; }&nbsp; &nbsp; public bool Success => Distance.HasValue;&nbsp; &nbsp; public RayHit(T target, float? distance)&nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; this.Target = target;&nbsp; &nbsp; &nbsp; &nbsp; this.Distance = distance;&nbsp; &nbsp; }}public IEnumerable<RayHit<RectangleF>> RayTest(PointF point, IEnumerable<RectangleF> rectangles, RayDirections directions = RayDirections.All){&nbsp; &nbsp; if (directions == RayDirections.None)&nbsp; &nbsp; &nbsp; &nbsp; return Enumerable.Empty<RayHit<RectangleF>>();&nbsp; &nbsp; var ray = new&nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; Horizontal = new&nbsp; &nbsp; &nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; LBound = directions.HasFlag(RayDirections.Left) ? float.MinValue : point.X,&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; UBound = directions.HasFlag(RayDirections.Right) ? float.MaxValue : point.X,&nbsp; &nbsp; &nbsp; &nbsp; },&nbsp; &nbsp; &nbsp; &nbsp; Vertical = new&nbsp; &nbsp; &nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; LBound = directions.HasFlag(RayDirections.Down) ? float.MinValue : point.Y,&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; UBound = directions.HasFlag(RayDirections.Up) ? float.MaxValue : point.Y,&nbsp; &nbsp; &nbsp; &nbsp; },&nbsp; &nbsp; };&nbsp; &nbsp; return rectangles&nbsp; &nbsp; &nbsp; &nbsp; .Select(x =>&nbsp; &nbsp; &nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; float left = x.Left, right = x.Left + x.Width;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; float top = x.Top, bottom = x.Top - x.Height;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if (IsBetween(point.X, left, right) && (IsBetween(top, ray.Vertical.LBound, ray.Vertical.UBound) || IsBetween(bottom, ray.Vertical.LBound, ray.Vertical.UBound)))&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return new RayHit<RectangleF>(x, GetClosestDistance(point.Y, bottom, top) as float?);&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; else if (IsBetween(point.X, bottom, top) && (IsBetween(left, ray.Horizontal.LBound, ray.Horizontal.UBound) || IsBetween(right, ray.Horizontal.LBound, ray.Horizontal.UBound)))&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return new RayHit<RectangleF>(x, GetClosestDistance(point.Y, left, right) as float?);&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; else return new RayHit<RectangleF>(x, default);&nbsp; &nbsp; &nbsp; &nbsp; });&nbsp; &nbsp; bool IsBetween(float value, float lBound, float uBound) => lBound <= value && value <= uBound;&nbsp; &nbsp; float GetClosestDistance(float value, float p0, float p1) => Math.Min(Math.Abs(p0 - value), Math.Abs(p1 - value));}注意:在这两个版本中,当点在矩形内时都会出现错误。计算的距离将是到最近边缘的距离,而不是 0 或负值。
打开App,查看更多内容
随时随地看视频慕课网APP