1

I've been having some trouble applying correct velocity changes to my ball when it hits bricks in my Breakout clone. In a previous question, I was advised to use continuous collision detection, as well as other methods such as finding the intersection between the ball and the brick when it hits a corner to determine which direction the ball should reflect. I've applied this to my code below, but there are still occasions when the ball will just completely plow through a collection of bricks. This is more noticeable when it hits moving bricks.

In Level.cs Update method:

Bricks.ForEach(brick => Balls.ForEach(ball => ball.Collide(brick)));

In Ball.cs:

public bool Touching(Brick brick)
{
    var position = Position + (Velocity * Speed);
    return position.Y + Size.Y >= brick.Position.Y &&
              position.Y <= brick.Position.Y + brick.Size.Y &&
              position.X + Size.X >= brick.Position.X &&
              position.X <= brick.Position.X + brick.Size.X && brick.Health > 0 && brick.Lifespan == 1F;
}

public void Collide(Brick brick)
{
    if (!Touching(brick)) return;

    var position = Position + (Velocity * Speed);

    var bounds = new Rectangle((int)position.X, (int)position.Y, Texture.Width, Texture.Height);

    var nonCCDBounds = new Rectangle((int)Position.X, (int)Position.Y, Texture.Width, Texture.Height);

    if (bounds.Intersects(brick.Top) || bounds.Intersects(brick.Bottom))
    {
        var change = new Vector2(Velocity.X, -Velocity.Y);
        if (bounds.Intersects(brick.Left) || bounds.Intersects(brick.Right))
        {
            var intersection = Rectangle.Intersect(bounds, brick.Texture.Bounds);
            var nonCCDIntersection = Rectangle.Intersect(nonCCDBounds, brick.Texture.Bounds);
            if (intersection.Width < intersection.Height || nonCCDIntersection.Width < nonCCDIntersection.Height){
                change = new Vector2(-Velocity.X, Velocity.Y);
            }
        }
        if (bounds.Intersects(brick.Top))
        {
            if (level.GetBrick(new Vector2(brick.GridPosition.X, brick.GridPosition.Y - 1)) != null)
                change = new Vector2(-Velocity.X, Velocity.Y);
            else if ((Position - Velocity).Y > brick.Position.Y)
                change = new Vector2(-Velocity.X, Velocity.Y);
        }
        if (bounds.Intersects(brick.Bottom))
        {
            if (level.GetBrick(new Vector2(brick.GridPosition.X, brick.GridPosition.Y + 1)) != null)
                change = new Vector2(-Velocity.X, Velocity.Y);
            else if ((Position - Velocity).Y < brick.Position.Y + brick.Texture.Bounds.Height)
                change = new Vector2(-Velocity.X, Velocity.Y);
        }
        ReflectBall(brick, change);
        return;
    }

    if (bounds.Intersects(brick.Left) || bounds.Intersects(brick.Right))
    {
        var change = new Vector2(-Velocity.X, Velocity.Y);
        if (bounds.Intersects(brick.Top) || bounds.Intersects(brick.Bottom))
        {
            var intersection = Rectangle.Intersect(bounds, brick.Texture.Bounds);
            var nonCCDIntersection = Rectangle.Intersect(nonCCDBounds, brick.Texture.Bounds);
            if (intersection.Width > intersection.Height || nonCCDIntersection.Width > nonCCDIntersection.Height)
            {
                change = new Vector2(Velocity.X, -Velocity.Y);
            }
        }
        if (bounds.Intersects(brick.Left))
        {
            if (level.GetBrick(new Vector2(brick.GridPosition.X - 1, brick.GridPosition.Y)) != null)
                change = new Vector2(Velocity.X, -Velocity.Y);
            else if ((Position - Velocity).X > brick.Position.X)
                change = new Vector2(Velocity.X, -Velocity.Y);
        }
        if (bounds.Intersects(brick.Right))
        {
            if (level.GetBrick(new Vector2(brick.GridPosition.X + 1, brick.GridPosition.Y)) != null)
                change = new Vector2(Velocity.X, -Velocity.Y);
            else if ((Position - Velocity).X < brick.Position.X + brick.Texture.Bounds.Width)
                change = new Vector2(Velocity.X, -Velocity.Y);
        }
        ReflectBall(brick, change);
    }
}

public void ReflectBall(Brick brick, Vector2 reflection)
{
    Position = Position - Velocity;

    Velocity = reflection;

    if (brick.Health < 9)
    {
        brick.Health--;
        brick.Life --;
    }

    if (brick.Health > 0 && brick.Life > 0)
    {
        brick.Texture = Assets.GetBrick(brick.TextureName, brick.Health);
    }
}

It's a bit of a mess but it's the closest I've got to having decent collision. It would be much easier if there was a fast way of finding out collision points and applying correct velocity changes.

Dragonphase
  • 169
  • 2
  • 4
  • 14
  • I think before anyone looks at your current code you might be better off giving your objects a boundingsphare/boundingbox, it will make the collision code a lot simpler and potentially easier to diagnose your problem, if not solve it (Not to mention more efficient!) – Sayse Apr 10 '14 at 18:12
  • @Sayse To be honest, that's the first time I've heard of BoundingBox... So I really have no clue how to use or implement it for a 2D game, and google turns up nothing useful. – Dragonphase Apr 10 '14 at 18:23
  • Closest thing i could find (quickly) was [this](http://www.riemers.net/eng/Tutorials/XNA/Csharp/Series2/Collision_detection.php), BoundingBox and BoundingSphere, are classes within xna that allow for collision detection, just have to make sure you move them when the object moves – Sayse Apr 10 '14 at 18:32
  • 1
    Have you checked the game update frequency? If the ball moves far enough in a single update it is entirely possible to pass through objects (i.e. pos = pos + velocity * deltaTime, if deltaTime is too large objects can move right through each other)... – Mikael Apr 10 '14 at 18:53
  • @Mikael That seems logical, however, `Position += (Velocity*Speed) * deltaTime;` causes the ball to move incredibly slowly – Dragonphase Apr 10 '14 at 19:08

0 Answers0