I am wring the bouncing ball program in java. And I Now have one bouncing ball, I would like to have at least five bouncing balls. I have tried a few ways to do it, however, I only end up with one ball or error.
Do you have any suggestions on how to proceed? This in the piece of code used for the one ball, is it possible to rewrite this piece of code to get multiple balls in a neat way?
import javafx.scene.shape.Rectangle;
public class World {
private final double width, height;
private Ball[] balls;
private final Rectangle pad;
public World(double width, double height) {
this.width = width;
this.height = height;
balls = new Ball[1];
balls[0] = new Ball(10, 10);
balls[0].setVelocity(75.0, 100.0);
pad = new Rectangle(width / 2, 0.9 * height,
width / 8, height / 32);
}
public void move(long elapsedTimeNs) {
balls[0].move(elapsedTimeNs);
constrainBall(balls[0]);
checkForCollisionWithPad(balls[0]);
}
public Ball[] getBalls() {
return (Ball[]) balls.clone();
}
public Rectangle getPad() {
return pad;
}
public void setPadX(double x) {
if (x > width) {
x = width;
}
if (x < 0) {
x = 0;
}
pad.setX(x);
}
private void constrainBall(Ball ball) {
double x = ball.getX(), y = ball.getY();
double dx = ball.getDx(), dy = ball.getDy();
double radius = ball.getRadius();
if (x < radius) {
dx = Math.abs(dx);
} else if (x > width - radius) {
dx = -Math.abs(dx);
}
if (y < radius) {
dy = Math.abs(dy);
} else if (y > height - radius) {
dy = -Math.abs(dy);
}
ball.setVelocity(dx, dy);
}
private void checkForCollisionWithPad(Ball ball) {
if (ball.intersectsArea(
pad.getX(), pad.getY(), pad.getWidth(), pad.getHeight())) {
double dx = ball.getDx();
// set dy negative, i.e. moving "up"
double newDy = -Math.abs(ball.getDy());
ball.setVelocity(dx, newDy);
}
}
}
Main
import javafx.animation.AnimationTimer;
import javafx.application.Application;
import static javafx.application.Application.launch;
import javafx.event.EventHandler;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.control.Alert;
import javafx.scene.input.MouseEvent;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
public class Bounce extends Application {
private World world;
private Canvas canvas;
private AnimationTimer timer;
protected class BounceTimer extends AnimationTimer {
private long previousNs = 0;
#Override
public void handle(long nowNs) {
if (previousNs == 0) {
previousNs = nowNs;
}
world.move(nowNs - previousNs);
previousNs = nowNs;
GraphicsContext gc = canvas.getGraphicsContext2D();
gc.setFill(Color.WHITESMOKE);
gc.fillRect(0, 0, canvas.getWidth(), canvas.getHeight());
Rectangle pad = world.getPad();
gc.setFill(Color.BLACK);
double x = pad.getX(), y = pad.getY(),
w = pad.getWidth(), h = pad.getHeight();
gc.fillRoundRect(x, y, w, h, h, h);
for (Ball b : world.getBalls()) {
b.paint(gc);
}
}
}
#Override
public void start(Stage stage) {
Group root = new Group();
Scene scene = new Scene(root, 300, 300, Color.WHITESMOKE);
canvas = new Canvas(scene.getWidth(), scene.getHeight());
root.getChildren().add(canvas);
stage.setTitle("Bounce");
stage.setScene(scene);
stage.setResizable(false);
stage.sizeToScene();
stage.show();
world = new World(canvas.getWidth(), canvas.getHeight());
timer = new BounceTimer();
timer.start();
canvas.addEventHandler(MouseEvent.MOUSE_DRAGGED,
new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent me) {
world.setPadX(me.getX());
}
});
}
public static void main(String[] args) {
launch(args);
}
private void showAlert(String message) {
alert.setHeaderText("");
alert.setTitle("Alert!");
alert.setContentText(message);
alert.show();
}
private final Alert alert = new Alert(Alert.AlertType.INFORMATION);
}
Ball
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.paint.Color;
public class Ball {
public static final double BILLION = 1_000_000_000.0;
private double x, y; // position of the balls center
private double dx, dy; // velocity measured in pixels/second
private double radius;
private Color color;
public Ball(double x0, double y0) {
x = x0;
y = y0;
radius = 10;
color = Color.MAGENTA;
}
public Ball(double x0, double y0, double rad, Color col) {
x = x0;
y = y0;
radius = rad;
color = col;
}
Ball(int i, int i0, Color BLUEVIOLET) {
throw new UnsupportedOperationException("Not supported yet.");
}
public void setColor(Color col) { // setColor
color = col; }
public double getX() {
return x;
}
public double getY() {
return y;
}
public void setX(double newX) {
x = newX;
}
public void setY(double newY) {
y = newY;
}
public double getRadius() {
return radius;
}
public double getDx() {
return dx;
}
public double getDy() {
return dy;
}
public void setVelocity(double newDx, double newDy) {
dx = newDx;
dy = newDy;
}
public void moveTo(double newX, double newY) {
x = newX;
y = newY;
}
public void move(long elapsedTimeNs) {
x += dx * elapsedTimeNs / BILLION;
y += dy * elapsedTimeNs / BILLION;
}
public void paint(GraphicsContext gc) {
gc.setFill(color);
// arguments to fillOval: see the javadoc for GraphicsContext
gc.fillOval(x - radius, y - radius, radius * 2, radius * 2);
}
public boolean intersectsArea(
double rectX, double rectY,
double rectWidth, double rectHeight) {
double closestX = clamp(x, rectX, rectX + rectWidth);
double closestY = clamp(y, rectY, rectY + rectHeight);
double distanceX = x - closestX;
double distanceY = y - closestY;
return (distanceX * distanceX) + (distanceY * distanceY)
< (radius * radius);
}
private double clamp(double value, double lower, double upper) {
if (value < lower) {
return lower;
}
if (value > upper) {
return upper;
}
return value;
}
}
As Stormblessed said, you are only targeting one ball in your move method.
You should do:
public void move(Ball ball, long elapsedTimeNs) {
ball.move(elapsedTimeNs);
constrainBall(ball);
checkForCollisionWithPad(ball);
}
Edit: Since you want the handler method to accept only the elapsedTimeNs argument, do:
public void move(long elapsedTimeNs) {
for (Ball ball : balls) {
ball.move(elapsedTimeNs);
constrainBall(ball);
checkForCollisionWithPad(ball);
}
}
Edit 2: You should probably have a method that creates a new ball, for convenience:
public Ball newBall(double x, double y, double velocity1, double velocity2) {
Ball tmp = new Ball(x, y);
tmp.setVelocity(velocity1, velocity2);
balls.add(tmp);
return tmp;
}
Edit 3: The reason it throws an error is that you designated balls to have only one index position by using balls = new Ball[1]. You should use an ArrayList (java.util.ArrayList) instead, like so:
import java.util.ArrayList;
ArrayList<Ball> balls = new ArrayList<>;
You should now use balls.add and balls.get instead of = and []. References have been updated accordingly.
Related
I have started a Snake game for a University project and I am currently having an issue where the first segment of the body stored in an array is in the same location as the head. I don't quite know how I would go about offsetting the position of the first segment of the body so it isn't in the same position as the head part.
I noticed this when I was creating collision detection between the body and the head and it wasn't working as the first segment was in the same position as the head.
MainApp.java
import processing.core.*;
import java.awt.*;
/**
* Created by Wills on 25/05/2018.
*/
public class MainApp extends PApplet{
Snake snake;
Food food;
public static void main(String[] args){
PApplet.main("MainApp", args);
}
int screenSize = 800;
int scale = 20;
public void settings(){
size(screenSize,screenSize);
snake = new Snake(width/2, height/2, 0, 1, scale, screenSize );
food = new Food(screenSize, scale);
}
public void setup(){
frameRate(10);
rect(food.getX(), food.getY(), scale, scale);
}
public void draw() {
background(0);
snake.update();
fill(255);
rect(snake.getX(), snake.getY(), snake.getScale(), snake.getScale());
if(snake.getX() == food.getX() && snake.getY() == food.getY()){
snake.eat();
}
for(Point p: snake.getBody()){
if(food.getX() == p.getX() && food.getY() == p.getY()){
food.rePos();
}
rect((float)p.getX(), (float)p.getY(), snake.getScale(), snake.getScale());
}
fill(255,0,0);
rect(food.getX(), food.getY(), scale, scale);
}
public void mouseClicked(){
snake.eat();
snake.eat();
snake.eat();
snake.eat();
}
public void keyPressed(){
if(key == 'a'){
snake.setDirection(-1, 0);
}
if(key == 'd'){
snake.setDirection(1, 0);
}
if(key == 'w'){
snake.setDirection(0, -1);
}
if(key == 's'){
snake.setDirection(0, 1);
}
}
}
Snake.java
import processing.core.PApplet;
import java.awt.*;
import java.util.ArrayList;
/**
* Created by Wills on 25/05/2018.
*/
public class Snake extends PApplet{
int x, y, yspeed, xspeed, scale;
int score;
int screenSize;
ArrayList<Point> body;
public Snake(int x, int y, int xspeed, int yspeed, int scale, int screenSize){
body = new ArrayList<Point>();
this.x = x;
this.y = y;
this.xspeed = xspeed;
this.yspeed = yspeed;
this.scale = scale;
this.screenSize = screenSize;
}
public int getX(){
return x;
}
public int getY() {
return y;
}
public void setDirection(int x, int y){
this.xspeed = x;
this.yspeed = y;
}
public float getScale(){
return scale;
}
void update(){
x = constrain(x + xspeed * scale,0, screenSize - scale);
y =constrain(y + yspeed * scale,0, screenSize - scale);
body.add(0, new Point(x, y));
body.remove(score);
}
void eat(){
body.add(new Point(x,y));
score ++;
System.out.println(score);
}
void kill(){
score = 0;
x = screenSize/2;
y = screenSize/2;
}
ArrayList<Point> getBody(){
return body;
}
}
Food.java
import processing.core.PApplet;
import java.awt.*;
import java.util.ArrayList;
/**
* Created by Wills on 25/05/2018.
*/
public class Food extends PApplet{
float screenSize;
int scale, x, y;
Food(int screenSize, int scale){
this.scale = scale;
this.screenSize = screenSize;
generate();
}
float getX(){
return x;
}
float getY(){
return y;
}
public void rePos() {
generate();
}
private void generate(){
x = floor(random(0, scale));
y = floor(random(0, scale));
x = (int) map(x, 0, scale, 0, screenSize);
y = (int) map(y, 0, scale, 0, screenSize);
}
}
Any help would be much appreciated!
I was making a class for rectangles that are able to change angle, fill color, line color, line thickness. And the rectangles are to be rendered that way. I decided to do a little check up. During the check, I found out that the rectangles are rendered too slowly. I could see them being rendered from top to bottom. What might be the reason?
GEdit
import javax.swing.*;
public class GEdit {
public static void main(String[] args)
{
frame app = new frame();
app.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
app.setSize(1000,1000);
app.setVisible(true);
}
}
Figure.java
import java.awt.*;
import java.awt.geom.Point2D;
public abstract class Figure
{
public static final int TOPLEFT = 0;
public static final int TOPCENTER = 1;
public static final int TOPRIGHT = 2;
public static final int CENTERLEFT = 3;
public static final int CENTER = 4;
public static final int CENTERRIGHT = 5;
public static final int BOTTOMLEFT = 6;
public static final int BOTTOMCENTER = 7;
public static final int BOTTOMRIGHT = 8;
private boolean fill_able;
private int fill_option;
private Color fill_color;
private int fill_transparency;
private Color line_color;
private float line_thickness;
private int line_transparency;
private int width;
private int height;
private float rotate_angle;
private boolean width_height_ratio_fixed;
private Point location;
private int base;
public Figure(boolean fill_able,
int fill_option,
Color fill_color,
int fill_transparency,
Color line_color,
float line_thickness,
int line_transparency,
int width, int height,
float rotate_angle,
boolean width_height_ratio_fixed,
int x, int y,
int base)
{
this.fill_able = fill_able;
this.fill_option = fill_option;
this.fill_color = fill_color;
this.fill_transparency = fill_transparency;
this.line_color = line_color;
this.line_thickness = line_thickness;
this.line_transparency = line_transparency;
this.width = width;
this.height = height;
this.rotate_angle = rotate_angle;
this.width_height_ratio_fixed = width_height_ratio_fixed;
this.location = new Point(x,y);
this.base = base;
}
public boolean is_fill_able()
{
return fill_able;
}
public int get_fill_option()
{
return fill_option;
}
public Color get_fill_color()
{
return fill_color;
}
public int get_fill_transparency()
{
return fill_transparency;
}
public Color get_line_color()
{
return line_color;
}
public float get_line_thickness()
{
return line_thickness;
}
public int get_line_transparency()
{
return line_transparency;
}
public int get_width()
{
return width;
}
public int get_height()
{
return height;
}
public float get_rotate_angle()
{
return rotate_angle;
}
public boolean is_width_height_ratio_fixed()
{
return width_height_ratio_fixed;
}
public Point get_location()
{
return location;
}
public Point get_render_location()
{
int x, y;
switch(base)
{
case TOPLEFT:
x = location.x;
y = location.y;
break;
case TOPCENTER:
x = location.x - (int)Math.round(width / 2);
y = location.y;
break;
case TOPRIGHT:
x = location.x - width;
y = location.y;
break;
case CENTERLEFT:
x = location.x;
y = location.y - (int)Math.round(height / 2);
break;
case CENTER:
x = location.x - (int)Math.round(width / 2);
y = location.y - (int)Math.round(height / 2);
break;
case CENTERRIGHT:
x = location.x - width;
y = location.y - (int)Math.round(height / 2);
break;
case BOTTOMLEFT:
x = location.x;
y = location.y - height;
break;
case BOTTOMCENTER:
x = location.x - (int)Math.round(width / 2);
y = location.y - height;
break;
case BOTTOMRIGHT:
x = location.x - width;
y = location.y - height;
break;
default:
x = 0; y = 0;
break;
}
return new Point(x, y);
}
public int get_base()
{
return base;
}
public void set_fill_option(int option)
{
this.fill_option = option;
}
public void set_fill_color(Color color)
{
this.fill_color = color;
}
public void set_fill_transparency(int transparency)
{
this.fill_transparency = transparency;
}
public void set_line_color(Color color)
{
this.line_color = color;
}
public void set_line_transparency(int transparency)
{
this.line_transparency = transparency;
}
public void set_width(int width)
{
if(this.width_height_ratio_fixed)
{
float ratio = this.height / this.width;
this.width = width;
this.height = (int)Math.round(width * ratio);
}
else { this.width = width;}
}
public void set_height(int height)
{
if(this.width_height_ratio_fixed)
{
float ratio = this.width / this.height;
this.height = height;
this.width = (int)Math.round(height * ratio);
}
else{ this.height = height;}
}
public void set_rotate_angle(float angle)
{
if(angle > 360)
{
this.rotate_angle = angle % 360;
}
else this.rotate_angle = angle;
}
public void set_location(int x, int y)
{
this.location.setLocation(x, y);
}
public void set_location(Point location)
{
this.location = location;
}
public void set_base(int base)
{
this.base = base;
}
abstract public void render(Graphics2D g);
}
Rectangle.java
import java.awt.*;
import java.awt.Graphics2D;
import java.awt.geom.*;
public class Rectangle extends Figure {
public Rectangle(Color fill_color, Color line_color, float line_thickness, int width, int height, int x, int y)
{
super(true,
0,
fill_color,
255,
line_color,
line_thickness,
255,
width, height,
0,
false,
x, y,
TOPLEFT);
}
public void render(Graphics2D g)
{
Rectangle2D rectangle = new Rectangle2D.Float(get_width(),get_height(),get_render_location().x,get_render_location().y);
g.rotate(Math.toRadians(get_rotate_angle()),rectangle.getCenterX(),rectangle.getCenterY());
if(is_fill_able())
{
Color color = new Color(
get_fill_color().getRed(),
get_fill_color().getGreen(),
get_fill_color().getBlue(),
get_fill_transparency());
g.setColor(color);
g.fill(rectangle);
}
Stroke old_stroke = g.getStroke();
g.setStroke(new BasicStroke(get_line_thickness()));
Color line_color = new Color(
get_line_color().getRed(),
get_line_color().getGreen(),
get_line_color().getBlue(),
get_line_transparency());
g.setColor(line_color);
g.draw(rectangle);
g.setStroke(old_stroke);
g.rotate(Math.toRadians(get_rotate_angle() * -1),rectangle.getCenterX(),rectangle.getCenterY());
}
}
frame.java
import javax.print.DocFlavor;
import javax.swing.JFrame;
import java.awt.*;
import java.util.Random;
public class frame extends JFrame{
public frame()
{
super("test");
}
#Override
public void paint(Graphics g) {
Graphics2D graphics2D = (Graphics2D)g;
RenderingHints renderingHints1 = new RenderingHints(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
RenderingHints renderingHints2 = new RenderingHints(RenderingHints.KEY_RENDERING,
RenderingHints.VALUE_RENDER_QUALITY);
graphics2D.setRenderingHints(renderingHints1);
graphics2D.setRenderingHints(renderingHints2);
Random random = new Random();
g.setColor(Color.WHITE);
g.fillRect(0,0,1000,1000);
for(int i =0;i < 10; i++)
{
Rectangle rectangle = new Rectangle(Color.CYAN,Color.BLACK,random.nextInt(10),random.nextInt(1000),random.nextInt(1000),random.nextInt(300),random.nextInt(300));
rectangle.set_fill_transparency(random.nextInt(255));
rectangle.set_rotate_angle(random.nextInt(180));
rectangle.render(graphics2D);
}
}
}
Don't override paint of top level containers like JFrame, start with something like JPanel and override it's paintComponent method - that way you get double buffering for free.
Consider not creating new objects on each paint cycle - this is create a number of short lived objects which can affect performance
You could perform some of the operations outside of the paint method (setting the properties) and just focus on painting the inside the paint method
Remember, transformations (such as rotate and translate) are compounding, this means they will affect everything painted after them. You either need to take a snapshot of the Graphics context before hand and dispose of it when you're finished, or reverse the transformations.
You might like to take a look at Performing Custom Painting and Painting in AWT and Swing for more details about how painting works in Swing.
Without changing anything, other then using a JPanel and paintComponent, it works fine.
I even threw in a Swing Timer and repainted the panel at 25fps a second without issue. And just for fun, I set it to 200fps without issue
import java.awt.BasicStroke;
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.RenderingHints;
import java.awt.Stroke;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.geom.Rectangle2D;
import java.util.Random;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
private Random random = new Random();
public TestPane() {
Timer timer = new Timer(40, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
repaint();
}
});
timer.start();
}
#Override
public Dimension getPreferredSize() {
return new Dimension(300, 300);
}
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
RenderingHints renderingHints1 = new RenderingHints(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
RenderingHints renderingHints2 = new RenderingHints(RenderingHints.KEY_RENDERING,
RenderingHints.VALUE_RENDER_QUALITY);
g2d.setRenderingHints(renderingHints1);
g2d.setRenderingHints(renderingHints2);
for (int i = 0; i < 10; i++) {
Rectangle rectangle = new Rectangle(Color.CYAN,
Color.BLACK,
random.nextInt(10),
random.nextInt(200),
random.nextInt(200),
random.nextInt(200), random.nextInt(200));
rectangle.set_fill_transparency(random.nextInt(255));
rectangle.set_rotate_angle(random.nextInt(180));
Graphics2D iDontTrustYou = (Graphics2D) g2d.create();
rectangle.render(iDontTrustYou);
iDontTrustYou.dispose();
}
g2d.dispose();
}
}
public abstract class Figure {
public static final int TOPLEFT = 0;
public static final int TOPCENTER = 1;
public static final int TOPRIGHT = 2;
public static final int CENTERLEFT = 3;
public static final int CENTER = 4;
public static final int CENTERRIGHT = 5;
public static final int BOTTOMLEFT = 6;
public static final int BOTTOMCENTER = 7;
public static final int BOTTOMRIGHT = 8;
private boolean fill_able;
private int fill_option;
private Color fill_color;
private int fill_transparency;
private Color line_color;
private float line_thickness;
private int line_transparency;
private int width;
private int height;
private float rotate_angle;
private boolean width_height_ratio_fixed;
private Point location;
private int base;
public Figure(boolean fill_able,
int fill_option,
Color fill_color,
int fill_transparency,
Color line_color,
float line_thickness,
int line_transparency,
int width, int height,
float rotate_angle,
boolean width_height_ratio_fixed,
int x, int y,
int base) {
this.fill_able = fill_able;
this.fill_option = fill_option;
this.fill_color = fill_color;
this.fill_transparency = fill_transparency;
this.line_color = line_color;
this.line_thickness = line_thickness;
this.line_transparency = line_transparency;
this.width = width;
this.height = height;
this.rotate_angle = rotate_angle;
this.width_height_ratio_fixed = width_height_ratio_fixed;
this.location = new Point(x, y);
this.base = base;
}
public boolean is_fill_able() {
return fill_able;
}
public int get_fill_option() {
return fill_option;
}
public Color get_fill_color() {
return fill_color;
}
public int get_fill_transparency() {
return fill_transparency;
}
public Color get_line_color() {
return line_color;
}
public float get_line_thickness() {
return line_thickness;
}
public int get_line_transparency() {
return line_transparency;
}
public int get_width() {
return width;
}
public int get_height() {
return height;
}
public float get_rotate_angle() {
return rotate_angle;
}
public boolean is_width_height_ratio_fixed() {
return width_height_ratio_fixed;
}
public Point get_location() {
return location;
}
public Point get_render_location() {
int x, y;
switch (base) {
case TOPLEFT:
x = location.x;
y = location.y;
break;
case TOPCENTER:
x = location.x - (int) Math.round(width / 2);
y = location.y;
break;
case TOPRIGHT:
x = location.x - width;
y = location.y;
break;
case CENTERLEFT:
x = location.x;
y = location.y - (int) Math.round(height / 2);
break;
case CENTER:
x = location.x - (int) Math.round(width / 2);
y = location.y - (int) Math.round(height / 2);
break;
case CENTERRIGHT:
x = location.x - width;
y = location.y - (int) Math.round(height / 2);
break;
case BOTTOMLEFT:
x = location.x;
y = location.y - height;
break;
case BOTTOMCENTER:
x = location.x - (int) Math.round(width / 2);
y = location.y - height;
break;
case BOTTOMRIGHT:
x = location.x - width;
y = location.y - height;
break;
default:
x = 0;
y = 0;
break;
}
return new Point(x, y);
}
public int get_base() {
return base;
}
public void set_fill_option(int option) {
this.fill_option = option;
}
public void set_fill_color(Color color) {
this.fill_color = color;
}
public void set_fill_transparency(int transparency) {
this.fill_transparency = transparency;
}
public void set_line_color(Color color) {
this.line_color = color;
}
public void set_line_transparency(int transparency) {
this.line_transparency = transparency;
}
public void set_width(int width) {
if (this.width_height_ratio_fixed) {
float ratio = this.height / this.width;
this.width = width;
this.height = (int) Math.round(width * ratio);
} else {
this.width = width;
}
}
public void set_height(int height) {
if (this.width_height_ratio_fixed) {
float ratio = this.width / this.height;
this.height = height;
this.width = (int) Math.round(height * ratio);
} else {
this.height = height;
}
}
public void set_rotate_angle(float angle) {
if (angle > 360) {
this.rotate_angle = angle % 360;
} else {
this.rotate_angle = angle;
}
}
public void set_location(int x, int y) {
this.location.setLocation(x, y);
}
public void set_location(Point location) {
this.location = location;
}
public void set_base(int base) {
this.base = base;
}
abstract public void render(Graphics2D g);
}
public class Rectangle extends Figure {
public Rectangle(Color fill_color, Color line_color, float line_thickness, int width, int height, int x, int y) {
super(true,
0,
fill_color,
255,
line_color,
line_thickness,
255,
width, height,
0,
false,
x, y,
TOPLEFT);
}
public void render(Graphics2D g) {
Rectangle2D rectangle = new Rectangle2D.Float(get_width(), get_height(), get_render_location().x, get_render_location().y);
g.rotate(Math.toRadians(get_rotate_angle()), rectangle.getCenterX(), rectangle.getCenterY());
if (is_fill_able()) {
Color color = new Color(
get_fill_color().getRed(),
get_fill_color().getGreen(),
get_fill_color().getBlue(),
get_fill_transparency());
g.setColor(color);
g.fill(rectangle);
}
Stroke old_stroke = g.getStroke();
g.setStroke(new BasicStroke(get_line_thickness()));
Color line_color = new Color(
get_line_color().getRed(),
get_line_color().getGreen(),
get_line_color().getBlue(),
get_line_transparency());
g.setColor(line_color);
g.draw(rectangle);
g.setStroke(old_stroke);
g.rotate(Math.toRadians(get_rotate_angle() * -1), rectangle.getCenterX(), rectangle.getCenterY());
}
}
}
A slightly better optimised version might look something like...
public class TestPane extends JPanel {
private Random random = new Random();
private List<Rectangle> rectangles = new ArrayList<>(25);
public TestPane() {
for (int i = 0; i < 1000; i++) {
Rectangle rectangle = new Rectangle(Color.CYAN,
Color.BLACK,
random.nextInt(10),
random.nextInt(200),
random.nextInt(200),
random.nextInt(200), random.nextInt(200));
rectangle.set_fill_transparency(random.nextInt(255));
rectangle.set_rotate_angle(random.nextInt(180));
rectangles.add(rectangle);
}
Timer timer = new Timer(5, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
for (Rectangle rectangle : rectangles) {
rectangle.set_height(random.nextInt(200));
rectangle.set_width(random.nextInt(200));
rectangle.set_location(random.nextInt(200), random.nextInt(200));
rectangle.set_fill_transparency(random.nextInt(255));
rectangle.set_rotate_angle(random.nextInt(180));
}
repaint();
}
});
timer.start();
}
#Override
public Dimension getPreferredSize() {
return new Dimension(300, 300);
}
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
RenderingHints renderingHints1 = new RenderingHints(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
RenderingHints renderingHints2 = new RenderingHints(RenderingHints.KEY_RENDERING,
RenderingHints.VALUE_RENDER_QUALITY);
g2d.setRenderingHints(renderingHints1);
g2d.setRenderingHints(renderingHints2);
for (Rectangle rectangle : rectangles) {
Graphics2D iDontTrustYou = (Graphics2D) g2d.create();
rectangle.render(iDontTrustYou);
iDontTrustYou.dispose();
}
g2d.dispose();
}
}
If you still find that the performance isn't up to what you want, you might consider having a look at BufferStrategy which will give you complete control over the painting process
You are overriding the method paint, which is usually a bad idea.
You are also calculating all the information needed DURING the paint, which is another bad idea: they should be calculated before. If you need 4x10 random numbers, maybe calculate them before the paint and store the 40 numbers in a int[] array.
As someone mentioned, you should want to use a JPanel and the method paintComponent.
The following link might be an interesting reading for you:
http://www.billharlan.com/papers/Improving_Swing_Performance.html
I am trying to make an Asteroids game clone in JavaFX. So far, I have been able to draw the ship and asteroids onto the screen (where Rectangles represent them, for now). I have also implemented movement for the ship, and randomized movements for the asteroids.
I am having trouble implementing the code needed to bounce the asteroids off each other. The current method that does the collision checking (called checkAsteroidCollisions) is bugged in that all asteroids start stuttering in place. They don't move, but rather oscillate back and forth in place rapidly. Without the call to this method, all asteroids begin moving normally and as expected.
Instead, I want each asteroid to move freely and, when coming into contact with another asteroid, bounce off each other like in the actual Asteroids game.
MainApp.java
import java.util.ArrayList;
import java.util.HashSet;
import javafx.animation.AnimationTimer;
import javafx.application.Application;
import javafx.event.EventHandler;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.input.KeyEvent;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
public class MainApp extends Application {
private static final int WIDTH = 700;
private static final int HEIGHT = 900;
private static final int NUM_OF_ASTEROIDS = 12;
private static final Color ASTEROID_COLOR = Color.GRAY;
private static final Color PLAYER_COLOR = Color.BLUE;
private Player player;
private ArrayList<Entity> asteroids;
long lastNanoTime; // For AnimationTimer
HashSet<String> inputs; // For inputs
private static final int MAX_SPEED = 150;
private static final int SPEED = 10;
private static final int ASTEROID_SPEED = 150;
private StackPane background;
/*
* Generates a random number between min and max, inclusive.
*/
private float genRandom(int min, int max) {
return (float) Math.floor(Math.random() * (max - min + 1) + min);
}
/*
* Initializes the asteroids
*/
private void initAsteroids() {
this.asteroids = new ArrayList<Entity>();
for (int i = 0; i < NUM_OF_ASTEROIDS; i++) {
Entity asteroid = new Entity(50, 50, ASTEROID_COLOR, EntityType.ASTEROID);
float px = (float) genRandom(200, WIDTH - 50);
float py = (float) genRandom(200, HEIGHT - 50);
asteroid.setPos(px, py);
// Keep recalculating position until there are no collisions
while (asteroid.intersectsWith(this.asteroids)) {
px = (float) genRandom(200, WIDTH - 50);
py = (float) genRandom(200, HEIGHT - 50);
asteroid.setPos(px, py);
}
// Randomly generate numbers to change velocity by
float dx = this.genRandom(-ASTEROID_SPEED, ASTEROID_SPEED);
float dy = this.genRandom(-ASTEROID_SPEED, ASTEROID_SPEED);
asteroid.changeVelocity(dx, dy);
this.asteroids.add(asteroid);
}
}
/*
* Initializes the player
*/
private void initPlayer() {
this.player = new Player(30, 30, PLAYER_COLOR, EntityType.PLAYER);
this.player.setPos(WIDTH / 2, 50);
}
/*
* Checks collisions with screen boundaries
*/
private void checkOffScreenCollisions(Entity e) {
if (e.getX() < -50)
e.setX(WIDTH);
if (e.getX() > WIDTH)
e.setX(0);
if (e.getY() < -50)
e.setY(HEIGHT);
if (e.getY() > HEIGHT)
e.setY(0);
}
/*
* Controls speed
*/
private void controlSpeed(Entity e) {
if (e.getDx() < -MAX_SPEED)
e.setDx(-MAX_SPEED);
if (e.getDx() > MAX_SPEED)
e.setDx(MAX_SPEED);
if (e.getDy() < -MAX_SPEED)
e.setDy(-MAX_SPEED);
if (e.getDy() > MAX_SPEED)
e.setDy(MAX_SPEED);
}
/*
* Controls each asteroid's speed and collision off screen
*/
private void controlAsteroids(ArrayList<Entity> asteroids) {
for (Entity asteroid : asteroids) {
this.checkOffScreenCollisions(asteroid);
this.controlSpeed(asteroid);
}
}
/*
* Checks an asteroid's collision with another asteroid
*/
private void checkAsteroidCollisions() {
for (int i = 0; i < NUM_OF_ASTEROIDS; i++) {
Entity asteroid = this.asteroids.get(i);
if (asteroid.intersectsWith(this.asteroids)){
float dx = (float) asteroid.getDx();
float dy = (float) asteroid.getDy();
asteroid.setDx(0);
asteroid.setDy(0);
asteroid.changeVelocity(-dx, -dy);
}
}
}
#Override
public void start(Stage primaryStage) throws Exception {
primaryStage.setTitle("Hello World!");
this.initAsteroids();
this.initPlayer();
background = new StackPane();
background.setStyle("-fx-background-color: pink");
this.inputs = new HashSet<String>();
Group root = new Group();
Scene scene = new Scene(root);
primaryStage.setScene(scene);
scene.setOnKeyPressed(new EventHandler<KeyEvent>() {
#Override
public void handle(KeyEvent e) {
String code = e.getCode().toString();
inputs.add(code);
}
});
scene.setOnKeyReleased(new EventHandler<KeyEvent>() {
#Override
public void handle(KeyEvent e) {
String code = e.getCode().toString();
inputs.remove(code);
}
});
Canvas canvas = new Canvas(WIDTH, HEIGHT);
GraphicsContext gc = canvas.getGraphicsContext2D();
background.getChildren().add(canvas);
root.getChildren().add(background);
lastNanoTime = System.nanoTime();
new AnimationTimer() {
#Override
public void handle(long currentNanoTime) {
float elapsedTime = (float) ((currentNanoTime - lastNanoTime) / 1000000000.0);
lastNanoTime = currentNanoTime;
/* PLAYER */
// Game Logic
if (inputs.contains("A"))
player.changeVelocity(-SPEED, 0);
if (inputs.contains("D"))
player.changeVelocity(SPEED, 0);
if (inputs.contains("W"))
player.changeVelocity(0, -SPEED);
if (inputs.contains("S"))
player.changeVelocity(0, SPEED);
// Collision with edge of map
checkOffScreenCollisions(player);
// Control speed
controlSpeed(player);
player.update(elapsedTime);
/* ASTEROIDS */
gc.setFill(ASTEROID_COLOR);
for(int i = 0; i < NUM_OF_ASTEROIDS; i++) {
checkAsteroidCollisions(i); // BUGGY CODE
}
controlAsteroids(asteroids);
gc.clearRect(0, 0, WIDTH, HEIGHT);
for (Entity asteroid : asteroids) {
asteroid.update(elapsedTime);
asteroid.render(gc);
}
gc.setFill(PLAYER_COLOR);
player.render(gc);
}
}.start();
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
Entity.java:
import java.util.ArrayList;
import javafx.geometry.Rectangle2D;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.paint.Color;
public class Entity {
private Color color;
private double x, y, width, height, dx, dy;
private EntityType entityType; // ID of this Entity
public Entity(float width, float height, Color color, EntityType type) {
this.x = this.dx = 0;
this.y = this.dy = 0;
this.width = width;
this.height = height;
this.color = color;
this.entityType = type;
}
/*
* Getters and setters
*/
public Color getColor() {
return color;
}
public void setColor(Color color) {
this.color = color;
}
public double getX() {
return x;
}
public void setX(float x) {
this.x = x;
}
public double getY() {
return y;
}
public void setY(float y) {
this.y = y;
}
public double getDx() {
return dx;
}
public void setDx(float dx) {
this.dx = dx;
}
public double getDy() {
return dy;
}
public void setDy(float dy) {
this.dy = dy;
}
public EntityType getEntityType() {
return entityType;
}
/*
* Adds to dx and dy (velocity)
*/
public void changeVelocity(float dx, float dy) {
this.dx += dx;
this.dy += dy;
}
/*
* Sets position
*/
public void setPos(float x, float y) {
this.setX(x);
this.setY(y);
}
/*
* Gets new position of the Entity based on velocity and time
*/
public void update(float time) {
this.x += this.dx * time;
this.y += this.dy * time;
}
/*
* Used for collisions
*/
public Rectangle2D getBoundary() {
return new Rectangle2D(this.x, this.y, this.width, this.height);
}
/*
* Checks for intersections
*/
public boolean intersectsWith(Entity e) {
return e.getBoundary().intersects(this.getBoundary());
}
/*
* If any of the entities in the passed in ArrayList
* intersects with this, then return true;
*/
public boolean intersectsWith(ArrayList<Entity> entities) {
for(Entity e : entities) {
if(e.getBoundary().intersects(this.getBoundary()))
return true;
}
return false;
}
/*
* Draws the shape
*/
public void render(GraphicsContext gc) {
gc.fillRoundRect(x, y, width, height, 10, 10);
}
#Override
public String toString() {
return "Entity [x=" + x + ", y=" + y + ", width=" + width + ", height=" + height + ", entityType=" + entityType
+ "]";
}
}
I think you will need to change the position of the two colliding asteroids slightly so their state is not on collision anymore.
What happens right now is that you change their movement after the collision but I guess that your algorithm notices that they are still touching, so it will try to change the movement again with the same result as before.
What you need to implement now is a change in the position of the two asteroids so your collision detection won´t act up again immediately after it´s first call.
I hope I could help you.
To be clear, I want to have a blue circle bouncing left to right which stops on click. This part works. But I also need to have a red rectangle bouncing up and down in the same gui which also stops on click. Each object should stop individually. However, two circles appear where one is moving and the other one is just still. Thanks in Advance! Help is appreciated.
/*
* This program creates an animation for a circle.
*/
package circleanimation;
import java.awt.*;
/*
* #author talhaiqbal18
*/
public class CircleAnimation
{
private int centerX, centerY, radius;
private Color color;
private int direction, speed;
private boolean filled;
public CircleAnimation(int x, int y, int r, Color c) {
centerX = x;
centerY = y;
radius = r;
color = c;
direction = 0;
speed = 0;
filled = false;
}
public void draw(Graphics g) {
Color oldColor = g.getColor();
g.setColor(color);
if (filled) {
g.fillOval(centerX - radius, centerY - radius, radius * 2, radius * 2);
g.fillRect(200, 200, 200, 200); }
else {
g.fillRect(200, 200, 200, 200);
g.fillOval(centerX - radius, centerY - radius, radius * 2, radius * 2);
g.setColor(oldColor); }
}
public void fill(Graphics g) {
Color oldColor = g.getColor();
g.setColor(color);
g.fillOval(centerX - radius, centerY - radius, radius * 2, radius * 2);
g.fillRect(200, 200, 200, 200);
g.setColor(oldColor);
}
public boolean containsPoint(int x, int y) {
int xSquared = (x - centerX) * (x - centerX);
int ySquared = (y - centerY) * (y - centerY);
int radiusSquared = radius * radius;
return xSquared + ySquared - radiusSquared <= 0;
}
public void move(int xAmount, int yAmount) {
centerX = centerX + xAmount;
centerY = centerY + yAmount;
}
public int getRadius() {
return radius;
}
public int getX() {
return centerX;
}
public int getY() {
return centerY;
}
public void setSpeed(int s) {
speed = s;
}
public void setDirection(int d) {
direction = d % 360;
}
public void turn(int degrees) {
direction = (direction + degrees) % 360;
}
public void move() {
move((int)(speed * Math.cos(Math.toRadians(direction))),
(int)(speed * Math.sin(Math.toRadians(direction))));
}
public void setFilled(boolean b) {
filled = b;
}
}
/*
* This is the color panel class for the circle animation project.
*/
package circleanimation;
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
/*
* #author talhaiqbal18
*/
public class ColorPanel extends JPanel
{
private CircleAnimation circle, rectangle;
private javax.swing.Timer timer;
private CircleAnimation selectedCircle, selectedRectangle;
private int x, y;
public ColorPanel(Color backColor, int width, int height) {
setBackground(backColor);
setPreferredSize(new Dimension(width, height));
circle = new CircleAnimation(350, 300, 350, Color.blue);
circle.setDirection(180);
circle.setSpeed(6);
rectangle = new CircleAnimation(350, 300, 400, Color.red);
rectangle.setDirection(90);
rectangle.setSpeed(6);
timer = new javax.swing.Timer(1, new MoveListener());
timer.start();
addMouseListener(new PanelListener());
addMouseMotionListener(new PanelMotionListener());
addMouseMotionListener(new PanelMotionListener1());
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
circle.fill(g);
rectangle.fill(g);
}
private class MoveListener implements ActionListener {
public void actionPerformed(ActionEvent e) {
int x = circle.getX();
int radius = circle.getRadius();
int width = getWidth();
if (x - radius <= 0 || x + radius >= width) {
circle.turn(180);
}
circle.move();
repaint();
}
}
private class MoveListener1 implements ActionListener {
public void actionPerformed(ActionEvent e) {
int x = rectangle.getX();
int radius = rectangle.getRadius();
int width = getWidth();
if (x - radius <= 0 || x + radius >= width) {
rectangle.turn(270);
}
rectangle.move();
repaint();
}
}
private class PanelListener extends MouseAdapter {
public void mousePressed(MouseEvent e)
{
x = e.getX();
y = e.getY();
if (circle.containsPoint(x, y))
selectedCircle = circle;
if (rectangle.containsPoint(x, y))
selectedRectangle = rectangle;
}
public void mouseReleased(MouseEvent e) {
//nothing
}
public void mouseClicked(MouseEvent e) {
if(timer.isRunning())
timer.stop();
else
timer.start();
}
}
private class PanelMotionListener extends MouseMotionAdapter
{
public void mouseDragged(MouseEvent e)
{
int newX = e.getX();
int newY = e.getY();
int dx = newX - x;
int dy = newY - y;
if (selectedCircle != null) {
selectedCircle.move(dx,dy);
x = newX;
y = newY;
repaint(); }
}
}
private class PanelMotionListener1 extends MouseMotionAdapter
{
public void mouseDragged(MouseEvent e)
{
int newX = e.getX();
int newY = e.getY();
int dx = newX - x;
int dy = newY - y;
if (selectedRectangle != null) {
selectedRectangle.move(dx,dy);
x = newX;
y = newY;
repaint(); }
}
}
}
/*
* This is the main method which will implement the actions of the previous classes.
*/
package circleanimation;
import java.awt.*;
import javax.swing.JFrame;
/*
* #author talhaiqbal18
*/
public class MainClass
{
public static void main(String[] args) throws Exception {
JFrame theGUI = new JFrame();
theGUI.setTitle("Circle Animation");
theGUI.setDefaultCloseOperation (JFrame.EXIT_ON_CLOSE);
ColorPanel panel = new ColorPanel(Color.white, 100, 100);
Container pane = theGUI.getContentPane();
pane.add(panel);
theGUI.setVisible(true);
}
}
You only register a timer for your circle, but not for your rectangle in the ColorPanel constructor
i have looked all over the internet and in my school books but I can't seem to slove my problem.
In my program "bouncing ball" (got the code from our teacher) i need to change the size of the ball from small to bigger and reverse. I understand that i need a boolean to do that and maybe alsow an if statment. This is what I have in the Ball class rigth now regarding the size change:
private boolean changeSize = true;
int maxSize = 10;
int minSize = 1;
public void changeSize(boolean size ){
if(size == maxSize ){
return minSize;
}
else return maxSize;
}
public void changeBallSize(int d, int f){
diameter = d*f;
This is the whole code for the class Ball:
class Ball {
static int defaultDiameter = 10;
static Color defaultColor = Color.yellow;
static Rectangle defaultBox = new Rectangle(0,0,100,100);
// Position
private int x, y;
// Speen and angel
private int dx, dy;
// Size
private int diameter;
// Color
private Color color;
// Bouncing area
private Rectangle box;
// New Ball
public Ball( int x0, int y0, int dx0, int dy0 ) {
x = x0;
y = y0;
dx = dx0;
dy = dy0;
color = defaultColor;
diameter = defaultDiameter;
}
// New color
public void setColor( Color c ) {
color = c;
}
public void setBoundingBox( Rectangle r ) {
box = r;
}
// ball
public void paint( Graphics g ) {
// Byt till bollens färg
g.setColor( color );
g.fillOval( x, y, diameter, diameter );
}
void constrain() {
// Ge absoluta koordinater för det rektangulära området
int x0 = box.x;
int y0 = box.y;
int x1 = x0 + box.width - diameter;
int y1 = y0 + box.height - diameter;
// Setting speed and angels
if (x < x0)
dx = Math.abs(dx);
if (x > x1)
dx = -Math.abs(dx);
if (y < y0)
dy = Math.abs(dy);
if (y > y1)
dy = -Math.abs(dy);
}
// movingt the ball
x = x + dx;
y = y + dy;
constrain();
}
}
I am a total rookie of java! Thanks for the help!
Add the following into your Ball class:
private int changeFlag=-1;
In your constrain() function, just before the last line, after moving the ball:
if(diameter==maxSize) {
changeFlag=-1;
}
else if (diameter==minSize) {
changeFlag=1;
}
diameter=diameter+changeFlag;
Add this code to your Ball Class
private minSize = 1;
private maxSize = 10;
public void setDiameter(int newDiameter) {
this.diameter = newDiameter;
}
public int getMinSize() {
return minSize;
}
public int getMinSize() {
return maxSize;
}
Use this when you use the ball
Ball ball = new Ball(1,1,1,1);
int newDiameter = 10;
if(newDiameter == ball.getMinSize()) {
ball.setDiameter(ball.getMaxSize());
}
id(newDiameter == ball.getMaxSize()) {
ball.setDiameter (ball.getMinSize());
}
I've edited it, is this what you mean?
Main Class:
import java.awt.Graphics;
import java.awt.Graphics2D;
import javax.swing.JFrame;
public class Main extends JFrame {
public static void main(String[] args) {
new Main();
}
public Main() {
// configure JFrame
setSize(640, 360);
setVisible(true);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// create ball
Ball ball = new Ball(this.getWidth()/2, this.getHeight()/2, 50, 100);
add(ball);
Thread t = new Thread(ball);
t.start();
}
public void paint(Graphics g) {
super.paint(g);
Graphics2D g2 = (Graphics2D) g;
}
}
Ball Class:
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import javax.swing.JComponent;
public class Ball extends JComponent implements Runnable {
private int x, y, minDiameter, maxDiameter, currentDiameter, growRate = -1;
private Color color = Color.BLUE;
public Ball(int x, int y, int minDiameter, int maxDiameter) {
this.x = x;
this.y = y;
this.minDiameter = minDiameter;
this.maxDiameter = maxDiameter;
this.currentDiameter = minDiameter;
setVisible(true);
}
#Override
public void run() {
while (true) {
// coerce max and min size
if (this.currentDiameter + growRate > maxDiameter) {
this.currentDiameter = maxDiameter;
this.growRate = -1;
}
if (this.currentDiameter + growRate < minDiameter) {
this.currentDiameter = minDiameter;
this.growRate = 1;
}
this.currentDiameter += this.growRate;
repaint();
try {
Thread.sleep(10);
}
catch(Exception e) {
System.out.println(e.toString());
}
}
}
public void paint(Graphics g) {
super.paint(g);
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHints(new RenderingHints(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON));
g2.setColor(this.color);
g2.fillOval(this.x, this.y, this.currentDiameter, this.currentDiameter);
}
}