public static HitPrediction PredictHit(Scene r, Thing Target, List <Type> hitDetectionTypes, float dx, float dy) { HitPrediction prediction = new HitPrediction(); if (Target.Bottom + dy >= r.Bounds.Size.H) { prediction.Direction = Direction.Down; prediction.Type = HitType.Boundary; prediction.BoundsOfItemBeingHit = new Rectangle(Target.Left + dx, r.Bounds.Size.H + dy, 1, 1); return(prediction); } else if (Target.Left + dx <= 0) { prediction.Direction = Direction.Left; prediction.Type = HitType.Boundary; prediction.BoundsOfItemBeingHit = new Rectangle(-dx, Target.Top + dy, 1, 1); return(prediction); } else if (Target.Top + dy <= 0) { prediction.Direction = Direction.Up; prediction.Type = HitType.Boundary; prediction.BoundsOfItemBeingHit = new Rectangle(Target.Left + dx, -dy, 1, 1); return(prediction); } else if (Target.Right + dx >= r.Bounds.Size.W) { prediction.Direction = Direction.Right; prediction.Type = HitType.Boundary; prediction.BoundsOfItemBeingHit = new Rectangle(r.Bounds.Size.W + dx, Target.Top + dy, 1, 1); return(prediction); } var testArea = new Rectangle(Target.Left + dx, Target.Top + dy, Target.Bounds.Size.W, Target.Bounds.Size.H); var match = (from t in r.Things where IsOneOfThese(t, hitDetectionTypes) && Target != t && testArea.Hits(t.Bounds) select t).OrderBy(t => t.Bounds.Location.CalculateDistanceTo(Target.Bounds.Location)); if (match.Count() == 0) { prediction.Direction = Direction.None; prediction.Type = HitType.None; } else { prediction.ThingHit = match.First(); prediction.Type = HitType.Thing; prediction.Direction = testArea.GetHitDirection(match.First().Bounds); prediction.BoundsOfItemBeingHit = prediction.ThingHit.Bounds; } return(prediction); }
public static HitPrediction PredictHit(HitDetectionOptions options) { HitPrediction prediction = new HitPrediction(); var left = Math.Min(options.MovingObject.Left, options.MovingObject.Left + options.Dx); var top = Math.Min(options.MovingObject.Top, options.MovingObject.Top + options.Dy); var right = Math.Max(options.MovingObject.Left + options.MovingObject.Width, options.MovingObject.Left + options.MovingObject.Width + options.Dx); var bottom = Math.Max(options.MovingObject.Top + options.MovingObject.Height, options.MovingObject.Top + options.MovingObject.Height + options.Dy); var relevantArea = RectangularF.Create(left, top, right - left, bottom - top); var effectiveObstacles = options.Obstacles.Where(o => o.Touches(relevantArea)).ToList(); if (options.Dx == 0 && options.Dy == 0) { prediction.Direction = Direction.None; prediction.Type = HitType.None; return(prediction); } var endPoint = RectangularF.Create(options.MovingObject.Left + options.Dx, options.MovingObject.Top + options.Dy, options.MovingObject.Width, options.MovingObject.Height); var angle = options.MovingObject.CalculateAngleTo(endPoint); var d = endPoint.CalculateDistanceTo(options.MovingObject); for (var dPrime = options.Precision; dPrime < d; dPrime += options.Precision) { var testLocation = options.MovingObject.Center().MoveTowards(angle, dPrime); var testArea = RectangularF.Create(testLocation.Left - options.MovingObject.Width / 2, testLocation.Top - options.MovingObject.Height / 2, options.MovingObject.Width, options.MovingObject.Height); var obstacleHit = effectiveObstacles.Where(o => IsIncluded(options, o) && o.Touches(testArea) == true).FirstOrDefault(); if (obstacleHit != null) { return(new HitPrediction() { Type = HitType.Obstacle, ObstacleHit = obstacleHit }); } } var obstacleHitFinal = effectiveObstacles.Where(o => IsIncluded(options, o) && o.Touches(endPoint) == true).FirstOrDefault(); if (obstacleHitFinal != null) { return(new HitPrediction() { Type = HitType.Obstacle, ObstacleHit = obstacleHitFinal }); } prediction.Type = HitType.None; return(prediction); }
public static HitPrediction PredictHit(SpaceTime r, SpacialElement Target, List <Type> hitDetectionTypes, List <SpacialElement> hitDetectionExclusions, float dx, float dy) { if (Math.Abs(dx) <= 1 && Math.Abs(dy) <= 1) { return(PredictHitInternal(r, Target, hitDetectionTypes, hitDetectionExclusions, dx, dy)); } HitPrediction latestResult = null; for (var i = 1; i <= 10; i++) { var dxP = Approach(0, dx, dx / 10 * i); var dyP = Approach(0, dy, dy / 10 * i); latestResult = PredictHitInternal(r, Target, hitDetectionTypes, hitDetectionExclusions, dxP, dyP); if (latestResult.Type != HitType.None) { return(latestResult); } } return(latestResult); }
public static HitPrediction PredictHit(SpaceTime r, SpacialElement Target, List <Type> hitDetectionTypes, float dx, float dy) { HitPrediction prediction = new HitPrediction(); if (dx == 0 && dy == 0) { prediction.Direction = Direction.None; prediction.Type = HitType.None; return(prediction); } if (dy > 0 && Target.Bottom() + dy >= r.Height) { prediction.Direction = Direction.Down; prediction.Type = HitType.Boundary; prediction.BoundsOfItemBeingHit = Rectangular.Create(Target.Left + dx, r.Bounds.Height + dy, 1, 1); return(prediction); } else if (dx < 0 && Target.Left + dx <= 0) { prediction.Direction = Direction.Left; prediction.Type = HitType.Boundary; prediction.BoundsOfItemBeingHit = Rectangular.Create(-dx, Target.Top + dy, 1, 1); return(prediction); } else if (dy < 0 && Target.Top + dy <= 0) { prediction.Direction = Direction.Up; prediction.Type = HitType.Boundary; prediction.BoundsOfItemBeingHit = Rectangular.Create(Target.Left + dx, -dy, 1, 1); return(prediction); } else if (dx > 0 && Target.Right() + dx >= r.Width) { prediction.Direction = Direction.Right; prediction.Type = HitType.Boundary; prediction.BoundsOfItemBeingHit = Rectangular.Create(r.Width + dx, Target.Top + dy, 1, 1); return(prediction); } var testArea = Rectangular.Create(Target.Left + dx, Target.Top + dy, Target.Width, Target.Height); var match = (from t in r.Elements where t.IsOneOfThese(hitDetectionTypes) && Target != t && testArea.NumberOfPixelsThatOverlap(t) > 0 select t).OrderBy(t => t.Center().CalculateDistanceTo(Target.Center())); if (match.Count() == 0) { prediction.Direction = Direction.None; prediction.Type = HitType.None; } else { prediction.ElementHit = match.First(); prediction.Type = HitType.Element; prediction.Direction = testArea.GetHitDirection(match.First().Bounds); prediction.BoundsOfItemBeingHit = prediction.ElementHit.Bounds; } return(prediction); }
public static HitPrediction PredictHit(HitDetectionOptions options) { HitPrediction prediction = new HitPrediction(); prediction.LKG = options.MovingObject.CopyBounds().TopLeft(); prediction.MovingObjectPosition = options.MovingObject.CopyBounds(); prediction.Visibility = options.Visibility; if (options.Visibility == 0) { prediction.Direction = Direction.None; prediction.Type = HitType.None; return(prediction); } var mov = options.MovingObject; List <Edge> rays; if (options.Mode == CastingMode.Precise) { rays = new List <Edge>() { new Edge() { From = mov.TopLeft(), To = mov.TopLeft().MoveTowards(options.Angle, options.Visibility, normalized: false) }, new Edge() { From = mov.TopRight(), To = mov.TopRight().MoveTowards(options.Angle, options.Visibility, normalized: false) }, new Edge() { From = mov.BottomLeft(), To = mov.BottomLeft().MoveTowards(options.Angle, options.Visibility, normalized: false) }, new Edge() { From = mov.BottomRight(), To = mov.BottomRight().MoveTowards(options.Angle, options.Visibility, normalized: false) }, }; var granularity = .5f; for (var x = mov.Left + granularity; x < mov.Left + mov.Width; x += granularity) { var top = LocationF.Create(x, mov.Top); var bot = LocationF.Create(x, mov.Bottom()); rays.Add(new Edge() { From = top, To = top.MoveTowards(options.Angle, options.Visibility, normalized: false) }); rays.Add(new Edge() { From = bot, To = bot.MoveTowards(options.Angle, options.Visibility, normalized: false) }); } for (var y = mov.Top + granularity; y < mov.Top + mov.Height; y += granularity) { var left = LocationF.Create(mov.Left, y); var right = LocationF.Create(mov.Right(), y); rays.Add(new Edge() { From = left, To = left.MoveTowards(options.Angle, options.Visibility, normalized: false) }); rays.Add(new Edge() { From = right, To = right.MoveTowards(options.Angle, options.Visibility, normalized: false) }); } } else if (options.Mode == CastingMode.Rough) { var center = options.MovingObject.Center(); rays = new List <Edge>() { new Edge() { From = mov.TopLeft(), To = mov.TopLeft().MoveTowards(options.Angle, options.Visibility, normalized: false) }, new Edge() { From = mov.TopRight(), To = mov.TopRight().MoveTowards(options.Angle, options.Visibility, normalized: false) }, new Edge() { From = mov.BottomLeft(), To = mov.BottomLeft().MoveTowards(options.Angle, options.Visibility, normalized: false) }, new Edge() { From = mov.BottomRight(), To = mov.BottomRight().MoveTowards(options.Angle, options.Visibility, normalized: false) }, new Edge() { From = center, To = center.MoveTowards(options.Angle, options.Visibility, normalized: false) } }; } else if (options.Mode == CastingMode.SingleRay) { var center = options.MovingObject.Center(); rays = new List <Edge>() { new Edge() { From = center, To = center.MoveTowards(options.Angle, options.Visibility, normalized: false) } }; } else { throw new NotSupportedException("Unknown mide: " + options.Mode); } var closestIntersectionDistance = float.MaxValue; IRectangularF closestIntersectingElement = null; var closestEdgeIndex = -1; var effectiveObstacles = options.Obstacles.ToArray(); for (var i = 0; i < effectiveObstacles.Length; i++) { var obstacle = effectiveObstacles[i]; for (var j = 0; j < obstacle.Edges.Length; j++) { var edge = obstacle.Edges[j]; for (var k = 0; k < rays.Count; k++) { var ray = rays[k]; var intersection = FindIntersectionPoint(ray, edge); if (intersection != null) { var d = ray.From.CalculateDistanceTo(intersection); if (d < closestIntersectionDistance && d <= options.Visibility) { closestIntersectionDistance = d; closestIntersectingElement = obstacle; closestEdgeIndex = j; } } } } } if (closestIntersectingElement != null) { prediction.ObstacleHit = closestIntersectingElement; prediction.LKGD = closestIntersectionDistance - .1f; prediction.LKG = options.MovingObject.MoveTowards(options.Angle, prediction.LKGD, normalized: false).TopLeft(); prediction.Type = HitType.Obstacle; prediction.EdgeIndex = closestEdgeIndex; } return(prediction); }
public static HitPrediction PredictHit(HitDetectionOptions options) { HitPrediction prediction = new HitPrediction(); prediction.MovingObjectPosition = options.MovingObject.CopyBounds(); prediction.Visibility = options.Visibility; if (options.Visibility == 0) { prediction.Direction = Direction.None; prediction.Type = HitType.None; return(prediction); } var effectiveObstacles = options.Obstacles.Where(o => o.CalculateDistanceTo(options.MovingObject) <= options.Visibility + options.Precision).ToList(); var endPoint = options.MovingObject.MoveTowards(options.Angle, options.Visibility); ILocationF lkg = null; for (var dPrime = options.Precision; dPrime < options.Visibility; dPrime += options.Precision) { var testArea = options.MovingObject.MoveTowards(options.Angle, dPrime); prediction.Path.Add(testArea); var obstacleHit = effectiveObstacles.Where(o => { var simpleTest = o.Touches(testArea) == true; if (simpleTest == false) { return(false); } if (o.Touches(options.MovingObject)) { var overlapBefore = options.MovingObject.NumberOfPixelsThatOverlap(o); var overlapAfter = testArea.NumberOfPixelsThatOverlap(o); if (overlapAfter < overlapBefore) { return(false); } else { return(true); } } else { return(true); } }).FirstOrDefault(); if (obstacleHit != null) { prediction.Type = HitType.Obstacle; prediction.ObstacleHit = obstacleHit; prediction.LKG = lkg; return(prediction); } else { lkg = testArea.TopLeft(); } } var obstacleHitFinal = effectiveObstacles.Where(o => o.Touches(endPoint) == true).FirstOrDefault(); if (obstacleHitFinal != null) { prediction.Type = HitType.Obstacle; prediction.ObstacleHit = obstacleHitFinal; prediction.LKG = lkg; return(prediction); } prediction.Type = HitType.None; return(prediction); }
private async Task ExecuteAsync() { while (this.Lifetime.IsExpired == false) { await Time.CurrentTime.YieldAsync(); if (this.Lifetime.IsExpired) { return; } if (MovementTakeover != null) { await MovementTakeover(); continue; } float dt = (float)Time.CurrentTime.Increment.TotalSeconds; if (dt == 0) { dt = (float)Time.CurrentTime.Increment.TotalSeconds; } float d = Speed * dt; if (d == 0) { OnVelocityEnforced?.Fire(); continue; } HitPrediction hitPrediction = null; IRectangularF bounds = null; if (HitDetectionDisabled == false) { var obstacles = GetObstacles(); bounds = BoundsTransform != null?BoundsTransform() : Element; hitPrediction = HitDetection.PredictHit(new HitDetectionOptions() { MovingObject = bounds, Obstacles = obstacles, Angle = Angle, Visibility = d, Mode = CastingMode.Precise, }); LastPrediction = hitPrediction; } if (hitPrediction != null && hitPrediction.Type != HitType.None) { var dx = BoundsTransform != null ? bounds.Left - Element.Left : 0; var dy = BoundsTransform != null ? bounds.Top - Element.Top : 0; var proposedBounds = BoundsTransform != null?BoundsTransform() : Element; var distanceToObstacleHit = proposedBounds.CalculateDistanceTo(hitPrediction.ObstacleHit); if (distanceToObstacleHit > .5f) { proposedBounds = proposedBounds.MoveTowards(Angle, distanceToObstacleHit - .5f, false); Element.MoveTo(proposedBounds.Left - dx, proposedBounds.Top - dy); haveMovedSinceLastHitDetection = true; } float angle = bounds.Center().CalculateAngleTo(hitPrediction.ObstacleHit.Center()); if (haveMovedSinceLastHitDetection) { LastImpact = new Impact() { Angle = angle, MovingObject = Element, ObstacleHit = hitPrediction.ObstacleHit, HitType = hitPrediction.Type, }; if (hitPrediction.ObstacleHit is SpacialElement) { Velocity.For(hitPrediction.ObstacleHit as SpacialElement)?.ImpactOccurred.Fire(new Impact() { Angle = angle.GetOppositeAngle(), MovingObject = hitPrediction.ObstacleHit as SpacialElement, ObstacleHit = Element, HitType = hitPrediction.Type, }); } ImpactOccurred?.Fire(LastImpact); GlobalImpactOccurred.Fire(LastImpact); haveMovedSinceLastHitDetection = false; Element.SizeOrPositionChanged.Fire(); } if (Bounce) { var side = Geometry.GetSideGivenEdgeIndex(hitPrediction.EdgeIndex); if (side == Side.Top || side == Side.Bottom) { Angle = 0.AddToAngle(-Angle); } else { Angle = 180.AddToAngle(-Angle); } } else { Stop(); } } else { var newLocation = Element.MoveTowards(Angle, d); Element.MoveTo(newLocation.Left, newLocation.Top); haveMovedSinceLastHitDetection = true; } OnVelocityEnforced?.Fire(); } }