Player now takes damage from spikes.

GitOrigin-RevId: e9096316218f2590aac74ce1055a0829f71bdff8
This commit is contained in:
Colin McMillen 2020-02-27 19:13:34 -05:00
parent a431ad094b
commit f4581ecaf8
5 changed files with 53 additions and 13 deletions

View File

@ -83,10 +83,15 @@ namespace SemiColinGames {
public readonly struct AABB { public readonly struct AABB {
public readonly Vector2 Position; // centroid public readonly Vector2 Position; // centroid
public readonly Vector2 HalfSize; public readonly Vector2 HalfSize;
public readonly Terrain Terrain;
public AABB(Vector2 position, Vector2 halfSize) { public AABB(Vector2 position, Vector2 halfSize) : this(position, halfSize, Terrain.Empty) {
}
public AABB(Vector2 position, Vector2 halfSize, Terrain terrain) {
Position = position; Position = position;
HalfSize = halfSize; HalfSize = halfSize;
Terrain = terrain;
} }
public float Top { public float Top {

View File

@ -34,12 +34,13 @@ namespace SemiColinGames {
private const int swordSwingMax = 6; private const int swordSwingMax = 6;
private float ySpeed = 0; private float ySpeed = 0;
private double jumpTime = 0; private double jumpTime = 0;
private float invincibilityTime = 0;
public Player() { public Player() {
Health = MaxHealth; Health = MaxHealth;
} }
public int MaxHealth { get; private set; } = 5; public int MaxHealth { get; private set; } = 3;
public int Health { get; private set; } public int Health { get; private set; }
@ -56,6 +57,8 @@ namespace SemiColinGames {
return BoxOffset(position, 0); return BoxOffset(position, 0);
} }
invincibilityTime -= modelTime;
Vector2 movement = HandleInput(modelTime, input); Vector2 movement = HandleInput(modelTime, input);
// Broad test: remove all collision targets nowhere near the player. // Broad test: remove all collision targets nowhere near the player.
@ -71,11 +74,12 @@ namespace SemiColinGames {
new Vector2(halfSize.X + Math.Abs(movement.X) + 1, halfSize.Y + Math.Abs(movement.Y) + 1)); new Vector2(halfSize.X + Math.Abs(movement.X) + 1, halfSize.Y + Math.Abs(movement.Y) + 1));
foreach (var box in collisionTargets) { foreach (var box in collisionTargets) {
if (box.Intersect(largeBox) != null) { if (box.Intersect(largeBox) != null) {
Debug.AddRect(box, Color.Green); // Debug.AddRect(box, Color.Green);
candidates.Add(box); candidates.Add(box);
} }
} }
bool harmedByCollision = false;
Point[] movePoints = Line.Rasterize(0, 0, (int) movement.X, (int) movement.Y); Point[] movePoints = Line.Rasterize(0, 0, (int) movement.X, (int) movement.Y);
for (int i = 1; i < movePoints.Length; i++) { for (int i = 1; i < movePoints.Length; i++) {
int dx = movePoints[i].X - movePoints[i - 1].X; int dx = movePoints[i].X - movePoints[i - 1].X;
@ -86,8 +90,12 @@ namespace SemiColinGames {
bool reject = false; bool reject = false;
foreach (var box in candidates) { foreach (var box in candidates) {
if (box.Intersect(player) != null) { if (box.Intersect(player) != null) {
Debug.AddRect(box, Color.Cyan);
reject = true; reject = true;
break; if (box.Terrain.IsHarmful) {
Debug.AddRect(box, Color.Red);
harmedByCollision = true;
}
} }
} }
if (!reject) { if (!reject) {
@ -100,8 +108,12 @@ namespace SemiColinGames {
bool reject = false; bool reject = false;
foreach (var box in candidates) { foreach (var box in candidates) {
if (box.Intersect(player) != null) { if (box.Intersect(player) != null) {
Debug.AddRect(box, Color.Cyan);
reject = true; reject = true;
break; if (box.Terrain.IsHarmful) {
Debug.AddRect(box, Color.Red);
harmedByCollision = true;
}
} }
} }
if (!reject) { if (!reject) {
@ -114,8 +126,12 @@ namespace SemiColinGames {
AABB groundIntersect = BoxOffset(position, 1); AABB groundIntersect = BoxOffset(position, 1);
foreach (var box in candidates) { foreach (var box in candidates) {
if (groundIntersect.Intersect(box) != null) { if (groundIntersect.Intersect(box) != null) {
Debug.AddRect(box, Color.Cyan);
standingOnGround = true; standingOnGround = true;
break; if (box.Terrain.IsHarmful) {
Debug.AddRect(box, Color.Red);
harmedByCollision = true;
}
} }
} }
@ -133,6 +149,11 @@ namespace SemiColinGames {
Debug.AddRect(Box(position), Color.Orange); Debug.AddRect(Box(position), Color.Orange);
} }
if (harmedByCollision && invincibilityTime <= 0) {
Health -= 1;
invincibilityTime = 0.6f;
}
if (movement.X > 0) { if (movement.X > 0) {
Facing = 1; Facing = 1;
} else if (movement.X < 0) { } else if (movement.X < 0) {
@ -242,7 +263,11 @@ namespace SemiColinGames {
Vector2 spriteCenter = new Vector2(spriteWidth / 2, spriteHeight / 2 + spriteCenterYOffset); Vector2 spriteCenter = new Vector2(spriteWidth / 2, spriteHeight / 2 + spriteCenterYOffset);
SpriteEffects effect = Facing == 1 ? SpriteEffects effect = Facing == 1 ?
SpriteEffects.FlipHorizontally : SpriteEffects.None; SpriteEffects.FlipHorizontally : SpriteEffects.None;
spriteBatch.Draw(Textures.Player.Get, position.ToVector2(), textureSource, Color.White, 0f, Color color = Color.White;
if (invincibilityTime > 0 && invincibilityTime % 0.2f > 0.1f) {
color = new Color(0.5f, 0.5f, 0.5f, 0.5f);
}
spriteBatch.Draw(Textures.Player.Get, position.ToVector2(), textureSource, color, 0f,
spriteCenter, Vector2.One, effect, 0f); spriteCenter, Vector2.One, effect, 0f);
} }
} }

View File

@ -24,7 +24,7 @@ namespace SemiColinGames {
// attempt to draw the scene. This is a workaround for the fact that otherwise the first few // attempt to draw the scene. This is a workaround for the fact that otherwise the first few
// frames can be really slow to draw. // frames can be really slow to draw.
int framesToSuppress; int framesToSuppress;
int levelIdx = 0; int levelIdx = -1;
Scene scene; Scene scene;
Player player; Player player;
@ -73,10 +73,10 @@ namespace SemiColinGames {
framesToSuppress = 2; framesToSuppress = 2;
camera = new Camera(); camera = new Camera();
player = new Player(); player = new Player();
levelIdx++;
world = new World(Levels.ALL_LEVELS[levelIdx % Levels.ALL_LEVELS.Length]); world = new World(Levels.ALL_LEVELS[levelIdx % Levels.ALL_LEVELS.Length]);
scene?.Dispose(); scene?.Dispose();
scene = new Scene(GraphicsDevice, camera); scene = new Scene(GraphicsDevice, camera);
levelIdx++;
GC.Collect(); GC.Collect();
GC.WaitForPendingFinalizers(); GC.WaitForPendingFinalizers();
@ -122,6 +122,10 @@ namespace SemiColinGames {
player.Update(modelTime, input, world.CollisionTargets); player.Update(modelTime, input, world.CollisionTargets);
linesOfSight.Update(player, world.CollisionTargets); linesOfSight.Update(player, world.CollisionTargets);
camera.Update(player.Position, world.Width); camera.Update(player.Position, world.Width);
if (player.Health <= 0) {
world = new World(Levels.ALL_LEVELS[levelIdx % Levels.ALL_LEVELS.Length]);
player = new Player();
}
} }
base.Update(gameTime); base.Update(gameTime);

View File

@ -5,7 +5,7 @@ using System.Collections.Generic;
namespace SemiColinGames { namespace SemiColinGames {
class TextureRef { public class TextureRef {
private static readonly List<TextureRef> allTextures = new List<TextureRef>(); private static readonly List<TextureRef> allTextures = new List<TextureRef>();
public static void LoadAll(ContentManager content) { public static void LoadAll(ContentManager content) {
@ -28,7 +28,7 @@ namespace SemiColinGames {
} }
} }
static class Textures { public static class Textures {
public static SpriteFont DebugFont; public static SpriteFont DebugFont;
public static SpriteFont BannerFont; public static SpriteFont BannerFont;

View File

@ -6,7 +6,7 @@ using System.Linq;
namespace SemiColinGames { namespace SemiColinGames {
class Terrain { public class Terrain {
public static Terrain FromSymbol(char symbol) { public static Terrain FromSymbol(char symbol) {
if (mapping.ContainsKey(symbol)) { if (mapping.ContainsKey(symbol)) {
@ -18,6 +18,7 @@ namespace SemiColinGames {
private readonly static Dictionary<char, Terrain> mapping = new Dictionary<char, Terrain>(); private readonly static Dictionary<char, Terrain> mapping = new Dictionary<char, Terrain>();
public static Terrain Empty = new Terrain('\0', false, Textures.Grassland, 0, 0);
public static Terrain Grass = new Terrain('=', true, Textures.Grassland, 3, 0); public static Terrain Grass = new Terrain('=', true, Textures.Grassland, 3, 0);
public static Terrain GrassL = new Terrain('<', true, Textures.Grassland, 2, 0); public static Terrain GrassL = new Terrain('<', true, Textures.Grassland, 2, 0);
public static Terrain GrassR = new Terrain('>', true, Textures.Grassland, 4, 0); public static Terrain GrassR = new Terrain('>', true, Textures.Grassland, 4, 0);
@ -49,6 +50,7 @@ namespace SemiColinGames {
public static Terrain Mushroom = new Terrain('t', false, Textures.Grassland, 17, 2); public static Terrain Mushroom = new Terrain('t', false, Textures.Grassland, 17, 2);
public bool IsObstacle { get; private set; } public bool IsObstacle { get; private set; }
public bool IsHarmful { get; private set; } = false;
public TextureRef Texture { get; private set; } public TextureRef Texture { get; private set; }
public Rectangle TextureSource { get; private set; } public Rectangle TextureSource { get; private set; }
@ -58,6 +60,10 @@ namespace SemiColinGames {
} }
mapping[symbol] = this; mapping[symbol] = this;
IsObstacle = isObstacle; IsObstacle = isObstacle;
// TODO: don't hard-code just the one spike.
if (symbol == '^') {
IsHarmful = true;
}
Texture = texture; Texture = texture;
int size = World.TileSize; int size = World.TileSize;
TextureSource = new Rectangle(x * size, y * size, size, size); TextureSource = new Rectangle(x * size, y * size, size, size);
@ -138,7 +144,7 @@ namespace SemiColinGames {
for (int i = 0; i < tiles.Length; i++) { for (int i = 0; i < tiles.Length; i++) {
Vector2 center = new Vector2( Vector2 center = new Vector2(
tiles[i].Position.Left + halfSize.X, tiles[i].Position.Top + halfSize.Y); tiles[i].Position.Left + halfSize.X, tiles[i].Position.Top + halfSize.Y);
CollisionTargets[i + 1] = new AABB(center, halfSize); CollisionTargets[i + 1] = new AABB(center, halfSize, tiles[i].Terrain);
} }
// Add a final synthetic collisionTarget on the right side of the world. // Add a final synthetic collisionTarget on the right side of the world.