From f31f4ae920ff902f4cd4fb64f5e6ccf0d5e58402 Mon Sep 17 00:00:00 2001 From: Charles Date: Mon, 19 Apr 2010 20:05:26 +0000 Subject: [PATCH] Smooth movement between cells. Tested with two computers over the network.. git-svn-id: https://bd85.net/svn/cs3505_group@113 92bb83a3-7c8f-8a45-bc97-515c4e399668 --- CarFire/CarFire/CarFire/CarFire.csproj | 1 + CarFire/CarFire/CarFire/Display.cs | 10 +- CarFire/CarFire/CarFire/Human.cs | 177 ++++--------------- CarFire/CarFire/CarFire/IPlayer.cs | 9 +- CarFire/CarFire/CarFire/MovementManager.cs | 188 +++++++++++++++++++++ CarFire/CarFire/CarFire/ScreenManager.cs | 2 +- 6 files changed, 233 insertions(+), 154 deletions(-) create mode 100644 CarFire/CarFire/CarFire/MovementManager.cs diff --git a/CarFire/CarFire/CarFire/CarFire.csproj b/CarFire/CarFire/CarFire/CarFire.csproj index 2edc12d..6548035 100644 --- a/CarFire/CarFire/CarFire/CarFire.csproj +++ b/CarFire/CarFire/CarFire/CarFire.csproj @@ -84,6 +84,7 @@ + diff --git a/CarFire/CarFire/CarFire/Display.cs b/CarFire/CarFire/CarFire/Display.cs index 5a85fdc..9e06485 100644 --- a/CarFire/CarFire/CarFire/Display.cs +++ b/CarFire/CarFire/CarFire/Display.cs @@ -117,7 +117,7 @@ namespace CarFire if(mCharacters[j] != null) for (int i = 0; i < mProjectiles.Count; i++) { - if (mProjectiles[i].GridX == mCharacters[j].GridX && mProjectiles[i].GridY == mCharacters[j].GridY) + if (mProjectiles[i].GridX == mCharacters[j].Coordinates.X && mProjectiles[i].GridY == mCharacters[j].Coordinates.Y) { mCharacters[j].causeDamageTo(mProjectiles[i].Damage); Console.WriteLine(mCharacters[j].Health); @@ -134,22 +134,20 @@ namespace CarFire { if (state.keysDown[i].Contains(Keys.Enter)) { - mCharacters[i] = (new Human(mMap, "", everything, projectile1, this)); - mCharacters[i].GridX = mMap.GetStartingPositionForPlayer(i + 1).X; - mCharacters[i].GridY = mMap.GetStartingPositionForPlayer(i + 1).Y; + mCharacters[i] = new Human(mMap, "", everything, projectile1, this, mMap.GetStartingPositionForPlayer(i+1)); } } //Regular player input updates else { - mCharacters[i].MovePlayer(state.keysDown[i]); + mCharacters[i].MovePlayer(timespan, state.keysDown[i]); } } if (mCharacters[0] != null) { - mMap.CenterCell = new Vector2(mCharacters[0].PixelX / Map.PixelsToUnitSquares, mCharacters[0].PixelY / Map.PixelsToUnitSquares); + mMap.CenterCell = mCharacters[0].Position; } //Handle wall collisions of projectiles again... for (int i = 0; i < mProjectiles.Count; i++) diff --git a/CarFire/CarFire/CarFire/Human.cs b/CarFire/CarFire/CarFire/Human.cs index 07b4222..168fa06 100644 --- a/CarFire/CarFire/CarFire/Human.cs +++ b/CarFire/CarFire/CarFire/Human.cs @@ -25,23 +25,14 @@ namespace CarFire State state; String CharName; Map theMap; - int gridX; - int gridY; Texture2D charModel; Texture2D projectileModel; int health; int damage; int range; int score; - - //Used to smooth animations - bool isMoving; - float pixelX; - float pixelY; - int movementSteps; - int movementCoolDown; - float changeX; - float changeY; + + MovementManager mMotion; bool visible; Display theDisplay; @@ -51,7 +42,7 @@ namespace CarFire int projectileCoolDown; - public Human(Map _theMap, String Name, Texture2D model, Texture2D projectile, Display mDisplay) + public Human(Map _theMap, String Name, Texture2D model, Texture2D projectile, Display mDisplay, Point position) { theMap = _theMap; CharName = Name; @@ -64,9 +55,9 @@ namespace CarFire charModel = model; projectileModel = projectile; projectileSpeed = 30; - //The number of animation steps between each square movement. - movementSteps = moveCoolDown -2; - + + // Speed is the number of grid cells you can move through per second. + mMotion = new MovementManager(position, 5.0f); } public void LoadContent(ContentManager contentManager) @@ -93,38 +84,18 @@ namespace CarFire /// public long Draw(SpriteBatch spriteBatch) { - //If the sprite is moving there are still movement animations to do. - if (isMoving && movementSteps > 0) - { - movementSteps--; - pixelX = pixelX + changeX; - pixelY = pixelY + changeY; - Rectangle position3 = theMap.GetRectangleFromCoordinates(new Vector2(pixelX / Map.PixelsToUnitSquares, pixelY / Map.PixelsToUnitSquares)); - spriteBatch.Draw(charModel, position3, Color.White); - } - // If the sprite is not moving then just draw it. - else - { - pixelX = gridX * Map.PixelsToUnitSquares; - pixelY = gridY * Map.PixelsToUnitSquares; - changeX = 0; - changeY = 0; - isMoving = false; - movementSteps = moveCoolDown - 2; - spriteBatch.Draw(charModel, theMap.GetRectangleFromCoordinates(gridX, gridY), Color.White); - } + Rectangle position = theMap.GetRectangleFromCoordinates(mMotion.Position); + spriteBatch.Draw(charModel, position, Color.White); return 0; } - public int GridX { get { return gridX; } set { gridX = value; } } - public int GridY { get { return gridY; } set { gridY = value; } } - public float PixelX { get { return pixelX; } } - public float PixelY { get { return pixelY; } } public int Health { get { return health; } } - public bool IsMoving { get { return isMoving; } } public int Score { get { return score; } } public bool alive { get { return health > 0; } } + public Vector2 Position { get { return mMotion.Position; } } + public Point Coordinates { get { return mMotion.Coordinates; } } + public void causeDamageTo(int amount) { health -= amount; @@ -137,104 +108,24 @@ namespace CarFire /// that is being moved to is an open space. /// /// A general list of keys that are pressed. Other keys can be included but only direction keys will be used - public void MovePlayer(List keysPressed) + public void MovePlayer(TimeSpan timeSpan, List keysPressed) { - if(movementCoolDown > 0) - movementCoolDown--; - else if (movementCoolDown == 0) + bool moveLeft = keysPressed.Contains(Keys.Left); + bool moveRight = keysPressed.Contains(Keys.Right); + bool moveUp = keysPressed.Contains(Keys.Up); + bool moveDown = keysPressed.Contains(Keys.Down); + + Point destination = MovementManager.GetNeighborCell(mMotion.Coordinates, moveLeft, moveRight, moveUp, moveDown); + if (theMap.IsCellOpen(destination)) { - // move upleft - keysPressed.Contains(Keys.Left); - if (keysPressed.Contains(Keys.Up) && keysPressed.Contains(Keys.Left) && theMap.IsCellOpen(gridX - 1, gridY - 1)) - { - pixelX = (float)(gridX * Map.PixelsToUnitSquares); - pixelY = (float)(gridY * Map.PixelsToUnitSquares); - gridX -= 1; - gridY -= 1; - movementCoolDown = moveCoolDown; - isMoving = true; - - - } - // move upright - else if (keysPressed.Contains(Keys.Up) && keysPressed.Contains(Keys.Right) && theMap.IsCellOpen(gridX + 1, gridY - 1)) - { - pixelX = (float)(gridX * Map.PixelsToUnitSquares); - pixelY = (float)(gridY * Map.PixelsToUnitSquares); - gridX += 1; - gridY -= 1; - movementCoolDown = moveCoolDown; - isMoving = true; - - - } - // move downleft - else if (keysPressed.Contains(Keys.Down) && keysPressed.Contains(Keys.Left) && theMap.IsCellOpen(gridX - 1, gridY + 1)) - { - pixelX = (float)(gridX * Map.PixelsToUnitSquares); - pixelY = (float)(gridY * Map.PixelsToUnitSquares); - gridX -= 1; - gridY += 1; - movementCoolDown = moveCoolDown; - isMoving = true; - - } - // move downright - else if (keysPressed.Contains(Keys.Down) && keysPressed.Contains(Keys.Right) && theMap.IsCellOpen(gridX + 1, gridY + 1)) - { - pixelX = (float)(gridX * Map.PixelsToUnitSquares); - pixelY = (float)(gridY * Map.PixelsToUnitSquares); - gridX += 1; - gridY += 1; - movementCoolDown = moveCoolDown; - isMoving = true; - - } - // move up - else if (keysPressed.Contains(Keys.Up) && theMap.IsCellOpen(gridX, gridY - 1)) - { - pixelX = (float)(gridX * Map.PixelsToUnitSquares); - pixelY = (float)(gridY * Map.PixelsToUnitSquares); - state = State.up; - gridY -= 1; - movementCoolDown = moveCoolDown; - isMoving = true; - changeY = (float)(-Map.PixelsToUnitSquares / moveCoolDown); - } - // move down - else if (keysPressed.Contains(Keys.Down) && theMap.IsCellOpen(gridX, gridY + 1)) - { - pixelX = (float)(gridX * Map.PixelsToUnitSquares); - pixelY = (float)(gridY * Map.PixelsToUnitSquares); - state = State.down; - gridY += 1; - movementCoolDown = moveCoolDown; - isMoving = true; - changeY = (float)(Map.PixelsToUnitSquares / moveCoolDown); - } - // move left - else if (keysPressed.Contains(Keys.Left) && theMap.IsCellOpen(gridX - 1, gridY)) - { - pixelX = (float)(gridX * Map.PixelsToUnitSquares); - pixelY = (float)(gridY * Map.PixelsToUnitSquares); - state = State.left; - gridX -= 1; - movementCoolDown = moveCoolDown; - isMoving = true; - changeX = (float)(-Map.PixelsToUnitSquares / moveCoolDown); - } - // move right - else if (keysPressed.Contains(Keys.Right) && theMap.IsCellOpen(gridX + 1, gridY)) - { - pixelX = (float)(gridX * Map.PixelsToUnitSquares); - pixelY = (float)(gridY * Map.PixelsToUnitSquares); - state = State.right; - gridX += 1; - movementCoolDown = moveCoolDown; - isMoving = true; - changeX = (float)(Map.PixelsToUnitSquares / moveCoolDown); - } + mMotion.Update(timeSpan, moveLeft, moveRight, moveUp, moveDown); } + else + { + mMotion.Update(timeSpan); + } + + if (projectileCoolDown > 0) projectileCoolDown--; else if (projectileCoolDown == 0) @@ -245,22 +136,26 @@ namespace CarFire if (state == State.up) { projectileCoolDown = shootCoolDown; - theDisplay.AddProjectiles(new Projectile(theMap, projectileModel, new Vector2(0, -projectileSpeed), GridX, GridY - 1)); + theDisplay.AddProjectiles(new Projectile(theMap, projectileModel, + new Vector2(0, -projectileSpeed), Coordinates.X, Coordinates.Y - 1)); } if (state == State.down) { projectileCoolDown = shootCoolDown; - theDisplay.AddProjectiles(new Projectile(theMap, projectileModel, new Vector2(0, projectileSpeed), GridX, GridY + 1)); + theDisplay.AddProjectiles(new Projectile(theMap, projectileModel, + new Vector2(0, projectileSpeed), Coordinates.X, Coordinates.Y + 1)); } if (state == State.right) { projectileCoolDown = shootCoolDown; - theDisplay.AddProjectiles(new Projectile(theMap, projectileModel, new Vector2(projectileSpeed, 0), GridX + 1, GridY)); + theDisplay.AddProjectiles(new Projectile(theMap, projectileModel, + new Vector2(projectileSpeed, 0), Coordinates.X + 1, Coordinates.Y)); } if (state == State.left) { projectileCoolDown = shootCoolDown; - theDisplay.AddProjectiles(new Projectile(theMap, projectileModel, new Vector2(-projectileSpeed, 0), GridX - 1, GridY)); + theDisplay.AddProjectiles(new Projectile(theMap, projectileModel, + new Vector2(-projectileSpeed, 0), Coordinates.X - 1, Coordinates.Y)); } } } @@ -274,8 +169,8 @@ namespace CarFire public void Spawn(Vector2 spawn) { - gridX = (int)spawn.X; - gridY = (int)spawn.Y; + //gridX = (int)spawn.X; + //gridY = (int)spawn.Y; visible = true; } diff --git a/CarFire/CarFire/CarFire/IPlayer.cs b/CarFire/CarFire/CarFire/IPlayer.cs index b60ef1a..a65ced5 100644 --- a/CarFire/CarFire/CarFire/IPlayer.cs +++ b/CarFire/CarFire/CarFire/IPlayer.cs @@ -17,16 +17,13 @@ namespace CarFire long Draw(SpriteBatch spriteBatch); int Health { get; } void causeDamageTo(int amount); - int GridX { get; set; } - int GridY { get; set; } - float PixelX { get; } - float PixelY { get; } - bool IsMoving { get; } + Vector2 Position { get; } + Point Coordinates { get; } } public interface IPlayer : ICharacter { - void MovePlayer(List keysPressed); + void MovePlayer(TimeSpan timeSpan, List keysPressed); int Score { get; } void powerUp(int amount); void Spawn(Vector2 spawn); diff --git a/CarFire/CarFire/CarFire/MovementManager.cs b/CarFire/CarFire/CarFire/MovementManager.cs new file mode 100644 index 0000000..372be6c --- /dev/null +++ b/CarFire/CarFire/CarFire/MovementManager.cs @@ -0,0 +1,188 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Microsoft.Xna.Framework; + +namespace CarFire +{ + /// + /// A class to manage the motion of objects on a grid of cells. + /// Each update you can pass a direction and the manager will move + /// the position to that point while also enforcing a speed limit. + /// This class does not detect collisions, so care must be taken + /// to only pass directions to a walkable cell during an update. + /// + public class MovementManager + { + #region Public Properties + + /// + /// Get the current position in map coordinates. This is the + /// smooth, interpolated set of coordinates. + /// + public Vector2 Position { get { return mPosition; } } + + /// + /// Get the grid coordinates where the object is at or + /// is moving to. + /// + public Point Coordinates { get { return mCoordinates; } } + + /// + /// Get and set the speed of movement in grid cells / second. + /// + public float Speed; + + #endregion + + + #region Public Methods + + /// + /// Construct a movement manager with the initial position of + /// the thing you want to track. + /// + /// Grid coordinates. + public MovementManager(Point position) + { + mPosition = new Vector2((float)position.X, (float)position.Y); + mCoordinates = position; + mLastCoordinates = position; + Speed = 1.0f; + } + + /// + /// Construct a movement manager with the initial position of + /// the thing you want to track and its speed. + /// + /// Grid coordinates. + /// Speed: Grid cells per second. + public MovementManager(Point position, float speed) + { + mPosition = new Vector2((float)position.X, (float)position.Y); + mCoordinates = position; + mLastCoordinates = position; + Speed = speed; + } + + + /// + /// Update the movement manager with the timeslice and no directions. + /// + /// The timeslice. + public void Update(TimeSpan timeSpan) + { + Update(timeSpan, false, false, false, false); + } + + /// + /// Update the movement manager with the timeslice and the directions + /// the object is supposed to go. The directions will be ignored if the + /// object is currently in transit from one cell to another. + /// + /// The timeslice. + /// Want to move left. + /// Want to move right. + /// Want to move up. + /// Want to move down. + public void Update(TimeSpan timeSpan, bool moveLeft, bool moveRight, bool moveUp, bool moveDown) + { + float passedTime = (float)timeSpan.TotalSeconds; + + bool requestMove = (moveLeft ^ moveRight) || (moveUp ^ moveDown); + if (!mIsMoving && requestMove) + { + UpdateCoordinates(moveLeft, moveRight, moveUp, moveDown); + + mIsMoving = true; + mTimeAccumulator = passedTime; + + RecalculatePosition(mTimeAccumulator / mInverseSpeed); + } + else if (mIsMoving) + { + mTimeAccumulator += passedTime; + + float alpha = mTimeAccumulator / mInverseSpeed; + if (alpha >= 1.0f) + { + if (requestMove) + { + mTimeAccumulator = mTimeAccumulator - mInverseSpeed; + alpha = mTimeAccumulator / mInverseSpeed; + + UpdateCoordinates(moveLeft, moveRight, moveUp, moveDown); + } + else + { + mIsMoving = false; + alpha = 1.0f; + } + } + + RecalculatePosition(alpha); + } + } + + + /// + /// Helper method to get neighbor cells from a point and directions. + /// + /// The point. + /// To the left. + /// To the right. + /// Above. + /// Below. + /// The neighbor cell coordinates. + public static Point GetNeighborCell(Point point, bool left, bool right, bool up, bool down) + { + if (left) point.X--; + if (right) point.X++; + if (up) point.Y--; + if (down) point.Y++; + return point; + } + + #endregion + + + #region Private Methods + + void RecalculatePosition(float alpha) + { + Console.WriteLine("last: " + mLastCoordinates + ", now: " + mCoordinates + ", alpha: " + alpha); + mPosition.X = (float)mLastCoordinates.X + alpha * ((float)mCoordinates.X - (float)mLastCoordinates.X); + mPosition.Y = (float)mLastCoordinates.Y + alpha * ((float)mCoordinates.Y - (float)mLastCoordinates.Y); + } + + void UpdateCoordinates(bool moveLeft, bool moveRight, bool moveUp, bool moveDown) + { + mLastCoordinates = mCoordinates; + mCoordinates = GetNeighborCell(mCoordinates, moveLeft, moveRight, moveUp, moveDown); + + if ((moveLeft && moveUp) || (moveUp && moveRight) || (moveRight && moveDown) || (moveDown && moveLeft)) + { + mInverseSpeed = 1.4f / Speed; + } + else + { + mInverseSpeed = 1.0f / Speed; + } + } + + #endregion + + + #region Private Variables + + Vector2 mPosition; // Position on the viewable map. + Point mCoordinates; // Position on the grid. + Point mLastCoordinates; // Last position on the grid. + float mInverseSpeed; // The time it takes to move from one cell to another. + float mTimeAccumulator; // Amount of time passed since last move. + bool mIsMoving; // Whether or not it is currently in the process of moving. + + #endregion + } +} diff --git a/CarFire/CarFire/CarFire/ScreenManager.cs b/CarFire/CarFire/CarFire/ScreenManager.cs index e4dc0f4..b7a30b5 100644 --- a/CarFire/CarFire/CarFire/ScreenManager.cs +++ b/CarFire/CarFire/CarFire/ScreenManager.cs @@ -365,7 +365,7 @@ namespace CarFire } //allows host to start the game when all players are ready, change count below to different number for testing with less then 4 players - if (allReady && players.Count <= 1 && localPlayer == players[0]) + if (allReady && players.Count <= 4 && localPlayer == players[0]) { if (currentKeyboardState.IsKeyDown(Keys.B) && previousKeyboardState.IsKeyUp(Keys.B)) { -- 2.43.0