Search code examples
javagame-physics

How do I implement a collision response between two non-rotatable rectangles


I need help making a top-down shooter game, I don't know how to stop my player from going right through the block, and down below is my Player class and my keyInput class. I'm new to game programming, I have searched a lot of different solutions but don't know how to implement them... (Sorry if I'm not being very detailed, I just don't really know how to properly explain my code) * The Image is what happens when you run the code, I can move the player

package main;

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Rectangle;

public class Player extends GameObject{

    Handler handler;


    private int timer = 20;
    private boolean canFire = false;
    public static int direction = 1;
    public Player(int x, int y, ID id , Handler handler) {
        super(x, y, id);
        this.handler = handler;



    }
    public Rectangle getBounds() {
        return new Rectangle (x, y, 16, 16);

    }
    public void tick() {
        x += velX;
        y += velY;


        x = Game.clamp(x, 0, Game.WIDTH - 31);
        y = Game.clamp(y, 1, Game.HEIGHT - 54);
        if(timer >= 0) timer --;





        if(KeyInput.facing[0] == "Up") {

            direction = 4;
        } else if(KeyInput.facing[1] == "Down") {

            direction = 3;
        }else if(KeyInput.facing[2] == "Left") {

            direction = 2;
        }else if(KeyInput.facing[3] == "Right") {

            direction = 1;
        }


        if(KeyInput.Fire == true) {
            if(timer <=0)
            {

                handler.addObject(new Bullet(x + 8, y + 8,ID.Bullet, handler, direction));
            timer = 20;
            }

        }


        collision();
    }

    private void collision() {
        for (int i = 0; i < handler.object.size(); i++) {
        GameObject tempObject = handler.object.get(i);

        if(tempObject.getId() == ID.Block)
        {
            if(getBounds().intersects(tempObject.getBounds()))
            {
                //collision events

            }
        }
    }

} 
    public void render(Graphics g) {

        g.setColor(Color.white);
        g.fillRect((int)x,(int)y,24,24);


}

}
package main;

import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;

public class KeyInput  extends KeyAdapter{

    private Handler handler;
        private boolean[] keyDown = new boolean[4]; 
        private double Multi = 1;
        public static String[] facing = new String[4];
        public static boolean Fire = false;


    public KeyInput(Handler handler) {
        this.handler = handler;
        keyDown[0] = false;
        keyDown[1] = false;
        keyDown[2] = false;
        keyDown[3] = false;
        facing[0] = "";
        facing[1] = "";
        facing[2] = "";
        facing[3] = "";
        }   
    public void keyPressed(KeyEvent e) {
        int key = e.getKeyCode();

            GameObject tempObject = handler.object.get(0);

        if (key == KeyEvent.VK_W) {tempObject.setVelY((int)(-3* Multi)); keyDown[0] = true;facing[0] = "Up";}

        if (key == KeyEvent.VK_S) {tempObject.setVelY((int)(3* Multi)); keyDown[1] = true;facing[1] = "Down";}

        if (key == KeyEvent.VK_A) {tempObject.setVelX((int) (-3* Multi)); keyDown[2] = true;facing[2] = "Left";}

        if (key == KeyEvent.VK_D) {tempObject.setVelX((int)(3 * Multi)); keyDown[3] = true;facing[3] = "Right";}

        if (key == KeyEvent.VK_SHIFT) {Multi = 2.2;}

        if (key == KeyEvent.VK_SPACE) {Fire = true;}

        if(key == KeyEvent.VK_ESCAPE) System.exit(0); 
        }

    public void keyReleased(KeyEvent e) {

        int key = e.getKeyCode();
        GameObject tempObject = handler.object.get(0);
        if (key == KeyEvent.VK_W)  {keyDown[0] = false;facing[0] = "";}//tempObject.setVelY(0); 
        if (key == KeyEvent.VK_S) {keyDown[1] = false;facing[1] = "";}//tempObject.setVelY(0);
        if (key == KeyEvent.VK_A) {keyDown[2] = false;facing[2] = "";}//tempObject.setVelX(0);
        if (key == KeyEvent.VK_D) {keyDown[3] = false; facing[3] = "";}//tempObject.setVelX(0);
        if (key == KeyEvent.VK_SHIFT) {Multi = 1;}
        if (key == KeyEvent.VK_SPACE) {Fire = false;}

    if(!keyDown[0] && !keyDown[1]) tempObject.setVelY(0);
    if(!keyDown[2] && !keyDown[3]) tempObject.setVelX(0);
    }
    }   


Solution

  • There are at least two strategies you can use for collisions: correction or prevention.

    Correction means you move the character and then you check if it collides, and if it does, you apply a correction. Which means, you find the position where the player should be and move it accordingly. This is called "responding to the collision".

    Prevention, on the other hand, implies doing some check before moving, and then only move the player as far as the collision point. This would require, for instance, some raycast technique.

    In your case, you probably want to implement a "correction" approach, given how your code is organized.

    You have a pice of code that checks if a collision occurred (I'll assume it works). When a collision is detected you can call some method to correct the position.

    private void collision() {
        for (int i = 0; i < handler.object.size(); i++) {
            GameObject tempObject = handler.object.get(i);
    
            if(tempObject.getId() == ID.Block)
            {
                if(getBounds().intersects(tempObject.getBounds()))
                {
                    //collision events
    
                }
            }
        }
    }
    

    Theres an interesting question on gamedev stackexchange you can look at for how to implement the collision response: https://gamedev.stackexchange.com/questions/160248/2d-rectangle-collision-resolution

    I would strongly recommend to use reified vectors instead of plain numbers for the position and velocity. It makes everything easier to understand on the long run.