user1009573
user1009573

Reputation: 113

C# XNA Draw method and collision bugs

I've been trying to make a 2D RPG game after learning C# XNA from a book creating 3 games but these bugs are driving me insane and I'm gonna have to give up as I can not find any fix.

First of all is the Draw method, i have rocks on the screen and i have them drawn at the same depth but drawn in order so it goes along to X axes, then it increments the Y axes and resets X and so on. So they should overlap other rocks above them or to the left but it doesn't work. They randomly go on top of each other when i move around or shoot arrows, here is my code if it helps:

Game1.cs Draw Method

Protected override void Draw(GameTime gameTime)
    {
        GraphicsDevice.Clear(Color.CornflowerBlue);

        spriteBatch.Begin(
            SpriteSortMode.BackToFront,
            BlendState.AlphaBlend);

        TileMap.Draw(spriteBatch);
        ObjectManager.Draw(spriteBatch);
        player.Draw(spriteBatch);
        WeaponManager.Draw(spriteBatch);

        spriteBatch.End();

        base.Draw(gameTime);
    }

ObjectManager Draw Method

static public void Draw(SpriteBatch spriteBatch)
    {
        // Get the tile locations of each corner tile on the screen
        int startX = (((int)Camera.Position.X) / 32) - 2;
        int endX = ((int)Camera.Position.X + Camera.ViewPortWidth) / 32;

        int startY = (((int)Camera.Position.Y) / 32) - 2;
        int endY = ((int)Camera.Position.Y + Camera.ViewPortHeight) / 32;

        // Go through all possible objects and draw them to the screen if they exist
        for (int y = startY; y <= endY; y++)
            for (int x = startX; x <= endX; x++)
            {
                if ((x > 0) && (y > 0) && (x < MapWidth - 1) && (y < MapHeight - 1))
                {
                    if (objects[x, y] != null)
                    {
                        spriteBatch.Draw(
                                texture,
                                Camera.Transform(objects[x, y].destinationRectangle),
                                objects[x, y].Frame,
                                new Color(256, 256, 256, objects[x, y].Transparency),
                                0,
                                new Vector2(0, 0),
                                SpriteEffects.None,
                                objects[x, y].Depth);
                    }
                }
            }
    }

The second problem is with my collision, I am using bounding box collision with the Rectangle class for everything. When my player character moves, I create a new Rectangle for the new location, and if he is colliding with any of the bounding boxes of any object then movement is cancelled but sometimes he will still move to that location and get stuck in the object by 1 pixel. It just doesn't make any sense how it happens sometimes and other times it doesn't, even with the same object. If anyone has any idea why this could be happening please post, there is way too much code to paste on here so I'll leave it, I'm more bothered about the Draw method problem.

Upvotes: 0

Views: 1167

Answers (3)

John McDonald
John McDonald

Reputation: 1799

Instead of using SpriteSortMode.BackToFront, you can use Deferred:

spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.AlphaBlend);

Here's the doc: http://msdn.microsoft.com/en-us/library/microsoft.xna.framework.graphics.spritesortmode.aspx

Upvotes: 0

cwharris
cwharris

Reputation: 18125

Problem 1:

As Blau stated, your rocks will be sorted from front to back, and because they have the same depth, they will (some times) be sorted randomly. This is probably due to float precision (or lack thereof).

Instead of changing the depth of your rocks, you can use SpriteSortMode.Deferred, which will draw the sprites in order, and delay the render until you call End (or until the spriteBatch can't handle any more sprites and needs to flush).

Problem 2:

As Blau stated, this is also probably due to float precision. However, the problem can be avoided. Imagine having a moving tile, and your player walks near that tile. The tile then moves on top of your player (because we forgot to code the tile not to). You're player now cannot move, because any direction the player tries to move is an "invalid" move, because he'll be touching that tile.

The problem is that we're preventing the character from moving. If the character could always move, we wouldn't have this problem. However, if the character could always move, he could just more right through things. To prevent this, we need an opposite action acting against the character when he moves against a solid object.

Solution:

Allow the player to move into an object, and then scan for all of the objects he's touching. If he's touching any, figure out what direction you should move him away from each object, and combine all of those directions to form a single vector. Scale that vector by some reasonable amount (to prevent the character from being pushed away dramatically). Move the player according to that vector

Problems with this method:

If you have a force from gravity, or the like, you may find your character "sinking" into the ground. This is a result of the gravity pushing against the force that pushed the player back from the tiles. The problem can be solved by displacing the character away from any tiles before applying a force on the player to move him away.

This turns out to be a very complex problem, and it is already solved for you.

Box 2D / Farseer Physics Engine:

You've probably heard of these before and thought "Oh, I'll just roll my own, because that sounds super fun!" And while programming your own physics engine can be super fun, it also takes an extremely long time. If you want to get your game up and running quickly with physics (even if they're simple Sonic-like physics), Erin Catto has laid all the ground work for you. :)

Check these out:

Box2D XNA: http://box2dxna.codeplex.com/

Farseer Physics: http://farseerphysics.codeplex.com/

Upvotes: 0

Blau
Blau

Reputation: 5762

You are doing this:

 spriteBatch.Begin(
        SpriteSortMode.BackToFront,
        BlendState.AlphaBlend);

This will be reorder your draw calls based in depth.

So if the objects have the same depth will be ramdomly drawed.

Is better that you modify the depth of the rocks, accordinly you want to be drawn.

float min_depth = 0.7f;
float max_depth = 0.5f;
int num_rocks_total;
int index_rocks;
for (rock_top_left to rock_bottom_right) 
  rock.depth = MathHelper.Lerp(min_depth, max_depth, index_rocks/ (float) num_rocks_total);

the second bug may be due to float precision.

Upvotes: 1

Related Questions