Java Application: drawing a large number of polygons(path2d) taking too long - java

I am trying to write an application that draws a map, using polygon, from arraylist of Path2D.Double coordinates.
The problem is my sample size is over 26,000 polygons and it takes ages(around 5~6 minutes) to draw the whole thing. Not to mention the fact that it repaints itself when I try to scroll. When the program is run with few thousand coordinates it takes about 5~10 seconds.
is there anyway I can make the program run faster? also is possible to draw it once and use it?, instead of re-drawing the entire map every time I try to scroll or re-size the window.
Thx.
here is the code:
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.geom.Path2D;
import java.awt.geom.Point2D;
import java.util.ArrayList;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
public class WholeMap extends JPanel {
ArrayList<PropertyInfo> propertyList;
ArrayList<Path2D.Double> polygonList= new ArrayList<Path2D.Double>();
double mapOffsetX = 4200;
double mapOffsetY = 4000;
public WholeMap(ArrayList<PropertyInfo> pl) {
propertyList = pl;
JFrame frame = new JFrame();
JScrollPane scroll = new JScrollPane(this);
frame.setVisible(true);
frame.setBounds(10,10,400,300); //(10, 10, 1100, 900);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setContentPane(scroll);
initDraw();
}
public void initDraw() {
Path2D.Double polygon = new Path2D.Double();
for (int i = 0; i < propertyList.size(); i++) {
Point2D[] points = propertyList.get(i).getCoordinate();
polygon.moveTo(((points[0].getX()-mapOffsetX)/20)+2000,((-points[0].getY()+mapOffsetY)/20)+300);
for (int j = 1; j < points.length; j++) {
polygon.lineTo(((points[j].getX()-mapOffsetX)/20)+2000,((-points[j].getY()+mapOffsetY)/20)+300);
}
polygon.closePath();
polygonList.add(polygon);
}
}
#Override
public Dimension getPreferredSize() {
return new Dimension(3000, 3000);
}
public void paintComponent(Graphics g1) {
super.paintComponent(g1);
Graphics2D g = (Graphics2D) g1;
this.setOpaque(true);
this.setBackground(Color.black);
g.setColor(Color.GRAY);
for (int k = 0; k < polygonList.size(); k++) {
g.draw(polygonList.get(k));
System.out.println("Polygon Count: "+ k);
}
}
} //EOF

In your initDraw function, don't you want to put the line
Path2D.Double polygon = new Path2D.Double();
inside of the for loop?
As written, it looks like each polygon will include all of the points of all of previous polygons as well.
Also, as you point out, it would be better to do the drawing once if possible. You should trying making a BufferedImage and drawing the polygons on it. Then your paintComponent function could just redraw from the saved BufferedImage which would be very fast.

Related

Draw Image 2D Graphics

I am doing a game in java, but I didnt put image, I already tried to put it by "drawimage", but it is not working right = (
I do not know where would be the best place to save the image and how would be the best method to call the image.`
public void paint(Graphics2D g2){
g2.setColor(getRandomColor());
g2.fillOval((int)x, (int)y, (int)diametro, (int)diametro);
}
`
This code is from my class asteroides they are a circle (because I'm going to test the collision with circles).
Thanks
I'm pretty sure you mean you want do draw an image on the Panel. First of all, I'd recommend you create a resources folder inside the projects src folder, and add all images there. Once you're done, you have to load the image with imageIO and draw it with drawImage. Here's a short example:
package asteroid;
import java.awt.Graphics2D;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import javax.imageio.ImageIO;
public class Nave {
BufferedImage iconeNave;
public Nave( ... ) {
try{
iconeNave = ImageIO.read(getClass().getResource("/resources/nave.png"));
}catch(IOException e){e.printStackTrace();}
catch(Exception e){e.printStackTrace();}
}
#Override
public void paint(Graphics2D g2){
AffineTransform at = new AffineTransform();
at.translate((int)x + radius/2.5,(int)y + radius/2.5);
at.rotate(Math.PI/2 + angle);
at.translate(-iconeNave.getWidth()/2, -iconeNave.getHeight()/2);
g2.drawImage(iconeNave, at, null);
}
}
In order to create an image in Java, you will first need to create a JPanel (essentially a window on your screen). It will look something like this (taken from javacodex.com):
import java.awt.*;
import java.awt.image.*;
import java.io.*;
import javax.swing.*;
import javax.imageio.ImageIO;
public class JPanelExample {
public static void main(String[] arguments) throws IOException {
JPanel panel = new JPanel();
BufferedImage image = ImageIO.read(new File("./java.jpg"));
JLabel label = new JLabel(new ImageIcon(image));
panel.add(label);
// main window
JFrame.setDefaultLookAndFeelDecorated(true);
JFrame frame = new JFrame("JPanel Example");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// add the Jpanel to the main window
frame.add(panel);
frame.pack();
frame.setVisible(true);
}
}
However, I am assuming that by "image," you mean a shape drawn out onto the screen instead (since you are using a paint method that draws a circle in your example). Then, you have your code in the correct method but need to implement a JPanel (since without a JPanel or JFrame you can't really display anything onto the screen graphics-wise).
Here is an example taken from Board.java of my Atari Breakout mini-game:
public class Board extends JPanel implements KeyListener {
// Other Game Code
public void paint(Graphics g) {
super.paint(g);
Graphics2D g2 = (Graphics2D) g;
g2.setColor(Color.BLACK);
// Shows lives left and score on screen
g2.drawString("Lives left: " + lives, 400, 600);
g2.drawString("Score: " + score, 400, 500);
// Paints Walls (separate paint method created in wall class)
topWall.paint(g2);
bottomWall.paint(g2);
leftWall.paint(g2);
rightWall.paint(g2);
// Paints 2 Balls (separate paint method created in ball class)
b.paint(g2);
b2.paint(g2);
// Paints Paddle (separate paint method created in paddle class)
paddle.paint(g2);
// Paints Bricks based on current level (separate paint method created in brick class)
// Bricks were created/stored in 2D Array, so drawn on screen through 2D Array as well.
if (level == 1) {
for (int x = 0; x < bricks.length; x++) {
for (int i = 0; i < bricks[0].length; i++) {
bricks[x][i].paint(g2);
}
}
}
if (level == 2) {
for (int x = 0; x < bricks.length; x++) {
for (int i = 0; i < bricks[0].length; i++) {
bricks[x][i].paint(g2);
}
}
}
}
Here is an example of a Paint Method in the Paddle Class:
// Constructor
public Paddle(int x, int y, int w, int h){
xpos = x;
ypos = y;
width = w;
height = h;
r = new Rectangle(xpos, ypos, width, height);
}
public void paint(Graphics2D g2){
g2.fill(r);
}
Output (What is shown on Screen):
Hope this helps you in drawing your circle!

How can I add more than one paintComponent() to a frame?

So this is my main class:
package testgame;
import java.awt.EventQueue;
import javax.swing.JFrame;
public class Game extends JFrame {
public static JFrame frame = new JFrame("Just a test!");
public static void LoadUI() {
frame.setDefaultCloseOperation(frame.EXIT_ON_CLOSE);
frame.setSize(550, 500);
frame.setLocationRelativeTo(null);
frame.setVisible(true); }
public static void main(String[] args) {
LoadUI();
frame.add(new Circles());
}
}
And this is the class that handles what I want to paint:
package testgame;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.geom.AffineTransform;
import java.awt.geom.Ellipse2D;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
public class Circles extends JPanel {
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
drawBubbles(g); }
public void drawBubbles(Graphics g) {
Graphics2D g2d = (Graphics2D) g;
RenderingHints rh
= new RenderingHints(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
rh.put(RenderingHints.KEY_RENDERING,
RenderingHints.VALUE_RENDER_QUALITY);
g2d.setRenderingHints(rh);
int x, y, size;
x = (int) (Math.random() * 500) + 15;
y = (int) (Math.random() * 450) + 15;
size = (int) (Math.random() * 50) + 25;
g2d.setColor(Color.GREEN);
g2d.drawOval(x, y, size, size);
g2d.fillOval(x, y, size, size); }
}
If I add another
frame.add(new Circles());
Nothing happens. I think it has to do with the layout of the frame, but the coordinates of the bubbles are random so I'm not sure how to work with this.
In this case I'm using a fixed-size array of 5, you may change it to a bigger fixed-size array or an ArrayList, as shown in this answer
For your particular case I would create a Circle class that may contain the data for each circle, being the coords and the size
Then create a CirclePane class that would paint all the Circles in a single paintComponent() method.
And finally, the Main class that would have a JFrame that may contain the CirclePane added to it.
With the above tips in mind, you could end up with something like this:
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.geom.Ellipse2D;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class CircleDrawer {
private JFrame frame;
public static void main(String[] args) {
SwingUtilities.invokeLater(new CircleDrawer()::createAndShowGui); //We place our program on the EDT
}
private void createAndShowGui() {
frame = new JFrame(getClass().getSimpleName());
CirclePane circle = new CirclePane(5); //We want to create 5 circles, we may want to add more so we change it to 10, or whatever number we want
frame.add(circle);
frame.pack();
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
//Data class
class Circle {
private Point coords;
private int size;
public Circle(Point coords, int size) {
this.coords = coords;
this.size = size;
}
public Point getCoords() {
return coords;
}
public void setCoords(Point coords) {
this.coords = coords;
}
public int getSize() {
return size;
}
public void setSize(int size) {
this.size = size;
}
}
//The drawing class
#SuppressWarnings("serial")
class CirclePane extends JPanel {
private int numberOfCircles;
private Circle[] circles;
public CirclePane(int numberOfCircles) {
this.numberOfCircles = numberOfCircles;
circles = new Circle[numberOfCircles];
for (int i = 0; i < numberOfCircles; i++) {
Point coords = new Point((int) (Math.random() * 500) + 15, (int) (Math.random() * 450) + 15); //We generate random coords
int size = (int) (Math.random() * 50) + 25; //And random sizes
circles[i] = new Circle(coords, size); //Finally we create a new Circle with these properties
}
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
for (int i = 0; i < numberOfCircles; i++) {
g2d.draw(new Ellipse2D.Double(circles[i].getCoords().getX(), circles[i].getCoords().getY(), circles[i].getSize(), circles[i].getSize())); //We iterate over each circle in the array and paint it according to its coords and sizes
}
}
#Override
public Dimension getPreferredSize() { //Never call JFrame.setSize(), instead override this method and call JFrame.pack()
return new Dimension(500, 500);
}
}
}
Which produces a similar output to this:
I hope this helps you to get a better idea, read about the MVC pattern as I made use of it for this answer.
Note:
In this answer I used the Shapes API, according to the recommendation of #MadProgrammer in this other answer. I used it in the g2d.draw(...) line.
For a deeper understanding in how custom painting works in Swing, check Oracle's Lesson: Performing Custom Painting and Painting in AWT and Swing tutorials.

Java: Fast zoom operations, set initial size of the buffer (image)

I am thinking about the implementation of the zoom operations (zoom in/out, panning) in Swing. The main problem refers to the amount of data represented by 1e+06 points/lines that need to be drawn.
To make the zoom operations as fast as possible, the following approach has been adopted.
1] I am using an off-screen buffer represented by the buffered image img.
2] During the zoom operations the data remains unchained. Instead of drawn all points, the current image is replaced with the off-screen buffer.
g2d.drawImage(img, 0, 0, this);
There is a simple example illustrating basic operations zoom in/out over the randomly generated points:
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseWheelEvent;
import java.awt.geom.AffineTransform;
import java.awt.geom.Point2D;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class ZoomOperations extends JPanel{
private boolean repaint;
private double zoom;
private List<Point2D.Double> points;
private AffineTransform at;
private BufferedImage img;
public ZoomOperations() {
repaint = true;
zoom = 0.1;
points = new ArrayList<>();
at = new AffineTransform();
img = null;
Random r = new Random();
for (int i = 0; i < 100; i++)
points.add(new Point2D.Double(5000 * r.nextDouble(), 5000 * r.nextDouble()));
addMouseWheelListener(new MouseAdapter() {
public void mouseWheelMoved(MouseWheelEvent e) {
if (e.getPreciseWheelRotation() < 0)
zoom = Math.min(zoom+= 0.02,2);
else
zoom = Math.max(zoom-= 0.02,0.01);
repaint();
}
});
}
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
if (img == null)
img = (BufferedImage)createImage(5000, 5000);
at = g2d.getTransform();
at.translate(0, 0);
at.scale(zoom, zoom);
g2d.setTransform(at);
if (repaint){
Graphics2D g2c = img.createGraphics();
for (Point2D.Double p:points)
g2c.fillOval((int)p.x-50, (int)p.y-50, 50, 50);
g2c.dispose();
repaint = false; //Only illustrative example
}
g2d.drawImage(img, 0, 0, this);
}
public static void main(String[] args) {
JFrame jf = new JFrame();
jf.setSize(800, 600);
jf.add(new ZoomOperations());
jf.setVisible(true);
}
}
The decision whether points will be drawn or not is made using the flag
private boolean repaint;
The zoom operations are relatively fast but I am not sure how to set the initial size of the buffer
img = (BufferedImage) createImage(5000, 5000); // How to set the size ?
to avoid getting outside the buffer when new points are added. There is no a priori information about the point coordinates X,Y, the zoom ratio is [1/100.0, 100.0]. Keep in mind that affine transformation shrinks / enlarges the buffer width and height (zoom < 1 / zoom > 1).
Unfortunately, the vector data drawn from the off-screen buffer are not smooth, but rasterized (pixels are visible under the magnification)...
Is there any more suitable approach?
Thanks for your help...

Detecting collision of two sprites that can rotate [duplicate]

This question already has answers here:
How to check intersection between 2 rotated rectangles?
(14 answers)
Closed 9 years ago.
I have a problem with collision detection in a 2D Java game.
Normally, what I would do is create a getBounds() method for an object that can collide with other objects. This method would return a new Rectangle(x,y,width,height), where x and y are the coordinates for the top-left corner of the sprite, and width and height are the width and height of the sprite.
But in the game I'm currently working on, there is a "tank" controlled by the user. The sprite of this tank rotates as long as the player holds one of the left or right arrow buttons. In other words, it can rotate to any angle. The tank's sprite is a rectangle.
So I can't simply do what I always do in this case.
How can I detect collision with this kind of sprite?
Thanks
A lot will depend on how you are managing your objects, but...
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.geom.AffineTransform;
import java.awt.geom.Area;
import java.awt.geom.GeneralPath;
import javax.swing.AbstractAction;
import javax.swing.ActionMap;
import javax.swing.InputMap;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.KeyStroke;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class RotateTest {
public static void main(String[] args) {
new RotateTest();
}
public RotateTest() {
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 Rectangle rect01;
private Rectangle rect02;
private int angle = 0;
public TestPane() {
rect01 = new Rectangle(0, 0, 100, 100);
rect02 = new Rectangle(0, 0, 100, 100);
Timer timer = new Timer(40, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
angle++;
repaint();
}
});
timer.start();
}
#Override
public Dimension getPreferredSize() {
return new Dimension(250, 250);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
int width = getWidth();
int height = getHeight();
AffineTransform at = new AffineTransform();
int center = width / 2;
int x = center + (center - rect01.width) / 2;
int y = (height - rect01.height) / 2;
at.translate(x, y);
at.rotate(Math.toRadians(angle), rect01.width / 2, rect01.height / 2);
GeneralPath path1 = new GeneralPath();
path1.append(rect01.getPathIterator(at), true);
g2d.fill(path1);
g2d.setColor(Color.BLUE);
g2d.draw(path1.getBounds());
at = new AffineTransform();
x = (center - rect02.width) / 2;
y = (height - rect02.height) / 2;
at.translate(x, y);
at.rotate(Math.toRadians(-angle), rect02.width / 2, rect02.height / 2);
GeneralPath path2 = new GeneralPath();
path2.append(rect02.getPathIterator(at), true);
g2d.fill(path2);
g2d.setColor(Color.BLUE);
g2d.draw(path2.getBounds());
Area a1 = new Area(path1);
Area a2 = new Area(path2);
a2.intersect(a1);
if (!a2.isEmpty()) {
g2d.setColor(Color.RED);
g2d.fill(a2);
}
g2d.dispose();
}
}
}
Basically, what this does, is it creates a PathIterator of the Rectangle, which allows me to apply a AffineTransformation to the shape without effecting the original shape...don't know if this is important or not, but this is how I did it...
Then I created a GeneralPath which allows me to paint the PathIterator.
Now, the funky bit...
I create two Areas, one for each GeneralPath representing each object I want to check. I then use the Area's intersect method, which generates a Area that represents the intersection points of the two objects and then check to see if this result is empty or not.
If it's empty, it's does not intersect, if it's not (empty), they touch.
Have fun playing with that ;)
You could use a Polygon rather than a Rectangle.
http://docs.oracle.com/javase/7/docs/api/java/awt/Polygon.html
This would manual calculation of the (x,y) corners however. It's possible that there is some way to construct a shape by rotating a Rectangle, but this would work at least. It has a getBounds() method.
What if you keep all the objects the tank can run into in an ArrayList, and simply use a for statement in a loop in a separate thread to determine if the tank has hit something. For example, create a class for each object, say class Wall(), and keep the x, y variables available. Place the Wall() into the ArrayList, in this case tanks. Then in a for loop, within a while loop: EC
while(true) {
for(int i = 0; i < tanks.size(); i++) {
//Do the code to determine if the tank has hit something.
if(tanks.get(i).x => tank.x) //Do whatever
}
}
Of course adapt this to what you need, but the idea is the same. If the object intersects the tank, or vice versa, do whatever you want to happen. To sum it up, just check the area the tank takes up, and see if that area intersects another object.
Actually, even better:
I assume youre using the paint() method? Whenever you update the Component, simply add some code that does basically what I just said to, checks to see if the area is intersecting with an if() statement. EC:
if(tanks.get(i).x => tank.x || tanks.get(i).y => tank.y) DO SOMETHING
of course, again adapt this if() to suit your needs

How can I remove a rectangle after a collision?

I have two rectangles that I need to be taken off screen when they intersect. The rectangles I need to disappear are, bulletObject and e1. They do intersect when I run it but nothing happens. I have tried putting "e1 = new Rectangle (0,0,0,0);" after the "if (bulletObject.intersects(e1)){" but then it tells me that it is never used. All help I appreciated. A chunk of my code is below.
public void draw(Graphics g){
g.setColor(Color.BLUE);
g.fillRect(x, y, 40, 10);
g.fillRect(x+18, y-7, 4, 7);
Rectangle bulletObject = new Rectangle(x+18, y-7, 4, 7);
if (shot){
g.setColor(Color.BLUE);
g.fillRect(bullet.x, bullet.y , bullet.width, bullet.height);
}
//enemies
g.setColor(Color.RED);
Rectangle e1 = new Rectangle(20,75,35,35);
Rectangle e2 = new Rectangle(85,75,35,35);
Rectangle e3 = new Rectangle(150,75,35,35);
Rectangle e4 = new Rectangle(205,75,35,35);
Rectangle e5 = new Rectangle(270,75,35,35);
Rectangle e6 = new Rectangle(335,75,35,35);
Rectangle e7 = new Rectangle(405,75,35,35);
g.setColor(Color.RED);
g.fillRect(e1.x,e1.y,e1.width,e1.height);
g.fillRect(e2.x,e2.y,e2.width,e2.height);
g.fillRect(e3.x,e3.y,e3.width,e3.height);
g.fillRect(e4.x,e4.y,e4.width,e4.height);
g.fillRect(e5.x,e5.y,e5.width,e5.height);
g.fillRect(e6.x,e6.y,e6.width,e6.height);
g.fillRect(e7.x,e7.y,e7.width,e7.height);
g.fillRect(bulletObject.x,bulletObject.y,
bulletObject.width,bulletObject.height);
if (bulletObject.intersects(e1)){
g.clearRect(e1.x, e1.y,e1.width, e1.height );
}
}
Lets start with...
Your paint routines is not the appropriate place to be making decisions about the state of the game, it should be simple responsible for painting the current state.
You need to maintain a List of renderable elements which you can manipluate based on your needs and requirements.
Start by taking a look at Collections
You may also find reading through Performing Custom Lainting and Painting in AWT and Swing useful
The following example demonstrates the basic concept of a series of animated, random, rectangles which will be removed when hit by a fireball, which you trigger by pressing the space bar
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.geom.Ellipse2D;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Random;
import javax.swing.AbstractAction;
import javax.swing.ActionMap;
import javax.swing.InputMap;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.KeyStroke;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class Bullet {
public static void main(String[] args) {
new Bullet();
}
public Bullet() {
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 List<Rectangle> ships;
private Map<Rectangle, Integer> delats;
private Ellipse2D fireBall;
public TestPane() {
delats = new HashMap<>(25);
ships = new ArrayList<>(25);
Random rnd = new Random();
while (ships.size() < 12) {
boolean intersects = true;
Rectangle rect = null;
while (intersects) {
intersects = false;
int x = (int) (Math.random() * 400);
int y = (int) (Math.random() * 400);
int width = (int) (Math.random() * 50) + 25;
int height = (int) (Math.random() * 50) + 25;
if (x + width >= 400) {
x = 400 - width;
} else if (y + height >= 400) {
y = 400 - height;
}
rect = new Rectangle(x, y, width, height);
for (Rectangle other : ships) {
if (other.intersects(rect)) {
intersects = true;
break;
}
}
}
ships.add(rect);
delats.put(rect, (rnd.nextBoolean() ? 1 : -1));
}
Timer timer;
timer = new Timer(40, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
if (fireBall != null) {
Rectangle bounds = fireBall.getBounds();
bounds.x += 5;
if (bounds.x >= getWidth()) {
fireBall = null;
} else {
fireBall.setFrame(bounds);
}
}
Iterator<Rectangle> it = ships.iterator();
while (it.hasNext()) {
Rectangle rct = it.next();
int delta = delats.get(rct);
rct.y += delta;
if (rct.y + rct.height >= getHeight()) {
rct.y = getHeight() - rct.height;
delta *= -1;
} else if (rct.y <= 0) {
rct.y = 0;
delta *= -1;
}
delats.put(rct, delta);
if (fireBall != null) {
if (fireBall.intersects(rct)) {
it.remove();
delats.remove(rct);
}
}
}
repaint();
}
});
timer.start();
InputMap im = getInputMap(WHEN_IN_FOCUSED_WINDOW);
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_SPACE, 0), "fire");
ActionMap am = getActionMap();
am.put("fire", new AbstractAction() {
#Override
public void actionPerformed(ActionEvent e) {
fireBall = new Ellipse2D.Float(0, (getHeight() / 2) - 5, 10, 10);
}
});
}
#Override
public Dimension getPreferredSize() {
return new Dimension(400, 400);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
if (fireBall != null) {
g2d.setColor(Color.RED);
g2d.fill(fireBall);
}
g2d.setColor(Color.BLACK);
for (Rectangle rct : ships) {
g2d.draw(rct);
}
g2d.dispose();
}
}
}
It sounds like you are new to programming since you said you don't understand what a list is. I would suggest you check out the java tutorials available online (for instance for understanding lists and other collections: http://docs.oracle.com/javase/tutorial/collections/).
As far as your question, I agree with MadProgrammer, you should have a list of objects which your draw function draws and when a rectangle is "hit" then you should remove it from this list.
For instance to create your list use:
List<Shape> shapeList = new ArrayList<Shape>();
Rectangle e1 = new Rectangle(20,75,35,35);
shapeList.add(e1);
Rectangle e2 = new Rectangle(85,75,35,35);
shapeList.add(e2);
//repeat for each rectangle
This shouldn't be in your draw method as this method will be called many times in your program I assume and you don't want to have to recreate this list and each rectangle every time you do this.
I suggest you look at the documentation for List and ArrayList and learn how to use them well since you will use them almost every time you write a program in Java.
Good Luck
Store your Rectangles in some kind of data structure - if you know the maximum possible numer of Rectangles you may use an array. If not - a list would be a better choice as its size is not fixed (you just add and remove the elements to/from the list and do not care much about its size - you can read more about Lists here: http://docs.oracle.com/javase/tutorial/collections/implementations/list.html , online search should also be fruitful.)
Once all your Rectangles are in an array or a list (or any other collection you choose) you can paint all the rectangles that are in the collection at the moment. If two or more of them collide - remove them from the collection and repaint (so again paint all the rectangles which are in the collection now).
If all of this appear to be unfamiliar for you (arrays, collections...) I highly recommend you check out some Java tutorials (or general programming tutorials) - for example the official Java Turorial by Oracle: http://docs.oracle.com/javase/tutorial/tutorialLearningPaths.html

Categories

Resources