Collision Detection Between Two Images in Java

Collision Detection between two images in Java

I think your problem is that you are not using good OO design for your player and enemies. Create two classes:

public class Player
{
int X;
int Y;
int Width;
int Height;

// Getters and Setters
}

public class Enemy
{
int X;
int Y;
int Width;
int Height;

// Getters and Setters
}

Your Player should have X,Y,Width,and Height variables.

Your enemies should as well.

In your game loop, do something like this (C#):

foreach (Enemy e in EnemyCollection)
{
Rectangle r = new Rectangle(e.X,e.Y,e.Width,e.Height);
Rectangle p = new Rectangle(player.X,player.Y,player.Width,player.Height);

// Assuming there is an intersect method, otherwise just handcompare the values
if (r.Intersects(p))
{
// A Collision!
// we know which enemy (e), so we can call e.DoCollision();
e.DoCollision();
}
}

To speed things up, don't bother checking if the enemies coords are offscreen.

How to detect if two images collide in Java?

The basic approach would be to first determine if there is collision of the image boundaries.

If the two images overlap, you will need to determine if any of non-transparent pixels in the image overlap/collide.

This can be achieved through the use of BufferedImage#getRGB

As an example, check out the second example in the answer

Updated with example

Collision

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.geom.Area;
import java.awt.image.BufferedImage;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;

public class TransparentImageCollision {

public static void main(String[] args) {
new TransparentImageCollision();
}

public TransparentImageCollision() {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}

JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}

public class TestPane extends JPanel {

private BufferedImage fly;
private BufferedImage spider;

private Rectangle spiderBounds;
private Rectangle flyBounds;

private Point spiderDelta;
private Point flyDelta;

private Rectangle collision;

public TestPane() {
try {
fly = ImageIO.read(getClass().getResource("/fly.png"));
spider = ImageIO.read(getClass().getResource("/spider.png"));

Dimension size = getPreferredSize();
int width = size.width;
int height = size.height;

spiderBounds = new Rectangle();
spiderBounds.setSize(spider.getWidth(), spider.getHeight());
spiderBounds.setLocation(0, (height - spider.getHeight()) / 2);

flyBounds = new Rectangle();
flyBounds.setSize(fly.getWidth(), fly.getHeight());
flyBounds.setLocation(width - fly.getWidth(), (height - fly.getHeight()) / 2);

spiderDelta = new Point(1, 0);
flyDelta = new Point(-1, 0);

Timer timer = new Timer(40, new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
update(spiderBounds, spiderDelta);
update(flyBounds, flyDelta);
detectCollision();
repaint();
}
});
timer.start();
} catch (IOException exp) {
exp.printStackTrace();
}
}

protected void update(Rectangle bounds, Point delta) {
bounds.x += delta.x;
bounds.y += delta.y;
if (bounds.x < 0) {
bounds.x = 0;
delta.x *= -1;
}
if (bounds.x + bounds.width > getWidth()) {
bounds.x = getWidth() - bounds.width;
delta.x *= -1;
}
if (bounds.y < 0) {
bounds.y = 0;
delta.y *= -1;
}
if (bounds.y + bounds.height > getHeight()) {
bounds.y = getHeight() - bounds.height;
delta.y *= -1;
}
}

@Override
public Dimension getPreferredSize() {
return new Dimension(400, 200);
}

@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();

g2d.drawImage(spider, spiderBounds.x, spiderBounds.y, this);
g2d.drawImage(fly, flyBounds.x, flyBounds.y, this);

if (collision != null) {
g2d.setColor(new Color(255, 0, 0, 128));
g2d.fill(collision);
}

g2d.dispose();
}

/**
* Used to detect the collision between non-alpha portions of the two
* images
*/
protected void detectCollision() {
collision = null;
// Check if the boundires intersect
if (spiderBounds.intersects(flyBounds)) {
// Calculate the collision overlay
Rectangle bounds = getCollision(spiderBounds, flyBounds);
if (!bounds.isEmpty()) {
// Check all the pixels in the collision overlay to determine
// if there are any non-alpha pixel collisions...
for (int x = bounds.x; x < bounds.x + bounds.width; x++) {
for (int y = bounds.y; y < bounds.y + bounds.height; y++) {
if (collision(x, y)) {
collision = bounds;
break;
}
}
}
}
}
}

protected Rectangle getCollision(Rectangle rect1, Rectangle rect2) {
Area a1 = new Area(rect1);
Area a2 = new Area(rect2);
a1.intersect(a2);
return a1.getBounds();
}

/**
* Test if a given x/y position of the images contains transparent
* pixels or not...
* @param x
* @param y
* @return
*/
protected boolean collision(int x, int y) {
boolean collision = false;

int spiderPixel = spider.getRGB(x - spiderBounds.x, y - spiderBounds.y);
int flyPixel = fly.getRGB(x - flyBounds.x, y - flyBounds.y);
// 255 is completely transparent, you might consider using something
// a little less absolute, like 225, to give you a sligtly
// higher hit right, for example...
if (((spiderPixel >> 24) & 0xFF) < 255 && ((flyPixel >> 24) & 0xFF) < 255) {
collision = true;
}
return collision;
}
}
}

Collision detection between two images in java

You can create a Rectangle for every object like for a rocket or meteor

and there is a Rectangle method you can use

boolean intersects(Rectangle r)
Determines whether or not this Rectangle and the specified Rectangle intersect.

so you could check

if (rectangleA.intersects(rectangleB){
System.out.println("Colliosion!!")
}

How to know collision between 2 images javafx

You can create a wrapper for the image, a Sprite class for example, and you can use that wrapper to give it x,y coordinates. You can get the intersection with the help of a Rectangle2D shape.

import javafx.geometry.Rectangle2D;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.image.Image;

public class Sprite {

private Image image;
private double positionX;
private double positionY;
private double width;
private double height;

public Sprite(Image image) {
this.image = image;
width = image.getWidth();
height = image.getHeight();
positionX = 0;
positionY = 0;
}

public void setPosition(double x, double y) {
positionX = x;
positionY = y;
}

public void render(GraphicsContext gc) {
gc.drawImage(image, positionX, positionY);
}

public Rectangle2D getBoundary() {
return new Rectangle2D(positionX, positionY, width, height);
}

public boolean intersects(Sprite spr) {
return spr.getBoundary().intersects(this.getBoundary());
}
}

How to check collision between two ImageView objects in JavaFX

The reason the score "becomes huge" is that the background thread is repeatedly checking for collisions as fast and as often as it can. Any time it checks and the bounds intersect, it adds one to the score.

The reason you see incorrect results sometimes, such as a collision being detected immediately when none should happen, is more complex. This occurs because values (in particular the boundsInParent of the two image views) are being changed on one thread (the FX Application thread) and observed on another thread (your background thread) without proper synchronization. JavaFX, like most UI toolkits, is designed as a single-threaded toolkit, and so there is no way to add synchronization to this.

What actually happens here is due to something called "hoisting". The code in your background thread is essentially

public void run(){
while(scene.getWindow().isShowing() == true){

if(imageView.getBoundsInParent().intersects(imageView2.getBoundsInParent())){
score++;
System.out.println(score);
System.out.println("Boom");
}
}
}

It is legal, per the Java Language Specification, for a JVM to optimize code in certain ways. For variables that are not declared volatile, and without synchronization, the JVM is allowed to reorder code assuming that each thread is independent of each other. Under this assumption, since there are no changes to the boundsInParent made in this thread, the code can be treated as being equivalent to

public void run(){
if(imageView.getBoundsInParent().intersects(imageView2.getBoundsInParent())){
while(scene.getWindow().isShowing() == true){

score++;
System.out.println(score);
System.out.println("Boom");
}
}
}

Furthermore, this optimization may be made at an arbitrary time (when the JVM decides it may be beneficial), so if you are accessing the boundsInParent from a thread other than the FX Application Thread, the behavior of this code becomes essentially non-deterministic.

For more information, I recommend reading the relevant items in Joshua Bloch's book Effective Java. (No one who has spent more than an hour programming in Java should be without this book.)

Using a background thread here is completely the wrong approach anyway.

What you actually want to do, I assume, is add one to the score (and perhaps perform other actions) when the state of the images changes from "not intersecting" to "intersecting". You can do this by creating a BooleanBinding with the correct value, and which is bound to the two boundsInParent properties. Then register a listener with that binding and react when it changes from false to true:

    BooleanBinding collision = Bindings.createBooleanBinding(
() -> wolfIv.getBoundsInParent().intersects(eggsList.get(0).getBoundsInParent()),
wolfIv.boundsInParentProperty(),
eggsList.get(0).boundsInParentProperty()
);

collision.addListener((obs, wasCollision, isNowCollision) -> {
if (isNowCollision) {
score++;
System.out.println(score);
System.out.println("Boom");
}
});

Here's a complete runnable example which demonstrates this:

import java.util.ArrayList;

import javafx.animation.PathTransition;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.beans.binding.Bindings;
import javafx.beans.binding.BooleanBinding;
import javafx.scene.Scene;
import javafx.scene.image.ImageView;
import javafx.scene.image.WritableImage;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.CubicCurveTo;
import javafx.scene.shape.MoveTo;
import javafx.scene.shape.Path;
import javafx.stage.Stage;
import javafx.util.Duration;

public class TestCollision extends Application {

private Scene scene;
private Pane pane;
private ImageView wolfIv;
private ArrayList<ImageView> eggsList;
private int score;

@Override
public void start(Stage primaryStage) throws Exception {

pane = new Pane();
scene = new Scene(pane,800, 600);

WritableImage wolf = new WritableImage(1, 1);
wolf.getPixelWriter().setColor(0, 0, Color.RED);
wolfIv = new ImageView(wolf);

eggsList = new ArrayList<>();
WritableImage egg = new WritableImage(1, 1);
egg.getPixelWriter().setColor(0, 0, Color.YELLOW);
eggsList.add(new ImageView(egg));


BooleanBinding collision = Bindings.createBooleanBinding(
() -> wolfIv.getBoundsInParent().intersects(eggsList.get(0).getBoundsInParent()),
wolfIv.boundsInParentProperty(),
eggsList.get(0).boundsInParentProperty()
);

collision.addListener((obs, wasCollision, isNowCollision) -> {
if (isNowCollision) {
score++;
System.out.println(score);
System.out.println("Boom");
}
});


eggsList.get(0).setFitHeight(45);
eggsList.get(0).setFitWidth(35);
pane.getChildren().add(eggsList.get(0));
MoveTo moveToEgg = new MoveTo();
moveToEgg.setX(60.0f);
moveToEgg.setY(95.0f);
Path eggPath = new Path();
eggPath.getElements().add(moveToEgg);
eggPath.getElements().add(new CubicCurveTo(190,200,190, 200,190,480));

PathTransition pathTransition = new PathTransition();
pathTransition.setDuration(Duration.millis(4000));
pathTransition.setNode(eggsList.get(0));
pathTransition.setPath(eggPath);
pathTransition.setCycleCount(PathTransition.INDEFINITE);
pathTransition.setAutoReverse(false);
pathTransition.play();

wolfIv.setFitWidth(200);
wolfIv.setFitHeight(250);

wolfIv.setX(140);
wolfIv.setY(218);


pane.getChildren().addAll(wolfIv);



primaryStage.setResizable(false);
primaryStage.setScene(scene);
primaryStage.show();

}

public static void main(String[] args) {
launch(args);
}

}

Collision detection between ImageViews

Collision detction and score increase ;-)

public class MainActivity extends AppCompatActivity 
{
//Layout
private RelativeLayout myLayout = null;

//Screen Size
private int screenWidth;
private int screenHeight;

//Position
private float ballDownY;
private float ballDownX;

//Initialize Class
private Handler handler = new Handler();
private Timer timer = new Timer();

//Images
private ImageView net = null;
private ImageView ball = null;

//score
private TextView score = null;

//for net movement along x-axis
public float x = 0;
public float y = 0;

//points
private int points = 0;

@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);

this.setContentView(R.layout.activity_main);
this.myLayout = (RelativeLayout) findViewById(R.id.myLayout);

this.score = (TextView) findViewById(R.id.score);

this.net = (ImageView) findViewById(R.id.net);
this.ball = (ImageView) findViewById(R.id.ball);

//retrieving screen size
WindowManager wm = getWindowManager();
Display disp = wm.getDefaultDisplay();
Point size = new Point();
disp.getSize(size);
screenWidth = size.x;
screenHeight = size.y;

//move to out of screen
this.ball.setX(-80.0f);
this.ball.setY(screenHeight + 80.0f);

//Error here
/*//Run constantly
new Handler().postDelayed(new Runnable()
{
@Override
public void run()
{
Render();
}
}, 100); //100 is miliseconds interval than sleep this process, 1000 miliseconds is 1 second*/

Thread t = new Thread() {
@Override
public void run() {
try {
while (!isInterrupted()) {
Thread.sleep(100);
runOnUiThread(new Runnable() {
@Override
public void run(){Render();}});}
}catch (InterruptedException e) {}}};

t.start();

}

public void Render()
{
changePos();
if(Collision(net, ball))
{
points++; //You dont need findView Textview score for that exists in OnCreate Method
this.score.setText("Score:" + points);
}
}

public void changePos()
{

//down
ballDownY += 10;
if (ball.getY() > screenHeight) {
ballDownX = (float) Math.floor((Math.random() * (screenWidth - ball.getWidth())));
ballDownY = -100.0f;

}
ball.setY(ballDownY);
ball.setX(ballDownX);

//make net follow finger
myLayout.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View view, MotionEvent event) {
x = event.getX();
y = event.getY();

if (event.getAction() == MotionEvent.ACTION_MOVE) {
net.setX(x);
net.setY(y);
}
return true;
}

});

public boolean Collision(ImageView net, ImageView ball)
{
Rect BallRect = new Rect();
ball.getHitRect(BallRect);
Rect NetRect = new Rect();
net.getHitRect(NetRect);
return BallRect.intersect(NetRect);
}
}

How do I do collision detection using images?

There are a lot of ways of doing it. If you don't need a lot of precission, you can use bounding boxes, and with the sizes and locations of them, find out if there is a collision. You can read this: http://gpsnippets.blogspot.com.es/2008/10/bounding-box-collision-detection.html for more info.



Related Topics



Leave a reply



Submit