0
\$\begingroup\$

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

}  

}

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

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

        }
\$\endgroup\$
0
\$\begingroup\$

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.

\$\endgroup\$
1
  • \$\begingroup\$ Hi, that is the correct answer. \$\endgroup\$ Jul 12 '12 at 20:57
0
\$\begingroup\$

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

}

\$\endgroup\$
0

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .