|
|
@ -22,18 +22,20 @@ namespace SemiColinGames { |
|
|
|
|
|
|
|
// 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.
|
|
|
|
private Point position; |
|
|
|
private Vector2 position; |
|
|
|
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 Pose pose = Pose.Jumping; |
|
|
|
private double swordSwingTime = 0; |
|
|
|
private int swordSwingNum = 0; |
|
|
|
private const int swordSwingMax = 6; |
|
|
|
private float ySpeed = 0; |
|
|
|
private float invincibilityTime = 0; |
|
|
|
|
|
|
|
public Player(Point position, int facing) { |
|
|
|
public Player(Vector2 position, int facing) { |
|
|
|
this.position = position; |
|
|
|
Facing = facing; |
|
|
|
Health = MaxHealth; |
|
|
@ -45,20 +47,22 @@ namespace SemiColinGames { |
|
|
|
|
|
|
|
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) { |
|
|
|
AABB BoxOffset(Point position, int yOffset) { |
|
|
|
AABB BoxOffset(Vector2 position, int yOffset) { |
|
|
|
return new AABB(new Vector2(position.X, position.Y + yOffset), halfSize); |
|
|
|
} |
|
|
|
|
|
|
|
AABB Box(Point position) { |
|
|
|
AABB Box(Vector2 position) { |
|
|
|
return BoxOffset(position, 0); |
|
|
|
} |
|
|
|
|
|
|
|
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.
|
|
|
|
// 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
|
|
|
|
// discarded by later tests anyhow.)
|
|
|
|
AABB largeBox = new AABB( |
|
|
|
new Vector2(position.X + movement.X / 2, position.Y + movement.Y / 2), |
|
|
|
new Vector2(halfSize.X + Math.Abs(movement.X) + 1, halfSize.Y + Math.Abs(movement.Y) + 1)); |
|
|
|
Vector2.Add(position, Vector2.Divide(movement, 2)), |
|
|
|
Vector2.Add(halfSize, new Vector2(Math.Abs(movement.X) + 1, Math.Abs(movement.Y) + 1))); |
|
|
|
foreach (var box in world.CollisionTargets) { |
|
|
|
if (box.Intersect(largeBox) != null) { |
|
|
|
// Debug.AddRect(box, Color.Green);
|
|
|
@ -84,7 +88,7 @@ namespace SemiColinGames { |
|
|
|
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); |
|
|
|
Vector2 newPosition = new Vector2(position.X, position.Y + dy); |
|
|
|
AABB player = Box(newPosition); |
|
|
|
bool reject = false; |
|
|
|
foreach (var box in candidates) { |
|
|
@ -102,7 +106,7 @@ namespace SemiColinGames { |
|
|
|
} |
|
|
|
} |
|
|
|
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); |
|
|
|
bool reject = false; |
|
|
|
foreach (var box in candidates) { |
|
|
@ -149,16 +153,16 @@ namespace SemiColinGames { |
|
|
|
invincibilityTime = 0.6f; |
|
|
|
} |
|
|
|
|
|
|
|
if (movement.X > 0) { |
|
|
|
if (inputMovement.X > 0) { |
|
|
|
Facing = 1; |
|
|
|
} else if (movement.X < 0) { |
|
|
|
} else if (inputMovement.X < 0) { |
|
|
|
Facing = -1; |
|
|
|
} |
|
|
|
if (swordSwingTime > 0) { |
|
|
|
pose = Pose.SwordSwing; |
|
|
|
} else if (jumps == 0) { |
|
|
|
pose = Pose.Jumping; |
|
|
|
} else if (movement.X != 0) { |
|
|
|
} else if (inputMovement.X != 0) { |
|
|
|
pose = Pose.Walking; |
|
|
|
} else if (input[0].Motion.Y > 0) { |
|
|
|
pose = Pose.Stretching; |
|
|
@ -172,7 +176,7 @@ namespace SemiColinGames { |
|
|
|
// Returns the desired (dx, dy) for the player to move this frame.
|
|
|
|
Vector2 HandleInput(float modelTime, History<Input> input) { |
|
|
|
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) { |
|
|
@ -182,7 +186,7 @@ namespace SemiColinGames { |
|
|
|
|
|
|
|
if (input[0].Attack && !input[1].Attack && swordSwingTime <= 0) { |
|
|
|
swordSwingTime = 0.3; |
|
|
|
swordSwingNum = (swordSwingNum + 1) % swordSwingMax; |
|
|
|
swordSwingNum++; |
|
|
|
SoundEffects.SwordSwings[swordSwingNum % SoundEffects.SwordSwings.Length].Play(); |
|
|
|
} |
|
|
|
|
|
|
@ -219,7 +223,7 @@ namespace SemiColinGames { |
|
|
|
if (invincibilityTime > 0 && invincibilityTime % 0.2f > 0.1f) { |
|
|
|
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); |
|
|
|
} |
|
|
|
} |
|
|
|