Player: track position with a Vector2.

Fixes #10 (mostly).
This commit is contained in:
Colin McMillen 2020-03-11 16:04:22 -04:00
parent dfb80c9fde
commit 393e9d14b9
3 changed files with 24 additions and 20 deletions

View File

@ -21,8 +21,8 @@ namespace SemiColinGames {
get => Matrix.CreateOrthographicOffCenter(Left, Left + Width, Height, 0, -1, 1); get => Matrix.CreateOrthographicOffCenter(Left, Left + Width, Height, 0, -1, 1);
} }
public void Update(float modelTime, Point player, int worldWidth) { public void Update(float modelTime, Vector2 player, int worldWidth) {
int diff = player.X - bbox.Center.X; float diff = player.X - bbox.Center.X;
if (Math.Abs(diff) > 16) { if (Math.Abs(diff) > 16) {
bbox.Offset((int) (diff * 0.1), 0); bbox.Offset((int) (diff * 0.1), 0);
} }

View File

@ -22,18 +22,20 @@ namespace SemiColinGames {
// Position is tracked at the Player's center. The Player's bounding box is a rectangle // Position is tracked at the Player's center. The Player's bounding box is a rectangle
// centered at that point and extending out by halfSize.X and halfSize.Y. // centered at that point and extending out by halfSize.X and halfSize.Y.
private Point position; private Vector2 position;
private Vector2 halfSize = new Vector2(11, 24); private Vector2 halfSize = new Vector2(11, 24);
// Fractional-pixel movement that was left over from a previous frame's movement.
// Useful so that we can run at a slow time-step and still get non-zero motion.
private Vector2 residual = Vector2.Zero;
private int jumps = 0; private int jumps = 0;
private Pose pose = Pose.Jumping; private Pose pose = Pose.Jumping;
private double swordSwingTime = 0; private double swordSwingTime = 0;
private int swordSwingNum = 0; private int swordSwingNum = 0;
private const int swordSwingMax = 6;
private float ySpeed = 0; private float ySpeed = 0;
private float invincibilityTime = 0; private float invincibilityTime = 0;
public Player(Point position, int facing) { public Player(Vector2 position, int facing) {
this.position = position; this.position = position;
Facing = facing; Facing = facing;
Health = MaxHealth; Health = MaxHealth;
@ -45,20 +47,22 @@ namespace SemiColinGames {
public int Facing { get; private set; } public int Facing { get; private set; }
public Point Position { get { return position; } } public Vector2 Position { get { return position; } }
public void Update(float modelTime, World world, History<Input> input) { public void Update(float modelTime, World world, History<Input> input) {
AABB BoxOffset(Point position, int yOffset) { AABB BoxOffset(Vector2 position, int yOffset) {
return new AABB(new Vector2(position.X, position.Y + yOffset), halfSize); return new AABB(new Vector2(position.X, position.Y + yOffset), halfSize);
} }
AABB Box(Point position) { AABB Box(Vector2 position) {
return BoxOffset(position, 0); return BoxOffset(position, 0);
} }
invincibilityTime -= modelTime; invincibilityTime -= modelTime;
Vector2 movement = HandleInput(modelTime, input); Vector2 inputMovement = HandleInput(modelTime, input);
Vector2 movement = Vector2.Add(residual, inputMovement);
residual = new Vector2(movement.X - (int) movement.X, movement.Y - (int) movement.Y);
// Broad test: remove all collision targets nowhere near the player. // Broad test: remove all collision targets nowhere near the player.
// TODO: don't allocate a list here. // TODO: don't allocate a list here.
@ -69,8 +73,8 @@ namespace SemiColinGames {
// sure (the only downside is a small number of false-positive AABBs, which should be // sure (the only downside is a small number of false-positive AABBs, which should be
// discarded by later tests anyhow.) // discarded by later tests anyhow.)
AABB largeBox = new AABB( AABB largeBox = new AABB(
new Vector2(position.X + movement.X / 2, position.Y + movement.Y / 2), Vector2.Add(position, Vector2.Divide(movement, 2)),
new Vector2(halfSize.X + Math.Abs(movement.X) + 1, halfSize.Y + Math.Abs(movement.Y) + 1)); Vector2.Add(halfSize, new Vector2(Math.Abs(movement.X) + 1, Math.Abs(movement.Y) + 1)));
foreach (var box in world.CollisionTargets) { foreach (var box in world.CollisionTargets) {
if (box.Intersect(largeBox) != null) { if (box.Intersect(largeBox) != null) {
// Debug.AddRect(box, Color.Green); // Debug.AddRect(box, Color.Green);
@ -84,7 +88,7 @@ namespace SemiColinGames {
int dx = movePoints[i].X - movePoints[i - 1].X; int dx = movePoints[i].X - movePoints[i - 1].X;
int dy = movePoints[i].Y - movePoints[i - 1].Y; int dy = movePoints[i].Y - movePoints[i - 1].Y;
if (dy != 0) { if (dy != 0) {
Point newPosition = new Point(position.X, position.Y + dy); Vector2 newPosition = new Vector2(position.X, position.Y + dy);
AABB player = Box(newPosition); AABB player = Box(newPosition);
bool reject = false; bool reject = false;
foreach (var box in candidates) { foreach (var box in candidates) {
@ -102,7 +106,7 @@ namespace SemiColinGames {
} }
} }
if (dx != 0) { if (dx != 0) {
Point newPosition = new Point(position.X + dx, position.Y); Vector2 newPosition = new Vector2(position.X + dx, position.Y);
AABB player = Box(newPosition); AABB player = Box(newPosition);
bool reject = false; bool reject = false;
foreach (var box in candidates) { foreach (var box in candidates) {
@ -149,16 +153,16 @@ namespace SemiColinGames {
invincibilityTime = 0.6f; invincibilityTime = 0.6f;
} }
if (movement.X > 0) { if (inputMovement.X > 0) {
Facing = 1; Facing = 1;
} else if (movement.X < 0) { } else if (inputMovement.X < 0) {
Facing = -1; Facing = -1;
} }
if (swordSwingTime > 0) { if (swordSwingTime > 0) {
pose = Pose.SwordSwing; pose = Pose.SwordSwing;
} else if (jumps == 0) { } else if (jumps == 0) {
pose = Pose.Jumping; pose = Pose.Jumping;
} else if (movement.X != 0) { } else if (inputMovement.X != 0) {
pose = Pose.Walking; pose = Pose.Walking;
} else if (input[0].Motion.Y > 0) { } else if (input[0].Motion.Y > 0) {
pose = Pose.Stretching; pose = Pose.Stretching;
@ -172,7 +176,7 @@ namespace SemiColinGames {
// Returns the desired (dx, dy) for the player to move this frame. // Returns the desired (dx, dy) for the player to move this frame.
Vector2 HandleInput(float modelTime, History<Input> input) { Vector2 HandleInput(float modelTime, History<Input> input) {
Vector2 result = new Vector2() { Vector2 result = new Vector2() {
X = (int) (input[0].Motion.X * moveSpeed * modelTime) X = input[0].Motion.X * moveSpeed * modelTime
}; };
if (input[0].Jump && !input[1].Jump && jumps > 0) { if (input[0].Jump && !input[1].Jump && jumps > 0) {
@ -182,7 +186,7 @@ namespace SemiColinGames {
if (input[0].Attack && !input[1].Attack && swordSwingTime <= 0) { if (input[0].Attack && !input[1].Attack && swordSwingTime <= 0) {
swordSwingTime = 0.3; swordSwingTime = 0.3;
swordSwingNum = (swordSwingNum + 1) % swordSwingMax; swordSwingNum++;
SoundEffects.SwordSwings[swordSwingNum % SoundEffects.SwordSwings.Length].Play(); SoundEffects.SwordSwings[swordSwingNum % SoundEffects.SwordSwings.Length].Play();
} }
@ -219,7 +223,7 @@ namespace SemiColinGames {
if (invincibilityTime > 0 && invincibilityTime % 0.2f > 0.1f) { if (invincibilityTime > 0 && invincibilityTime % 0.2f > 0.1f) {
color = new Color(0.5f, 0.5f, 0.5f, 0.5f); color = new Color(0.5f, 0.5f, 0.5f, 0.5f);
} }
spriteBatch.Draw(Textures.Ninja.Get, position.ToVector2(), textureSource, color, 0f, spriteBatch.Draw(Textures.Ninja.Get, Vector2.Floor(position), textureSource, color, 0f,
spriteCenter, Vector2.One, effect, 0f); spriteCenter, Vector2.One, effect, 0f);
} }
} }

View File

@ -141,7 +141,7 @@ namespace SemiColinGames {
int y = entity.SelectToken("y").Value<int>(); int y = entity.SelectToken("y").Value<int>();
int facing = entity.SelectToken("flippedX").Value<bool>() ? -1 : 1; int facing = entity.SelectToken("flippedX").Value<bool>() ? -1 : 1;
if (name == "player") { if (name == "player") {
player = new Player(new Point(x, y), facing); player = new Player(new Vector2(x, y), facing);
} else if (name == "executioner") { } else if (name == "executioner") {
npcs.Add(new NPC(new Vector2(x, y), facing)); npcs.Add(new NPC(new Vector2(x, y), facing));
} }