1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
public static class RectangleExtensions
{
	public static float GetHorizontalIntersectionDepth(this Rectangle rectA, Rectangle rectB)
	{
		// Calculate half sizes.
		float halfWidthA = rectA.Width / 2.0f;
		float halfWidthB = rectB.Width / 2.0f;

		// Calculate centers.
		float centerA = rectA.Left + halfWidthA;
		float centerB = rectB.Left + halfWidthB;

		// Calculate current and minimum-non-intersecting distances between centers.
		float distanceX = centerA - centerB;
		float minDistanceX = halfWidthA + halfWidthB;

		// If we are not intersecting at all, return (0, 0).
		if (Math.Abs(distanceX) >= minDistanceX)
			return 0f;

		// Calculate and return intersection depths.
		return distanceX > 0 ? minDistanceX - distanceX : -minDistanceX - distanceX;
	}

	public static float GetVerticalIntersectionDepth(this Rectangle rectA, Rectangle rectB)
	{
		// Calculate half sizes.
		float halfHeightA = rectA.Height / 2.0f;
		float halfHeightB = rectB.Height / 2.0f;

		// Calculate centers.
		float centerA = rectA.Top + halfHeightA;
		float centerB = rectB.Top + halfHeightB;

		// Calculate current and minimum-non-intersecting distances between centers.
		float distanceY = centerA - centerB;
		float minDistanceY = halfHeightA + halfHeightB;

		// If we are not intersecting at all, return (0, 0).
		if (Math.Abs(distanceY) >= minDistanceY)
			return 0f;

		// Calculate and return intersection depths.
		return distanceY > 0 ? minDistanceY - distanceY : -minDistanceY - distanceY;
	}
}

public enum Direction
{
	Horizontal,
	Vertical
}

public void HandleInput(float elapsed)
{
	KeyboardState state = Keyboard.GetState();

	// Handle Keyboard Input
	int horizontal = state.IsKeyDown(Keys.Left) ? -1 : (state.IsKeyDown(Keys.Right) ? 1 : 0);
	int vertical = state.IsKeyDown(Keys.Up) ? -1 : (state.IsKeyDown(Keys.Down) ? 1 : 2);

	// Handle Horizontal Movement
	if (horizontal != 0)
	{
		_playerPosition += Vector2.UnitX * Math.Min(PlayerSpeed * elapsed, Math.Min(_playerSize.X, TileSize)) * horizontal;
		_playerPosition = new Vector2((float)Math.Round(_playerPosition.X), _playerPosition.Y);
		HandleCollisions(Direction.Horizontal);
	}

	// Handle Vertical Movement
	if(vertical != 0)
	{
		_playerPosition += Vector2.UnitY * Math.Min(PlayerSpeed * elapsed, Math.Min(_playerSize.Y, TileSize)) * vertical;
		_playerPosition = new Vector2(_playerPosition.X, (float)Math.Round(_playerPosition.Y));
		HandleCollisions(Direction.Vertical);
	}
}

private void HandleCollisions(Direction direction)
{
	Rectangle playerBounds = GetPlayerBounds();
	int leftTile = playerBounds.Left / TileSize;
	int topTile = playerBounds.Top / TileSize;
	int rightTile = (int)Math.Ceiling((float)playerBounds.Right / TileSize) - 1;
	int bottomTile = (int)Math.Ceiling(((float)playerBounds.Bottom / TileSize)) - 1;

	for (int y = topTile; y <= bottomTile; ++y)
	{
		for (int x = leftTile; x <= rightTile; ++x)
		{
			Vector2 depth;
			if (TileIsObstacle(x, y) && TileIntersectsPlayer(playerBounds, GetTileBounds(y, x), direction, out depth))
			{
				_playerPosition += depth;
				playerBounds = GetPlayerBounds();
			}
		}
	}
}

private static Rectangle GetTileBounds(int y, int x)
{
	return new Rectangle(x * TileSize, y * TileSize, TileSize, TileSize);
}

public Rectangle GetPlayerBounds()
{
	return new Rectangle((int)Math.Round(_playerPosition.X), (int)Math.Round(_playerPosition.Y), (int)Math.Round(_playerSize.X), (int)Math.Round(_playerSize.Y));
}

private static bool TileIntersectsPlayer(Rectangle player, Rectangle block, Direction direction, out Vector2 depth)
{
	depth = direction == Direction.Vertical ?  new Vector2(0, player.GetVerticalIntersectionDepth(block)) :  new Vector2(player.GetHorizontalIntersectionDepth(block), 0);
	return depth.Y != 0 || depth.X != 0;
}

private bool TileIsObstacle(int x, int y)
{
	if (x < 0 || y < 0 || x >= _width || y >= _height) return true;
	return _blocks[x, y] != ' ';
}