how to draw multiple rectangles - java

so I am trying to make a simple program where you click on the screen and it creates a block that falls and collides with a larger block beneath and sticks to it. Kind of like a simple collision program. The problem is when I create one block it deletes the block previously. I made an array, but it still does this. Do any of you know what Im doing wrong? Im sure its a simple fix.
public class Screen extends JPanel implements Runnable {
public static JLabel statusbar; //displays a status bar showing what mouse movements are taking place
private Image cat; //image of the cat
public int xCoord ; //get the coordinates of the mouse pressed
public int yCoord ;
public int xCreate;
public int yCreate;
public Rectangle Ground;
public Rectangle Block;
public boolean isClicked = false;
public int clickCount = 0;
Rectangle blocks[] = new Rectangle[10];
int blocknum = 0;
public Screen(Frame frame) {
loadPic(); //calls the loadPic method above
Handlerclass handler = new Handlerclass(); //creates a new class to use the mouse motion listener
System.out.println("mouse works!");
addMouseListener(handler);
addMouseMotionListener(handler);
statusbar = new JLabel("default");
add(statusbar);
}
public void run(){ //this is the game run loop
System.out.println("this is running");
try{
} catch(Exception e) {} //exception handling
}
public void loadPic(){ //loads the picture from the other project but its the same pic
cat = new ImageIcon("C:\\Users\\Camtronius\\Documents\\NetBeansProjects\\Moving Block Proj\\src\\MovingBlock\\catIcon1.png").getImage(); //gets the image
System.out.println("Image Loaded!");
}
#Override public void paintComponent(Graphics g){
super.paintComponent(g); //paints the component, the picture, on top
Graphics2D g2d = (Graphics2D) g.create();
g2d.drawImage(cat, xCoord, yCoord, null);
g2d.setColor(Color.BLUE);
Ground = new Rectangle(0,450,550,50);
g2d.fillRect(0,450, 550, 50);
for(Rectangle blocknum : blocks){
if (blocks != null) {
g2d.setColor(Color.RED);
g2d.fillRect(xCreate,yCreate,50,50);
System.out.println(blocknum);
}
}
//move();
}
public void move(){
if(yCreate<400){
yCreate+=1;
}else{
}
if(Ground.intersects(blocks[blocknum])){
yCreate=400;
System.out.println("contains!");
}
}
private class Handlerclass implements MouseListener, MouseMotionListener{
public void mouseClicked(MouseEvent event){
}
public void mousePressed(MouseEvent event){
}
public void mouseReleased(MouseEvent event){
if(blocknum<blocks.length){
xCreate=event.getX();
yCreate=event.getY();
blocks[blocknum] = new Rectangle(50,50, xCreate, yCreate);
repaint();
}
blocknum=blocknum+1;
}
public void mouseEntered(MouseEvent event){
}
public void mouseExited(MouseEvent event){
}
public void mouseDragged(MouseEvent event){
}
public void mouseMoved(MouseEvent event){
statusbar.setText(String.format("Coordinates are: %d, %d", event.getX(),event.getY()));
xCoord=event.getX();
yCoord=event.getY();
}
}
}

Painting is a destructive process. That is, when a new paint cycle runs, the previous contents of the Graphics context should be cleared...
So, in you paintComponent method you are only painting the last block...
if(isClicked = true){
blocks[blocknum] = new Rectangle(50,50, xCreate, yCreate);
g2d.setColor(Color.RED);
g2d.fillRect(xCreate,yCreate,50,50);
System.out.println(blocknum);
repaint(); // THIS IS A BAD IDEA
}
DO NOT call any method that might cause repaint to be called. This will put you in a potential cycle of death that will consume your CPU.
Instead, you should loop through the blocks array and paint each one...
for (Rectangle block : blocks) {
if (block != null) {
g2d.setColor(Color.RED);
g2d.fill(block);
}
}
And in you mouseReleased method, you should be adding the new rectangles...
public void mouseReleased(MouseEvent event){
blocknum=blocknum+1;
if (blocknum < blocks.length) {
xCreate=event.getX();
yCreate=event.getY();
blocks[blocknum] = new Rectangle(xCreate, yCreate, 50, 50);
}
}
I'd suggest you take a look at Custom Painting, Painting in AWT and Swing and Concurrency in Swing for more details

Related

Drawing shape on mouse drag

class MyPanel extends JPanel implements Observer, MouseMotionListener, MouseListener {
private MyModel model;
private View view;
private String mode;
private Rectangle rectangle;
private Square square;
public MyPanel(MyModel model, View view) {
this.setBackground(Color.black);
this.setPreferredSize(new Dimension(300, 300));
this.addMouseListener(this);
this.addMouseMotionListener(this);
this.model = model;
this.model.addObserver(this);
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
ArrayList<Rectangle> rectangles = this.model.getRectangles();
for (Rectangle r : getRectangles()) {
int width = Math.abs(r.getStartPoint().getX() - r.getEndPoint().getX());
int height = Math.abs(r.getStartPoint().getY() - r.getEndPoint().getY());
}
ArrayList<Square> squares = this.model.getSquares();
for (Square sqr : getSquares()) {
int xPosition = Math.min(sqr.getStartPoint().getX(), sqr.getEndPoint().getX());
int yPosition = Math.min(sqr.getStartPoint().getY(), sqr.getEndPoint().getY());
int width = Math.abs(sqr.getStartPoint().getX() - sqr.getEndPoint().getX());
int height = Math.abs(sqr.getStartPoint().getY() - sqr.getEndPoint().getY());
}
g2d.dispose();
}
public void update(Observable o, Object arg) {
this.repaint();
}
#Override
public void mouseDragged(MouseEvent e) {
if (this.mode.equals("Rectangle")) {
this.rectangle.setEndPoint(e.getX(), e.getY());
this.model.addRectangle(this.rectangle);
}
else if (this.mode.equals("Square")) {
// What code should I add here?
this.model.addSquare(this.square);
}
}
// MouseListener below
#Override
public void mouseClicked(MouseEvent e) {}
#Override
public void mousePressed(MouseEvent e) {
if (this.mode.equals("Rectangle")) {
this.rectangle = new Rectangle(e.getX(), e.getY());
}
else if (this.mode.equals("Square")) {
this.square = new Square(e.getX(), e.getY());
}
}
#Override
public void mouseReleased(MouseEvent e) {
if (this.mode.equals("Rectangle")) {
this.rectangle.setEndPoint(e.getX(), e.getY());
this.model.addRectangle(this.rectangle);
this.rectangle = null;
}
else if (this.mode.equals("Square")) {
this.model.addSquare(this.square);
this.square = null;
}
}
#Override
public void mouseEntered(MouseEvent e) {}
#Override
public void mouseExited(MouseEvent e) {}
}
The user chooses a mode, rectangle or square. Then they can draw a square or a rectangle with their mouse (live feedback is shown). Here is my drawing panel class. I was successfully able to implement the rectangle mode. The user can draw a rectangle and as they move their mouse, the rectangle is shown in mid construction. I want to do the same for the square mode. For some reason, I'm having a hard time doing this. How would I show a perfect square in mid construction when the user is moving their mouse and how would I draw it once released? What code should I add to my paintComponent method, mouseDragged, mousePressed and mouseReleased method to do this? It was easy for a rectangle because there was no constraint but I'm not sure how to do it for a square with my current implementation.
int width = Math.abs(r.getStartPoint().getX() - r.getEndPoint().getX());
int height = Math.abs(r.getStartPoint().getY() - r.getEndPoint().getY());
I would guess that the "size" of the square would be the maximum of the above two values.
Then I would think you would just use:
r.drawStyle(g2d, xPosition, yPosition, size, size);

Add moving objects to JFrame

I'm creating a java game. In the game there are a hero and a bubble. The hero is supposed to move when I press the arrow keys and the bubble is supposed to have continuous diagonal movement. When I add the hero or the bubble directly into to the JFrame separately I get the desired behavior, but when I add them both I just get a very small square! I tried to add them to the same JPanel and after add that JPanel to the JFrame but it is not working. Probably I have to define some type of layout to the JPanels.
What am I doing wrong?
Code:
public class Pang {
public static void main(String[] args) {
JFrame f=new JFrame();
JPanel gamePanel=new JPanel();
gamePanel.setPreferredSize(new Dimension(800, 600));
DrawHero d=new DrawHero();
DrawBubble bubble=new DrawBubble();
gamePanel.add(d);
gamePanel.add(bubble);
f.add(gamePanel);
f.setVisible(true);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setSize(800, 600);
}
}
public class DrawHero extends JPanel implements ActionListener, KeyListener {
Timer myTimer = new Timer(5, this);
int x = 0, y = 0, dx = 0, dy = 0, step = 10;
private transient Image imageHero = null;
public DrawHero() {
myTimer.start();
addKeyListener(this);
setFocusable(true);
setFocusTraversalKeysEnabled(false);
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
imageHero = getHeroImage();
g2.drawImage(imageHero, x, y, 40, 40, null);
}
public Image getHeroImage() {
Image image = null;
image = getImage("hero.png");
return image;
}
public Image getImage(String path) {
Image tempImage = null;
try {
URL heroiURL = DrawHero.class.getResource(path);
tempImage = Toolkit.getDefaultToolkit().getImage(heroiURL);
} catch (Exception e) {
System.out.println("Error loading hero image! - "
+ e.getMessage());
}
return tempImage;
}
public void actionPerformed(ActionEvent e) {
repaint();
}
public void moveUp() {
y = y - step;
}
public void moveDown() {
y = y + step;
}
public void moveLeft() {
x = x - step;
}
public void moveRight() {
x = x + step;
}
public void keyPressed(KeyEvent e) {
int keyCode = e.getKeyCode();
if (keyCode == KeyEvent.VK_UP) {
moveUp();
}
if (keyCode == KeyEvent.VK_DOWN) {
moveDown();
}
if (keyCode == KeyEvent.VK_LEFT) {
moveLeft();
}
if (keyCode == KeyEvent.VK_RIGHT) {
moveRight();
}
}
public void keyTyped(KeyEvent e) {
}
public void keyReleased(KeyEvent e) {
}
}
public class DrawBubble extends JPanel implements ActionListener, KeyListener {
Timer myTimer = new Timer(5, this);
int x = 100, y = 200, dx = 0, dy = 0, step = 10;
private transient Image imageHero = null;
public DrawBubble() {
myTimer.start();
addKeyListener(this);
setFocusable(true);
setFocusTraversalKeysEnabled(false);
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.fill(new Ellipse2D.Double(x, y, 40, 40));
}
public void actionPerformed(ActionEvent e) {
x=x+dx;
y=y+dy;
repaint();
}
public void moveBubble() {
dy=2;
dx=2;
}
public void keyPressed(KeyEvent e) {
moveBubble();
}
public void keyTyped(KeyEvent e) {
}
public void keyReleased(KeyEvent e) {
}
}
I recommend that neither the DrawHero nor DrawBubble (which should be called Hero nor Bubble respectively) should extend any JComponent. Instead each should simply know how to draw itself to a Graphics object passed to it, when requested to do so.
Then a single GameField or PlayingArea class should keep references to all the Bubble objects and the Hero and draw call the draw(Graphics) method of those objects.
Using this approach it is not necessary to worry about layouts within the GameField component (they become irrelevant).
That is the basic strategy I pursue for rendering the stationary objects in this answer to [Collision detection with complex shapes.
When I add the hero or the bubble directly into to the JFrame separately I get the desired behavior, but when I add them both i just get a very small square!
The default layout manager for a JFrame is a BorderLayout. When you use add(component) without a constraint the component goes to the CENTER. Only one component can be added to the CENTER, so only the last one added is displayed.
I tried to add them to the same JPanel and after add that JPanel to the JFrame but it is not working.
The default layout manager for a JPanel is the FlowLayout which respects the preferred size of component. The problem is you don't override the getPreferredSize() method so the size is (0, 0) and there is nothing to paint.
Probably I have to define some type of layout to the JPanels.
Actually since you want random motion you need to use a null layout on the panel and then use the setSize() and setLocation() method of your components to position the components.
Then when you do this the custom painting should always be done at (0, 0) instead of (x, y) since the location will control where the component is painted on the panel.

How to draw an image over another?

So I am working on a project for a class. And I've been struggling with this issue for a while now. The code below is a start screen, and when the enter key is pressed (when atTitle turns to false) I would like it to draw the next image. The problem with that is I can not think of a way for it to draw the next image when it turns to false. I've tried using ifs and whiles. Mainly the problem is that you obviously put another public void paintComponent in an if statement. And I can't carry the Graphics g variable to the KeyPressed method.
I'm stuck.
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D)g;
AffineTransform at = new AffineTransform();
g2.setTransform(at);
if (atTitle == true) {
g.drawImage(titlescreen, 0, 0, this);
if (start_visible == true) {
g.drawImage(start_symbol, -70, 30, this);
jf.addKeyListener(this);
}
}
}
public void keyTyped(KeyEvent e) {}
public void keyReleased(KeyEvent e) {}
public void keyPressed(KeyEvent e) {
//int keyCode = e.getKeyCode();
if (atTitle == true) {
if (e.getKeyCode() == KeyEvent.VK_ENTER) {
atTitle = false;
System.out.println("It Works.");
}
}
}
you need to call repaint and wait system calls to paint function. you should not keep graphic object and use it in a function is not child function of paint
as a simple canvas component, you can use this one
class Game extends Canvas implements Runnable {
private Thread thread;
public Game(){
thread = new Thread(this);
thread.start();
}
#Override
public void paint(Graphics g) {
// paint your game
}
public void stop(){
thread = null;
}
#Override
public void run() {
while (thread == Thread.currentThread()){
long ti = System.currentTimeMillis();
repaint();
long ti2 = System.currentTimeMillis();
long waitTime = 60 - (ti2-ti);
if (waitTime > 0){
try {
Thread.sleep(waitTime);
} catch (Exception e){
}
}
}
}
}
If you are using JPanel you should:
panel.setFocusable(true);
panel.requestFocusInWindow();
Referenced here
Also you can call panel.revalidate() and/or panel.repaint() in the key press.
Maybe try to stay away from JPanel and Swing altogether (not suited for gaming, but for forms) and just use the graphics2D functionality with a Window, Frame and Canvas, as suggested in previous comment.

How to get JPanel to remember user-drawn graphics when panel is resized?

I'm trying to make a program that allows users to place music notes on staffs. There is a class called SheetMusicPane that extends JPanel, and it starts out with a bunch of staffs drawn from top to bottom. By clicking on the buttons in the toolbar at the top, users can choose notes to draw. There is also a button on the toolbar that allows the user to increase the amount of sheet music available (the SheetMusicPane is set inside a JScrollPane). I don't know how to make the music notes copy over when the user resizes the SheetMusicPane. The initial staffs stay put, however.
I tried the JPanel.repaint() method; it didn't work.
The resizing is pretty simple:
JButton moreMusicButton = new JButton("Get More Sheet Music");
tb.add(moreMusicButton);
moreMusicButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
smp.setPreferredSize(new Dimension(scrollSize.width, smp.getHeight() + moreMusicAmount));
smp.repaint();
}
});
As said above, repaint() isn't doing anything.
Thanks in advance!
Adding note-drawing code:
The note-drawing buttons each call this method (the String "note" is obtained
from the button's ActionCommand):
public void getShape(final Graphics g, final String note) {
MouseListener[] listeners = this.getMouseListeners();
for (MouseListener ml : listeners) {
this.removeMouseListener(ml); //makes graphics stop drawing previous note
}
this.addMouseListener(new MouseListener() {
public void mousePressed(MouseEvent e) {
}
public void mouseReleased(MouseEvent e) {
}
public void mouseEntered(MouseEvent e) {
}
public void mouseExited(MouseEvent e) {
}
public void mouseClicked(MouseEvent e) {
Point p = MouseInfo.getPointerInfo().getLocation();
addShape(g, p.x, p.y, note);
int pitch = 12;
piano.playNote(pitch);
advance(1.0, piano);
try { addToFile(pitch, note);}
catch(FileNotFoundException fnfe) {}
catch(IOException ioe) {}
}
});
}
Here's how the panel itself is created. It comprises a set of private nested classes and it starts out showing plain, note-free sheet music.
private class SheetMusicPane extends JPanel {
public SheetMusicPane() {
final Graphics g = this.getGraphics();
}
//paints staffs
public void paint(Graphics g) {
super.paintComponent(g);
for (int i = 0; (i - 1)*staffWidth < scrollSize.height + 100; i++) {
int y = paddingNorth + i*staffWidth;
Staff staff = new Staff(g, y, "treble");
}
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
for (String note : noteList) {
addShape(g, 100, 200, note);
}
}
//takes in mouse info and calls addShape to draw notes on clicks
public void getShape(final Graphics g, final String note) { ... } //as shown above
//draws a note
private void addShape(Graphics g, int x, int y, String note) { ... }
private void addToFile(int pitch, String note) throws FileNotFoundException, IOException { ...//adds note info to a file }
//just makes a five-line staff at this point
private class Staff {
private Graphics g;
private int y;
private int[] pitches;
public Staff(Graphics g, int y, String cleff) { ...//draws the lines }
}
you need to override the paintComponent method of your Panel.
You save everything to paint in an Array (or List) and the paintComponent method draws them. (on every repaint).
If you draw them on click, the next time repaint() is called the Panel will be cleared and repainted using the paintComponent Method. Your drawings are as long on the Panel as reapaint() is not called.

Custom painting in Java - drawing UI elements onto the JPanel

I am creating a drawing board program, basically a simplified version of MS Paint. It is working for the most part but when I draw on the board, the currently selected UI element shows up on the drawing area.
I tried paintComponent, which worked when I called super.paintComponent(g) (as one does) but it cleared the drawing area before I drew the next object. I overrode update and put a println statement in there and it is never being called.
The buttons at the top are red because I set the background to the bottom JPanel to red to see what the background of these buttons would be. So clearly they are part of the bottom JPanel. I am using a BorderLayout for the layout.
Here is my code (with some irrelevant bits removed):
public class JSPaint extends JFrame implements Serializable
{
private static final long serialVersionUID = -8787645153679803322L;
private JFrame mainFrame;
private JPanel bp;
private JButton ...
private DrawingArea da;
public JSPaint()
{
setTitle("JS Paint");
setSize(1024, 768);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// Drawing area
da = new DrawingArea();
setLayout(new BorderLayout());
// add the buttons to the panel
buttonPanel();
// Add the drawing area
add(bp, BorderLayout.SOUTH);
bp.setBackground(Color.RED);
add(da, BorderLayout.CENTER);
da.setBackground(Color.BLUE);
setVisible(true);
}
// I put it here too just in case
#Override
public void update(Graphics g)
{
System.out.println("update in JSPaint called.");
paint(g);
}
/*
* Creates the panel for the buttons, creates the buttons and places them on
* the panel
*/
public void buttonPanel()
{
// Create the panel for the buttons to be placed in
bp = new JPanel();
saveButton = new JButton("Save");
loadButton = new JButton("Load");
//more buttons
bp.add(saveButton);
bp.add(loadButton);
//more buttons
// ActionListeners
colorButton.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent ae)
{
System.out.println("color");
da.color();
}
});
}
public class DrawingArea extends JPanel
{
private static final long serialVersionUID = -8299084743195098560L;
boolean dragged = false;
#Override
public void update(Graphics g)
{
System.out.println("Update in DrawingArea called");
paint(g);
}
/*
* Draws the selected shape onto the screen and saves it into a Stack.
*
*/
public void draw()
{
this.addMouseMotionListener(new MouseMotionListener()
{
public void mouseDragged(MouseEvent me)
{
dragged = true;
}
public void mouseMoved(MouseEvent me) {}
});
//more listeners...
});
}
/*
* Draws the selected String onto the screen when the mouse is held down.
*
*/
public void brush()
{
this.addMouseMotionListener(new MouseMotionListener()
{
public void mouseDragged(MouseEvent me)
{
// If we are in drawing mode, draw the String. Create a new
// Figure Object and push it onto the Stack
if(activeButton == "brush")
{
startPoint = me.getPoint();
Figure fig = new Figure("String", startPoint, null, currentColor);
// figures.push(calculate(fig));
toPaint.push(calculate(fig));
repaint();
}
}
public void mouseMoved(MouseEvent me) {}
});
}
// more of the same...
public void paint(Graphics g)
{
toSave.addAll(toPaint);
while(!toPaint.isEmpty())
{
Figure f = toPaint.pop();
String t = f.type;
if(f.color != null)
{
g.setColor(f.color);
}
switch(t)
{
case "Rectangle": g.drawRect(f.x1, f.y1, f.width, f.height);
break;
case "Oval": g.drawOval(f.x1, f.y1, f.width, f.height);
break;
case "Line": g.drawLine(f.x1, f.y1, f.x2, f.y2);
break;
case "Clear":
g.fillRect(0, 0, da.getWidth(), da.getHeight());
clearStack(toSave);
break;
case "String": g.drawString(f.toPrint, f.x1, f.y1);
break;
}
}
}
}
private class Figure implements Serializable
{
private static final long serialVersionUID = 4690475365105752994L;
String type, toPrint;
Color color;
Point start;
Point end;
int x1, y1, x2, y2, width, height;
public Figure(String figureType,
Point startPoint, Point endPoint, Color figureColor)
{
type = figureType;
color = figureColor;
start = startPoint;
end = endPoint;
}
// Rect, Oval
public Figure(String figureType, int figureX, int figureY,
int figureWidth, int figureHeight, Color figureColor)
{
type = figureType;
x1 = figureX;
y1 = figureY;
width = figureWidth;
height = figureHeight;
color = figureColor;
}
// more shapes
}
public static void main(String args[])
{
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
new JSPaint();
}
});
}
}
If the currently selected UI element shows up on the drawing area, this can have only one reason: you are doing something wrong with the Graphics objects, possibly calling a translate on them, and not resetting the tranlation when you are done. (See this link for an example of translate used correctly).

Categories

Resources