Game collision detection with rectangles - java

I have started with my first game but cannot figure out how the collision detection should work. The goal is that the player should not be able to go into the wall.
I have following classes Player(), Wall(), Levels() and Game()
Player class is loading the player and handling movement and collisions for the player.
Wall is creating walls, I have one Arraylist in this class which is creating an rectangle for each wall that is created.
Levels class is loading creating and loading all the levels.
Game class is handling the whole game loop and more.
I have tried to compare x and y positions with player and wall, right now I am trying to use the .intersect class which has not been succesfull yet.
Part of Player Class:
public void collision() {
Rectangle r3 = getOffsetBounds();
for (int i = 0; i < Wall.wallList.size(); i++) {
Rectangle w1 = Wall.wallList.get(i);
if (r3.intersects(w1)) {
}}}
public int moveUp(int velY) {
collision();
y -= velY;
return y;
}
public int moveDown(int velY) {
collision();
y += velY;
return y;
}
public int moveLeft(int velX) {
collision();
x -= velX;
return x;
}
public int moveRight(int velX) {
collision();
x += velX;
return x;
}
public Rectangle getOffsetBounds() {
return new Rectangle(x + velX, y + velY, 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 getVelX() {
return velX;
}
public void setVelX(int velX) {
this.velX = velX;
}
public int getVelY() {
return velY;
}
public void setVelY(int velY) {
this.velY = velY;
}}
Part of Wall Class:
static ArrayList<Rectangle> wallList = new ArrayList<Rectangle>();
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 void setVisisble(Boolean visible) {
this.visible = visible;
}
public Rectangle getBounds() {
return new Rectangle(x,y,width,height);
}
public Rectangle setBounds(int x,int y,int width,int height) {
this.x = x;
this.y = y;
this.width = width;
this.height = height;
return new Rectangle(x,y,width,height);
}}
Part of Levels class where the wall list is setting
if(level1[i][j]==1) {
w = new Wall(j*25, i*25, width, height);
w.paint(g);
Wall.wallList.add(w.setBounds(j*25+10, i*25+10, width, height));
}
Part of Game class where keybindings are
public void KeyBindings() {
keyManager.addKeyBinding(lvl, KeyEvent.VK_UP, "moveUp", (evt) -> {
lvl.player.moveUp(5);
});
keyManager.addKeyBinding(lvl, KeyEvent.VK_DOWN, "moveDown", (evt) -> {
lvl.player.moveDown(5);
});
keyManager.addKeyBinding(lvl, KeyEvent.VK_LEFT, "moveLeft", (evt) -> {
lvl.player.moveLeft(5);
});
keyManager.addKeyBinding(lvl, KeyEvent.VK_RIGHT, "moveRight", (evt) -> {
lvl.player.moveRight(5);
});
}

This is how I always handle any collisions
if (((x1<x2 && x1+w1>x2) || (x2<x1 && x2+w2>x1)) &&
((y1<y2 && y1+h1>y2) || (y2<y1 && y2+h2>y1)))

Related

Swing application does not display anything

I am currently trying to create a simple dodge game using Java Swing and AWT. Right now, I have created some simple outline code to test if the concept I am trying to create will work. I have used polymorphism to try to create multiple types of object Enemy which will have individual draw() and act() code that makes them be drawn by AWT and then move in a specific fashion based on what type they are. I imported Graphics2D to draw() in an attempt to make the code more reusable. I then used a while loop to run the Java Swing/AWT built-in thread to allow animations for the enemies. However, when I run the code, it compiles correctly, but only a blank screen is displayed.
How can I fix it?
Here is the code I used. The code involving the Mouse is incomplete.
Game.java
import javax.swing.*;
import java.awt.*;
public class Game extends JPanel {
//FPS Setup
int fps = 30; //FPS
int secConst = 1000; //milliseconds per second
int frmConst = (int) Math.floor((double) secConst / (double) fps); //delay between frames
//FRAME Setup
String appName = "Dodge This"; //app name
int frameW = 500; //frame width
int frameH = 500; //frame height
//ENEMY TEST
//TO REPLACE WITH ARRAY OF ENEMIES
Square square = new Square(0, 100, 0, 10);
Circle circle = new Circle(50, 50, 10);
boolean lose = true;
#Override
public void paint(Graphics g) {
super.paint(g);
Graphics2D g2d = (Graphics2D) g;
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
//TO REPLACE WITH LOOP THROUGH ARRAY OF ENEMIES
square.act();
square.draw(g2d);
circle.act();
circle.draw(g2d);
if (this.lose) {
g2d.setColor(new Color(0, 0, 0));
g2d.setFont(new Font("Sans Serif", Font.PLAIN, 32));
g2d.drawString("You Lose", 0, 0); //TO REPLACE WITH RANDOMIZED LOSE MESSAGE
}
}
public static void main(String [] args) throws InterruptedException {
Game game = new Game();
JFrame frame = new JFrame(game.appName);
frame.setSize(game.frameW, game.frameH);
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
while (true) {
if (MouseInfo.getPointerInfo().getLocation().equals(new Point(game.circle.getX(), game.circle.getY())) && MouseInfo.getPointerInfo().getLocation().equals(new Point(game.square.getX(), game.square.getY()))) {
game.lose = true;
break;
}
game.repaint();
Thread.sleep(game.frmConst);
}
}
}
Enemy.java
import java.awt.*;
public abstract class Enemy {
public int x;
public int y;
public double direction;
public double speed;
//BASIC METHODS
public void setX(int x) {
this.x = x;
}
public void setY(int y) {
this.y = y;
}
public void setDirection(double direction) {
this.direction = direction;
}
public void setSpeed(double speed) {
this.speed = speed;
}
public int getX() {
return this.x;
}
public int getY() {
return this.y;
}
public double getDirection() {
return this.direction;
}
public double getSpeed() {
return this.speed;
}
//METHODS FOR UNIQUE ENEMIES
public abstract void act();
public abstract void draw(Graphics2D g2d);
}
Square.java
import java.awt.*;
public class Square extends Enemy{
//CONSTRUCTORS
public Square() {
this.x = 0;
this.y = 0;
this.direction = (int) Math.floor(Math.random() * 8) * 45;
this.speed = (int) Math.floor(Math.random() * 15);
} //default constructor
public Square(int x, int y) {
this.x = x;
this.y = y;
this.direction = (int) Math.floor(Math.random() * 8) * 45;
this.speed = (int) Math.floor(Math.random() * 15);
}
public Square(int x, int y, double direction) {
this.x = x;
this.y = y;
this.direction = direction;
this.speed = (int) Math.floor(Math.random() * 15);
}
public Square(int x, int y, double direction, double speed) {
this.x = x;
this.y = y;
this.direction = direction;
this.speed = speed;
}
//ACTIONS
#Override
public void act() {
this.x += Math.floor(this.speed * (Math.cos(this.direction)));
this.y += Math.floor(this.speed * -1 * (Math.sin(this.direction)));
}
#Override
public void draw(Graphics2D g2d) {
g2d.setColor(new Color(100, 100, 100));
g2d.fillRect(this.x, this.y, 50, 50);
}
}
Circle.java
import java.awt.*;
public class Circle extends Enemy{
double mouseX;
double mouseY;
//CONSTRUCTORS
public Circle() {
this.x = 0;
this.y = 0;
this.direction = 0;
this.speed = 5;
} //default constructor
public Circle(int x, int y) {
this.x = x;
this.y = y;
this.direction = 0;
this.speed = 5;
}
public Circle (int x, int y, int speed) {
this.x = x;
this.y = y;
this.direction = 0;
this.speed = speed;
}
//ACTIONS
#Override
public void act() {
this.mouseX = MouseInfo.getPointerInfo().getLocation().getX();
this.mouseY = MouseInfo.getPointerInfo().getLocation().getY();
this.x += this.speed * Math.floor(Math.abs(this.y - this.mouseY) / Math.abs(this.x - this.mouseX));
this.y += this.speed * -1 * Math.floor(Math.abs(this.x - this.mouseX) / Math.abs(this.y - this.mouseY));
}
#Override
public void draw(Graphics2D g2d) {
g2d.setColor(new Color(50, 50, 50));
g2d.fillOval(this.x, this.y, 10, 10);
}
}
See the following code for some improvements in the program structure as well as better use of Swing tools. Note the comments:
import java.awt.*;
import javax.swing.*;
public class Game extends JPanel {
//FPS Setup
int fps = 3; // FPS (slow for testing)
int secConst = 1000; //milliseconds per second
int frmConst = secConst / fps; //delay between frames
private Timer timer;
String appName = "Dodge This"; //app name
int frameW = 500, frameH = 500; //frame width and height
Square square = new Square(0, 100, 0., 10.);
Circle circle = new Circle(50, 50, 0., 10.);
boolean lose = false; //the correct state at start ;
//override paintComponent rather than paint
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
square.draw(g2d);
circle.draw(g2d);
if (lose) {
g2d.setColor(new Color(0, 0, 0));
g2d.setFont(new Font("Sans Serif", Font.PLAIN, 32));
g2d.drawString("You Lose", 0, 0);
}
}
public void start(){
//use swing timer to invoke game cycles
if(timer != null && timer.isRunning()) {
timer.stop();
}
timer = new Timer(frmConst, e->{
if(lose) {
timer.stop();
} else {
play();
}
}
);
timer.start();
}
public void play(){
square.act();
circle.act();
//lose criteria (not sure what are you trying to check)
if (MouseInfo.getPointerInfo().getLocation().equals(new Point(circle.getX(), circle.getY()))
&& MouseInfo.getPointerInfo().getLocation().equals(new Point(square.getX(), square.getY()))) {
lose = true;
}
repaint();
}
public static void main(String [] args) throws InterruptedException {
Game game = new Game();
JFrame frame = new JFrame(game.appName);
frame.setSize(game.frameW, game.frameH);
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(game);// add game to frame
game.start();
}
}
abstract class Enemy {
public int x,y;
public double direction, speed;
public Enemy(int x, int y, double speed) {
this(x,y,0, speed);
}
public Enemy(int x, int y, double direction, double speed) {
this.x = x;
this.y = y;
this.direction = direction;
this.speed = speed;
}
//BASIC METHODS
public void setX(int x) {
this.x = x;
}
public void setY(int y) {
this.y = y;
}
public void setDirection(double direction) {
this.direction = direction;
}
public void setSpeed(double speed) {
this.speed = speed;
}
public int getX() {
return x;
}
public int getY() {
return y;
}
public double getDirection() {
return direction;
}
public double getSpeed() {
return speed;
}
//METHODS FOR UNIQUE ENEMIES
public abstract void act();
public abstract void draw(Graphics2D g2d);
}
class Square extends Enemy{
public Square(int x, int y, double direction, double speed) {
super(x, y, direction, speed);
}
#Override
public void act() {
x += Math.floor(speed * Math.cos(direction));
y += Math.floor(speed * -1 * Math.sin(direction));
}
#Override
public void draw(Graphics2D g2d) {
g2d.setColor(new Color(100, 100, 100));
g2d.fillRect(x, y, 50, 50);
}
}
class Circle extends Enemy{
double mouseX, mouseY;
public Circle(int x, int y, double direction, double speed) {
super(x, y, direction, speed);
}
#Override
public void act() {
mouseX = MouseInfo.getPointerInfo().getLocation().getX();
mouseY = MouseInfo.getPointerInfo().getLocation().getY();
x += speed * Math.floor(Math.abs(y - mouseY) / Math.abs(x - mouseX));
y += speed * -1 * Math.floor(Math.abs(x - mouseX) / Math.abs(y - mouseY));
}
#Override
public void draw(Graphics2D g2d) {
g2d.setColor(new Color(50, 50, 50));
g2d.fillOval(x, y, 10, 10);
}
}
(Run it online here)

How to get Delta x and Delta y in class to add them to x and y?

I am try to solve OOP Java problems and I reached a class named "Ball", The class is easy to design but I have problem that is how to get xDelta and yDelta so I can add them to initial x and y to get the position of the ball after moving it?
Now I wrote this code but the xDelta and yDelta are wrong. What is the need for speed and direction in the constructor?
This is the class diagram:
This is the class code:
public class Ball {
private float x;
private float y;
private int radius;
private float xDelta;
private float yDelta;
public Ball(float x,float y,int radius,int speed,int direction){
this.x=x;
this.y=y;
this.radius=radius;
}
float getX() {
return x;
}
void setX(float x) {
this.x=x;
}
float getY() {
return y;
}
void setY(float y) {
this.y=y;
}
int getRadius() {
return radius;
}
void setRadius(int radius) {
this.radius = radius;
}
float getXDelta() {
return xDelta;
}
void setXDelat(float xDelta) {
this.xDelta=xDelta;
}
float getYDelta() {
return yDelta;
}
void setYDelat(float yDelta) {
this.yDelta=yDelta;
}
void move() {
x += xDelta ;
y += yDelta ;
}
void reflectHorizontal() {
xDelta = -xDelta ;
}
void reflectVertical() {
yDelta = -yDelta ;
}
public String toString () {
return "Ball[("+x+","+y+"),speed=("+xDelta+","+yDelta+")]";
}
}
Let direction be the direction of the ball flight in degrees (0 to 360).
Add this to your constructor.
double radian = Math.toRadians(direction);
this.xDelta = (float)Math.cos(radian) * speed;
this.yDelta = (float)Math.sin(radian) * speed;

Having trouble with my polymorphism

Could someone explain to me why I am getting these errors on my coding when it comes to the main method project5 class. thearray[count++] keeps giving me errors, and I am not entirely sure where I made my error. I've been stuck on it for awhile now. Some guidance would be nice.
Here is my main method:
public class Project5 {
private Shape [] thearray = new Shape[100];
public static void main (String [] args) {
Project5 tpo = new Project5();
tpo.run();
}
public void run () {
int count = 0;
thearray[count++] = new Circle(20, 20, 40);
thearray[count++] = new Triangle(70, 70, 20, 30);
thearray[count++] = new Rectangle(150, 150, 40, 40);
for (int i = 0; i < count; i ++ ) {
thearray[i].display();
}
int offset = 0;
double totalarea = 0.0;
while (thearray[offset] != null) {
totalarea = totalarea + thearray[offset].area();
offset++;
}
System.out.println("The total area for " + offset + " Shape objects is " + totalarea);
}
}
Here is my Circle class:
public class Circle {
private double radius;
public Circle() {
radius = 1.0;
}
public Circle(double newRadius) {
radius = 1.0;
setRadius(newRadius);
}
public void setRadius(double newRadius) {
if(newRadius > 0) {
radius = newRadius;
}
else {
System.out.println("Error: "
+ newRadius + " is a bad radius value.");
}
}
public double getRadius() {
return radius;
}
public double getArea() {
return radius * radius * Math.PI;
}
}
and here is my Shape class:
abstract class Shape{
int x = 1;
int y = 1;
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 Shape(int x, int y) {
this.x = x;
this.y = y;
}
public void display() {
}
public abstract double area();
}
I would post my triangle and rectangle class, but I figured this would be enough to at least have it explained where I am messing up.
I looked at your Circle class and the constructor currently only takes one argument. However, when you create a Circle you are specifying 3 arguments.
Circle should probably extend from Shape and the first two parameters are then the x and y position of the shape, correct? If this is the case, change your class as follows:
public class Circle extends Shape {
private double radius;
public Circle(int x, int y) {
super(x, y);
radius = 1.0;
}
public Circle(int x, int y, double newRadius) {
super(x, y);
setRadius(newRadius);
}
...etc

Java 2D Platformer Gravity Improvements

I have started making a small game in Java, and have put in collisions, and a form of gravity, the speed of the player does not increase as it falls like in real life. I'd like to improve on this form of gravity to make it more realistic, also I have a collision block for the floor, and when the player walks off the floor, it stays at that height and does not fall.
Below is my Player class:
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.Rectangle;
public class Player {
private int x, y, width, height, lx, ly, dx, dy;
private final int FallSpeed = 2;
private final long JumpingTime = 8;
public boolean jumping = false, falling = true;
public Player(int x, int y, int width, int height) {
this.x = x;
this.y = y;
this.width = width;
this.height = height;
lx = x;
ly = y;
dx = x;
dy = y;
new Thread(new gravityThread()).start(); // Make user fall in case they are in the air
}
public void setX(int x) {
this.x = x;
lx = x;
dx = x;
}
public void setY(int y) {
this.y = y;
ly = y;
dy = y;
}
public int getX() {
return x;
}
public int getY() {
return y;
}
public int getWidth() {
return width;
}
public int getHeight() {
return height;
}
public Rectangle getBounds() {
return new Rectangle(x, y, width, height);
}
public void update() {
}
public void move(int ax, int ay) {
dx += ax;
dy += ay;
checkCollisions();
}
public void checkCollisions() {
if (collided()) {
falling = false;
dx = lx;
dy = ly;
}
x = dx;
y = dy;
lx = x;
ly = y;
}
public boolean collided() {
Rectangle desired = new Rectangle(dx, dy, width, height);
for (Rectangle r : CollisionManager.collisions) if (r.intersects(desired)) return true;
return false;
}
public void jump() {
falling = false;
jumping = true;
new Thread(new gravityThread()).start();
}
public void render(Graphics2D g2d) {
g2d.setColor(Color.black);
g2d.fill(getBounds());
}
private class gravityThread implements Runnable {
int i = 0;
#Override
public void run() {
while(jumping) {
try {
i++;
Thread.sleep(JumpingTime);
move(0, -FallSpeed);
if (i == 40) {
jumping = false;
falling = true;
}
} catch (Exception e) {
e.printStackTrace();
System.exit(0);
}
}
while(falling) {
try {
Thread.sleep(JumpingTime);
move(0, FallSpeed);
} catch (Exception e) {
e.printStackTrace();
System.exit(0);
}
}
}
}
}
In another class which handles drawing everything to the window, I control player movement with this snippet:
public void onUpdate() {
if (this.screenFactory.getGame().getKeyboardListener().isKeyPressed(KeyEvent.VK_A))
player.move(-WalkSpeed, 0);
if (this.screenFactory.getGame().getKeyboardListener().isKeyPressed(KeyEvent.VK_D))
player.move(WalkSpeed, 0);
if (this.screenFactory.getGame().getKeyboardListener().isKeyPressed(KeyEvent.VK_SPACE)) {
if (!player.jumping && !player.falling)
player.jump();
}
if (player.getY() >= screenHeight - player.getHeight()) {
player.setY(screenHeight - player.getHeight());
player.falling = false;
}
if (player.getY() <= 0)
player.setY(0);
if (player.getX() >= screenWidth - player.getWidth())
player.setX(screenWidth - player.getWidth());
if (player.getX() <= 0)
player.setX(0);
}
g-force is 9.81 m/s²
get your "draw-update-speed", this is s (to be realistic, you should update at least 30 times per second. have this in mind when calculating)
get your definition of lengths, this is m
Cuple objects position depending on current speed v and accelleration (g-force) a with each frame.
The longer something falls, the faster it will fall...
Edit: to make sideward movement while falling/jumping more realistic, you need to work with sin(), cos().

BackGround in my JPanel, how can I resolve this?

Well my problem is that I'm making the stars move with a thread, they move verticaly and it works good but i do a random X for the star and sometimes it intersecs other stars like this :
This is my code for the JPanel:
class Backgroundmoving
public class Backgroundmoving extends JPanel {
ArrayList<starmoving> star;
public Backgroundmoving() {
this.setSize(650, 501);
star = new ArrayList<>();
for (int i = 0; i < 20; i++)
this.addStar();
}
public void addStar() {
int x, y;
x = (int) (Math.random() * 625);
y = (int) (Math.random() * 476);
starmoving e = new starmoving(x, y);
star.add(e);
Thread t = new Thread(e);
t.start();
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
draw(g);
}
public void draw(Graphics g) {
g.drawImage(new ImageIcon("background.png").getImage(), 0, 0, 650, 501, null);
for (int i = 0; i < star.size(); i++) {
star.get(i).draw(g);
}
repaint();
}
public static void main(String[] args) {
// TODO code application logic here
JFrame gui = new JFrame();
gui.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
gui.setSize(650, 510);
gui.setResizable(false);
gui.add(new Backgroundmoving());
gui.setVisible(true);
}
}
class starmoving
public class starmoving implements Runnable {
int x;
int y;
int yVel;
public starmoving(int x, int y) {
this.x = x;
this.y = y;
yVel = 1;
}
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;
}
private void move() {
y += yVel;
if (y > 476) {
y = 0;
x = (int) (Math.random() * 625);
}
}
private boolean isOffScreen() {
if (y <= 476)
return false;
return true;
}
public void draw(Graphics g) {
g.drawImage(new ImageIcon("star.png").getImage(), x, y, 12, 12, null);
}
#Override
public void run() {
while (true) {
move();
try {
Thread.sleep(7);
} catch (InterruptedException ex) {
System.out.println(ex.getMessage());
}
}
}
}
i don't want stars intersecting, was thinking in a if before the random X but how can i know if other star is in that X ?
You have an ArrayList that contains all your "StarMoving" objects. So you need to iterate through that list to make sure that none of the object intersect.
On top of that you have other problems.
Use proper Java class names. Java classes SHOULD start with an upper case character. (ie. "starmoving" is wrong)
Don't use multiple Threads for the animation. You current code starts 20 Threads. You should have a single Thread and then iterate through your ArrayList to move all the stars.
Don't read the image in the draw() method. Currently you code is reading the image every 7ms. This is not very efficient. The image should be read once and then stored as a property of your class.

Categories