Search code examples
javacollision-detectionintersectionslick2drectangles

Slick2D Rectangle Collision Detection


I'm having an issue showing that one rectangle has collided with another. So my question is, how can I get the intersect method to check for collision? Or are there any other ways to handle collision in this situation?

I'm creating a turn-based combat game (similar to Final Fantasy or Legend of Dragoon) where the player's character is on the right side of the screen and the enemy is on the left side. The player and enemy each take turns attacking. So when the player attacks, the sprite animation moves across the screen from right to left until it stops in front of the enemy, attacks, and returns to it's starting coordinates. Both the player and enemy have a rectangle drawn around them to represent the bounds of each character.

When the player moves forward, he passes through the Enemy's rectangle and stops within it. At this point there should be output to the console saying "INTERSECT!" to show that there was a collision between the two rectangles, but unfortunately there isn't.

Please note that I have omitted the unnecessary pieces of code within my classes and tried to provide the code that pertains to my problem.

This is my entry point, GameClass:

public class GameClass extends BasicGame{
//other variable declarations

public void init(GameContainer container) throws SlickException {
player = new Player();
enemy = new Enemy();
skeleton = new Skeleton();
enemy = skeleton;
playX = player.getStartX(); //700
playY = player.getStartY(); //140
playW = player.rect.getWidth(); //40
playH = player.rect.getHeight(); //70
enemyX = enemy.getStartX(); //100
enemyY = enemy.getStartY(); //140
enemyWidth = enemy.getWidth(); //50
enemyHeight = enemy.getHeight(); //55

SpriteSheet sheet = new SpriteSheet("data/homeranim.png", 36, 65);
    anim = new Animation();
    for (int i=0;i<8;i++) {
        anim.addFrame(sheet.getSprite(i,0), 150);
    }
}

public void render(GameContainer container, Graphics g)
        throws SlickException {
anim.draw(playX,playY); // draws player animation
skeletonAnim.draw(enemyX, enemyY); // draws enemy
g.draw(player.rect); //draws player bounds
g.draw(enemy.rect); //draws enemy bounds
}

public void update(GameContainer container, int delta)
        throws SlickException {
playerUpdate(delta);
if (player.rect.intersects(enemy.rect)) {
             System.out.println("INTERSECT!");

         System.out.println("Player minX: " + player.rect.getMinX());
         System.out.println("Player maxX: " + player.rect.getMaxX());
         System.out.println("Enemy minX: " + enemy.rect.getMinX());
         System.out.println("Enemy maxX: " + enemy.rect.getMaxX());
         }
      }

public void playerUpdate(int delta) {
    if (playerForward == true){
       playX -= delta * 0.4f;
       if (playX <= 140) {
             playX = 140;
             playerForward = false;
             playerBackward = true;}
  }

   if (playerBackward == true) {
         playX += delta * 0.4f;
         if (playX >= 700) {
             playX = 700;
             playerBackward = false;
             delay = 1250;
  }

public void keyReleased(int key, char c) {
       if (key == Input.KEY_ENTER){
          playerForward = true;}
}
}

This is a glimpse at my Player class:

public class Player {

private int startX = 700;
private int startY = 140;
public Shape rect = new Rectangle(startX, startY, 40, 70);
//plus getters and setters
}

And my entire Enemy class:

public class Enemy {

private int startX, startY, width, height;
public Shape rect = new Rectangle(startX, startY, width, height);
// plus getters and setters
   }

My Skeleton class extends Enemy:

public class Skeleton extends Enemy {

public Skeleton() {
    // TODO Auto-generated constructor stub
    setMaxHealth(120);
    setStartX(100);
    setStartY(140);
    setWidth(50);
    setHeight(55);
}

}

Note: Since I've switched g.drawRect() to g.draw(), enemy rectangle isn't being drawn.

Rect bounds at starting point: http://i.imgur.com/QDDk858.png

Rect bound where collision should be: http://i.imgur.com/pOANfvN.png

I hope I've provided enough code to show you what my problem is. I've rummaged through the internet for hours with no luck. If there is any other code I need to provide, please do not hesitate to ask. Thank you very much for your help and support!


Solution

  • You are not updating the hitbox positions themselves.

    You are drawing this:

    g.drawRect(playX, playY, playW, playH); //draws player bounds
    g.drawRect(enemyX, enemyY, enemyW, enemyH); //draws enemy bounds
    

    But this isn't the actual hitbox, it's just the position of the player/enemy and the rectangles drawn here will be on the correct position while the hitboxes themselves aren't.

    I suggest you do the following:

    public void update(GameContainer container, int delta)
    {
        playerUpdate(delta);
    
        player.rect.setLocation(playX, playY);
        enemy.rect.setLocation(enemyX, enemyY); // update the hitboxes to the new positions
    
        if (player.rect.intersects(enemy.rect))
        {
            System.out.println("INTERSECT!");
        }
    }
    
    public void playerUpdate(int delta)
    {
        if (playerForward == true)
        {
           playX -= delta * 0.4f;
    
           if (playX <= 140) 
           {
                 playX = 140;
                 playerForward = false;
                 playerBackward = true;
           }
        }
    
        if (playerBackward == true) 
        {
             playX += delta * 0.4f;
    
             if (playX >= 700) 
             {
                 playX = 700;
                 playerBackward = false;
                 delay = 1250;
             }
        }
    }
    
    public void keyReleased(int key, char c) 
    {
       if (key == Input.KEY_ENTER)
       {
          playerForward = true;
       }
    }
    

    Furthermore, as you seem to be new to game development in Java, some tips for you:

    • Format your code properly

    Always place full {...} after if, else, switch, while, for, etc.; proper line indentation, .

    • Think OO (Object-Oriented)

    This one is pretty important. Your enemy and player class should both extend some kind of entity class because they both will pretty much want to obtain similar behavior (avoid code duplication!). Sum up similar behavior to a super class, simplify the behavior to be controlled with a few adjustable parameters and so on.

    For example, you store the positions of your enemy and player as a static integer in your main class. This is not OO. Move the positions to the entity class where you can implement it in whatever manner you wish.

    • Don't just throw an exception for no reason

    Your update(...) method throws a SlickException even though it's never needed.

    • Be careful about encapsulation

    This is something a lot of beginners do: Just grab some parameters, put them in a class as private (or maybe even public) and generate getters- and setters for them. This is not encapsulation. This is almost as bad as making them public in the first place.

    But why don't we just make everything public?

    We don't want anyone (or even ourselves) to rely on some parameters that just happen to be there because of some very specific implementation of something we might want to change later. Don't just put all possible values out there to be changed, the sense of encapsulation is to be independent from what kind of implementation we end up using and to protect the usability of our code by guarding what can be set/changed.

    • Performance does matter

    This is one of the aspects you should watch out for, for any kind of software, but you can often most drastically see the consequences in games. Performance is important! And by that, I don't mean that you have to watch out for every single detail, but just keep an overview in mind on how to improve and fasten up your code, especially with frequently called methods such as update(..) and render(..) in Slick2D.

    Update

    As a solution to another problem discussed in the comments:

    public class Enemy {
    
        private int startX, startY, width, height;
        public Shape rect = new Rectangle(startX, startY, width, height);
        // plus getters and setters
    }
    

    width and height can only be 0 as they are never assigned an integers have the value 0 per default, so the enemy rectangle hitbox does have 0 width and will never trigger.

    Try something like:

     public class Enemy {
    
        private int startX, startY, width = 50, height = 70;
        public Shape rect = new Rectangle(startX, startY, width, height);
        // plus getters and setters
     }
    

    This should work, but you should probably move all these attributes to the enemy class and put them in the constructor.