Any ideas why my oval is not moving to the right? I have a game loop but somehow the circle stays where it appears, instead of moving by 2pixels to the right every cycle.
The strange thing is that it enters the loop before actually painting. But then the graphics object show up(at a fixed position).
public class GamePanel extends JPanel implements Runnable {
public static int WIDTH = 1024;
public static int HEIGHT = WIDTH / 16 * 9;
private Thread t1;
boolean running;
private int FPS = 60;
private long optimalTime = 1000 / FPS;
private int heroX = 200;
private int heroY = 200;
public void addNotify(){
Dimension size = new Dimension(WIDTH,HEIGHT);
setPreferredSize(size);
setFocusable(true);
requestFocus();
running = true;
t1 = new Thread(this);
t1.start();
}
public void paintComponent (Graphics g){
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.setColor(Color.WHITE);
g2.fillRect(0, 0, WIDTH, HEIGHT);
g2.setColor(Color.BLACK);
g2.fillOval(heroX, heroY, 50, 50);
g2.dispose();
}
public void run() {
long startTime;
long passedTime;
long waitTime;
while (running){
startTime = System.nanoTime();
System.out.println("Runs");
update();
draw();
repaint();
passedTime = System.nanoTime() - startTime;
waitTime = optimalTime - passedTime / 1000000;
try {
if (waitTime <= 0){
waitTime = 2;
}
Thread.sleep(waitTime);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
private void draw() {
}
private void update() {
heroX += 2;
}
}
You should use a swing Timer instead of trying to use threads. If you need to do something time consuming in the background, use SwingWorkers. It is possible to use your own threading with swing should you really need to (you need a twisted user case to need to do that), but do not try to do that until you have a good grasp of threads and the way to use them with swing.
public class GamePanel extends JPanel {
private static final int DELAY = 1000 / 60;
private final Timer timer;
// ...
private int heroX = 200;
private int heroY = 200;
public GamePanel() {
timer = new Timer(DELAY, new ActionListener() {
#Override
public void actionPerformed(final ActionEvent e) {
update();
repaint();
}
});
// ...
}
// No need to make this public
#Override
protected void paintComponent (Graphics g) {
// ...
}
#Override
public Dimension getPreferredSize() {
// Overriding is cleaner than using set*Size(). Search old questions to see why
}
public void startAnimation() {
timer.start();
}
public void stopAnimation() {
timer.stop();
}
// Remove addNotify() and run()
}
After reading and testing, I just wanted to do this. It works as intended, even if paintComponent is outside the EDT.
public class GamePanel extends JPanel implements Runnable {
public static int WIDTH = 1024;
public static int HEIGHT = WIDTH / 16 * 9;
private int cordX = WIDTH / 2;
private Thread t1;
private boolean running = true;
public void addNotify() {
super.addNotify();
Dimension size = new Dimension (WIDTH, HEIGHT);
setPreferredSize(size);
t1 = new Thread(this);
t1.start();
}
public void paintComponent(Graphics g) {
g.setColor(Color.WHITE);
g.fillRect(0, 0, WIDTH, HEIGHT);
g.setColor(Color.BLACK);
g.fillOval(cordX, HEIGHT /2 , 20, 20);
}
public void run() {
while(running) {
cordX += 2;
repaint();
try {
Thread.sleep(17);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
Related
The 20 x 20 box is not being displayed(including motion listener and no errors). Another class is the window which sets up the JFrame and game start(). Here is the code below(with a package called "javagame9" .
public class Game extends Canvas implements Runnable {
private static final long serialVersionUID = -2713820159854096116L;
public static final int WIDTH = 640, HEIGHT = 700;
private Thread thread;
private boolean running = false;
public static boolean paused = false;
public Game() {
this.addMouseMotionListener(new Mouse());
new Window(WIDTH, HEIGHT, "A Game", this);
}
public synchronized void start() {
thread = new Thread(this);
thread.start();
running = true;
}
public synchronized void stop() {
try {
thread.join();
running = false;
} catch (Exception e) {
e.printStackTrace();
}
}
public void run() {
this.requestFocus();
long LastTime = System.nanoTime();
double amountOfTicks = 60.0;
double ns = 1000000000 / amountOfTicks;
double delta = 0;
long timer = System.currentTimeMillis();
int frames = 0;
while (running) {
long now = System.nanoTime();
delta += (now - LastTime) / ns;
LastTime = now;
while (delta >= 1) {
tick();
delta--;
}
if (running)
render();
frames++;
if (System.currentTimeMillis() - timer > 1000) {
timer += 1000;
System.out.println("FPS: " + frames);
frames = 0;
}
}
stop();
}
private void tick() {
}
private void render() {
}
public static void main(String args[]) {
new Game();
}
}
public class Mouse extends Canvas implements MouseMotionListener {
private static final long serialVersionUID = 7986961236445581989L;
private Image dbImage; //Mouse - class
private Graphics dbg;
int mx, my;
boolean mouseDragged;
public void paint(Graphics g) {
dbImage = createImage(getWidth(), getHeight());
dbg = dbImage.getGraphics();
paintComponent(dbg);
g.drawImage(dbImage, 0, 0, this);
}
public void paintComponent(Graphics g) {
if (mouseDragged) {
g.setColor(Color.DARK_GRAY);
g.fillRect(0, 0, getWidth(), getHeight());
g.setColor(Color.LIGHT_GRAY);
g.fillRect(mx, my, 20, 20);
} else {
g.setColor(Color.LIGHT_GRAY);
g.fillRect(0, 0, getWidth(), getHeight());
g.setColor(Color.DARK_GRAY);
g.fillRect(mx, my, 20, 20);
}
repaint();
}
#Override
public void mouseDragged(MouseEvent e) {
mx = e.getX() - 10;
my = e.getY() - 10;
mouseDragged = true;
e.consume();
}
public void mouseMoved(MouseEvent e) {
mx = e.getX() - 10;
my = e.getY() - 10;
mouseDragged = false;
e.consume();
}
}
The problem is that you have 2 Canvas: Game and Mouse.
Assuming that the Window class is the following (found it here):
public class Window extends Canvas {
public Window(int width, int height, String title, Game game) {
JFrame frame = new JFrame(title);
frame.setPreferredSize(new Dimension(width, height));
frame.setMaximumSize(new Dimension(width, height));
frame.setMinimumSize(new Dimension(width, height));
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
frame.setResizable(false);
frame.setLocationRelativeTo(null);
frame.add(game);
frame.setVisible(true);
game.start();
}
}
we see that the Canvas being added to the JFrame is the Game one, so it will be the one visible.
But you are only painting on the Mouse canvas, therefore you are not going to see anything.
You could move the paint logic from Mouse to Game, and use Mouse only for the MouseMotionListener functionality.
Modified Game class (differences highlighted by comments):
public class Game extends Canvas implements Runnable {
private static final long serialVersionUID = -2713820159854096116L;
public static final int WIDTH = 640, HEIGHT = 700;
private Thread thread;
private boolean running = false;
public static boolean paused = false;
// fields previously in Mouse moved here:
private Image dbImage;
private Graphics dbg;
// mouse field so we can reuse it
private Mouse mouse;
public Game() {
// we create an instance of mouse and use it as MouseMotionListener
mouse = new Mouse();
this.addMouseMotionListener(mouse);
new Window(WIDTH, HEIGHT, "A Game", this);
}
public synchronized void start() {
thread = new Thread(this);
thread.start();
running = true;
}
public synchronized void stop() {
try {
thread.join();
running = false;
} catch (Exception e) {
e.printStackTrace();
}
}
#Override
public void run() {
this.requestFocus();
long LastTime = System.nanoTime();
double amountOfTicks = 60.0;
double ns = 1000000000 / amountOfTicks;
double delta = 0;
long timer = System.currentTimeMillis();
int frames = 0;
while (running) {
long now = System.nanoTime();
delta += (now - LastTime) / ns;
LastTime = now;
while (delta >= 1) {
tick();
delta--;
}
if (running)
render();
frames++;
if (System.currentTimeMillis() - timer > 1000) {
timer += 1000;
System.out.println("FPS: " + frames);
frames = 0;
}
}
stop();
}
private void tick() {
}
private void render() {
}
public static void main(String args[]) {
new Game();
}
// paint methods previously in Mouse moved here:
#Override
public void paint(Graphics g) {
dbImage = createImage(getWidth(), getHeight());
dbg = dbImage.getGraphics();
paintComponent(dbg);
g.drawImage(dbImage, 0, 0, this);
}
public void paintComponent(Graphics g) {
if (mouse.mouseDragged) {
g.setColor(Color.DARK_GRAY);
g.fillRect(0, 0, getWidth(), getHeight());
g.setColor(Color.LIGHT_GRAY);
g.fillRect(mouse.mx, mouse.my, 20, 20);
}
else {
g.setColor(Color.LIGHT_GRAY);
g.fillRect(0, 0, getWidth(), getHeight());
g.setColor(Color.DARK_GRAY);
g.fillRect(mouse.mx, mouse.my, 20, 20);
}
repaint();
}
}
Now from the Mouse class you can remove all the methods/fields used for the painting, also it no longer extends Canvas:
public class Mouse implements MouseMotionListener {
private static final long serialVersionUID = 7986961236445581989L;
int mx, my;
boolean mouseDragged;
#Override
public void mouseDragged(MouseEvent e) {
mx = e.getX() - 10;
my = e.getY() - 10;
mouseDragged = true;
e.consume();
}
#Override
public void mouseMoved(MouseEvent e) {
mx = e.getX() - 10;
my = e.getY() - 10;
mouseDragged = false;
e.consume();
}
}
Using these classes you should now be able to see what you are painting.
In more detail, I have a componentResized event attached to the JFrame (which contains the canvas, and nothing else), and in that event a call a method which sets the bounds of the canvas accordingly. This works fine, except that while I'm resizing the canvas, it doesn't show anything. I just see the back of the JFrame. Once I've stopped resizing the JFrame, the canvas paints fine again.
public class MyCanvas implements ComponentListener {
public static void main(String[] args) {
new MyCanvas("MyCanvas",new Dimension(300,300));
}
private static final int frameRate = 30;
private JFrame frame;
private JPanel panel;
private Canvas canvas;
private BufferStrategy strategy;
private int delta;
private boolean running = false;
private int frameCount = 0;
public MyCanvas(String name, Dimension size) {
frame = new JFrame(name);
panel = (JPanel) frame.getContentPane();
canvas = new Canvas();
frame.setSize(size);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
panel.setPreferredSize(size);
panel.setLayout(null);
canvas.setBounds(0,0,size.width,size.height);
panel.add(canvas);
canvas.setIgnoreRepaint(true);
frame.setResizable(true);
frame.pack();
frame.addComponentListener(this);
canvas.createBufferStrategy(2);
strategy = canvas.getBufferStrategy();
running = true;
frame.setVisible(true);
long lastLoopTime = 0;
while (running) {
frameCount++;
delta = (int) (System.currentTimeMillis() - lastLoopTime);
lastLoopTime = System.currentTimeMillis();
Graphics2D graphics = (Graphics2D) strategy.getDrawGraphics();
graphics.setColor(Color.black);
graphics.fillRect(0,0,getSize().width,getSize().height);
graphics.dispose();
strategy.show();
try {
Thread.sleep(1000/frameRate);
} catch (InterruptedException e) {}
}
}
public final Dimension getSize() {
return frame.getSize();
}
public final void setSize(Dimension size) {
frame.setSize(size);
canvas.setBounds(0,0,size.width,size.height);
}
public synchronized void componentResized(ComponentEvent e) {
setSize(frame.getSize());
}
public synchronized void componentHidden(ComponentEvent e) {
// unused
}
public synchronized void componentShown(ComponentEvent e) {
// unused
}
public synchronized void componentMoved(ComponentEvent e) {
// unused
}
}
Edit
After tweaking with the code for a while, I have come up with a solution:
public class MyCanvas {
public static void main(String[] args) {
new MyCanvas("MyCanvas",new Dimension(400,400));
}
private static final int frameRate = 1000 / 30;
private JFrame frame;
private JPanel panel;
private int delta;
private long lastLoopTime;
private boolean running = false;
private int frameCount = 0;
private BufferedImage backBuffer = null;
private int lastPaintFrame = -1;
public MyCanvas(String name, Dimension size) {
frame = new JFrame(name);
panel = new JPanel() {
protected void paintComponent(Graphics g) {
super.paintComponent(g);
redraw(g);
}
};
frame.setContentPane(panel);
frame.setSize(size);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
panel.setPreferredSize(size);
panel.setLayout(null);
frame.setResizable(true);
running = true;
frame.setVisible(true);
Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
backBuffer = new BufferedImage(screenSize.width,screenSize.height,BufferedImage.TYPE_INT_ARGB);
lastLoopTime = System.nanoTime();
while (running) {
long thisLoopTime = System.nanoTime();
delta = (int) ((thisLoopTime - lastLoopTime) / 1000000);
draw(backBuffer.getGraphics());
frameCount++;
lastLoopTime = thisLoopTime;
redraw(panel.getGraphics());
try {
Thread.sleep(1000/30);
} catch (InterruptedException e) {}
}
}
private final void redraw(Graphics g) {
if (g != null && backBuffer != null) {
g.drawImage(backBuffer,0,0,null);
}
}
int x = 30;
public final void draw(Graphics g) {
g.setColor(Color.darkGray);
g.fillRect(0,0,getSize().width,getSize().height);
g.setColor(Color.gray);
g.fillRect(0,0,500,500);
g.setColor(Color.blue);
g.fillRect(x,30,300,300);
x++;
}
public final Dimension getSize() {
return frame.getSize();
}
public final void setSize(Dimension size) {
frame.setSize(size);
}
}
However, this is not quite solved yet, because it still has odd little graphical glitches which only seem to appear when redraw is called from panel's paintComponent method, though not consistently. Those glitches manifest themselves as odd rectangles of color (usually black or grey) which promptly disappear again. I'm really not sure of what it could be... maybe problems with the double buffering? BTW, if I threw a runtime exception in paintComponent, it worked perfectly.
If this should be moved to a new question, please let me know.
I found the solution: different loops for draw and update:
public class MyCanvas {
public static void main(String[] args) {
new MyCanvas("MyCanvas",new Dimension(400,400));
}
private static final int frameRate = 1000 / 30;
private JFrame frame;
private JPanel panel;
private int delta;
private long lastLoopTime;
private volatile boolean running = false;
private int frameCount = 0;
public MyCanvas(String name, Dimension size) {
frame = new JFrame(name);
panel = new JPanel() {
protected void paintComponent(Graphics g) {
super.paintComponent(g);
draw(g);
if (running) repaint();
}
};
frame.setContentPane(panel);
frame.setSize(size);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
panel.setPreferredSize(size);
panel.setLayout(null);
frame.setResizable(true);
running = true;
frame.setVisible(true);
lastLoopTime = System.nanoTime();
new Thread(()->{
while (running) {
update();
frameCount++;
try {
Thread.sleep(frameRate);
} catch (InterruptedException e) {}
}
},"Game Loop").start();
}
int x = 30;
public final void update() {
x++;
}
public final void draw(Graphics g) {
g.setColor(Color.darkGray);
g.fillRect(0,0,getSize().width,getSize().height);
g.setColor(Color.gray);
g.fillRect(0,0,500,500);
g.setColor(Color.blue);
g.fillRect(x,30,300,300);
}
public final Dimension getSize() {
return frame.getSize();
}
public final void setSize(Dimension size) {
frame.setSize(size);
}
}
I am building the test application to improve later. I have a Java Graphic Element drawn on a canvas using a Game Loop (update,render). It is a red ball that changes its color when the mouse is placed on top of it.
I am trying to figure out a method to create a JPanel when the mouse is on top of the ball,to show some sort of "Hidden Information" inside the ball. My original idea is to display a histogram made with JFreeChart API as the "Hiden information, so I believe that if I create this JPanel I can later add the histogram to the JPanel created. Similar to this http://www.bitjuice.com.au/research/#hierarchicalclassificationexample. In the link, whenever you put the mouse on top of the rectangles, you display extra information.
So far I've got this code:
*Window.java * (The JFrame)
public class Window extends JFrame {
JLabel title_label = new JLabel();
public Window(int width, int height, String title, Animation animation){
setTitle(title);
setPreferredSize(new Dimension(width,height));
setMaximumSize(new Dimension(width,height));
setMinimumSize(new Dimension(width,height));
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setResizable(false);
setLocationRelativeTo(null);
add(animation);
add(title_label, BorderLayout.SOUTH);
setVisible(true);
animation.start();
}
public void update(){
title_label.setText(Animation.mouseX + " " + Animation.mouseY);
}
}
Animation.java(The game Loop)
public class Animation extends Canvas implements Runnable {
public static final int WIDTH = 1024, HEIGHT = WIDTH/12*9 ;
private Thread thread;
private boolean running = false;
public static int mouseX,mouseY;
public Window window;
Button button = new Button();
public Animation(){
window = new Window(WIDTH, HEIGHT,"Test", this);
addMouseMotionListener(new Handler(window));
addMouseListener(new Handler(window));
}
public void run() {
this.requestFocus();
long lastTime = System.nanoTime();
double amountOfTicks = 60.0;
double ns = 1000000000/amountOfTicks;
double delta = 0;
long timer = System.currentTimeMillis();
int frames = 0;
while(running){
long now = System.nanoTime();
delta += (now-lastTime) / ns;
lastTime = now;
while(delta >= 1){
update();
delta--;
}
if(running)
render();
frames++;
if(System.currentTimeMillis() - timer >1000){
//System.out.println(frames);
timer += 1000;
frames = 0;
}
}
stop();
}
public synchronized void start(){
thread = new Thread(this);
thread.start();
running = true;
}
public synchronized void stop(){
try{
thread.join();
running = false;
}catch(Exception e){
e.printStackTrace();
}
}
public static int getMouseX(){
return mouseX;
}
public static int getMouseY(){
return mouseY;
}
public static void setMouseX(int x){
mouseX = x;
}
public static void setMouseY(int y){
mouseY = y;
}
private void update(){
window.update();
button.update();
}
private void render(){
BufferStrategy bs = this.getBufferStrategy();
if(bs == null){
this.createBufferStrategy(4);
return;
}
RenderingHints rh = new RenderingHints(
RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
rh.put(RenderingHints.KEY_RENDERING,
RenderingHints.VALUE_RENDER_QUALITY);
Graphics g = bs.getDrawGraphics();
Graphics2D g2d = (Graphics2D)g;
g2d.setRenderingHints(rh);
g2d.setColor(Color.white);
g2d.fillRect(0, 0, WIDTH, HEIGHT);
button.render(g);
g.dispose();
g2d.dispose();
bs.show();
}
public static void main(String args[]){
new Animation();
}
}
Handler.java
public class Handler extends MouseAdapter {
int x,y;
private Window window;
public Handler(Window window){
this.window = window;
}
public void mouseMoved(MouseEvent e){
Animation.setMouseX(e.getX());
Animation.setMouseY(e.getY());
}
}
Button.java
public class Button {
Ellipse2D mask;
boolean mouseIsOn = false;
public Button(){
mask = new Ellipse2D.Double(500,350,50,50);
}
public void update(){
if(mask.contains(Animation.mouseX,Animation.mouseY)){
mouseIsOn = true;
}else
mouseIsOn = false;
}
public void render(Graphics g){
if(mouseIsOn)
g.setColor(Color.green);
else
g.setColor(Color.red);
g.fillOval(500,350, 50, 50);
}
}
I appreciate the help.
Heavy weight and light weight components don't mix. JPanel is a lightweight component and Canvas is heavyweight. Heavyweight components always get drawn on top of lightweight ones.
What you may want to do instead is just draw the mouseover portion directly to your canvas. You can use FontMetrics for drawing Strings if that is what you need.
How can I detect if the text ("Resume", "Restart", "Quit") that I drew with a drawString() method is being clicked?
My code so far:
public class Pause {
public Pause() {
}
public void draw(Graphics2D g) {
g.setFont(new Font("Arial", Font.BOLD, 14));
int intValue = Integer.parseInt( "ff5030",16);
g.setColor(new Color(intValue));
g.drawString("Resume", 200, 156);
g.drawString("Restart", 200, 172);
g.drawString("Quit", 200, 188);
}
}
I hope you can help me. Thanks
#aioobe I tried to write it simple as possible. Here the SSCCE:
GameFrame.java
public class GameFrame extends JFrame implements Runnable {
/**
*
*/
private static final long serialVersionUID = 1L;
// dimensions
public static final int WIDTH = 448;
public static final int HEIGHT = 288;
public static final double SCALE = 2.5;
// game thread
private Thread thread;
private boolean running;
private int FPS = 60;
private long targetTime = 1000 / FPS;
// image
private BufferedImage image;
private Graphics2D g;
//displays
private Pause pauseDisplay;
public GameFrame() {
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setResizable(false);
this.pack();
this.setLocationRelativeTo(null);
this.setPreferredSize(new Dimension((int)(WIDTH * SCALE), (int)(HEIGHT * SCALE)));
this.setBounds(100, 100, (int)(WIDTH * SCALE), (int)(HEIGHT * SCALE));
this.setLocationRelativeTo(null);
this.setFocusable(true);
this.requestFocus();
this.setVisible(true);
}
public void addNotify() {
super.addNotify();
if(thread == null) {
thread = new Thread(this);
thread.start();
}
}
private void init() {
image = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_RGB);
g = (Graphics2D) image.getGraphics();
running = true;
}
public void run() {
init();
long start;
long elapsed;
long wait;
// game loop
while(running) {
start = System.nanoTime();
elapsed = System.nanoTime() - start;
wait = targetTime - elapsed / 1000000;
if(wait < 0) wait = 5;
try {
Thread.sleep(wait);
}
catch(Exception e) {
e.printStackTrace();
}
pauseDisplay = new Pause(this);
drawToScreen();
draw();
}
}
private void draw() {
pauseDisplay.draw(g);
}
private void drawToScreen() {
Graphics g2 = getGraphics();
g2.drawImage(image, 0, 0, (int)(WIDTH * SCALE), (int)(HEIGHT * SCALE), null);
g2.dispose();
}
public static void main(String[] args) {
GameFrame game = new GameFrame();
}
}
Pause.java
public class Pause implements MouseListener{
private Rectangle2D resumeRect;
private Rectangle2D restartRect;
public Pause(GameFrame GameFrame) {
GameFrame.addMouseListener(this);
}
public void draw(Graphics2D g) {
g.setFont(new Font("Arial", Font.BOLD, 14));
int intValue = Integer.parseInt( "ff5030",16);
g.setColor(new Color(intValue));
g.drawString("Resume", 200, 156);
resumeRect= g.getFontMetrics().getStringBounds("Resume", g);
g.drawString("Restart", 200, 172);
restartRect = g.getFontMetrics().getStringBounds("Restart", g);
g.drawString("Quit", 200, 188);
}
public void mouseClicked(MouseEvent e) {
if (resumeRect.contains(e.getPoint())) {
System.out.println("clicked");
}
System.out.println(resumeRect);
System.out.println(restartRect);
System.out.println(e.getPoint());
}
public void mousePressed(MouseEvent e) {}
public void mouseReleased(MouseEvent e) {}
public void mouseEntered(MouseEvent e) {}
public void mouseExited(MouseEvent e) {}
}
You would have to remember the position at which you drew the string. You could have one Rectangle2D for each string.
The rectangle of the String can be computed as follows:
Rectangle2D r = g.getFontMetrics().getStringBounds(str, g);
(You need to adjust the rect position according to where you draw the string.)
You would then register a mouse listener that checks click coordinates against these rectangles:
if (resumeRect.contains(mouseEvent.getPoint())) {
...
}
That being said, I'd recommend you to reconsider your GUI code and see if you can't use JLabel or JButton for this purpose.
Regarding your edit:
Your NullPointerException is due to the fact that you can click on the frame before the image is rendered (i.e. before the rectangles have been initialized).
Apart from that you need to do two edits:
You need to take the SCALE into account.
if (resumeRect.contains(e.getPoint().getX() / GameFrame.SCALE,
e.getPoint().getY() / GameFrame.SCALE)) {
and
you need to compensate for the fact that drawString draws the string on the base line, so the rectangle should be lifted up from the base line to the top left corner of the text:
g.drawString("Resume", 200, 156);
resumeRect= g.getFontMetrics().getStringBounds("Resume", g);
// Add this:
resumeRect.setRect(200,
156 - g.getFontMetrics().getAscent(),
resumeRect.getWidth(),
resumeRect.getHeight());
Don't use drawString() for that, put the text in a JLabel instead. Then you can attach the listener to the label which is much easier.
A JLabel also has the advantages that you can use html for formatting and that it allows the layoutmanager to position the text.
Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 8 years ago.
Improve this question
I'm trying to learn Java and game programming by myself by just playing around with some different things. But now I have come across this problem, when my java app goes fullscreen via GraphicsDevice, the KeyListeners don't work. It's like it doesn't register anything when I press the buttons on my keyboard. When the app isn't fullscreen, everything works as it is supposed to.
I am using Mac.
The code is a bit messy, but it should be somewhat easy to navigate etc.
Game.class
public class Game extends Canvas implements Runnable {
private static final long serialVersionUID = 1L;
// Render Vars
public static final int WIDTH = 1280;
public static final int HEIGHT = 800; //WIDTH / 16 *
public static final int SCALE = 1;
public final static String TITLE = "Test Game - inDev 1.0.0";
public static boolean fullscreen = false;
public static JFrame window = new JFrame(TITLE);
public static Font defaultFont = new Font("Dialog", Font.PLAIN, 12);
public static Color defaultColor = Color.gray;
// Thread Vars
public static boolean running = false;
private static Thread thread;
private BufferedImage image = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_RGB);
// Mechanic Vars
public static boolean mouseDown;
public static int mouseX;
public static int mouseY;
// Game Vars
public static GameState gameState = new Play();
public static boolean keyPressed;
public static Game game = new Game();
public static void main(String arghs[]) {
game.setSize(new Dimension(WIDTH, HEIGHT));
game.setPreferredSize(new Dimension(WIDTH, HEIGHT));
fullscreen = true;
window.add(game);
window.setUndecorated(true);
window.pack();
window.setLocationRelativeTo(null);
window.setResizable(false);
window.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
window.setVisible(true);
// HERE I GO FULLSCREEN
GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().setFullScreenWindow(window);
game.addKeyListener(new KeyEventListener());
game.addMouseListener(new MouseEventListener());
game.start();
}
public void run() {
init();
long lastTime = System.nanoTime();
final double numTicks = 100.0;
double ns = 1000000000 / numTicks;
double delta = 0;
int updates = 0;
int frames = 0;
long timer = System.currentTimeMillis();
while (running) {
long now = System.nanoTime();
delta += (now - lastTime) / ns;
lastTime = now;
if (delta >= 1) {
update();
updates++;
delta--;
}
render();
frames++;
if (System.currentTimeMillis() - timer > 1000) {
timer += 1000;
System.out.println(updates + " Ticks, FPS: " + frames);
updates = 0;
frames = 0;
}
}
stop();
}
private synchronized void start() {
if (running) {
return;
}
running = true;
thread = new Thread(this);
thread.setName("My Game");
thread.start();
}
public synchronized static void stop() {
if (!running) {
return;
}
running = false;
thread = null;
System.exit(1);
}
private void init() {
gameState.init();
}
private void update() {
gameState.update();
}
private void render() {
BufferStrategy bs = this.getBufferStrategy();
if (bs == null) {
createBufferStrategy(3);
return;
}
Graphics g = bs.getDrawGraphics();
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
g.setColor(defaultColor);
g.drawImage(image, 0, 0, getWidth(), getHeight(), this);
// Draw Content
gameState.render(g);
// Draw to Screen;
g.dispose();
bs.show();
}
public static void setGameState(GameState state) {
gameState = state;
gameState.init();
}
}
KeyEventListener.class
public class KeyEventListener extends KeyAdapter {
public void keyPressed(KeyEvent e) {
if (!Game.keyPressed) {
Game.gameState.keyPressed(e);
}
Game.keyPressed = true;
}
public void keyReleased(KeyEvent e) {
Game.gameState.keyReleased(e);
Game.keyPressed = false;
}
}
MouseEventListener.class (Just for recording the mouse position)
public class MouseEventListener extends MouseAdapter {
public void mousePressed(MouseEvent e) {
Game.mouseDown = true;
}
public void mouseReleased(MouseEvent e) {
Game.mouseDown = false;
}
public void mouseMoved(MouseEvent e) {
Game.mouseX = e.getX();
Game.mouseY = e.getY();
}
public void mouseDragged(MouseEvent e) {
Game.mouseX = e.getX();
Game.mouseY = e.getY();
}
}
GameState.class
public abstract class GameState {
public abstract void init();
public abstract void update();
public abstract void render(Graphics g);
public void keyPressed(KeyEvent e) {
}
public void keyReleased(KeyEvent e) {
}
}
And my Play.class which is the current gameState
public class Play extends GameState {
public static int key;
public void init() {
}
public void update() {
}
public void render(Graphics g) {
g.drawString("Key: " + key, 100, 100);
}
public void keyPressed(KeyEvent e) {
key = e.getKeyCode();
}
public void keyReleased(KeyEvent e) {
}
}
KeyListener will only respond to key events when the component is registered to is focusable and has focus.
After you have set the window to full screen, try adding...
game.requestFocusInWindow();
You may also need to use game.setFocusable(true)
If it wasn't for the fact that you're using a Canvas, I'd suggest using the key bindings API to over all these focus issues
Updated
I added...
window.addWindowFocusListener(new WindowAdapter() {
#Override
public void windowGainedFocus(WindowEvent e) {
System.out.println("gainedFocus");
if (!game.requestFocusInWindow()) {
System.out.println("Could not request focus");
}
}
});
To the window after it was visible but before it was made full screen
Updated
Oh, you're going to love this...
Based on this question: FullScreen Swing Components Fail to Receive Keyboard Input on Java 7 on Mac OS X Mountain Lion
I added setVisible(false) followed by setVisible(true) after setting the window to full screen mode and it seems to have fixed it...
GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().setFullScreenWindow(window);
window.setVisible(false);
window.setVisible(true);
Verified running on Mac, using Java 7