# Code Problem Involving Vertical Movement of Platforms

I am trying to create vertical moving tiles with the Microsoft Platformer Starter Kit. My platforms can move in one direction; however, they fail to switch direction.

If my platform starts at the top, it scrolls down. Once the platform reaches the bottom, however, it fails to move up. I have an issue with

``````if (Level.GetCollision(tileX + (int)direction, tileY) ==
``````

in the code below this sentence. It always returns Impassable. Here is my modified code:

``````using System;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System.Diagnostics;

namespace Platformer
{
//http://robotfootgames.com/xna-tutorials/78-xna-platformer-starter-kit-movable-platforms
public class MovableTile
{
private Texture2D texture;
private Vector2 origin;

public Level Level
{
get { return level; }
}
Level level;

public Vector2 Position
{
get { return position; }
}
Vector2 position;

public Vector2 Velocity
{
get { return velocity; }
}
Vector2 velocity;

/// <summary>
/// Gets whether or not the player's feet are on the MovableTile.
/// </summary>
public bool PlayerIsOn { get; set; }

public Rectangle BoundingRectangle
{
get
{
int left = (int)Math.Round(Position.X - ScaleManager.GetNewNumber(origin.X)) + localBounds.X;
int top = (int)Math.Round(Position.Y - ScaleManager.GetNewNumber(origin.Y)) + localBounds.Y;
return new Rectangle(left, top, localBounds.Width,
localBounds.Height);
}
}

public FaceDirection Direction
{
get { return direction; }
set { direction = value; }
}

FaceDirection direction;

public TileCollision Collision
{
get { return collision; }
set { collision = value; }
}

public float WaitTime {
get {return waitTime;}
set {WaitTime = value;}
}

private TileCollision collision;

private Rectangle localBounds;
private float waitTime;
private const float MaxWaitTime = 2.0f;
private float MoveSpeed = ScaleManager.GetNewNumber(120.0f);

public MovableTile(Level level, Vector2 position, TileCollision collision, FaceDirection faceDirection)
{
this.level = level;
this.position = position;
this.collision = collision;
this.direction = faceDirection;
}

{
texture = collision == TileCollision.Platform ? Level.Content.Load<Texture2D>("Tiles/Platform") : Level.Content.Load<Texture2D>("Tiles/BlockB0");
origin = new Vector2(texture.Width / 2.0f, texture.Height / 2.0f);
localBounds = new Rectangle(0, 0, (int)ScaleManager.GetNewNumber(texture.Width), (int)ScaleManager.GetNewNumber(texture.Height));
}

public void Update(GameTime gameTime)
{
float elapsed = (float)gameTime.ElapsedGameTime.TotalSeconds;

// Calculate tile position based on the side we are moving towards.
float posX = Position.X + localBounds.Width / 2 * (int)direction;
int tileX = (int)Math.Floor(posX / Tile.Width) - (int)direction;
int tileY = (int)Math.Floor(Position.Y / Tile.Height);
if (waitTime > 0)
{
// Wait for some amount of time.
waitTime = Math.Max(0.0f, waitTime - (float)gameTime.ElapsedGameTime.TotalSeconds);
if (waitTime <= 0.0f)
{
// Then turn around.
direction = (FaceDirection)(-(int)GetVerticalDirection());
Debug.WriteLine((int)direction);
}
}
else
{
// If we are about to run into a wall that is not a MovableTile move in other direction.
if (Level.GetCollision(tileX + (int)direction, tileY) == TileCollision.Impassable || Level.GetCollision(tileX + (int)direction, tileY) == TileCollision.Platform)
{
Debug.WriteLine("Collision: " + Level.GetCollision(tileX + (int)direction, tileY));
velocity = new Vector2(0.0f, 0.0f);
waitTime = MaxWaitTime;
}
else
{
// Move in the current direction.
int newDirection = GetVerticalDirection();

if(direction == FaceDirection.Left || direction == FaceDirection.Right)
{
velocity = new Vector2((int)direction * MoveSpeed * elapsed, 0.0f);
}
else
{
velocity = new Vector2(0.0f, (int)newDirection * MoveSpeed * elapsed);
}

//I need to make velocity.x an even number. otherwise, the tile and player move at different speeds.
//this causes the player to fall off or glide on the tile, depending on the device
position = position + new Vector2(Convert.ToInt32(velocity.X), Convert.ToInt32(velocity.Y));
}
}

if (level.movableTiles.Count > 0)
{
// If we are about to run into a MovableTile move in other direction.
foreach (var movableTile in level.movableTiles)
{
if (BoundingRectangle != movableTile.BoundingRectangle)
{
if (BoundingRectangle.Intersects(movableTile.BoundingRectangle))
{
direction = (FaceDirection)(-(int)direction);
velocity = new Vector2((int)direction * MoveSpeed * elapsed, 0.0f);
}
}
}
}
}

public void Draw(GameTime gameTime, SpriteBatch spriteBatch)
{
spriteBatch.Draw(texture, Position, null, Color.White, 0.0f, origin, ScaleManager.GetNewNumber(1f), SpriteEffects.None, 0.0f);
}

public int GetVerticalDirection()
{
int newDirection = 0;
//get direction
if(direction == FaceDirection.Up)
{
newDirection = 1;
}
else if(direction == FaceDirection.Down)
{
newDirection = -1;
}
else
{
newDirection = (int)direction;
}

return newDirection;
}

}
``````

}

-
What does Level.GetCollision do? Please post the source code of this method. Also, what does the Debug.WriteLine's output look like? – Marton Jul 12 '12 at 7:15

It sounds like it's colliding with itself. I have no idea what Level.GetCollision actually does, but I'm assuming that based on its name and call signature that you give it a coordinate and it checks to see if there is anything in the level that exists at that point, and I'm assuming that your tile is a perfectly valid candidate to collide with. The issue might be that you are adding the direction to the x component, not the y component. If you're moving vertically, it would make sense that 'what's coming up' would be a result of adding or subtracting a value from the y component.

``````tileX + (int)direction, tileY
``````

should be

``````tileX, tileY + (int)direction
``````

That should probably work, assuming that your tileX and tileY calculations are working correctly and producing a spot directly on the edge of your tile.

-
Hi, that is the correct answer. – Joseph Anderson Jul 12 '12 at 20:57

I would add this to your MovableTile code. Note, I only did the Y axis but the logic could easily be added for Horizontal movement the same way.

Class fields:

``````//Change to the rate of change the platform should move per Update
private const float MovementRate = 5f;

//wherever the platform should start from
private Vector2 _startPosition = new Vector2(50, 50);

//So we know if the direction should be positive or negative
private bool _isMovingDown = false;

//Where 1234 is the Y location to move to
Vector2 _moveTo = new Vector2(0, 1234);
``````

Update method:

``````        if (_moveTo != Vector2.Zero)
{
//Y-Axis
if (_isMovingDown)
{
if (Position.Y < _moveTo.Y)
{
_isMovingDown = false;
}
else
{
Position -= new Vector2(0f, MovementRate);
}
}
else if (Position.Y > _startPosition.Y)
{
isMovingDown = true;
}
else
{
Position += new Vector2(0f, MovementRate);
}

}
``````
-

Here is GetCollision. It doesn't seem to get the high and low collisions:

``````public TileCollision GetCollision(int x, int y)
{
// Prevent escaping past the level ends.
if (x < 0 || x >= Width)
return TileCollision.Impassable;
// Allow jumping past the level top and falling through the bottom.
if (y < 0 || y >= Height)
return TileCollision.Passable;

return tiles[x, y].Collision;
}
``````

Level Class:

``````    private Tile LoadTile(char tileType, int x, int y)
{
switch (tileType)
{
// Blank space
case '.':
return new Tile(null, TileCollision.Passable);

// Exit
case 'X':

// Gem
case 'G':
return LoadGemTile(x, y, Color.YellowGreen, 30, PowerUpType.None);

case 'S':
return LoadGemTile(x, y, Color.White, 100, PowerUpType.Invincible);

case 'L':
return LoadGemTile(x, y, Color.Red, 1000, PowerUpType.Bonus);

//add a one up
case 'O':
return LoadGemTile(x, y, Color.White, 100, PowerUpType.ExtraLife);

case 'H':

case '^':
return LoadSpikeTile(x, y, TileCollision.Spikes);

// Floating platform
case '-':

case '2':

case '3':

// Various enemies
case 'A':
return LoadEnemyTile(x, y, "MonsterA", 1);
case 'B':
return LoadEnemyTile(x, y, "MonsterB", 2);
case 'C':
return LoadEnemyTile(x, y, "MonsterC", 1);
case 'D':
return LoadEnemyTile(x, y, "MonsterD", 2);

// Platform block
case '~':
return LoadVarietyTile("BlockB", 2, TileCollision.Platform);

// Passable block
case ':':
return LoadVarietyTile("BlockB", 2, TileCollision.Passable);

// Player 1 start point
case '1':

// Impassable block
case '#':
return LoadVarietyTile("BlockA", 7, TileCollision.Impassable);

case '\$':

// Moving platform - Horizontal
case 'M':
return LoadMovableTile(x, y, TileCollision.Platform, FaceDirection.Left, VerticalFaceDirection.None);

case 'U':
return LoadMovableTile(x, y, TileCollision.Impassable, FaceDirection.Left, VerticalFaceDirection.Up);

case 'I':
return LoadMovableTile(x, y, TileCollision.Impassable, FaceDirection.Left, VerticalFaceDirection.Down);

// Unknown tile type character
default:
throw new NotSupportedException(String.Format("Unsupported tile type character '{0}' at position {1}, {2}.", tileType, x, y));
}
}
``````

Here is my altered code:

``````using System;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System.Diagnostics;

namespace GemFinder
{
//http://robotfootgames.com/xna-tutorials/78-xna-platformer-starter-kit-movable-platforms
public enum VerticalFaceDirection
{
None = 0,
Up = -1,
Down = 1
}

public class MovableTile
{

private Texture2D texture;
private Vector2 origin;

public Level Level
{
get { return level; }
}
Level level;

public Vector2 Position
{
get { return position; }
}
Vector2 position;

public Vector2 Velocity
{
get { return velocity; }
}
Vector2 velocity;

/// <summary>
/// Gets whether or not the player's feet are on the MovableTile.
/// </summary>
public bool PlayerIsOn { get; set; }

public Rectangle BoundingRectangle
{
get
{
int left = (int)Math.Round(Position.X - ScaleManager.GetNewNumber(origin.X)) + localBounds.X;
int top = (int)Math.Round(Position.Y - ScaleManager.GetNewNumber(origin.Y)) + localBounds.Y;
return new Rectangle(left, top, localBounds.Width,
localBounds.Height);
}
}

public FaceDirection Direction
{
get { return direction; }
set { direction = value; }
}

FaceDirection direction;

VerticalFaceDirection verticalFaceDirection;
public VerticalFaceDirection VerticalFaceDirection
{
get { return verticalFaceDirection; }
set { verticalFaceDirection = value; }
}

public TileCollision Collision
{
get { return collision; }
set { collision = value; }
}

public float WaitTime
{
get { return waitTime; }
set { WaitTime = value; }
}

private TileCollision collision;

private Rectangle localBounds;
private float waitTime;
private const float MaxWaitTime = 2.0f;
private float MoveSpeed = ScaleManager.GetNewNumber(120.0f);

public MovableTile(Level level, Vector2 position, TileCollision collision, FaceDirection faceDirection, VerticalFaceDirection verticalFaceDirection)
{
this.level = level;
this.position = position;
this.collision = collision;
this.direction = faceDirection;
this.verticalFaceDirection = verticalFaceDirection;
}

{
texture = collision == TileCollision.Platform ? Level.Content.Load<Texture2D>("Tiles/Platform") : Level.Content.Load<Texture2D>("Tiles/BlockB0");
origin = new Vector2(texture.Width / 2.0f, texture.Height / 2.0f);
localBounds = new Rectangle(0, 0, (int)ScaleManager.GetNewNumber(texture.Width), (int)ScaleManager.GetNewNumber(texture.Height));
}

public void Update(GameTime gameTime)
{
float elapsed = (float)gameTime.ElapsedGameTime.TotalSeconds;

// Calculate tile position based on the side we are moving towards.
float posX = Position.X + localBounds.Width / 2 * (int)direction;
float posY = Position.Y + localBounds.Height / 2 * (int)verticalFaceDirection;
int tileX = (int)Math.Floor(posX / Tile.Width) - (int)direction;
int tileY = (int)Math.Floor(posY / Tile.Height) - (int)verticalFaceDirection;

if (waitTime > 0)
{
// Wait for some amount of time.
waitTime = Math.Max(0.0f, waitTime - (float)gameTime.ElapsedGameTime.TotalSeconds);
if (waitTime <= 0.0f)
{
// Then turn around.
direction = (FaceDirection)(-(int)direction);
verticalFaceDirection = (VerticalFaceDirection)(-(int)verticalFaceDirection);
}
}
else
{
// If we are about to run into a wall that is not a MovableTile move in other direction.
//check for a vertical collision

if (verticalFaceDirection != GemFinder.VerticalFaceDirection.None &&

Level.GetCollision(tileX + (int)direction, tileY + (int)verticalFaceDirection) == TileCollision.Impassable || Level.GetCollision(tileX + (int)direction, tileY + (int)verticalFaceDirection) == TileCollision.Platform)
{
velocity = new Vector2(0.0f, 0.0f);
waitTime = MaxWaitTime;
}
//check for top of screen
else if (position.Y <= 0 + Tile.Height / 2 && verticalFaceDirection == GemFinder.VerticalFaceDirection.Up)
{
velocity = new Vector2(0.0f, 0.0f);
waitTime = MaxWaitTime;
}

//check for a horizontal collision
else if (verticalFaceDirection == GemFinder.VerticalFaceDirection.None && Level.GetCollision(tileX + (int)direction, tileY) == TileCollision.Impassable || verticalFaceDirection == GemFinder.VerticalFaceDirection.None && Level.GetCollision(tileX + (int)direction, tileY) == TileCollision.Platform)
{
velocity = new Vector2(0.0f, 0.0f);
waitTime = MaxWaitTime;
}
else
{

if (verticalFaceDirection == GemFinder.VerticalFaceDirection.None)
{
velocity = new Vector2((int)direction * MoveSpeed * elapsed, 0.0f);
}
else
{
velocity = new Vector2(0.0f, (int)verticalFaceDirection * MoveSpeed * elapsed);
}

//I need to make velocity.x an even number. otherwise, the tile and player move at different speeds.
//this causes the player to fall off or glide on the tile, depending on the device
position = position + new Vector2(Convert.ToInt32(velocity.X), Convert.ToInt32(velocity.Y));
}
}

if (level.movableTiles.Count > 0)
{
// If we are about to run into a MovableTile move in other direction.
foreach (var movableTile in level.movableTiles)
{
if (BoundingRectangle != movableTile.BoundingRectangle)
{
if (BoundingRectangle.Intersects(movableTile.BoundingRectangle))
{
direction = (FaceDirection)(-(int)direction);
velocity = new Vector2((int)direction * MoveSpeed * elapsed, 0.0f);
}
}
}
}
}

public void Draw(GameTime gameTime, SpriteBatch spriteBatch)
{
spriteBatch.Draw(texture, Position, null, Color.White, 0.0f, origin, ScaleManager.GetNewNumber(1f), SpriteEffects.None, 0.0f);
}
}
``````

}

-