0
\$\begingroup\$

I've been in trouble for weeks now and I don't understand why it doesn't work for me! I have tried so many things and I just can not understand.

I try to get one player to get a knockback as soon as another player collide with him. But for some reason the result of this is weird. Here's the code so you can understand how my physics engine works and the collision detection mechanism.

A little background: I started learning game development a month ago and mainly writing game engines, I don't have that much background in math, I know overall connection, subtraction, multiplication, division and some fractions. I learned the basics of advanced programming and programming - data structures, algorithms, concurrency, networks, file management and the like.

Now I have been trying for a long time to write a good Collision detection mechanism and physics engine. Now what I was trying to do in the current code is make Player1 fly back when Player1 collides with it. And it works .. But when one player pushes another or makes it fly back, such a strange space is created between them. I would be very grateful if you could read the code and try it out and tell me what I can fix or teach me how to write the engine better.

import java.awt.EventQueue;

import javax.swing.JFrame;

public class Launcher extends JFrame {

    private static final long serialVersionUID = 1L;

    public Launcher() {
        add(new GamePanel());
        
        setTitle("Game Engine");
        
        setResizable(false);
        pack();
        setLocationRelativeTo(null);
        setDefaultCloseOperation(EXIT_ON_CLOSE);
    }
    
    public static void main(String[] args) {
        EventQueue.invokeLater(() -> {
            Launcher launcher = new Launcher();
            launcher.setVisible(true);
        });
    }
}
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.util.ArrayList;

import javax.swing.JPanel;

public class GamePanel extends JPanel implements Runnable {

    private static final long serialVersionUID = 1L;

    private final int WIDTH = 800, HEIGHT = 600;
    
    private ArrayList<Entity> entities;
    
    private Player player1;
    private Player player2; 
    
    private Camera camera;
    
    private Thread thread;
    
    public GamePanel() {
        addKeyListener(new KeyListener1());
        addKeyListener(new KeyListener2());
        setPreferredSize(new Dimension(WIDTH, HEIGHT));
        setFocusable(true);
        requestFocus();
    
        entities = new ArrayList<Entity>();
        
        for (int i = 0; i < 100; i++)
            entities.add(new Block(this, i * 60, 500, 60, 60));
        
        player1 = new Player(this, 120, 100, 60, 100);
        player2 = new Player(this, 120 * 3, 100, 60, 100);
        
        entities.add(player1);
        entities.add(player2);
        
        camera = new Camera(this);
        
        thread = new Thread(this, "Game Engine - Main Thread");
        thread.start();
    }
    
    public Camera getCamera() {
        return camera;
    }
    
    public int getWidth() {
        return WIDTH;
    }
    
    public int getHeight() {
        return HEIGHT;
    }
    
    public ArrayList<Entity> getEntities() {
        return entities;
    }
    
    public Player getPlayer1() {
        return player1;
    }

    public Player getPlayer2() {
        return player2;
    }

    /*
     * 
     * Player2 is getting farther before Player1 reaching him and getting to 0 pixels between them
     * 
     * But in the other hand, he pushes him in non realistic way
     * 
     */
    public void tick() {
        player1.tick();
        player2.tick();
    }
    
    public void paintComponent(Graphics g) {
        super.paintComponent(g);
    
        for (Entity entity : entities)
            entity.draw(g);
        player1.draw(g);
        player2.draw(g);
    }
    
    public void run() {
        while (true) {
            tick();
            repaint();
            
            try {
                Thread.sleep(16);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    
    public class KeyListener1 extends KeyAdapter {
        
        public void keyPressed(KeyEvent e) {
            int key = e.getKeyCode();
            
            if (key == KeyEvent.VK_D) player1.setRight(true);
            if (key == KeyEvent.VK_A) player1.setLeft(true);
        }
    
        public void keyReleased(KeyEvent e) {
            int key = e.getKeyCode();
            
            if (key == KeyEvent.VK_D) player1.setRight(false);
            if (key == KeyEvent.VK_A) player1.setLeft(false);
        }
    }
    
    public class KeyListener2 extends KeyAdapter {
        
        public void keyPressed(KeyEvent e) {
            int key = e.getKeyCode();
            
            if (key == KeyEvent.VK_RIGHT) player2.setRight(true);
            if (key == KeyEvent.VK_LEFT) player2.setLeft(true);
        }
    
        public void keyReleased(KeyEvent e) {
            int key = e.getKeyCode();

            if (key == KeyEvent.VK_RIGHT) player2.setRight(false);
            if (key == KeyEvent.VK_LEFT) player2.setLeft(false);
        }
    }
}
import java.awt.Graphics;
import java.awt.Rectangle;
import java.util.ArrayList;

public abstract class Entity {

    protected GamePanel gp;

    protected int x;
    protected int y;
    
    protected int xDir;
    protected int yDir;
    
    protected int width;
    protected int height;
    
    protected double xVel;
    protected double yVel;
    
    protected Rectangle hitbox;
    
    public Entity(GamePanel gp, int x, int y, int width, int height) {
        this.gp = gp;

        this.x = x;
        this.y = y;
        
        xDir = 0;
        yDir = 0;
        
        this.width = width;
        this.height = height;
        
        xVel = 0;
        yVel = 0;
        
        hitbox = new Rectangle(x, y, width, height);
    }

    public int getX() {
        return x;
    }

    public void setX(int x) {
        this.x = x;
    }

    public int getY() {
        return y;
    }

    public void setY(int y) {
        this.y = y;
    }

    public int getxDir() {
        return xDir;
    }

    public void setxDir(int xDir) {
        this.xDir = xDir;
    }

    public int getyDir() {
        return yDir;
    }

    public void setyDir(int yDir) {
        this.yDir = yDir;
    }

    public int getWidth() {
        return width;
    }

    public void setWidth(int width) {
        this.width = width;
    }

    public int getHeight() {
        return height;
    }

    public void setHeight(int height) {
        this.height = height;
    }

    public double getxVel() {
        return xVel;
    }

    public void setxVel(double xVel) {
        this.xVel = xVel;
    }

    public double getyVel() {
        return yVel;
    }

    public void setyVel(double yVel) {
        this.yVel = yVel;
    }

    public Rectangle getHitbox() {
        return hitbox;
    }

    public void setHitbox(Rectangle hitbox) {
        this.hitbox = hitbox;
    }
    
    public void collisionDetection() {
        ArrayList<Entity> entities = gp.getEntities();      
        hitbox.x += xVel;
        
        for (Entity entity : entities) {
            if (hitbox.intersects(entity.getHitbox()) && !entity.equals(this)) {
                hitbox.x -= xVel;
                                
                if (entity instanceof Player) {
                    Player player = (Player) entity;
                    player.setCollidingWithPlayer(true);
                    
                    if (xDir == 1)
                        player.setxVel(7);
                    else
                        player.setxVel(-7);
                }
                
                while (!hitbox.intersects(entity.getHitbox())) hitbox.x += Math.signum(xVel);
                hitbox.x -= Math.signum(xVel);
                xVel = 0;
                x = hitbox.x;
            }
        }
        hitbox.y += yVel;
        
        for (Entity entity : entities) {
            if (hitbox.intersects(entity.getHitbox()) && !entity.equals(this)) {
                hitbox.y -= yVel;
                
                while (!hitbox.intersects(entity.getHitbox())) hitbox.y += Math.signum(yVel);
                hitbox.y -= Math.signum(yVel);
                yVel = 0;
                y = hitbox.y;
            }
        }
    }
    
    public abstract void tick();
    
    public abstract void draw(Graphics g);
}
import java.awt.Color;
import java.awt.Graphics;

public class Block extends Entity {

    public Block(GamePanel gp, int x, int y, int width, int height) {
        super(gp, x, y, width, height);
    }

    public void tick() {
        
    }

    public void draw(Graphics g) {
        g.setColor(Color.black);
        g.drawRect(x - gp.getCamera().getxOffset(), y, width, height);
        
        g.setColor(Color.white);
        g.fillRect(x + 1 - gp.getCamera().getxOffset(), y + 1, width - 1, height - 1);
    }
}
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;

public class Player extends Entity {
        
    private boolean right;
    private boolean left;
    
    private boolean collidingWithPlayer;
    private int timer;
    
    public Player(GamePanel gp, int x, int y, int width, int height) {
        super(gp, x, y, width, height);
        
        right = false;
        left = false;
        
        collidingWithPlayer = false;
        timer = 0;
    }

    public boolean isCollidingWithPlayer() {
        return collidingWithPlayer;
    }

    public void setCollidingWithPlayer(boolean collidingWithPlayer) {
        this.collidingWithPlayer = collidingWithPlayer;
    }

    public boolean isRight() {
        return right;
    }

    public void setRight(boolean right) {
        this.right = right;
    }

    public boolean isLeft() {
        return left;
    }

    public void setLeft(boolean left) {
        this.left = left;
    }

    public void tick() {
        if (right && left || !right && !left) {
            if (!collidingWithPlayer)
                xVel = 0;
        } else if (right && !left) {
            xVel = 7;
            xDir = 1;
        } else if (!right && left) {
            xVel = -7;
            xDir = -1;
        }
        yVel += 0.3;

        if (collidingWithPlayer) {          
            timer++;
            
            if (timer % 5 == 0)
                xVel -= Math.signum(xVel);
            
            if (xVel == 0) {
                timer = 0;
                collidingWithPlayer = false;
            }
        }
        collisionDetection();
        
        x += xVel;
        y += yVel;
        
        hitbox.x = x;
        hitbox.y = y;   
        
        if (this.equals(gp.getPlayer1()))
            gp.getCamera().focus(this);
    }

    public void draw(Graphics g) {
        g.setColor(Color.black);
        g.fillRect(x - gp.getCamera().getxOffset(), y, width, height);
        
        g.setColor(Color.pink);
        g.setFont(new Font("Ariel", Font.BOLD, 12));
        //g.drawString("" + weight, x, y + width / 2);
        //g.drawString("Kilograms", x, y + width / 2 + 20);
    }
}
public class Camera {

    private GamePanel gp;
    
    private int xOffset;
    
    public Camera(GamePanel gp) {
        this.gp = gp;
        xOffset = 0;
    }

    public int getxOffset() {
        return xOffset;
    }

    public void setxOffset(int xOffset) {
        this.xOffset = xOffset;
    }
    
    public void focus(Player player) {
        xOffset = player.getX() - gp.getWidth() / 2 + player.getWidth() / 2;
    }
}

I do not know if it is acceptable to insert a link to Git but I will insert anyway. You can download the code from here:

https://github.com/RDev16/Game

\$\endgroup\$
  • 2
    \$\begingroup\$ Please do not delete and re-post the same question. It suffices to edit your question. Editing still bumps the question in the Active Questions feed, so you don't sacrifice any visibility this way. And it helps avoid false positives from our automated anti-spam systems applying restrictions to your account. \$\endgroup\$ – DMGregory Jul 10 at 19:54
  • 1
    \$\begingroup\$ @DMGregory Ok thanks I will it remember this the next time. \$\endgroup\$ – RonDev Jul 10 at 19:56

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.