Click here to Skip to main content
15,884,592 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
See more:
Good day all. I hope you can help me.

I'm busy making a small top view 2D game on a JFrame. The player can walk around and needs to be able to right click on objects.

I have everything working up till this point. My problem is that when you right-click, the menu shows up but when the screen refreshes(backBuffer re-draws the screen) it hides the menu. When you move the mouse over where the menu must be it shows up and hides again when the screen refreshes(FPS is set to 50).

I'm not sure if it hides the menu behind the graphics.

Is there a way to constantly keep the menu top-most?

EDIT:
Here is my code. I hope this helps.

Java
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Insets;
import java.awt.event.KeyEvent;
import java.awt.image.BufferedImage;

import javax.swing.JFrame;

public class Driver extends JFrame {
	boolean isRunning = true;
	int fps = 50;
	int windowWidth = 400;
	int windowHeight = 400;

	BufferedImage backBuffer;
	Insets insets;
	InputHandler input;

	int x = 0;
	int y = 0;

	static Driver myGame;

	public static void main(String[] args) {
		myGame = new Driver();
		myGame.run();
	}

	public void run() {
		initialize();

		while (isRunning) {
			long time = System.currentTimeMillis();

			update();
			draw();

			time = (1000 / fps) - (System.currentTimeMillis() - time);

			if (time > 0) {
				try {
					Thread.sleep(time);
				} catch (Exception e) {
				}
			}
		}

		setVisible(false);

	}

	void initialize() {
		setTitle("Game Tutorial");
		setSize(windowWidth, windowHeight);
		setResizable(false);
		setDefaultCloseOperation(EXIT_ON_CLOSE);
		setVisible(true);

		insets = getInsets();
		setSize(insets.left + windowWidth + insets.right, insets.top
				+ windowHeight + insets.bottom);

		backBuffer = new BufferedImage(windowWidth, windowHeight,
				BufferedImage.TYPE_INT_RGB);
		input = new InputHandler(this);

		myGame.addMouseListener(new PopClickListener());
	}

	void update() {
		if (input.isKeyDown(KeyEvent.VK_RIGHT)) {
			x += 20;
			try {
				Thread.sleep(100);
			} catch (Exception e) {
			}
		}
		if (input.isKeyDown(KeyEvent.VK_LEFT)) {
			x -= 20;
		}
		if (input.isKeyDown(KeyEvent.VK_DOWN)) {
			y += 20;
		}
		if (input.isKeyDown(KeyEvent.VK_UP)) {
			y -= 20;
		}
	}

	void draw() {

		Graphics g = getGraphics();

		Graphics bbg = backBuffer.getGraphics();

		bbg.setColor(Color.WHITE);
		bbg.fillRect(0, 0, windowWidth, windowHeight);

		bbg.setColor(Color.BLACK);
		bbg.drawOval(x, y, 20, 20);

		g.drawImage(backBuffer, insets.left, insets.top, this);

	}
}


import java.awt.Component;
import java.awt.event.*;

public class InputHandler implements KeyListener {
	boolean keys[] = new boolean[256];

	public InputHandler(Component c) {
		c.addKeyListener(this);
	}

	public boolean isKeyDown(int keyCode) {
		if (keyCode > 0 && keyCode < 256) {
			return keys[keyCode];
		}

		return false;
	}

	public void keyPressed(KeyEvent e) {
		if (e.getKeyCode() > 0 && e.getKeyCode() < 256) {
			keys[e.getKeyCode()] = true;
		}
	}

	public void keyReleased(KeyEvent e) {
		if (e.getKeyCode() > 0 && e.getKeyCode() < 256) {
			keys[e.getKeyCode()] = false;
		}
	}

	public void keyTyped(KeyEvent e) {
	}
}

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

class MenuActionListener implements ActionListener {
	public void actionPerformed(ActionEvent e) {
		System.out.println(e.getActionCommand());
	}
}


import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;

class PopClickListener extends MouseAdapter {
	public void mousePressed(MouseEvent e) {
		if (e.isPopupTrigger()) {
			doPop(e);
		}
	}

	public void mouseReleased(MouseEvent e) {
		if (e.isPopupTrigger()) {
			doPop(e);
		}
	}

	private void doPop(MouseEvent e) {
		PopUpDemo menu = new PopUpDemo();
		menu.show(e.getComponent(), e.getX(), e.getY());
	}
}


import javax.swing.JMenuItem;
import javax.swing.JPopupMenu;

class PopUpDemo extends JPopupMenu {
	JMenuItem anItem;

	public PopUpDemo() {
		anItem = new JMenuItem("Click Me!");
		add(anItem);
		anItem.addActionListener(new MenuActionListener());
	}
}


Thanks in advance :)
Posted
Updated 24-Nov-13 1:35am
v2
Comments
Glek 23-Nov-13 21:03pm    
What's going on depends on how you're drawing to the screen and how you're displaying the right click menu. Can you clarify how you're drawing to the screen and how you're making the menu? Are you using the repaint() method? Are you using native Swing components to draw?
wikus70 24-Nov-13 7:36am    
Thanks for getting back to me. I updated the question to include my code. I hope this helps?
Glek 24-Nov-13 11:50am    
It certainly did! I've played with the code some and provided one solution. There's more then one way to skin a cat, though!
wikus70 24-Nov-13 15:01pm    
Thank you very much. The code works 100%
I've been playing around with it all day and just couldn't get the flicker to stop. I managed to reduce it though.
But I prefer your code. Thanks again :)
Glek 24-Nov-13 17:23pm    
Not a problem!

1 solution

The issue appears to be with how you're drawing to the window. Your draw function doesn't account for the fact that you need to draw the pop up menu too. The way the Swing system works is that the frame has its repaint method called by the operating system. This message trickles down to each component, painting it along the way using the frame's Graphics object. You're bypassing that system and painting to the screen manually.

The solution is fairly simple. You need to keep a reference to the PopUpDemo class you create and use that to paint it.

For the PopClickListener:

Java
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
 
class PopClickListener extends MouseAdapter {
        PopUpDemo menu;
    
	public void mousePressed(MouseEvent e) {
		if (e.isPopupTrigger()) {
			doPop(e);
		}
	}
 
	public void mouseReleased(MouseEvent e) {
		if (e.isPopupTrigger()) {
			doPop(e);
		}
	}
 
	private void doPop(MouseEvent e) {
		menu = new PopUpDemo();
                menu.setIgnoreRepaint(true);
		menu.show(e.getComponent(), e.getX(), e.getY());
	}
}


Note the addition of the menu variable.

For the Driver class:

Java
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Insets;
import java.awt.event.KeyEvent;
import java.awt.image.BufferedImage;
 
import javax.swing.JFrame;
 
public class Driver extends JFrame {
    
	boolean isRunning = true;
	int fps = 50;
	int windowWidth = 400;
	int windowHeight = 400;
 
	BufferedImage backBuffer;
	Insets insets;
	InputHandler input;
        
        PopClickListener popupClick;
 
        ...
 
	void draw() {
 
		Graphics g = getGraphics();
 
		Graphics bbg = backBuffer.getGraphics();
 
		bbg.setColor(Color.WHITE);
		bbg.fillRect(0, 0, windowWidth, windowHeight);
 
		bbg.setColor(Color.BLACK);
		bbg.drawOval(x, y, 20, 20);
                
                if(popupClick.menu != null && popupClick.menu.isVisible()){
                    if(popupClick.menu.getParent() != null){
                        bbg.translate(popupClick.menu.getParent().getX()
                                , popupClick.menu.getParent().getY());
                        popupClick.menu.paint(bbg);
                    }
                }
                
		g.drawImage(backBuffer, insets.left, insets.top, this);
	}
}


Two things to note here. First is the addition of the popupClick variable, which keeps track of the PopClickListener you're using. This way we can access the menu variable I mentioned earlier. Second is the change to the draw() method. You'll note that I added two lines, one to check if the menu exists and the other to call the paint method of the popup menu, passing it the Graphics object you're using for your buffering. The other important thing to note is that I had to call getParent().getX() and getParent().getY() to get the menu to draw in the right place. This is because the menu normally draws itself at 0,0 (in its parent's component space). That parent component is normally a JPanel located inside the JFrame's LayeredPane.

An alternative is to call repaint() on the menu component instead of translating the origin and then calling paint, but that tends to generate flickering because repaint() doesn't paint immediately, but instead paints when the JVM has a chance to get around to it.
 
Share this answer
 

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900