2

I'm trying to create a simple game using AWT but I want to have some JButtons aswell to exit/reset the game. The problem is, I want the BufferedImage to be drawn inside the visible frame like so in my container I have this at the end:

g.drawImage(bf,getParent().getInsets().left,getParent().getInsets().top,null);

My problem is, when I add a JButton to that frame, it only detects rollover in space that doesn't take into account the offsetting, but is drawn in a space that does. This is the relevant code (con is the container).

    private void addButtons()
{
    reset = new JButton("reset");
    reset.setBounds(180,460, 75,30);
    reset.addActionListener( this );
    con.add(reset);
    exit = new JButton("exit");
    exit.setBounds(290,460, 60,30);
    exit.addActionListener( this );
    con.add(exit);
    con.repaint();
}

The paint method in the Container

public void paint(Graphics g)
{
    bf = new BufferedImage(this.getWidth(), this.getHeight(), BufferedImage.TYPE_INT_RGB);
    Graphics b = bf.getGraphics();
    b.setColor(Color.GRAY);
    b.fillRect(0, 0, this.getWidth(), this.getHeight());
    b.setColor(Color.BLACK);
    b.drawRect(0,0,420,420);
    super.paint(b);
    g.drawImage(bf,getParent().getInsets().left,getParent().getInsets().top,null);
}

How can I make the button be drawn and detected in the same spot?

here is a screenshot of the problem

As requested:

import java.awt.*;
import javax.swing.*;
import java.util.*;
import java.awt.image.*;
import java.awt.event.*;
import java.awt.Color;
public class Draw implements ActionListener{
private SnakeFrame frame;
private SnakeCon con;
JButton reset, exit;
private boolean res;
public Draw() 
{
    frame = new SnakeFrame("Snake");
    frame.setResizable(false);
    frame.setLayout(null);
    frame.setSize(600,600);
    frame.setVisible(true);
    con = new SnakeCon();
    con.setBounds(0,0,600,600);
    frame.add(con);
}

private void addButtons()
{
    reset = new JButton("reset");
    reset.setBounds(180,460, 75,30);
    reset.addActionListener( this );
    con.add(reset);
    exit = new JButton("exit");
    exit.setBounds(290,460, 60,30);
    exit.addActionListener( this );
    con.add(exit);
    con.repaint();
}
public void run()
{

    addButtons();
    res = false;
    boolean dead = false;       
    while(!dead)
    {
        if( (res) )
            dead = true;
        if (!dead)
        {
            try{
            Thread.sleep(100);
            }
            catch (Exception e)
            {}
            frame.repaint();
        }
    }
    con.removeAll();
}

public void actionPerformed(ActionEvent e)
{
    if (e.getSource() == reset)
    res = true;
else if (e.getSource() == exit)
    System.exit(0);
}
}

--

    import java.awt.*;
import javax.swing.*;
import java.util.*;
import java.awt.image.*;
import java.awt.event.*;
import java.awt.Color;
public class SnakeCon extends Container{
    private BufferedImage bf;

    public SnakeCon()
    {
        super();
        setBounds(0,0,600,600);
    }
    public void paint(Graphics g)
    {
        bf = new BufferedImage(this.getWidth(), this.getHeight(), BufferedImage.TYPE_INT_RGB);
        Graphics b = bf.getGraphics();
        b.setColor(Color.GRAY);
        b.fillRect(0, 0, this.getWidth(), this.getHeight());
        b.setColor(Color.BLACK);
        b.drawRect(0,0,420,420);
        super.paint(b);
        g.drawImage(bf,getParent().getInsets().left,getParent().getInsets().top,null);
    }
    public void update(Graphics g)
    {
        paint(g);
    }
    }

--

    import java.awt.*;
import javax.swing.*;
import java.util.*;
import java.awt.image.*;
import java.awt.event.*;
import java.awt.Color;
public class SnakeFrame extends Frame implements WindowListener{
    private BufferedImage bf;

    public SnakeFrame(String s)
    {
        super(s);
        addWindowListener( this );
    }
    public void paint(Graphics g)
    {

        bf = new BufferedImage(this.getWidth(), this.getHeight(), BufferedImage.TYPE_INT_RGB);
        Graphics b = bf.getGraphics();
        super.paint(b);
        g.drawImage(bf,0,0,null);
    }
    public void update(Graphics g)
    {
        paint(g);
    }

    public void windowClosing(WindowEvent e) 
    {
            System.exit(0);
    }

    public void windowClosed(WindowEvent e) {    }

    public void windowOpened(WindowEvent e) {    }

    public void windowIconified(WindowEvent e) {    }

    public void windowDeiconified(WindowEvent e) {    }

    public void windowActivated(WindowEvent e) {    }

    public void windowDeactivated(WindowEvent e) {    }
    }

--

public class Main {
public static void main(String[] args) 
{
    boolean never = false;
    Draw d = new Draw();
    while(!never)
    {
        d.run();
    }
    System.exit(0);
}
}
JasonG
  • 45
  • 5
  • 3
    for better help sooner, please include an [sscce](http://sscce.org). – mre Dec 05 '12 at 16:17
  • Is it related to this code? reset.setBounds(180,460, 75,30); – Bryan Glazer Dec 05 '12 at 17:17
  • @BryanGlazer that should just put the button at 180x, 460y with a width of 75 and height of 30. The problem is the mouse detection if off because I offset the insets of the frame when painting the Buffered Image. I'm not sure how to manually change that bounding box or how to fix it. – JasonG Dec 05 '12 at 17:26

1 Answers1

1

Im not sure exactly what is wrong/what you want (why do you have a loop to constantly drawing buttons and invoking removeAll()? etc but i cant shake the feeling it might be implemented in a more readable/efficient way)...

But here are some suggestions that can only help your code get better:

  • Dont use null/Absolute Layout choose an appropriate LayoutManager.

  • Do not override JFrame paint(..), rather add JPanel to JFrame and override paintComponent(Graphics g) of JPanel and do drawing there.(Do not forget to have super.paintComponent(..) as 1st call in overriden paintComponent method. See here for more: Performing Custom Painting

  • Do not set JFrame visible before adding all components to JFrame

  • Always create and manipulate Swing components on Event Dispatch Thread like so:

     SwingUtilities.invokeLater(new Runnable() {
          @Override
          public void run() {
             //create Swing components
         }
     });
    
  • Do not do long running tasks on Event Dispatch Thread rather use Swing Timer/Swing Worker

  • Do not call setSize(..) on JFrame rather override getPreferredSize() of JPanel and return Dimensions which fit all components (see here for reasoning), than call pack() on JFrame before setting it visible

  • Dont extend JFrame unnecessarily or Container!

  • Adding WindowListener for detecting JFrame exit is not worth the lines rather use:

    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    

to exit Swing application when X is pressed.

Community
  • 1
  • 1
David Kroukamp
  • 36,155
  • 13
  • 81
  • 138
  • Thanks for the reply. I'll implement the EDT. I think something that you might have missed is that I everything except the JButtons is implemented in AWT, not Swing - which is why I had to use the WindowListener. I changed the code a bit to fix the problem, the Buttons are now added in the constructor to the Frame instead of the container, and that seemed to fix things. Would you recommend doing it entirely in Swing? From what I could read up, Swing isn't really suited to making 2D animation/games, and only GUI type things. I originally started in Swing, but I changed to AWT for some reason. – JasonG Dec 06 '12 at 04:51
  • Also if I am using AWT, do I need to do a similar event thread type thing? – JasonG Dec 06 '12 at 04:54