Components and active rendering

I’m not sure how easy of a question this is, but I’m definitely a newless clubie, so I figured I’d try my luck here first.

I’m just getting started with active rendering, and basically from what I’ve read, my understanding is that it goes something like this:

while ( running ) {
Graphics2D g = (Graphics2D) bufferStrategy.getDrawGraphics();
drawStuff(g);
}

Then the drawStuff() method will have the things like g.drawRect() and whatnot. I’ve got the double buffering/page flipping stuff working as far as I can tell…

My question revolves around adding Swing components to my HUD/GUI. The GUI consists of several images, placed manually by me at certain coordinates on the screen. Each image is filled in a Rectangle… So something like this in my drawStuff() method:

	image = (BufferedImage) images.get("right.png");
	imagePaint = new TexturePaint(image, rightRect);
	g.setPaint(imagePaint);
	g.fill(rightRect);

Now, with all of my hud elements drawn to the screen with no problem, I’ve left open an area in the middle where the actual game will take place. What I want to do is display an image in that middle panel, and on top of that image have a JTextField (or something similar, if it exists) for the user to enter his/her name, along with a submit button.

I’m having a few problems with this:

1.) I’m not sure how to go about adding a JComponent (text field, in this case) to the pane without having it get covered by the image that I’m drawing.
2.) It seems inefficient to call this.add(text field obj) on every call that I make to drawStuff()
3.) Creating a separate JPanel and placing it in the middle, then adding a JTextField to it, causes the JPanel to flicker a lot.

I hope I’ve made it clear what I want to accomplish here… It’s probably a fairly elementary question, but I haven’t been able to find a whole lot regarding it. I can supply some code if necessary, but it’s pretty basic atm. Thanks

  1. When you use active rendering the subcomponents must (AFAI can see) similarly use a kind of active rendering, since they must be drawn every time your container redraws (otherwise the container will just draw on top of them, rendering them invisible). The ‘normal’ way, with passive rendering, would not require any specific action, because when the panel is repainted the painting system will automatically draw sub components on top of the container. So if you can keep the subcomponents clear of the one using active rendering everything is easy, otherwise you have to actively render the text components too.

That’s what I assumed… thanks for clarifying…

on that note, I’d like to expand my question to include “How do you actively render a JComponent?” The best I could figure was to keep adding it to my frame, but that seemed very inefficient and there has to be an easier or better way…

I’ll do some research, but if anyone has an easy answer, I wouldn’t mind hearing it here :slight_smile: Thanks

I just stumbled across your thread here: http://www.java-gaming.org/forums/index.php?&topic=12737.msg102261

I’ll give it a skim

I recently got Swing components working in my direct-rendering game, but beware because the threading issues are very difficult and Swing components are a bit of a black box.

see http://www.java-gaming.org/forums/index.php?topic=13211.0

Keith

I took a glance at that thread earlier and tried (but failed) to make any sense out of it :slight_smile:

Would it be easier to create custom input components? All I really want are simple things like text input fields and a text output field. a few buttons… nothing really that fancy. It seems that with all of the quirks of Swing components, it still might be easier to implement them than to try to make my own, given my limited knowledge.

Just skip to Luke’s post and use the code example he gives, which is a modified version of the test case I put forward.

Simple GUI components are harder to do than you think, so I’d probably try to use Swing.

Also, using Swing you can take advantage of the Look and Feel settings to make a colour scheme that suits your game!

Keith

I gave luke’s example a shot and still ran into some problems (in some cases worse problems) :slight_smile:

I’m just going to post my code and maybe someone can tell me what I’m doing wrong… I’ve since reverted back to my original code, which at least seems to display stuff without the major flickering issues that I was having with Luke’s example. No doubt it was user error on my end…

A lot of this was adapted from a site online, so chances are it’ll look familiar to anyone who’s been to the same site, which I don’t have handy at the moment, unfortunately.


public class BBClient extends JFrame implements Runnable, ActionListener, Observer {

private static final int NUM_BUFFERS = 2;
private static final int NO_DELAYS_PER_YIELD = 16;
private static int MAX_FRAME_SKIPS = 5;
private int period;
private GraphicsDevice gd;
private Graphics2D gScr;
private BufferStrategy bufferStrategy;
private HashMap images;
private Thread animator;
private boolean running;

// Button Rectangles/Areas
private Rectangle leftRect;
private Rectangle topRect;
private Rectangle rightRect;
private Rectangle bottomRect;
private Rectangle nukeRect;
private Rectangle cannonRect;
private Rectangle connectRect;
private Rectangle chatButtonRect;
private Rectangle chatBarRect;
private Rectangle switchRect;
private Rectangle contentRect;

// Hover vars -- Right now I only have the "connect" button implemented...
private boolean isOverConnectRect;

// Stage vars

private int stage;
private JPanel gridPane;

// at different “stages,” the user can interact with different things.  Not important right now

private JLabel bgImage;
private JTextField loginField;
private static final long serialVersionUID = 1L;

public BBClient(int period) {
	super("MyGame");
	this.period = period;
	setIgnoreRepaint(true);

	stage = 0;  // no importance right now
	loadImages();
	createRects();
	initFullScreen();

	// I colored the background green to see what space the JPanel actually occupies.  
	// From the looks of it, the JPanel is in the right space in the GUI, and it's the right size.  
	// However the components within it are all screwed up and out of place.

	gridPane = new JPanel();
	gridPane.setBackground(Color.green);

	// Load the "login" image that will serve as a sort of splash/intro screen while the player logs in
	try {
		String url = System.getProperty("user.dir")+ "\\view\\images\\login.png";
		bgImage = new JLabel(new ImageIcon(new URL("file:" + url)));
	} catch (Exception e) {
		System.out.println("wra!");
	}

	// I've tried all sorts of ways to reposition the bgImage JLabel, to no avail.  
	// It always appears offset to the right in the JPanel no matter what I do.
	// I've also tried to add a JTextField here, also to no avail.  textfield.setSize(x, y) also seems to have no effect.

	gridPane.add(bgImage);
	gridPane.add(new JTextField());

	add(gridPane);

	gameStart();
}

// ImageLoader is a separate class
public void loadImages() {

	// … this part’s not a problem, but it sure is too lengthy to post here  :)

}

// We'll be able to interact with some of these later on, but for now we're just using them for image placement.
public void createRects() {

	leftRect = new Rectangle(0, 0, 173, 600);
	// … etc

}

public void initFullScreen() {
	GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
	gd = ge.getDefaultScreenDevice();

	setUndecorated(true);
	setIgnoreRepaint(true);
	setResizable(false);

	if (!gd.isFullScreenSupported()) {
		System.out.println("Couldn't get into full-screen...");
		System.exit(0);
	}
	gd.setFullScreenWindow(this); // switch on full-screen mode

	setDisplayMode(800, 600, 32); // set the resolution so the GUI fits

	setBufferStrategy(); // set up the drawing/rendering mechanisms
}

private void setDisplayMode(int width, int height, int bitDepth) {
	if (!gd.isDisplayChangeSupported()) {
		System.out.println("Display mode changing not supported");
		return;
	}
	DisplayMode dm = new DisplayMode(width, height, bitDepth,
	DisplayMode.REFRESH_RATE_UNKNOWN);
	try {
		gd.setDisplayMode(dm);
	} catch (IllegalArgumentException e) {
		System.out.println("Error setting Display mode (" + width + ","	+ height + "," + bitDepth + ")");
	}
	try {
		Thread.sleep(1000);
	} catch (InterruptedException ex) {
	}
}

private void setBufferStrategy() {
	try {
		EventQueue.invokeAndWait(new Runnable() {
		public void run() {
			createBufferStrategy(NUM_BUFFERS);
		}
		});
	} catch (Exception e) {
		System.out.println("Couldn't start page flipping...");
		System.exit(0);
	}

	// Allow the new BufferStrategy enough time to execute
	try {
		Thread.sleep(500); // 0.5 sec
	} catch (InterruptedException ex) {
	}

	bufferStrategy = getBufferStrategy(); // store for later
}

// Start our main game thread
private void gameStart() {
	if (animator == null || !running) {
		animator = new Thread(this);
		animator.start();
	}
}

public void run() {

	// Framerate and timer settings have no relevance to my current problem

	running = true;

	while (running) {
		screenUpdate();
		// framerate stuff goes below here…
	}
	closeGame();
}

public void screenUpdate() {
	try {
		gScr = (Graphics2D) bufferStrategy.getDrawGraphics();
		gameRender();
		gScr.dispose();

		if (!bufferStrategy.contentsLost()) {
			bufferStrategy.show();
		} else {
			System.out.println("Contents Lost");
		}

		Toolkit.getDefaultToolkit().sync();
	} catch (Exception e) {
		e.printStackTrace();
		running = false;
	}
}

// Here's where the actual drawing takes place.  This is called from screenUpdate which is called from 
// our main run loop. The first part of this section simply draws my HUD.  It basically creates a "BorderLayout" 
// of my own, with images on the left, top, right and bottom.  The middle of the screen is left empty and shouldn't 
// be drawn in.... yet.

public void gameRender() {
	BufferedImage image;
	TexturePaint imagePaint;

	image = (BufferedImage) images.get("left.png");
	imagePaint = new TexturePaint(image, leftRect);
	gScr.setPaint(imagePaint);
	gScr.fill(leftRect);

	image = (BufferedImage) images.get("top.png");
	imagePaint = new TexturePaint(image, topRect);
	gScr.setPaint(imagePaint);
	gScr.fill(topRect);

	image = (BufferedImage) images.get("right.png");
	imagePaint = new TexturePaint(image, rightRect);
	gScr.setPaint(imagePaint);
	gScr.fill(rightRect);

	image = (BufferedImage) images.get("bottom.png");
	imagePaint = new TexturePaint(image, bottomRect);
	gScr.setPaint(imagePaint);
	gScr.fill(bottomRect);

	// If this is the Login stage (which, right now, is the only stage that exists), draw the splash logo 
	// and login components (JTextField and JButton, for example)
	
	if (stage == 0) {
		Graphics2D g2d = (Graphics2D)gScr.create(173, 93, 550, 421);
		gridPane.paint(g2d);
	
		// gridPane.paintComponents(g2d) results in 2 of my "splash images" being drawn on the screen.  
		// One is solid and displayed off-center, while the other is flickering with a green background 
		// (I set the gridPane JPanel's background to green, if you recall).

		// gridPane.repaint() causes the entire screen to flicker.  The HUD images and the JPanel flicker between one another.
		
		// Right now gridPane.paint(g2d) yields the best results, showing the solid splash image from my JLabel (no flickering), 
		// but showing it off-centered.  Again, my JTextField wouldn't show up either when I tried to add it.  
		// Trying to reposition the JComponents yields no results.  The only way I seem to be able to move anything is if 
		// I move the rectangle that it's all drawn in.  This is unacceptable, as it would cause it to cover up the other 
		// elements on the screen, such as the HUD.
	}

	// Not important for the purposes of my current problem.  It works when I actually have it implemented.
	if (isOverConnectRect) {
		this.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
		image = (BufferedImage) images.get("h_connect.png");
		imagePaint = new TexturePaint(image, connectRect);
		gScr.setPaint(imagePaint);
		gScr.fill(connectRect);
	} else {
		this.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
	}
}


public void closeGame() {
	Window w = gd.getFullScreenWindow();
	if (w != null) {
		w.dispose();
	}
	gd.setFullScreenWindow(null);
	System.exit(0);
}


public void update(Observable o, Object arg) {
}

public void actionPerformed(ActionEvent e) {
}

public static void main(String[] args) {
	int p = 1;
	new BBClient(p).setVisible(true);

	// I've noticed that removing the call to setVisible here causes my JPanel to not get drawn at all... 
	// I thought JFrames were visible by default?
}

}


I appreciate the help of anyone who’s willing to take the time to sift through my crappy coding and help me figure out what I’m doing wrong. This seems to be a question that’s asked all-too-often, yet it’s strange how it’s still so difficult to find a straight answer. Thanks again.

I actually made some semblance of progress by trial and error… if this is how it’s SUPPOSED to be done, then shame on Sun :slight_smile:

Right now I have my background/splash image drawing at the correct location – I had to use a rather peculiar way of positioning it, though, and it goes through a strange “fade in” when it’s first drawn.

On top of that splash image, I also have my JTextField displayed. Unfortunately, I’m unable to set the location/position of this JTextField, however I AM able to set its size. Why this is the case, God only knows. I’m also unable to edit the JTextField,even if I have setEditable(true)…

Here’s my revised code… now instead of adding the components to a JPanel (which I would dearly like to do), I just instantiate them, and then individually call their paint() methods from my game loop… this seems idiotic, but it’s teh closest I’ve gotten it to working…


	private JLabel bgImage;
	private JTextField loginField;

	public BBClient(int period) {
		super("MyGame");
		this.period = period;
		setIgnoreRepaint(true);

		loadImages();
		createRects();
		initFullScreen();

                // Editing the x,y values in setBounds yields no results.  e.g. - I'm unable to reposition this JPanel.  The only way I can change it is by changing its size.
                // This is pretty odd....
		try {
			String url = System.getProperty("user.dir")
					+ "\\view\\images\\login.png";
			bgImage = new JLabel(new ImageIcon(new URL("file:" + url)));
			bgImage.setBounds(0, 0, 550, 421);
			bgImage.setIgnoreRepaint(true);
		} catch (Exception e) {
			System.out.println("wra!");
		}

		loginField = new JTextField("OMGZZZ!!");
		loginField.setSize(1000, 30);  // This works.... 
                loginField.setLocation(250, 25); // This doesn't.... it's always displayed at (0,0) in relation to the Graphics2D area that it's drawn in.  So actually (173,93)
		loginField.setEditable(true);  // This doesn't work either...
		loginField.setEnabled(true); // setEnabled() works, however it only controls whether the field is lit up, or dimmed out.  It doesn't affect whether or not I can edit it
		loginField.setIgnoreRepaint(true);

		gameStart();  // Calls my main run() loop, which then calls the gameRender() method

       }

       public void gameRender() {
                // gScr is my main graphics object.... here I'm creating a new Graphics2D object to draw in a certain area
                // so as to avoid drawing over my loaded HUD image elements.

                // Position the rectangle or drawing area/canvas of this graphics object in the middle pane area where the content should go.
		Graphics2D g2d = (Graphics2D) gScr.create(173, 93, 550, 421);
		bgImage.paint(g2d);  // I have to call each component's paint() method individually
		loginField.paint(g2d); // Calling paintcomponent() or anything else yields unhappy results...
        }

Maybe it’s a little more clear what’s going on in my funky code now… I appreciate any help.

I think your problem is that you call the paint method of the text field directly. I believe your graphics object should have the correct clip and translation first.

Could you elaborate on that?

I have the Graphics2D object, gScr which is used to paint the rest of the UI – HUD elements, etc.

I do gScr.create() to create another graphics object (or duplicate) in the area that I want my content pane to be drawn.

Then I pass it to component.paint(g2d) to draw that specific component.

What other way is there to do this?

I tried doing Graphics2D g2d = loginField.getGraphics()
then loginField.paint(g2d)

but got an exception… i’m guessing that’s not what you meant, but I’m a little unclear

[edit]

Actually I think I get what you meant. So I basically have to g.create() for every component that I want to add in a different location. Doing that, I was able to reposition my JTextField, however I still have the problem of not being able to edit the text in it. Is this tied in to the paint method somehow or is there something else that’s causing this issue?

The paint method assumes that the Graphics object has to correct translational transform and the correct clipping region.

However you have not specified these two. When you write a paint method you NEVER think about the location of the component, because the painting system will already have applied to correct clip/transform (and perhaps other things I have not thought of). This is why your component is indifferent to its location graphically: it draws itself at (0,0), so you have to change the Graphics object to make sure (0,0) in this context corresponds to the correct point of your container.

Thanks for the explanation. I’ve now got it drawing in the correct place, and updating properly, however the setEditable() method still won’t let me edit it… any idea why that may be? I’ll continue my usual method of trial & error and hope I can find a fix… thanks for your help