Search code examples
c#xna-4.0

C# : XNA4 : SpriteSortMode on X-axis sorts at random?


I'm working on a tile/sprite-based game, calling my Draws via

public void Draw(SpriteBatch spriteBatch)
    {
        DrawBehind(spriteBatch);
        DrawEven(spriteBatch);
        DrawInFront(spriteBatch);
    }

    private void DrawBehind(SpriteBatch spriteBatch)
    {
        spriteBatch.Begin();
        ActiveMap.DrawAtDepth(spriteBatch, Player, 1);
        spriteBatch.End();
    }

    private void DrawEven(SpriteBatch spriteBatch)
    {
        spriteBatch.Begin(SpriteSortMode.BackToFront, null);
        ActiveMap.DrawAtDepth(spriteBatch, Player, -1);
        Player.Draw(spriteBatch);
        spriteBatch.End();
    }

    private void DrawInFront(SpriteBatch spriteBatch)
    {
        spriteBatch.Begin();
        ActiveMap.DrawAtDepth(spriteBatch, Player, 0);
        spriteBatch.End();
    }

As expected, DrawBehind and DrawInFront just draw based on the order they are called, stable and well on both X and Y.

DrawEven sorts on the Y-axis correctly, but on the X-axis, the sprites just seem to flip in front of eachother and back for no apparent reason.

If it would be stable, I don't really care in which order it sorts it. Instead, everytime I "move my character" (move the position of all the sprites but my character's), it flickers and switches all around. Anyone knows what causes this or how to fix it?

As per request

EDIT:

Map:

public void DrawAtDepth(SpriteBatch spriteBatch, Player player, int depth)
    {
        Area.DrawAtDepth(spriteBatch, player, depth);
    }

Area:

public void DrawAtDepth(SpriteBatch spriteBatch, Player player, int depth)
    {
        const int border = 10;
        var screenTiles = new Rectangle(
            0 - border,
            10 - border,
            (GameMain.ScreenRectangle.Width / Tile.Width - 1) + border,
            (GameMain.ScreenRectangle.Height / Tile.Height - 1) + 10 + border);

        var playerLevelPosition = player.LevelPosition;

        for (int layer = 0; layer < _layerCount; layer++)
            if (ActiveLevels[1, 1].GetLayerDepth(layer) == depth)
                for (int y = screenTiles.Y; y < screenTiles.Height; y++)
                    for (int x = screenTiles.X; x < screenTiles.Width; x++)
                        Draw(spriteBatch, player, playerLevelPosition, layer, x, y);
    }
private void Draw(SpriteBatch spriteBatch, Player player, Vector2 playerLevelPosition, int layer, int x, int y)
    {
        int levelX;
        var tileX = TilePos((int)playerLevelPosition.X, Level.Width, x, out levelX);

        int levelY;
        var tileY = TilePos((int)playerLevelPosition.Y, Level.Height, y, out levelY);

        var level = ActiveLevels[levelX, levelY];

        if (level != null)
            level.Draw(spriteBatch, player, layer, new Vector2(tileX, tileY));
    }

    private float TilePos(int playerPos, int mapBounds, int offset, out int levelPos)
    {
        var tilePos = ((playerPos) - (mapBounds/2f - 1)) + offset;

        levelPos = 1;
        if (tilePos < 0)
        {
            levelPos = 0;
            return tilePos + mapBounds;
        }
        if (tilePos >= mapBounds)
        {
            levelPos = 2;
            return tilePos - mapBounds;
        }
        return tilePos;
    }

    public static float GetDepth(float tilePos)
    {
        return 1 - tilePos / (Level.Height * 3);
    }

Layer:

public void Draw(SpriteBatch spriteBatch, Vector2 tile, Vector2 drawPosition, Color color, bool transparent = false)
    {
        if (_grid[(int)tile.X, (int)tile.Y] == null) return;
        _grid[(int)tile.X, (int)tile.Y].Draw(spriteBatch, drawPosition, color, transparent, (Depth != -1) ? Depth : Area.GetDepth(Level.Height + tile.Y));
    }

Tile:

public override void Draw(SpriteBatch spriteBatch, Vector2 drawPosition, Color color, bool transparent = false, float depth = 0)
    {
        if (!GameMain.ScreenRectangle.Intersects(_sprite.Bounds)) return;

        if (transparent && _fade)
            color = new Color(255, 255, 255, 100);

        spriteBatch.Draw(
            _sprite,
            drawPosition,
            null,
            color,
            0,
            new Vector2(_sprite.Width / 2 - Width / 2, _sprite.Height),
            1,
            SpriteEffects.None,
            depth
            );
    }

Also, any general tips for the sake of it are welcome. I'm a pretty new programmer, styling tips are appreciated.


Solution

  • I'm not completely familiar with the latest XNA 4, so this might not be -the- correct answer, but:

    From the symptoms you describe and the fact that it's only happening when you set the BackToFront SpriteSortMode, I would expect that you're either not setting a depth coordinate for some of the sprites being drawn in DrawEven or not making them different enough. As such, I believe that it is trying to override the normal deferred render order, but doesn't have enough depth information and just picks an arbitrary order each frame (perhaps due to floating point inaccuracy? Not certain, but it seems likely.)

    Since you mention it's sorting properly on the Y axis, then assuming you're using the Y coordinate as an input to the layer depth, try adding a very small amount of the X coordinate as well. Assuming 32 by 32 tiles on screen, I would calculate the layer depth floating point value like this:

    float depth = (someSprite.ScreenYCoord / 32) + (someSprite.ScreenXCoord / 1025);
    spriteBatch.Draw(texture, position, sourceRect, color, rotation, origin, scale, spriteEffects, depth);
    

    where 1025 is 32 * 32 + 1 (to make sure that a difference of 1 tile in Y is always more important when sorting than even a full 32 tiles difference in X) and assuming a zero-based screen coordinate system where something can be on tile 0 to 31 in either axis.

    Also as far as I can tell ActiveMap.DrawAtDepth is not an XNA library function. Can you show us the code for that please, or show us wherever else the actual calls to SpriteBatch.Draw() are - that would help people work out what exactly is happening, and hopefully mean we have to guess less.