# 2D collision detection / determining final velocity - Java

• Comp Sci
• Vold
In summary, the conversation is about determining if two circular objects, either both moving or one stationary, will collide and calculating their final velocity if they do. The objects are being rendered using Java's Graphics library class and their position and velocity are determined by the frame's width and length. The objects also have a mass and a radius. The discussion also mentions a modification to the equation for the objects' final velocity to account for an inverted Y-Axis, but the solution provided still has some issues with collisions.
Vold

## Homework Statement

Determine if two circular objects (both moving or one stationary), collide and if so, determine their final velocity.
The two objects are being rendered using Graphics's Java library class, updating their position 60 times per second. Their position (x,y) is determined based on the Width and Length of the frame, and their velocity is given in pixels (eg: -1 , 2; the object moves 1 pixel to the right and 2 pixels upwards (Y-Axis is inverted), the objects have a mass and a radius.

## Homework Equations

Here.

Note: In the code below there is a modification to the equation (theta of the other object is replaced by Phi - 2PI), because I was told that the equation wasn't accurate (I may be mistaked though, regardless this does not fix the problem either way).

## The Attempt at a Solution

Here is the class that tries to do the math but fails miserably. Sometimes the objects collide properly but sometimes very awkwardly. I think that my main issue is the inverted Y-Axis.

Java:
import com.game.src.gameObjects.GameObject;

public class Physics {
private Controller1 c1;
private Controller2 c2;
private LinkedList<GameObject> gameObjects;    public Physics(Controller1 c1, Controller2 c2){
this.c1 = c1;
this.c2 = c2;

}

public void tick(){

collisions(gameObjects);
gameObjects.clear();

}

double m1Vxf;
double m1Vyf;
double m2Vxf;
double m2Vyf;

for(int i = 0; i < modules.size(); i++){

for (int j = 0; j < modules.size(); j++){

GameObject m1 = modules.get(i);
GameObject m2 = modules.get(j);

if(m1.intersects(m2)){

if (m1.getFaction() != m2.getFaction()){

m1Vxf = (( ( Math.sqrt( Math.pow(m1.getVelX(),2) + Math.pow(m1.getVelY(),2) ) * (Math.cos(m1.theta()-m1.phi(m2))) * (m1.getMass() - m2.getMass()) + 2 * m2.getMass() * Math.sqrt( Math.pow(m2.getVelX(),2) + Math.pow(m2.getVelY(),2) ) * (Math.cos(Math.PI/2 - m1.phi(m2))) ) * Math.cos(m1.phi(m2))) / (m1.getMass()+m2.getMass()) + Math.sqrt( Math.pow(m1.getVelX(),2) + Math.pow(m1.getVelY(),2) ) * Math.sin(m1.theta()-m1.phi(m2))*Math.cos(m1.phi(m2)+Math.PI/2) );

m1Vyf = (( ( Math.sqrt( Math.pow(m1.getVelX(),2) + Math.pow(m1.getVelY(),2) ) * (Math.cos(m1.theta()-m1.phi(m2))) * (m1.getMass()- m2.getMass()) + 2 * m2.getMass() * Math.sqrt( Math.pow(m2.getVelX(),2) + Math.pow(m2.getVelY(),2) ) * (Math.sin(Math.PI/2-m1.phi(m2))) ) * Math.sin(m1.phi(m2))) / (m1.getMass()+m2.getMass()) + Math.sqrt( Math.pow(m1.getVelX(),2) + Math.pow(m1.getVelY(),2) ) * Math.sin(m1.theta()-m1.phi(m2))*Math.sin(m1.phi(m2)+Math.PI/2) );

m2Vxf = (( ( Math.sqrt( Math.pow(m2.getVelX(),2) + Math.pow(m2.getVelY(),2) ) * (Math.cos(m2.theta()  - m2.phi(m1))) * (m2.getMass()- m1.getMass()) + 2 * m1.getMass() * Math.sqrt( Math.pow(m1.getVelX(),2) + Math.pow(m1.getVelY(),2) ) * (Math.cos(Math.PI/2 -  m2.phi(m1))) ) * Math.cos(m2.phi(m1)))/ (m2.getMass()+m1.getMass()) + Math.sqrt( Math.pow(m2.getVelX(),2) + Math.pow(m2.getVelY(),2) ) * Math.sin(m2.theta() - m2.phi(m1))*Math.cos( m2.phi(m1)+Math.PI/2) );

m2Vyf = (( ( Math.sqrt( Math.pow(m2.getVelX(),2) + Math.pow(m2.getVelY(),2) ) * (Math.cos(m2.theta()  - m2.phi(m1))) * (m2.getMass()- m1.getMass()) + 2 * m1.getMass() * Math.sqrt( Math.pow(m1.getVelX(),2) + Math.pow(m1.getVelY(),2) ) * (Math.sin(Math.PI/2 -  m2.phi(m1))) ) * Math.sin(m2.phi(m1)))/ (m2.getMass()+m1.getMass()) + Math.sqrt( Math.pow(m2.getVelX(),2) + Math.pow(m2.getVelY(),2) ) * Math.sin(m2.theta() - m2.phi(m1))* Math.sin( m2.phi(m1)+Math.PI/2) );

m1.setVelX(m1Vxf);

m1.setVelY(m1Vyf);

m2.setVelX(m2Vxf);

m2.setVelY(m2Vyf);

//Temporary solution to the objects getting stuck () they only collide once).
m2.setFaction(m1.getFaction());
}

}

}

}

}}

Here are the methods used by the previous class, which are all in the GameObject abstract class.
Java:
 public boolean intersects(GameObject m){

if( Math.pow(this.getXr() - m.getXr(), 2 ) + Math.pow(this.getYr() - m.getYr() , 2 ) <= Math.pow(this.getRadius() + m.getRadius(), 2) )

return true;
else
return false;

}public double theta(){

//si this.y - m.y = 0 y this.x-m.x < 0 ->  0.
if(this.getVelY() == 0 && this.getVelX() < 0){
return Math.PI;
//si this.y - m.y > 0 y this.x-m.x = 0 ->  90.
}else if(this.getVelY() > 0 && this.getVelX() == 0){
return 3*Math.PI/2;
//si this.y - m.y = 0 y this.x-m.x > 0 ->  180.
}else if(this.getVelY() == 0 && this.getVelX() > 0){
return 0;
//si this.y - m.y < 0 y this.x-m.x = 0 ->  270.
}else if(this.getVelY() < 0 && this.getVelX() == 0){
return Math.PI/2;

}else if(this.getVelY() > 0 && this.getVelX() < 0){
return Math.PI + Math.atan2(this.getVelY()  , this.getVelX()) ;

}else if(this.getVelY() > 0 && this.getVelX() > 0){
return 2*Math.PI - Math.atan2(this.getVelY()  , this.getVelX()) ;

}else if(this.getVelY() < 0 && this.getVelX() > 0){
return Math.atan2(this.getVelY()  , this.getVelX() );

}else if(this.getVelY() < 0 && this.getVelX() < 0){
return Math.PI - Math.atan2(this.getVelY()  , this.getVelX()) ;

}else
return Math.atan2(this.getVelY()  , this.getVelX() );

}

public double phi(GameObject m){
//si this.y - m.y = 0 y this.x-m.x < 0 ->  0.
if(this.getYr() - m.getYr() == 0 && this.getXr() - m.getXr() < 0){
return 0;
//si this.y - m.y > 0 y this.x-m.x = 0 ->  90.
}else if(this.getYr() - m.getYr() > 0 && this.getXr() - m.getXr() == 0){
return Math.PI/2;
//si this.y - m.y = 0 y this.x-m.x > 0 ->  180.
}else if(this.getYr() - m.getYr() == 0 && this.getXr() - m.getXr() > 0){
return Math.PI;
//si this.y - m.y < 0 y this.x-m.x = 0 ->  270.
}else if(this.getYr() - m.getYr() < 0 && this.getXr() - m.getXr() == 0){
return 3*Math.PI/2;

}else if(this.getYr() - m.getYr() > 0 && this.getXr() - m.getXr() < 0){
return Math.atan2((this.getYr() - m.getYr()) , this.getXr() - m.getXr() );

}else if(this.getYr() - m.getYr() > 0 && this.getXr() - m.getXr() > 0){
return Math.PI - Math.atan2((this.getYr() - m.getYr()) , this.getXr() - m.getXr()) ;

}else if(this.getYr() - m.getYr() < 0 && this.getXr() - m.getXr() > 0){
return Math.PI + Math.atan2((this.getYr() - m.getYr()) , this.getXr() - m.getXr() );

}else if(this.getYr() - m.getYr() < 0 && this.getXr() - m.getXr() < 0){
return 2*Math.PI - Math.atan2(this.getYr() - m.getYr() , this.getXr() - m.getXr() );

}else
return Math.atan2((this.getYr() - m.getYr()) , this.getXr() - m.getXr() );

public double getXr() {
}

public double getYr() {
}

Last edited:
It looks like you're using the Pythagorean theorem to solve for coordinate positions. That will cause issues when you cross an axis.

Vold
Borg said:
It looks like you're using the Pythagorean theorem to solve for coordinate positions. That will cause issues when you cross an axis.

Indeed, I was planning to add a condition so that collisions can only be checked within the frame, to prevent that. However, this should not cause problems when their positions are +x, and +y, isn't it?(unless that 2 objects enter the frame on top of each other, but that's another issue)

I think my problem is related to theta and phi and the inverted Y-Axis. I can't find the correct angles.

I have been reading other equations using vectors, but I am not sure about how to implement them.

Last edited:
Scratch the last code it was a mess.

theta and phi use Math.atan now.

Java:
/**
*
* @return  theta: the angle of the direction of the object with the X axis.
* currently the conditions are only useful to prevent Y/0 division (pretty sure it
* does not use the best solution to it - adding 0.001). It calculates the angle with
* Math.atan(dy/dx). So, the conditions are there to correct certain angles due to that
* it does not cover the full 2 PI range. But, what are those corrections? I am clueless.
*/
public double theta(){

if(this.getVelY() == 0 && this.getVelX() < 0){

return Math.atan(this.getVelY()  / this.getVelX()) ;

}else if(this.getVelY() > 0 && this.getVelX() == 0){

return Math.atan(this.getVelY()  / (this.getVelX()+0.001)) ;

}else if(this.getVelY() == 0 && this.getVelX() > 0){

return Math.atan(this.getVelY()  / this.getVelX()) ;

}else if(this.getVelY() < 0 && this.getVelX() == 0){

return Math.atan(this.getVelY()  / (this.getVelX()+0.001)) ;

}else if(this.getVelY() > 0 && this.getVelX() < 0){

return Math.atan(this.getVelY()  / this.getVelX()) ;

}else if(this.getVelY() > 0 && this.getVelX() > 0){

return Math.atan(this.getVelY()  / this.getVelX()) ;

}else if(this.getVelY() < 0 && this.getVelX() > 0){

return Math.atan(this.getVelY()  / this.getVelX() );

}else if(this.getVelY() < 0 && this.getVelX() < 0){

return Math.atan(this.getVelY()  / this.getVelX()) ;

}else

return Math.atan(this.getVelY()  / this.getVelX() );

}
/**
*
* @param m
* @return phi: the angle of the collision of the objects with the X axis.
* currently the conditions are only useful to prevent Y/0 division (pretty sure it
* does not use the best solution to it - adding 0.001). It calculates the angle with
* Math.atan(dy/dx). So, the conditions are there to correct certain angles due to that
* it does not cover the full 2 PI range. But, what are those corrections? I am clueless.
* Note: the position of the objects is based on x+radius, to check where is the center,
* because x,y point is located at the upper left of the object.
*/
public double phi(GameObject m){

if(this.getYr() - m.getYr() == 0 && this.getXr() - m.getXr() < 0){

return Math.atan((this.getYr() - m.getYr()) / (this.getXr() - m.getXr()) );

}else if(this.getYr() - m.getYr() > 0 && this.getXr() - m.getXr() == 0){

return Math.atan((this.getYr() - m.getYr()) / (this.getXr() - m.getXr()+0.001) );

}else if(this.getYr() - m.getYr() == 0 && this.getXr() - m.getXr() > 0){

return Math.atan((this.getYr() - m.getYr()) / (this.getXr() - m.getXr()) );

}else if(this.getYr() - m.getYr() < 0 && this.getXr() - m.getXr() == 0){

return Math.atan((this.getYr() - m.getYr()) / (this.getXr() - m.getXr()+0.001) );

}else if(this.getYr() - m.getYr() > 0 && this.getXr() - m.getXr() < 0){

return Math.atan((this.getYr() - m.getYr()) / (this.getXr() - m.getXr()) );

}else if(this.getYr() - m.getYr() > 0 && this.getXr() - m.getXr() > 0){

return Math.atan((this.getYr() - m.getYr()) / (this.getXr() - m.getXr()) );

}else if(this.getYr() - m.getYr() < 0 && this.getXr() - m.getXr() > 0){

return Math.atan((this.getYr() - m.getYr()) / (this.getXr() - m.getXr()) );

}else if(this.getYr() - m.getYr() < 0 && this.getXr() - m.getXr() < 0){

return Math.atan((this.getYr() - m.getYr()) / (this.getXr() - m.getXr()) );

}else

return Math.atan((this.getYr() - m.getYr()) / (this.getXr() - m.getXr()) );
}

public double getXr() {
}

public double getYr() {
}
Java:
    /**
*
* @param gameObjects
* Every time the game updates, this method determines whether or not any
* object contained in the list collides with each other. If they collide, it
* changes the direction and speed of both objects based on their position,
* angle of impact (Phi), speed vector angle at the moment of collision (Theta),
* the mass and radius of each object (the objects are circle shaped).
*/

//Declaration of the final velocity values of
double gO1Vxf;
double gO1Vyf;
double gO2Vxf;
double gO2Vyf;

//the loop.
for(int i = 0; i < gameObjects.size(); i++){
for (int j = i+1; j < gameObjects.size(); j++){

//Assigns the object located in i position to gO1 and j position to gO2
GameObject gO1 = gameObjects.get(i);
GameObject gO2 = gameObjects.get(j);

//checks if the objects intersect using a method of gameObject's class.
if(gO1.intersects(gO2)){
//checks if the objects intersecting each other have different faction,
//added this in case that I want a specific object not to be checked for collisions.
if (gO1.getFaction() != gO2.getFaction()){

//debug info.
System.out.println("");
System.out.println("Phi"+Math.toDegrees(gO1.phi(gO2)));
System.out.println("");
System.out.println("M1");
System.out.println("x:"+gO1.getXr());
System.out.println("y:"+gO1.getYr());
System.out.println("Vx:"+gO1.getVelX());
System.out.println("Vy:"+gO1.getVelY());
System.out.println("theta:"+Math.toDegrees(gO1.theta()));
System.out.println("");
System.out.println("M2");
System.out.println("x:"+gO2.getXr());
System.out.println("y:"+gO2.getYr());
System.out.println("Vx:"+gO2.getVelX());
System.out.println("Vy:"+gO2.getVelY());
System.out.println("theta:"+Math.toDegrees(gO2.theta()));

//calculates the final Vx of gO1.
gO1Vxf = (( ( Math.sqrt( Math.pow(gO1.getVelX(),2) + Math.pow(gO1.getVelY(),2) ) * (Math.cos(gO1.theta() - gO1.phi(gO2))) * (gO1.getMass() - gO2.getMass()) + 2 * gO2.getMass() * Math.sqrt( Math.pow(gO2.getVelX(),2) + Math.pow(gO2.getVelY(),2) ) * (Math.cos(Math.PI/2 - gO2.theta())) ) * Math.cos(gO1.phi(gO2))) / (gO1.getMass()+gO2.getMass()) + Math.sqrt( Math.pow(gO1.getVelX(),2) + Math.pow(gO1.getVelY(),2) ) * Math.sin(gO1.theta() - gO1.phi(gO2)) * Math.cos(gO1.phi(gO2)+Math.PI/2) );
//calculates the final Vy of gO1.
gO1Vyf = (( ( Math.sqrt( Math.pow(gO1.getVelX(),2) + Math.pow(gO1.getVelY(),2) ) * (Math.cos(gO1.theta() - gO1.phi(gO2))) * (gO1.getMass() - gO2.getMass()) + 2 * gO2.getMass() * Math.sqrt( Math.pow(gO2.getVelX(),2) + Math.pow(gO2.getVelY(),2) ) * (Math.sin(Math.PI/2 - gO2.theta())) ) * Math.sin(gO1.phi(gO2))) / (gO1.getMass()+gO2.getMass()) + Math.sqrt( Math.pow(gO1.getVelX(),2) + Math.pow(gO1.getVelY(),2) ) * Math.sin(gO1.theta() - gO1.phi(gO2)) * Math.sin(gO1.phi(gO2)+Math.PI/2) );
//calculates the final Vx of gO2.
gO2Vxf = (( ( Math.sqrt( Math.pow(gO2.getVelX(),2) + Math.pow(gO2.getVelY(),2) ) * (Math.cos(gO2.theta() - gO1.phi(gO2))) * (gO2.getMass() - gO1.getMass()) + 2 * gO1.getMass() * Math.sqrt( Math.pow(gO1.getVelX(),2) + Math.pow(gO1.getVelY(),2) ) * (Math.cos(Math.PI/2 - gO1.theta())) ) * Math.cos(gO1.phi(gO2))) / (gO2.getMass()+gO1.getMass()) + Math.sqrt( Math.pow(gO2.getVelX(),2) + Math.pow(gO2.getVelY(),2) ) * Math.sin(gO2.theta() - gO1.phi(gO2)) * Math.cos(gO1.phi(gO2)+Math.PI/2) );
//calculates the final Vy of gO2.
gO2Vyf = (( ( Math.sqrt( Math.pow(gO2.getVelX(),2) + Math.pow(gO2.getVelY(),2) ) * (Math.cos(gO2.theta() - gO1.phi(gO2))) * (gO2.getMass() - gO1.getMass()) + 2 * gO1.getMass() * Math.sqrt( Math.pow(gO1.getVelX(),2) + Math.pow(gO1.getVelY(),2) ) * (Math.sin(Math.PI/2 - gO1.theta())) ) * Math.sin(gO1.phi(gO2))) / (gO2.getMass()+gO1.getMass()) + Math.sqrt( Math.pow(gO2.getVelX(),2) + Math.pow(gO2.getVelY(),2) ) * Math.sin(gO2.theta() - gO1.phi(gO2)) * Math.sin(gO1.phi(gO2)+Math.PI/2) );

//sets the final velocity of each object.
gO1.setVelX(gO1Vxf);
gO1.setVelY(gO1Vyf);
gO2.setVelX(gO2Vxf);
gO2.setVelY(gO2Vyf);

//those objects can no longer collide (temporary "solution", to prevent objects getting stuck)
gO2.setFaction(gO1.getFaction());

//More debug info.
System.out.println("");
System.out.println("M1");
System.out.println("x:"+gO1.getXr());
System.out.println("y:"+gO1.getYr());
System.out.println("Vx:"+gO1.getVelX());
System.out.println("Vy:"+gO1.getVelY());
System.out.println("theta:"+Math.toDegrees(gO1.theta()));
System.out.println("");
System.out.println("M2");
System.out.println("x:"+gO2.getXr());
System.out.println("y:"+gO2.getYr());
System.out.println("Vx:"+gO2.getVelX());
System.out.println("Vy:"+gO2.getVelY());
System.out.println("theta:"+Math.toDegrees(gO2.theta()));

}

}

}

}

}}

I ran 4 collisions, here you can see the results:

Code:
Initial position, direction and speed

Angle of collision
Phi44.55387947558248

gO1
x:610.0
y:307.0
Vx:-1.0
Vy:-1.0
theta, deg:45.0

gO2
x:564.7000000000047
y:262.39999999999765
Vx:0.1
Vy:0.2
theta, deg:63.43494882292201

gO1
x:610.0
y:307.0
Vx:0.1347928708643486
Vy:0.0780045497865444
theta, deg:30.057939577479935

gO2
x:564.7000000000047
y:262.39999999999765
Vx:0.6618245460937124
Vy:0.753142930591247
theta, deg:48.6926021887038

Result: awkward collision
Initial position, direction and speed

Angle of collision
Phi-38.54716002467078

gO1
x:518.0
y:308.0
Vx:1.0
Vy:-1.0
theta, deg:-45.0

gO2
x:567.7000000000054
y:268.3999999999973
Vx:0.1
Vy:0.2
theta, deg:63.43494882292201

gO1
x:518.0
y:308.0
Vx:0.05737619804002281
Vy:-0.18661970335408506
theta, deg:-72.9099627395368

gO2
x:567.7000000000054
y:268.3999999999973
Vx:-0.6457889329971941
Vy:-0.45208696110824664
theta, deg:34.994086837063115

Result: Collision seemed correct
Initial position, direction and speed

Angle of collision
Phi:59.37821096161859

gO1
x:549.0
y:241.0
Vx:1.0
Vy:1.0
theta, deg:45.0

gO2
x:581.2000000000085
y:295.39999999999577
Vx:0.1
Vy:0.2
theta, deg:63.43494882292201

gO1
x:549.0
y:241.0
Vx:0.4040808389247806
Vy:-0.09282509782172968
theta, deg:-12.937479313695539

gO2
x:581.2000000000085
y:295.39999999999577
Vx:0.49575577862969233
Vy:0.8686060359455879
theta, deg:60.28449181093118

Result: awkward collision
Initial position, direction and speed

Angle of collision
Phi-55.11124190812558

M1
x:619.0
y:246.0
Vx:-1.0
Vy:1.0
theta, deg:-45.0

M2
x:582.6000000000088
y:298.1999999999956
Vx:0.1
Vy:0.2
theta, deg:63.43494882292201

M1
x:619.0
y:246.0
Vx:0.318051489820771
Vy:0.059985539221356285
theta, deg:10.680706869495856

M2
x:582.6000000000088
y:298.1999999999956
Vx:-0.4108658742107923
Vy:-0.7079128926404634
theta, deg:59.869530325480376

Result: awkward collision

Last edited:

## 1. What is 2D collision detection?

2D collision detection is a method used in computer graphics and physics simulations to determine when and where two objects in a 2D space have collided. It involves detecting when the boundaries of two objects intersect with each other.

## 2. How does 2D collision detection work?

2D collision detection works by first defining the boundaries of each object in the 2D space using mathematical equations. Then, the positions of each object are constantly checked in relation to each other to see if their boundaries have intersected. If a collision is detected, the necessary actions are taken, such as changing the objects' velocities.

## 3. What is the difference between pixel-perfect collision detection and bounding box collision detection?

Pixel-perfect collision detection involves comparing the pixels of two objects to determine if they have collided, while bounding box collision detection involves comparing the rectangular boundaries of two objects to detect collisions. Pixel-perfect collision detection is more accurate but also more computationally expensive, while bounding box collision detection is less accurate but more efficient.

## 4. How is final velocity determined in 2D collision detection?

The final velocity of an object after a collision is determined by taking into account the initial velocities of both objects, as well as their masses and the conservation of momentum and energy. This is usually calculated using equations such as the law of conservation of momentum and the coefficient of restitution.

## 5. How can I implement 2D collision detection and determining final velocity in Java?

There are a few different ways to implement 2D collision detection and determining final velocity in Java, depending on the specific requirements of your project. Some common methods include using built-in Java libraries such as the Shape and Rectangle classes, implementing your own algorithms using basic geometry concepts, or using third-party libraries specifically designed for collision detection and physics simulations. It's important to research and choose the most suitable method for your project's needs.

• Programming and Computer Science
Replies
2
Views
1K
• Programming and Computer Science
Replies
1
Views
3K
• Introductory Physics Homework Help
Replies
1
Views
1K
• Calculus and Beyond Homework Help
Replies
3
Views
2K
• Programming and Computer Science
Replies
1
Views
2K
• Programming and Computer Science
Replies
8
Views
4K
• Programming and Computer Science
Replies
4
Views
5K
• Introductory Physics Homework Help
Replies
2
Views
1K
• Introductory Physics Homework Help
Replies
3
Views
2K
• Programming and Computer Science
Replies
1
Views
2K