Take the 2-minute tour ×
Game Development Stack Exchange is a question and answer site for professional and independent game developers. It's 100% free, no registration required.

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;
        LoadContent();
    }

    public void LoadContent()
    {
        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;
    }

}  

}

share|improve this question
    
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
add comment

3 Answers

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.

share|improve this answer
    
Hi, that is the correct answer. –  Joseph Anderson Jul 12 '12 at 20:57
add comment

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);
            }

        }
share|improve this answer
add comment
up vote 0 down vote accepted

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':
                return LoadExitTile(x, y);

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

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

            //add red gem
            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);

            //Ladder 
            case 'H':
                return LoadTile("Ladder", TileCollision.Ladder);

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

            // Floating platform
            case '-':
                return LoadTile("Platform", TileCollision.Platform);

            case '2':
                return LoadTile("sand_block", TileCollision.Platform);

            case '3':
                return LoadTile("dirt_block", TileCollision.Platform);


            // 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':
                return LoadStartTile(x, y);

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

            case '$':
                return LoadTile("BlockC1", TileCollision.Platform);


            // 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;
        LoadContent();
    }

    public void LoadContent()
    {
        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);
    }
}

}

share|improve this answer
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.