From 5cb3ff9fbdcf950fa129f5a94c906ac532761176 Mon Sep 17 00:00:00 2001 From: Colin McMillen Date: Sun, 2 Feb 2020 08:57:49 -0500 Subject: [PATCH] first pass at implementing line-of-sight algorithm partial solution for #29 GitOrigin-RevId: bb5f5dc057ba5947573f9b9c8eb30cc20f635bb0 --- Shared/Player.cs | 94 ++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 91 insertions(+), 3 deletions(-) diff --git a/Shared/Player.cs b/Shared/Player.cs index 12e156d..1ce9b1d 100644 --- a/Shared/Player.cs +++ b/Shared/Player.cs @@ -31,6 +31,8 @@ namespace SemiColinGames { // centered at that point and extending out by halfSize.X and halfSize.Y. private Point position = new Point(64, 16 * 13); private Vector2 halfSize = new Vector2(11, 24); + private Vector2 eyeOffsetStanding = new Vector2(7, -14); + private Vector2 eyeOffsetWalking = new Vector2(15, -7); private int jumps = 0; private Facing facing = Facing.Right; @@ -70,7 +72,7 @@ namespace SemiColinGames { new Vector2(halfSize.X + Math.Abs(movement.X) + 1, halfSize.Y + Math.Abs(movement.Y) + 1)); foreach (var box in collisionTargets) { if (box.Intersect(largeBox) != null) { - Debug.AddRect(box, Color.Green); + // Debug.AddRect(box, Color.Green); candidates.Add(box); } } @@ -121,10 +123,10 @@ namespace SemiColinGames { if (standingOnGround) { jumps = 1; ySpeed = -0.0001f; - Debug.AddRect(Box(position), Color.Cyan); + // Debug.AddRect(Box(position), Color.Cyan); } else { jumps = 0; - Debug.AddRect(Box(position), Color.Orange); + // Debug.AddRect(Box(position), Color.Orange); } if (movement.X > 0) { @@ -145,6 +147,92 @@ namespace SemiColinGames { } else { pose = Pose.Standing; } + + DrawSightLines(collisionTargets); + } + + Vector2 Rotate(Vector2 point, float angle) { + float cos = FMath.Cos(angle); + float sin = FMath.Sin(angle); + return new Vector2( + point.X * cos - point.Y * sin, + point.Y * cos + point.X * sin); + } + + bool PointInCone( + float visionRangeSq, float fovCos, Vector2 eyePos, Vector2 direction, Vector2 test) { + Vector2 delta = Vector2.Subtract(test, eyePos); + if (delta.LengthSquared() > visionRangeSq) { + return false; + } + float dot = Vector2.Dot(Vector2.Normalize(direction), Vector2.Normalize(delta)); + return dot > fovCos; + } + + void DrawSightLines(AABB[] collisionTargets) { + float fov = FMath.DegToRad(45); + float fovCos = FMath.Cos(fov); + Color color = Color.LightYellow; + + Vector2 eyeOffset = pose == Pose.Walking ? eyeOffsetWalking : eyeOffsetStanding; + Vector2 eyePos = Vector2.Add( + Position.ToVector2(), new Vector2(eyeOffset.X * (int) facing, eyeOffset.Y)); + + float visionRange = 150; + float visionRangeSq = visionRange * visionRange; + Vector2 ray = new Vector2(visionRange * (int) facing, 0); + Vector2 coneBottom = Rotate(ray, fov); + Vector2 coneTop = Rotate(ray, -fov); + + List points = new List(); + List boxes = new List(); + points.Add(Vector2.Add(eyePos, coneBottom)); + points.Add(Vector2.Add(eyePos, coneTop)); + foreach (AABB box in collisionTargets) { + int hitCount = points.Count; + if (PointInCone(visionRangeSq, fovCos, eyePos, ray, box.TopLeft)) { + points.Add(box.TopLeft); + } + if (PointInCone(visionRangeSq, fovCos, eyePos, ray, box.TopRight)) { + points.Add(box.TopRight); + } + if (PointInCone(visionRangeSq, fovCos, eyePos, ray, box.BottomLeft)) { + points.Add(box.BottomLeft); + } + if (PointInCone(visionRangeSq, fovCos, eyePos, ray, box.BottomRight)) { + points.Add(box.BottomRight); + } + if (points.Count > hitCount) { + boxes.Add(box); + } + } + + HashSet boxesSeen = new HashSet(); + foreach (Vector2 hit in points) { + float minTime = 1; + AABB? closestBox = null; + Vector2 delta = Vector2.Subtract(hit, eyePos); + foreach (AABB box in boxes) { + Hit? maybeHit = box.IntersectSegment(eyePos, delta); + if (maybeHit != null && maybeHit.Value.Time < minTime) { + minTime = maybeHit.Value.Time; + closestBox = box; + } + } + if (closestBox != null) { + Vector2 target = Vector2.Add(eyePos, Vector2.Multiply(delta, minTime)); + Debug.AddLine(eyePos, target, color); + boxesSeen.Add(closestBox.Value); + } + } + + foreach (AABB box in boxesSeen) { + Debug.AddRect(box, color); + } + + Debug.AddLine(eyePos, Vector2.Add(eyePos, ray), Color.Red); + Debug.AddLine(eyePos, Vector2.Add(eyePos, coneTop), Color.Red); + Debug.AddLine(eyePos, Vector2.Add(eyePos, coneBottom), Color.Red); } // Returns the desired (dx, dy) for the player to move this frame.