I've have this code and the balls bounces around, but when I apply some gravity I want them to stay on top of each other. But instead they slowly fall through.
Screenshot
Here's my code.
for (int i = 0; i < balls.size(); i++) {
Ball curr = balls.get(i);
if (curr.getPos().getX() - curr.radius <= 0) {
curr.getVel().mul(-0.9, 1);
curr.getPos().set(curr.radius, curr.getPos().getY());
} else if (curr.getPos().getX() + curr.radius >= WIDTH) {
curr.getVel().mul(-0.9, 1);
curr.getPos().set(WIDTH - curr.radius, curr.getPos().getY());
}
if (curr.getPos().getY() - curr.radius <= 0) {
curr.getVel().mul(1, -0.9);
curr.getPos().set(curr.getPos().getX(), curr.radius);
} else if (curr.getPos().getY() + curr.radius >= HEIGHT) {
curr.getVel().mul(1, -0.9);
curr.getPos().set(curr.getPos().getX(), HEIGHT - curr.radius);
}
for (int j = 0; j < balls.size(); j++) {
if (j == i) continue;
Ball other = balls.get(j);
double xDist = curr.getPos().getX() - other.getPos().getX();
double yDist = curr.getPos().getY() - other.getPos().getY();
double distance = xDist * xDist + yDist * yDist;
if (distance <= (curr.radius + other.radius) * (curr.radius + other.radius)) {
double xVel = other.getVel().getX() - curr.getVel().getX();
double yVel = other.getVel().getY() - curr.getVel().getY();
double dot = xDist * xVel + yDist * yVel;
if (dot > 0) {
double colScale = dot / distance;
double xCol = xDist * colScale;
double yCol = yDist * colScale;
double mass = curr.mass + other.mass;
double weightCurr = 2 * other.mass / mass;
double weightOther = 2 * curr.mass / mass;
curr.getVel().add(weightCurr * xCol, weightCurr * yCol);
other.getVel().sub(weightOther * xCol, weightOther * yCol);
curr.getPos().add(curr.getVel().mul(DMP));
other.getPos().sub(other.getVel().mul(DMP));
}
}
}
}
Thanks!
A line should be inserted after this one:
for (int j = 0; j < balls.size(); j++) {
if( j == i ) continue;
Otherwise each ball is balling with itself.
Another potential source of error is the way balls are bounced against the four walls and each other: The first ball is wall-checked and then bounces against all others, and only then are all others wall-checked.
for (int i = 0; i < balls.size(); i++) {
Ball curr = balls.get(i);
// wall checks
} // wall-check done!
for (int i = 0; i < balls.size(); i++) {
Ball curr = balls.get(i);
for (int j = 0; j < balls.size(); j++) {
if( j == i ) continue;
// ball - ball check ...
}
}
Also, if ball 1 passes the check with ball 2, but ball 2 is then pushed away by ball 3 into the radius of ball 1, overlaps may occur. Not sure how to cope with this: repeat ball-ball until no corrections are necessary?
Related
I wrote a function which takes two parameters:
JPG image as 3D array
rotation degrees given by alpha
My approach was:
public static int[][] rotate(int[][] img, double alpha) {
double rad = Math.toRadians(alpha);
double sin = Math.sin(rad);
double cos = Math.cos(rad);
int height = img.length;
int width = img[0].length;
int[][] rotate = new int[height][width];
for(int i = 0; i < height; i++) {
for(int j = height - i - 1; j < width; j++) {
if(j < height && i < width) {
double i_new = Math.floor(cos * (img[i].length - i) - sin * (img[j].length - j)) + i;
double j_new = Math.floor(sin * (img[i].length - i) + cos * (img[j].length - j)) + j;
rotate[i][j] = img[(int)j_new][(int)i_new];
}
}
}
return rotate;
}
While fixing the index range, the output is a black image. What am I missing?
After a while I got to a solution.
Caution: Its not using any special pre-defined libraries.
The global function which run`s over the matrice:
public static int[][] rotate(int[][] img, double alpha) {
double rad = Math.toRadians(alpha); //construct of the relevant angles
double sin = Math.sin(rad);
double cos = Math.cos(rad);
int height = img.length;
int width = img[0].length;
int[][] rotate = new int[height][width];
int a = height / 2; //we will use the area of a and b to compare coordinates by the formula given
int b = width / 2;
for (int i = 0; i < height; i++) {
for (int j = 0; j < width; j++) {
double i_new = Math.floor(cos * (i - a) - sin * (j - b)) + a; // following the conversion function
double j_new = Math.floor(sin * (i - a) + cos * (j - b)) + b;
if (i_new >= rotate.length || i_new < 0 || j_new >= rotate[0].length || j_new < rotate[0][0]) { // if out of scope of the conversion --> print none
System.out.print(""); //mainly cause 'continue' statements are not necessary in java and JS
} else {
rotate[(int) i_new][(int) j_new] = img[i][j]; //convert
}
}
}
return rotate;
}
The global function which rotates each 2D matrice:
public static int[][][] rotate_alpha(int[][][] img, double alpha) {
int height = img[0].length;
int width = img[0][0].length;
int[][][] rotated = new int[3][height][width];
for (int k = 0; k < 3; k++) {
rotated[k] = rotate(img[k], alpha);
}
return rotated;
}
Hope this topic is solved by now, and stands by all the standards of the clean code.
I have a program in Java like this one (https://www3.ntu.edu.sg/home/ehchua/programming/java/J8a_GameIntro-BouncingBalls.html). It is the object oriented one after Example 2 but with some slightly changes.
I want to generate random coordinates for the balls to spawn. But they are not allowed to intersect each other at spawning moment. The generated coordinates are the top left corner of an rectangle around the circle.
So the coordinates need a minimum distance of 2 * ballRadius.
I only got ether coordinates that have the distance of 2 * ballRadius but then there are only unique coordinates for x and y. So i only got one ball per available y coordinate.
Example There could be a Ball at the red circle but the one left to it "blocks" the y coordinate.
Every other coordinates i get are intersecting each other.
Thats my code so far.
int uniqueXY[][] = new int[ballCount][2];
for (int i = 0; i < ballCount; i++) {
int tempx = 0;
int tempy = 0;
Boolean foundX = true;
Boolean foundY = true;
while(foundX && foundY) {
tempx = (int) (Math.random() * field.maxX); // generate random number in range of filed
tempy = (int) (Math.random() * field.maxY);
for (int j = 0; j < ballCount; j++) { // Here it should check if the number is within the given rules
if ((uniqueXY[j][0] - (2 * ballRadius) > tempx) || (uniqueXY[j][0] + (2 * ballRadius) < tempx)) {
foundX = false;
} else {
foundX = true;
if ((uniqueXY[j][1] - (2 * ballRadius) > tempy) || (uniqueXY[j][1] + (2 * ballRadius) < tempy)) {
foundY = false;
foundX = false;
break;
} else {
foundY = true;
break;
}
}
}
}
uniqueXY[i][0] = tempx;
uniqueXY[i][1] = tempy;
So I have come up with some new Code with the similar problem.
It calculates the distance between every set coordinate and the temporay ones. For the most part it works fine, it just behaves akward if I strees the code and force large numbers of balls.Example with 400 balls Only the balls at the border get forced together.
int uniqueXY[][] = new int[ballCount][2];
for (int k = 0; k < ballCount; k++) {
Boolean found = true;
while(found) {
int tempX = (int) (Math.random() * field.maxX); //create rnd x/y in range of field
int tempY = (int) (Math.random() * field.maxY);
if (k == 0) { // first case gets set, because nothing to compare
uniqueXY[k][0] = tempX;
uniqueXY[k][1] = tempY;
found = false;
break;
}
for (int j = 0; j < k; j++) { //calculates distance between every set coordinate and the temp
int erg1 = (int) (Math.pow(uniqueXY[j][0] - tempX, 2));
int erg2 = (int) (Math.pow(uniqueXY[j][1] - tempY, 2));
int distance = (int) Math.sqrt(erg1 + erg2);
if (distance < 60) { // if distance between coordinates < 60 temp gets discarded
found = true;
break;
}
if (j == k - 1) { // if every set case is checked and distance was always fine, temp gets set
uniqueXY[k][0] = tempX;
uniqueXY[k][1] = tempY;
found = false;
}
}
}
}
I new android. Trying to implement bouncing ball each other. Like this
My BouncingBallView class look like:
private ArrayList<Ball> balls;
public BouncingBallView(Context context) {
super(context);
this.balls = new ArrayList<>();
for(int i=0; i<2; i++)
this.balls.add(addBall());
}
public Ball addBall(){
Ball ball;
// Init the ball at a random location (inside the box) and moveAngle
Random rand = new Random();
int radius = 60;
int x = rand.nextInt(500 - radius * 2 - 20) + radius + 10;
int y = rand.nextInt(800- radius * 2 - 20) + radius + 10;
int speed = 10;
int angleInDegree = rand.nextInt(360);
ball = new Ball(x, y, radius, speed, angleInDegree);
return ball;
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
for(int i=0; i < balls.size(); i++)
balls.get(i).draw(canvas);
for(int i=0; i < balls.size(); i++){
balls.get(i).intersect(canvas);
// Update the ball's state with proper collision response if collided.
balls.get(i).update();
}
for(int i=0; i<balls.size(); i++){
balls.get(i).collide(balls);
}
invalidate();
}
And class Ball has method collide();
public void collide(ArrayList<Ball> balls){
//Log.d("TEst", "LOG");
// Calculate difference between centres
float distX = balls.get(0).getBallX() - balls.get(1).getBallX();
float distY = balls.get(0).getBallY() - balls.get(1).getBallY();
// Get distance with Pythagora
double dist = Math.sqrt((distX * distX) + (distY * distY));
float r = ballRadius + ballRadius;
if ((float) dist <= r) {
Log.d("Collide", "Detected");
this.ballSpeedX = -this.ballSpeedX;
this.ballSpeedY = -this.ballSpeedY++;
}
/*for(int i=0; i < balls.size(); i++) {
for(int j=1; j<balls.size(); j++) {
// Calculate difference between centres
float distX = balls.get(i).getBallX() - balls.get(j).getBallX();
float distY = balls.get(i).getBallY() - balls.get(j).getBallY();
// Get distance with Pythagora
double dist = Math.sqrt((distX * distX) + (distY * distY));
*//*double distance = Math.sqrt(((balls.get(0).getBallX() - balls.get(1).getBallX()) * (balls.get(0).getBallX() - balls.get(1).getBallX())) + ((balls.get(0).getBallY()
- balls.get(1).getBallY()) * (balls.get(0).getBallY() - balls.get(1).getBallY())));*//*
float r = ballRadius + ballRadius;
if ((float) dist <= r) {
Log.d("Collide", "Detected");
this.ballSpeedX = -this.ballSpeedX;
this.ballSpeedY = -this.ballSpeedY++;
}
}
}*/
}
Here, I'm using Pythagoras' theorem, a^2 + b^2 = c^2, to figure out the distance between the two circles' centers. How to calculate if ball count > 2. I try to in loop but it works very bad(frozen ball).
Full code you can find github
Now work like in this video my bouncing ball video
Thx for help ;)
P.s Sorry so poor English.
You can have a counter for balls in each stage, when a ball is destroyed(if you destroy them) you can change the count and you can check (the counter) in the collide method.
But i don't thing that you need to check this condition, if a collision happens then OK else nothing should happen to this method.
Recently I have been using the Polygon class to create asteroids as well as bullets and a spaceship. I am currently trying to create the collision detection for the program however it appears that the collision detection only works around 1/5 of the time (no pattern appears as to why it works).
Here's the code..
Creating the Polygon:
void renderPoly() {
int j;
int s = sides;
double r, angle;
int x, y;
for (j = 0; j < s; j++) {
angle = 2 * Math.PI / s * j;
r = MIN_ROCK_SIZE + (int) (Math.random() * (MAX_ROCK_SIZE - MIN_ROCK_SIZE));
x = (int) (r * Math.cos(angle));
y = (int) (r * -Math.sin(angle));
cOM[0] += x;
cOM[1] += y;
pointData[j][0] = x;
pointData[j][1] = y;
}
cOM[0] /= asteroidShape.npoints;
cOM[1] /= asteroidShape.npoints;
for (int i = 0; i < asteroidShape.npoints; i++) {
pointData[i][0] += cOM[0];
pointData[i][1] += cOM[1];
}
}
rotating and moving the polygon:
void move() {
int x, y, i;
//change rotation
theta += rotVel;
//change x
asteroidData[0] += deltaX;
//change y
asteroidData[1] += deltaY;
for (i = 0; i < asteroidShape.npoints; i++) {
x = (int) (pointData[i][0] * Math.cos(theta) - pointData[i][1] * Math.sin(theta) );
y = (int) (pointData[i][0] * Math.sin(theta) + pointData[i][1] * Math.cos(theta) );
asteroidShape.xpoints[i] = x + asteroidData[0];
asteroidShape.ypoints[i] = y + asteroidData[1];
asteroidShape.invalidate();
}
}
check if touching bullet:
boolean hitBullet(Bullet b) {
this.asteroidShape.invalidate();
for (int i = 0; i < b.bulletShape.npoints; i++)
if (this.asteroidShape.contains(b.bulletShape.xpoints[i], b.bulletShape.ypoints[i]) )
return true;
for (int j = 0; j < this.asteroidShape.npoints; j++)
if (b.bulletShape.contains(this.asteroidShape.xpoints[j], this.asteroidShape.ypoints[j]) )
return true;
return false;
}
(the ship method is the same except the constructor requires a ship object)
as well as the loop that calls it in the 'game' class:
for (int i = 0; i < aArray.length-1; i++) {
if (aArray[i] != null) {
for (int j = 0; j < bArray.length-1; j++) {
if (bArray[j] != null) {
if (aArray[i].hitBullet(bArray[j])) {
aArray[i] = null;
bArray[j] = null;
i = aArray.length-1;
j = bArray.length-1;
}
}
else {
i = aArray.length-1;
j = bArray.length-1;
}
}
}
else {
i = aArray.length-1;
}
}
I have been looking around at alternative solutions such as the Separating Axis Theorem however I do have convex polygons at times and since this method (.contains()) already exists I would like to use it.
Any help would be appreciated, thanks!
The easy way to solve this that I've found is to convert Shapes (in your case Polygon(2D?)) into Areas. You can use Area.intersect(Area) to see if two Areas have collided
My assignment is to have a button for adding balls which appear at random location and direction. I'm having trouble checking for collision between the balls. The trouble is on line 123. Thanks!
import java.util.ArrayList;
import java.util.Scanner;
import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.beans.property.DoubleProperty;
import javafx.event.ActionEvent;
import javafx.scene.layout.Pane;
import javafx.util.Duration;
class BallPane extends Pane {
private double radius = 10;
private ArrayList<Ball> list = new ArrayList();
private Timeline animation;
public BallPane() {
Scanner kb = new Scanner(System.in);
System.out.println("\nPlease press RIGHT and LEFT arrow keys to increase and decrease"
+ " the speed of the balls.");
animation = new Timeline(
new KeyFrame(Duration.millis(50), (ActionEvent e) -> {
moveBall();
}));
animation.setCycleCount(Timeline.INDEFINITE);
animation.play(); // Start animation
}
public void addBall() {
Ball balls = new Ball(BallPane.this);
getChildren().add(balls.getCircle());
list.add(balls);
}
public void moveBall() {
double x, y, dx, dy;
// Check boundaries
Ball a;
for (int i = 0; i < list.size(); i++) {
a = list.get(i);
x = a.getCircle().getCenterX();
y = a.getCircle().getCenterY();
dx = a.getDirectionX() + (int) Math.random() * 100;
dy = a.getDirectionY() + (int) Math.random() * 100;
if (x < a.getCircle().getRadius()
|| x > getWidth() - a.getCircle().getRadius()) {
dx *= -1; // Change ball move direction
}
if (y < a.getCircle().getRadius()
|| y > getHeight() - a.getCircle().getRadius()) {
dy *= -1; // Change ball move direction
}
// Adjust ball position
x += dx;
y += dy;
a.setDirectionX(dx);
a.setDirectionY(dy);
a.getCircle().setCenterX(x);
a.getCircle().setCenterY(y);
// Ball b = list.get(i + 1);
}
//line 123
//also trying to use the distance formula to check the distance between two circles' center points
for (int i = 0; i < Timeline.INDEFINITE; i++) {
if (Math.sqrt(Math.pow(list.get(i).getCenterX()
- list.get(i + 1).getCenterX(), 2) + Math.pow(list.get(i).getCenterY()
- list.get(i + 1).getCenterY(), 2)) <= 2 * radius) {
list.get(i).getCircle();
getChildren().remove(i);
list.remove(i);
}
}
}
}//End of BallPane class.
Thanks for the help! It is mostly code Stack Overflow... why're you making me type this.asdl;kfj dsal;kf jldfjals ;kdjfldksajf l;askfj
try the following codes modified from Mailkov's original post by reversing the looping order
for (int i = list.size()-1; i >=1 ; i--)
{
for (int j=(i-1); j >=0; j--)
{
if (Math.sqrt(Math.pow(list.get(i).getCenterX() - list.get(j).getCenterX(), 2) +
Math.pow(list.get(i).getCenterY() - list.get(j).getCenterY(), 2)) <= 2 * radius )
{
list.get(i).getCircle();
getChildren().remove(i);
list.remove(i);
break; // I think we need to break out the for (j) loop here
}
}
}
try this:
ArrayList<Integer> ballexplode=new ArrayList<Integer>();
for (int i = 0; i < list.size()-1; i++) {
for (int j=i+1; j < list.size();j++){
if (Math.sqrt(Math.pow(list.get(i).getCenterX()
- list.get(j).getCenterX(), 2) + Math.pow(list.get(i).getCenterY()
- list.get(j).getCenterY(), 2)) <= 2 * radius) {
list.get(i).getCircle();
ballexplode.add(i);
ballexplode.add(j);
}
}
}
for (int i=ballexplode.size()-1;i>=0;i--){
getChildren().remove(ballexplode.get(i));
list.get(ballexplode.get(i)).getCircle();
}