Bouncing balls - balls passing through boundaries - java

I've created a bouncing balls animation, that's drawn on a surface (library from my university) that has 2 frames in it, and 2 arrays of balls that half goes to the first frame and the second to the second frame.
For some reason - the balls pass through to y and x axis of their frame.
I've already tried doing some of the solutions that were suggested (like here) and it didn't help..
my code:
public static void updateBalVelocity(Ball ball, int width, int height,
int minWidth, int minHeight) {
// get default value of the dx and dy
double dx = ball.getVelocity().getDx();
double dy = ball.getVelocity().getDy();
// check if the ball is touching the border (x/y axis) and if so -
// change it's directions
if ((ball.getX() + ball.getSize() + dx) >= width) {
dx = (dx > 0) ? -dx : dx;
} else if ((ball.getX() - ball.getSize()) <= minWidth) {
dx = Math.abs(dx);
}
if ((ball.getY() + ball.getSize() + dy) >= height) {
dy = (dy > 0) ? -dy : dy;
} else if ((ball.getY() - ball.getSize()) <= minHeight) {
dy = Math.abs(dy);
}
// apply the velocity to the ball
ball.setVelocity(dx, dy);
}
public static void moveBalls(Ball[] balls, DrawSurface d, int height,
int width, int minHeight, int minWidth) {
for (Ball ball : balls) {
updateBalVelocity(ball, width, height, minWidth, minHeight);
ball.moveOneStep();
d.setColor(ball.getColor());
d.fillCircle(ball.getX(), ball.getY(), ball.getSize());
ball.drawOn(d);
}
}
and in the run method:
while (true) {
...
// move balls
BouncingBallHelper.moveBalls(ballsD1, d, 500, 500, 50, 50);
BouncingBallHelper.moveBalls(ballsD2, d, 600, 600, 450, 450);
gui.show(d);
// wait for 50 milliseconds.
sleeper.sleepFor(50);
}

Related

Why is paintComponent drawing the circle many times?

I need to make a circle look like it is bouncing but the output I get is many circles instead of only one moving. How can I make paint component only draw one circle during the animation instead of showing me several balls as it moves?
class thePanel extends JPanel {
int radius;
int diameter;
int x;
int y;
int dx;
int dy;
int timerInterval = 10;
Timer timer;
thePanel() {
x = 0;
y = 0;
dx = 3;
dy = 3;
radius = 20;
diameter = radius * 2;
timer = new Timer(timerInterval, new TimerListener());
timer.start();
repaint();
}
protected void paintComponent(Graphics g) {
super.paintComponents(g);
g.drawRect(getWidth() / 2, 0, getWidth() / 2, getHeight());
g.drawOval(this.x - radius, this.y - radius, diameter, diameter);
}
public void fixPosition() {
x += dx;
y += dy;
if (x < radius)
dx = Math.abs(dx);
if (x > getWidth() - radius)
dx = -Math.abs(dx);
if (y < radius)
dy = Math.abs(dy);
if (y > getHeight() - radius)
dy = -Math.abs(dy);
}
class TimerListener implements ActionListener {
#Override
public void actionPerformed(ActionEvent e) {
fixPosition();
repaint();
}
}
}

Change color on every 3rd bounce

I'm using Processing;
I have a ball that bounces when it hits the border and changes its color to random. Now I need this ball to change its color on every third bounce. Can't figure out how to do it.
Here is my current code:
float xPos;// x-position
float vx;// speed in x-direction
float yPos;// y-position
float vy;// speed in y-direction
float r;
float g;
float b;
void setup()
{
size(400, 300);
fill(255, 177, 8);
textSize(48);
// Initialise xPos to center of sketch
xPos = width / 2;
// Set speed in x-direction to -2 (moving left)
vx = -2;
yPos = height / 2;
vy = -1;
}
void draw()
{
r = random(255);
b = random(255);
g = random(255);
background(64);
yPos = yPos + vy;
// Change x-position on each redraw
xPos = xPos + vx;
ellipse(xPos, yPos, 50, 50);
if (xPos <= 0)
{
vx = 2;
fill(r, g, b);
} else if (xPos >= 400)
{
vx = -2;
fill(r, g, b);
}
if (yPos <= 0)
{
vy = 1;
fill(r, g, b);
} else if (yPos >= 300)
{
vy = -1;
fill(r, g, b);
}
}
It is quite easy. You maintain a counter which counts the amount of bounces. Therefore you increase the counter by one after every bounce. If it reaches 3 you change the color. After that you reset the counter and repeat.
Therefore add this member variable to your class (like you already did with xPos and others):
private int bounceCounter = 0;
which introduces the variable bounceCounter initially holding 0 as value.
Here is the modified draw method with highlighted changes and comments:
void draw() {
// New color to use if ball bounces
r = random(255);
b = random(255);
g = random(255);
background(64);
yPos = yPos + vy;
// Change x-position on each redraw
xPos = xPos + vx;
ellipse(xPos, yPos, 50, 50);
// Variable indicating whether the ball bounced or not
boolean bounced = false;
// Out of bounds: left
if (xPos <= 0) {
vx = 2;
bounced = true;
// Out of bounds: right
} else if (xPos >= 400) {
vx = -2;
bounced = true;
}
// Out of bounds: bottom
if (yPos <= 0) {
vy = 1;
bounced = true;
// Out of bounds: top
} else if (yPos >= 300) {
vy = -1;
bounced = true;
}
// React to bounce if bounced
if (bounced) {
// Increase bounce-counter by one
bounceCounter++;
// Third bounce occurred
if (bounceCounter == 3) {
// Change the color
fill(r, g, b);
// Reset the counter
bounceCounter = 0;
}
}
}

Midpoint Ellipse Algorithm in Java

I am trying to write a program to allow a user to draw an ellipse by using clicks. The user left-clicks at first to select the radius, then right-clicks to select the horizontal radius, then right-clicks again to select the vertical radius. Nothing is drawn after clicking. I don't understand where the error is.
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class Ellipse extends JPanel implements MouseListener{
Graphics P;
public Ellipse()
{
addMouseListener(this);
}
static int Radius = 0;
int CenterX, CenterY, RadiusX, RadiusY;
public void paintComponent(Graphics g)
{
P=g;
EllipseMidpoint(CenterX, CenterY, RadiusX, RadiusY);
}
public void EllipseMidpoint(int Cx, int Cy, int Rx, int Ry)
{
int Rx2 = Rx * Rx;
int Ry2 = Ry * Ry;
int twoRx2 = 2 * Rx2;
int twoRy2 = 2 * Ry2;
int x = 0;
int y = Ry;
int p;
int px= 0;
int py = twoRx2 * y;
PlotEllipsePoint(Cx, Cy, x, y);
//Region 1
p = (int)(Ry2 - (Rx2 * Ry) + (0.25 + Rx2));
while (px < py)
{
x = x + 1;
px = twoRy2 + px;
if (p < 0)
{
p = Ry2 + px + p;
}
else
{
y = y - 1;
py = twoRx2 - py;
p = Ry2 + px - py + p;
}
PlotEllipsePoint(Cx, Cy, x, y);
}
//Region2
p = (int)(Ry2 * (x + 0.5) * (x + 0.5) + Rx2 * (y - 1) * (y - 1) - Rx2 * Ry2);
while (y > 0)
{
y = y - 1;
py = twoRx2 - py;
if (p > 0)
{
p = Rx2 - py + p;
}
else
{
x = x + 1;
px = twoRy2 + px;
p = Rx2 + px - py + p;
}
PlotEllipsePoint(Cx, Cy, x, y);
}
}
public void PlotEllipsePoint(int CX, int CY, int X, int Y)
{
drawPixel(CX + X, CY + Y);
drawPixel(CX - X, CY + Y);
drawPixel(CX + X, CY - Y);
drawPixel(CX - X, CY - Y);
}
public void drawPixel(int x, int y)
{
P.fillOval(x, y, 5, 5);
}
public void mousePressed(MouseEvent e)
{
if (e.getButton() == MouseEvent.BUTTON1)
{
CenterX = e.getX();
CenterY = e.getY();
}
else if (e.getButton() == MouseEvent.BUTTON3)
{
Radius = Radius + 1;
if (Radius == 1)
{
RadiusX = (int) Math.pow((Math.pow((e.getX() - CenterX), 2) + Math.pow((e.getY() - CenterY), 2)), 0.5);
}
else if (Radius == 2)
{
RadiusY = (int) Math.pow((Math.pow((e.getX() - CenterX), 2) + Math.pow((e.getY() - CenterY), 2)), 0.5);
}
PlotEllipsePoint(CenterX, CenterY, RadiusX, RadiusY);
}
}
public static void main(String[] args)
{
JFrame JF = new JFrame("Ellipse");
JF.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
JF.setSize(500,500);
Ellipse E = new Ellipse();
JF.getContentPane().add(E);
JF.setVisible(true);
}
public void mouseClicked(MouseEvent e) {}
public void mouseReleased(MouseEvent e) {}
public void mouseEntered(MouseEvent e) {}
public void mouseExited(MouseEvent e) {}
}
Three things immediately jump out...
You should be calling super.paintComponent before doing any custom painting (and there is no need for paintComponent to be public), see Performing Custom Painting and Painting in AWT and Swing for more details
NEVER, EVER maintain a reference to the graphics context, you want something painted, you make a request to the repaint manager and you wait until one of your paint methods is called. Painting is controlled by the repaint manager an repaints may occur at any time, most of the time without yout knowledge or interaction.
You never call repaint to ask the repaint manager to repaint your component...At the end of your mousePressed method, call repaint()...

Basic bouncing ball program, ball won't bounce off wall

So I'm supposed to make a program where a ball bounces around a drawingpanel for 10 seconds. The ball has to bounce off the sides of the panel if it hits them. Right now when the ball hits the bottom panel instead of bouncing it appears in the middle of the screen and moves in the opposite direction until it hits the top and disappears.
I'm pretty sure the problem is in this part of my code...
(Earlier in the code I declared x to 1, y to 250, dx to 1, and dy to 1)
//Changes dirction
public static int newDirection1(int x, int dx, int size){
if (x < 0 || x > 500 || (x + size) < 0 || (x + size) > 500) {
dx *= -1;
return dx;
} else {
return dx;
}
}
//Changes direction
public static int newDirection2(int y, int dy, int size){
if (y < 0 || y > 500 || (y + size) < 0 || (y + size) > 500) {
dy *= -1;
return dy;
} else {
return dy;
}
}
//Moves ball one step
public static void move(Graphics g, Color color, int size, int x1, int y1, int x2, int y2){
g.setColor(Color.WHITE);
g.fillOval(x1, y1, size, size);
g.setColor(color);
g.fillOval(x2, y2, size, size);
}
//Pauses for 10ms
public static void sleep(int millis, DrawingPanel panel){
panel.sleep(millis);
}
public static void bounceLoop(DrawingPanel panel, Graphics g, Color color, int size, int x, int dx, int y, int dy, int millis){
int x1 = x + dx;
int x2 = x + dx;
int y1 = y + dy;
int y2 = y + dy;
for (int i = 0; i < 1000; i++) {
x1 = x + dx * i;
x2 = (x + dx * i) + dx;
y1 = y + dy * i;
y2 = (y + dy * i) + dy;
dx = newDirection1(x2, dx, size);
dy = newDirection2(y2, dy, size);
move(g, c, size, x1, y1, x2, y2);
sleep(millis, panel);
}
}
}
in the loop don't use:
x1 = x + dx * i
use
x1 = x1 + dx
(same for y)
because whenever dx is going to change, and multiply by -1, instead of continuing from where it was, and go to the other direction, it's going to continue from the other side of your panel, or a point that is really off.
Also a few things that could possibly fix the coding:
1- you don't need a dx parameter for your getNewDirection, you only need the coordinate.
2- the boundry conditions may give you errors, give it a small offset that can't be visible to the naked eye to avoid errors with creating objects outside the created panel or whatever you are using

Java Animations

I've started to take interest with making animations(slideshows, backgrounds etc) in Java. I know that JavaFX is much better for doing this, but I'm just to stubborn to bother switching over.
Here is what I got so far.
import java.awt.Color;
import java.awt.GradientPaint;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.awt.image.BufferedImageOp;
import java.awt.image.ConvolveOp;
import java.awt.image.Kernel;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Random;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class BlurredLightCells extends JPanel {
private static final long serialVersionUID = 4610174943257637060L;
private Random random = new Random();
private ArrayList<LightCell> lightcells;
private float[] blurData = new float[500];
public static void main(String[] args) {
JFrame frame = new JFrame("Swing animated bubbles");
frame.setSize(1000, 750);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new BlurredLightCells(60));
frame.setVisible(true);
}
public BlurredLightCells(int amtOfBCells) {
setSize(1000, 750);
/**
* Below we initiate all the cells that are going to be drawn on screen
*/
Arrays.fill(blurData, 1f / 20f);
lightcells = new ArrayList<LightCell>(amtOfBCells);
for (int i = 0; i < amtOfBCells; i++) {
/**
* Below we generate all the values for each cell(SHOULD be random for each one)
*/
int baseSpeed = random(0, 3);
int xSpeed = (int) Math.floor((Math.random() * (baseSpeed - -baseSpeed + baseSpeed)) + -baseSpeed);
int ySpeed = (int) Math.round((Math.random() * baseSpeed) + 0.5);
int radius = random(25, 100);
int x = (int) Math.floor(Math.random() * getWidth());
int y = (int) Math.floor(Math.random() * getHeight());
int blurrAmount = (int) (Math.floor(Math.random() * 10) + 5);
int alpha = (int) ((Math.random() * 15) + 3);
/**
* Now we draw a image, and apply transparency and a slight blur to it
*/
Kernel kernel = new Kernel(blurrAmount, blurrAmount, blurData);
BufferedImageOp op = new ConvolveOp(kernel);
BufferedImage circle = new BufferedImage(150, 150, BufferedImage.TYPE_INT_ARGB);
Graphics2D circlegfx = circle.createGraphics();
circlegfx.setColor(new Color(255, 255, 255, alpha));
circlegfx.fillOval(20, 20, radius, radius);
circle = op.filter(circle, null);
LightCell bubble = new LightCell(x, y, xSpeed, ySpeed, radius, getDirection(random.nextInt(3)), circle);
lightcells.add(bubble);
}
}
public int random(int min, int max) {
final int n = Math.abs(max - min);
return Math.min(min, max) + (n == 0 ? 0 : random.nextInt(n));
}
#Override
public void paint(Graphics g) {
int w = getWidth();
int h = getHeight();
final Graphics2D g2 = (Graphics2D) g;
GradientPaint gp = new GradientPaint(-w, -h, Color.LIGHT_GRAY, w, h, Color.DARK_GRAY);
g2.setPaint(gp);
g2.fillRect(0, 0, w, h);
long start = System.currentTimeMillis();
for (int i = 0; i < lightcells.size(); i++) {
LightCell cell = lightcells.get(i);
cell.process(g2);
}
System.out.println("Took " + (System.currentTimeMillis() - start) + " milliseconds to draw ALL cells.");
repaint();
}
public String getDirection(int i) {
switch (i) {
case 0:
return "right";
case 1:
return "left";
case 2:
return "up";
case 3:
return "down";
}
return "";
}
private class LightCell {
private int x, y, xSpeed, ySpeed, radius;
private String direction;
private BufferedImage image;
public LightCell(int x, int y, int xSpeed, int ySpeed, int radius, String direction, BufferedImage image) {
this.x = x;
this.y = y;
this.xSpeed = xSpeed;
this.ySpeed = ySpeed;
this.radius = radius;
this.direction = direction;
this.image = image;
}
public void process(Graphics g) {
switch (direction) {
case "right":
moveRight();
break;
case "left":
moveLeft();
break;
case "up":
moveUp();
break;
case "down":
moveDown();
break;
}
g.drawImage(image, x, y, null);
}
private void moveUp() {
x += xSpeed;
y -= ySpeed;
if (y + (radius / 2) < 0) {
y = getHeight() + (radius / 2);
x = (int) Math.floor(Math.random() * getWidth());
}
if ((x + radius / 2) < 0 || (x - radius / 2) > getWidth()) {
y = radius + (radius / 2);
x = (int) Math.floor(Math.random() * getWidth());
}
}
private void moveDown() {
x += xSpeed;
y += ySpeed;
if (y - (radius / 2) > getHeight()) {
y = 0 - (radius / 2);
x = (int) Math.floor(Math.random() * getWidth());
}
if ((x + radius / 2) < 0 || (x - radius / 2) > getWidth()) {
y = getHeight() + (radius / 2);
x = (int) Math.floor(Math.random() * getWidth());
}
}
private void moveRight() {
x += ySpeed;
y += xSpeed;
if (y - (radius / 2) > getHeight() || y + (radius / 2) < 0) {
x = 0 - (radius / 2);
y = (int) Math.floor(Math.random() * getHeight());
}
if ((x - radius / 2) > getWidth()) {
x = 0 - (radius / 2);
y = (int) Math.floor(Math.random() * getWidth());
}
}
private void moveLeft() {
x -= ySpeed;
y -= xSpeed;
if (y - (radius / 2) > getHeight() || y + (radius / 2) < 0) {
x = getWidth() + (radius / 2);
y = (int) Math.floor(Math.random() * getHeight());
}
if ((x + radius / 2) < 0) {
x = getWidth() + (radius / 2);
y = (int) Math.floor(Math.random() * getWidth());
}
}
}
}
If you run that, you will see the cells move at a very high speed, and if you look through the code, you see I call repaint() in the paint method in which I override. I know thats not good to do. But my question is, is their any other way in which I could draw each cell outside of the repaint() loop I have right now, because that causes other components to flash/flicker when I use this in a JFrame with other components.
FYI: Aventually I'd like to achieve something similar to this: Click Here
Thanks!
The issue of flicker is to do with the fact that top level containers are not double buffered. Instead of extending from JFrame (or other top level containers), you should consider using something more like JPanel and override it's paintComponent.
nb- Had it in my head that the OP was extending from JFrame...
Two issues could be causing the flickering. The first is overriding paint, the second is not calling super.paint(g) and the time between the updates. A better solution would be to override paintComponent and make sure you are calling super.paintComponent. Also using something like a javax.swing.Timer to schedule updates and regular intervals would also help...
Only call repaint when you want to encourage the RepaintManager to update you component. Don't call repaint from within any paintXxx method, this will cause a never ending loop of paint requests to schedule onto the event queue, eventually consuiming your CPU
I would avoid doing anything in your paintXxx methods that might take time to perform, this will slow down the rendering process. Instead, I would use a javax.swing.Timer for simple updates or for more complicated processing, a Thread which could be used to update the model before it is rendered to the screen.
This is an example of some simple optimisation process I did to take animation of 500 objects to 4500 with only a slight degration in the overall performance.
Updated
I changed you code slight and it works fine...
I changed your paint method to paintComponent and added super.paintComponent(g)
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
int w = getWidth();
int h = getHeight();
final Graphics2D g2 = (Graphics2D) g;
GradientPaint gp = new GradientPaint(-w, -h, Color.LIGHT_GRAY, w, h, Color.DARK_GRAY);
g2.setPaint(gp);
g2.fillRect(0, 0, w, h);
for (int i = 0; i < lightcells.size(); i++) {
LightCell cell = lightcells.get(i);
cell.process(g2);
}
}
And at the end of your constructor I added...
Timer timer = new Timer(40, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
repaint();
}
});
timer.start();
To update the UI on a regular bases...

Categories

Resources