0
votes

I'm trying to get the velocity of my character to be zero when it collides with another sprite. It does this, however the distance between the textures increases when it collides if I choose a higher value for speed, the higher the value the further the distance between the textures when collision occurs.

At a speed of 1f the collision seems to work fine (both textures touching) but 1f is too slow for my game and I can't figure out how to increase it without increasing the distance of collision.

I believe this is because I use: rectangle.Right + Velocity.X in my Collision region but I'm not sure how to alter this to fix my problem. Help would be appreciated.

//In Sprite class
    #region Collision
    protected bool IsTouchingLeft(Sprite sprite)
    {
        return rectangle.Right + Velocity.X > sprite.rectangle.Left &&
               rectangle.Left < sprite.rectangle.Left &&
               rectangle.Bottom > sprite.rectangle.Top &&
               rectangle.Top < sprite.rectangle.Bottom;
    }

    protected bool IsTouchingRight(Sprite sprite)
    {
        return rectangle.Left + Velocity.X < sprite.rectangle.Right &&
               rectangle.Right > sprite.rectangle.Right &&
               rectangle.Bottom > sprite.rectangle.Top &&
               rectangle.Top < sprite.rectangle.Bottom;
    }

    protected bool IsTouchingTop(Sprite sprite)
    {
        return rectangle.Bottom + Velocity.Y > sprite.rectangle.Top &
               rectangle.Top < sprite.rectangle.Top &&
               rectangle.Right > sprite.rectangle.Left &&
               rectangle.Left < sprite.rectangle.Right;
    }

    protected bool IsTouchingBottom(Sprite sprite)
    {
        return rectangle.Top + Velocity.Y < sprite.rectangle.Bottom &
               rectangle.Bottom > sprite.rectangle.Bottom &&
               rectangle.Right > sprite.rectangle.Left &&
               rectangle.Left < sprite.rectangle.Right;
    }
    #endregion
    //In MainChar class
            foreach (var sprite in sprites)
            {
                if (sprite == this)
                {
                    continue;
                }

                if (Velocity.X > 0 && IsTouchingLeft(sprite) || Velocity.X < 0 && IsTouchingRight(sprite)) 
                {
                    Velocity.X = 0;
                }

                if (Velocity.Y > 0 && IsTouchingTop(sprite) || Velocity.Y < 0 && IsTouchingBottom(sprite))
                {
                    Velocity.Y = 0;
                }

                if (sprite.rectangle.Intersects(rectangle))
                {
                    hasDied = true;
                }
            }


1
I think that's because your IsTouching* methods really should be named WillBeTouching* because you're adding velocity to current position. Two things come to mind. Reduce velocity to have them collide perfectly, or set position to where the collision would happen. - Zer0
When you detect a collision because one sprite overlaps another, set the speed of the moving sprite to 0, but then also set its position so that it's resting on/against/touching (whatever side) of the other sprite? - Caius Jard
If I reduce velocity my character will move too slow but, correct me if I’m wrong, I don’t think I can set position to where they collide because I am colliding with multiple sprites in different locations. - callum_y

1 Answers

0
votes

Handling multiple collisions correctly will require sub-stepping, repeating the collision checks until there are no more collisions or a counter expires. This is how physics engines operate.

I am going to show a different method. It requires not doing the move that caused the collision and adjusting the velocity by half each step. Slow down until you get there a maximum of log2(Velocity) steps(for velocities < 32, 5 steps):

foreach (var sprite in sprites)
{
   if (sprite == this)
   {
       continue;
   }
   // it is good practice to use () for mixed && and || 
   if ((Velocity.X > 0 && IsTouchingLeft(sprite)) || (Velocity.X < 0 && IsTouchingRight(sprite))) 
   {
      Velocity.X *= 0.5f;
      // by halving, the velocity will exponentially decay to 0 
   }

   if ((Velocity.Y > 0 && IsTouchingTop(sprite)) || (Velocity.Y < 0 && IsTouchingBottom(sprite)))
   {
      Velocity.Y *= 0.5f;
   }

   if (sprite.rectangle.Intersects(rectangle))
   {
      hasDied = true;
   }
   // use new velocity to update the position.
}

You may have to fix the death condition, it is unclear when it should be triggered. The code as written will move up to the other object, but never overlap on its own.

The following code is what I believe you intended(move at a speed of 1 until overlap)

foreach (var sprite in sprites)
{
   if (sprite == this)
   {
       continue;
   }
   // it is good practice to use () for mixed && and || 
   if ((Velocity.X > 0 && IsTouchingLeft(sprite)) || (Velocity.X < 0 && IsTouchingRight(sprite))) 
   {
   if (Velocity.X >= 2) // cap it at a minimum above 1
      {
         Velocity.X *= 0.5f;
         // by halving, the velocity will exponentially decay to 0 
      }
      if (sprite.rectangle.Intersects(rectangle))
      {
         Velocity.X = 0; // probably need to do this for Y here as well.
         hasDied = true;
      }

   }

   if ((Velocity.Y > 0 && IsTouchingTop(sprite)) || (Velocity.Y < 0 && IsTouchingBottom(sprite)))
   {
      if (Velocity.Y >= 2) // cap it at a minimum above 1
      {
         Velocity.Y *= 0.5f;
         // by halving, the velocity will exponentially decay to 0 
      }
      if (sprite.rectangle.Intersects(rectangle))
      {
         Velocity.Y = 0; // probably need to do this for X here as well.
         hasDied = true;
      }
   }      
}