Question

I have a tile engine and that's all working swell, my player walks around all good, I'm working on adding items, the player is always in the centre of the screen, until he gets close to the edges of the world then he starts going close to the edges.

When I draw items in the world, they draw fine, except when the player leaves the centre (at the edge of the world). I just can't wrap my head around how to fix this.

public static void Draw(SpriteBatch spriteBatch, World w, Item i, Player p, Point screenDimensions)
    {
        bool IsOnScreen = true;

        float leftX = p.X - ((screenDimensions.X / 32) / 2);
        float rightX = leftX + (screenDimensions.X / 32);

        float topY = p.Y - ((screenDimensions.Y / 32) / 2);
        float bottomY = topY + (screenDimensions.Y / 32);

        if (i.x < leftX || i.x > rightX || i.y < topY || i.y > bottomY)
            IsOnScreen = false;

        if (IsOnScreen)
            i.animation.Draw(spriteBatch, (int)Math.Floor((i.x - leftX) * 32), (int)Math.Floor((i.y - topY) * 32));
    }

Its pretty self explainatory, the world is passed in to get the dimensions (w.worldDimensions.x for width, and .y for height), the item is used to get the i.x and i.y (location in game world, not on screen), the player for drawing it relative (contains .x and .y for location) and then the screenDimensions.

Was it helpful?

Solution

Well it does not look very clear to me. Are you using a camera class? If you use a camera class and use that to navigate your world this should never happen.

Here is a basic one i currently use for my project.

class Camera
{
    float zoom;
    public float Rotation { get; private set; }
    public Vector2 Position { get; private set; }
    Matrix transform;

    int velocity = 60;

    UserInput input;

    public float Zoom
    {
        get { return zoom; }
        set { zoom = value; if (zoom < 0.1f) zoom = 0.1f; } // Negative zoom will flip image
    }

    public Camera(UserInput input)
    {
        zoom = 1.0f;
        Rotation = 0f;
        Position = new Vector2(0, 0);
        this.input = input;
    }

    public void MoveCam()
    {
        if (input.IsKeyHold(Keys.Up))
        {
            Position += new Vector2(0, -velocity);
        }
        if (input.IsKeyHold(Keys.Left))
        {
            Position += new Vector2(-velocity, 0);
        }
        if (input.IsKeyHold(Keys.Down))
        {
            Position += new Vector2(0, velocity);
        }
        if (input.IsKeyHold(Keys.Right))
        {
            Position += new Vector2(velocity, 0);
        }

        if (input.IsKeyHold(Keys.OemMinus))
        {
            Zoom -= .01f * Zoom;
        }
        else if (input.IsKeyHold(Keys.OemPlus))
        {
            Zoom += .01f * Zoom;
        }
    }

    public void FollowCam(int xPos, int yPos)
    {
        Position = new Vector2(xPos * TileData.Width, yPos * TileData.Height);
    }


    public Matrix TransformMatrix(GraphicsDevice device)
    {
        transform = Matrix.CreateTranslation(new Vector3(-Position.X, -Position.Y, 0)) *
            Matrix.CreateRotationX(MathHelper.ToRadians(Rotation)) *
            Matrix.CreateRotationY(MathHelper.ToRadians(Rotation)) *
            Matrix.CreateRotationZ(MathHelper.ToRadians(Rotation)) *
            Matrix.CreateScale(new Vector3(zoom, zoom, 0)) *
            Matrix.CreateTranslation(new Vector3(device.Viewport.Width * 0.5f, device.Viewport.Height * 0.5f, 0));
        return transform;
    }
}

Just instantiate the class like in main and use this in your draw method.

batch.Begin(SpriteSortMode.Immediate, BlendState.AlphaBlend, null, null, null, null, camera.TransformMatrix(graphicsDevice));
batch.End()

Draw everything in your world within this spritebatch and use a new basic to draw to screen cooridinates, like a gui/hud. You can use the camera move method to move it manually and the lock to lock it on any location (it follows if updated).

If you have large maps you might want to render only necessary tiles. I do it like this in my map class:

public void Draw(SpriteBatch batch, Vector2 camPosition, float camZoom, GraphicsDevice device)
    {
        float top = (camPosition.Y / TileData.Height) - ((device.Viewport.Height / 2) / TileData.Height + 1) / camZoom;
        float bottom = (camPosition.Y / TileData.Height) + ((device.Viewport.Height / 2) / TileData.Height + 2) / camZoom;
        float left = (camPosition.X / TileData.Width) - ((device.Viewport.Width / 2) / TileData.Width + 1) / camZoom;
        float right = (camPosition.X / TileData.Width) + ((device.Viewport.Width / 2) / TileData.Width + 2) / camZoom;

        for (int y = (int)top; y < (int)bottom; y++)
        {
            for (int x = (int)left; x < (int)right; x++)
            {
                if (y >= 0 && y < map.GetLength(1) && x >= 0 && x < map.GetLength(0))
                {
                    batch.Draw(map[x, y].texture, new Rectangle(x * TileData.Width, y * TileData.Height, TileData.Width, TileData.Height), Color.White);

                }
            }
        }
    }

Here first i figure out which tiles to draw from each direction. Note the camZoom, you want more tiles to be drawn when zooming out. Then i use these "bounderies" in my for loops, the if statement makes sure i am not accessing tiles that dont exist (out of bounds).

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top