From 946497160bd0d0744f328317557c27f260d44096 Mon Sep 17 00:00:00 2001 From: Colin McMillen Date: Wed, 29 Jan 2020 15:43:00 -0500 Subject: [PATCH] Player.Update() now uses Bresenham's line algorithm. This works, but is still a bit hacky. Cleanups to follow soon. GitOrigin-RevId: 597a857a200584fee2c06237d7d7dd10403bdfeb --- Shared/Player.cs | 107 +++++++++++++++++++++++++++++------------------ 1 file changed, 67 insertions(+), 40 deletions(-) diff --git a/Shared/Player.cs b/Shared/Player.cs index c3f702f..5124ffe 100644 --- a/Shared/Player.cs +++ b/Shared/Player.cs @@ -1,5 +1,6 @@ using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; +using System; using System.Collections.Generic; namespace SemiColinGames { @@ -17,10 +18,12 @@ namespace SemiColinGames { private const int gravity = 2400; private const int spriteSize = 48; + // TODO: rename to spriteHalfWidth / spriteHalfHeight. private const int spriteWidth = 7; + private const int spriteHeight = 13; private readonly Texture2D texture; - private Point position = new Point(64, 16 * 14); + private Point position = new Point(64, 16 * 10); private int jumps = 0; private Facing facing = Facing.Right; private Pose pose = Pose.Jumping; @@ -43,65 +46,89 @@ namespace SemiColinGames { } private Aabb Box(Point position, int yOffset) { - return new Aabb(new Vector2(position.X, position.Y - 7 + 13 + yOffset), - new Vector2(spriteWidth, 13)); + return new Aabb(new Vector2(position.X, position.Y - 7 + spriteHeight + yOffset), + new Vector2(spriteWidth, spriteHeight)); } public void Update(float modelTime, History input, Rectangle[] collisionTargets) { - Point oldPosition = position; Vector2 movement = HandleInput(modelTime, input); - position = new Point((int) (oldPosition.X + movement.X), (int) (oldPosition.Y + movement.Y)); - - Rectangle oldBbox = Bbox(oldPosition); - Rectangle playerBbox = Bbox(position); - bool standingOnGround = false; // TODO: we shouldn't hardcode the tile sizes here. Vector2 halfBoxSize = new Vector2(World.TileSize / 2, World.TileSize / 2); - foreach (var rect in collisionTargets) { - Aabb rectBox = new Aabb( + // Broad test: remove all collision targets nowhere near the player. + List candidates = new List(); + // TODO: This is strictly larger than it needs to be. We could expand only in the actual + // direction of movement. + Aabb largeBox = new Aabb( + new Vector2(position.X, position.Y - 7 + spriteHeight), // current player position + new Vector2(spriteWidth + Math.Abs(movement.X), spriteHeight + Math.Abs(movement.Y))); + for (int i = 0; i < collisionTargets.Length; i++) { + Rectangle rect = collisionTargets[i]; + Aabb box = new Aabb( new Vector2(rect.X + World.TileSize / 2, rect.Y + World.TileSize / 2), halfBoxSize); - Aabb playerBox = Box(position); - playerBbox = Bbox(position); + if (box.Intersect(largeBox) != null) { + Debug.AddRect(box, Color.Green); + candidates.Add(rect); + } + } - // first we check for left-right collisions... - if (playerBox.Intersect(rectBox) != null) { - if (oldBbox.Right <= rect.Left && playerBbox.Right > rect.Left) { - position.X = rect.Left - spriteWidth; + Point[] movePoints = Line.Rasterize(0, 0, (int) movement.X, (int) movement.Y); + for (int i = 1; i < movePoints.Length; i++) { + int dx = movePoints[i].X - movePoints[i - 1].X; + int dy = movePoints[i].Y - movePoints[i - 1].Y; + if (dy != 0) { + Point newPosition = new Point(position.X, position.Y + dy); + Aabb player = Box(newPosition); + bool reject = false; + foreach (var rect in candidates) { + Aabb box = new Aabb( + new Vector2(rect.X + World.TileSize / 2, rect.Y + World.TileSize / 2), halfBoxSize); + if (box.Intersect(player) != null) { + reject = true; + break; + } } - if (oldBbox.Left >= rect.Right && playerBbox.Left < rect.Right) { - position.X = rect.Right + spriteWidth; + if (!reject) { + position = newPosition; } - playerBox = Box(position); } - // after fixing that, we check for hitting our head or hitting the ground. - if (playerBox.Intersect(rectBox) != null) { - if (oldPosition.Y > position.Y) { - int diff = playerBbox.Top - rect.Bottom; - position.Y -= diff; - // TODO: set ySpeed = 0 here so that bonking our head actually reduces hangtime? - } else { - standingOnGround = true; - int diff = playerBbox.Bottom - rect.Top; - position.Y -= diff; + if (dx != 0) { + Point newPosition = new Point(position.X + dx, position.Y); + Aabb player = Box(newPosition); + bool reject = false; + foreach (var rect in candidates) { + Aabb box = new Aabb( + new Vector2(rect.X + World.TileSize / 2, rect.Y + World.TileSize / 2), halfBoxSize); + if (box.Intersect(player) != null) { + reject = true; + break; + } } - } else { - playerBox = Box(position, 1); - if (playerBox.Intersect(rectBox) != null) { - standingOnGround = true; - Debug.AddRect(rect, Color.Cyan); - } else { - Debug.AddRect(rect, Color.Green); + if (!reject) { + position = newPosition; } } } + + bool standingOnGround = false; + Aabb groundIntersect = Box(position, 1); + foreach (var rect in candidates) { + Aabb box = new Aabb( + new Vector2(rect.X + World.TileSize / 2, rect.Y + World.TileSize / 2), halfBoxSize); + if (groundIntersect.Intersect(box) != null) { + standingOnGround = true; + Debug.AddRect(rect, Color.Cyan); + // break; + } + } + if (standingOnGround) { jumps = 1; - ySpeed = 0; - Debug.AddRect(playerBbox, Color.Red); + ySpeed = -0.0001f; + // Debug.AddRect(playerBbox, Color.Red); } else { jumps = 0; - Debug.AddRect(playerBbox, Color.Orange); + // Debug.AddRect(playerBbox, Color.Orange); } if (movement.X > 0) {