I am making a game and I am trying to find the best way to implement Doublebuffering into it. Would anyone be able to show me how I could do it with my rendering code below?
public void paint(Graphics g){
super.paint(g);
Graphics2D g2d = (Graphics2D) g;
g2d.drawImage(p.getImage(), p.getX(),p.getY(),null);
//draw each ball object
for(int i=0;i<balls.size(); i++){
Ball tmp = (Ball) balls.get(i);
g2d.drawImage(tmp.getImage(), tmp.getX(),tmp.getY(),null);
}
//strings
g2d.drawString("Score: "+score,50,20);
}
Could someone please help?
If you're using Swing you just can use the built-in double buffering: http://java.sun.com/products/jfc/tsc/articles/painting/#db
In case you're implementing your own rendering, here are some hints:
Double Buffering basically means you have two draw buffers that you alternatingly write to while the other is displayed.
In your case, you might use an image to draw your game content to and then draw that image to the component. This should give you double buffering in a sense. You might use two images which you swap in order to reduce concurrent access, i.e. one would be the "front" buffer that is displayed and the other is the "back" buffer you draw to.
That said, I'd strongly recommend not to implement that yourself. Instead you should try and use one of the existing 2D libraries (like Swing) or 3D libraries (like OpenGL with JOGL or LWJGL as Java bindings - note that it doesn't have to be 3D with OpenGL). Alternatively you could also look for a game engine, there are plenty of them out there.
Related
I'm new to Java's graphics library and I'm still coming to grips with its limitations. Below I have a draw function for a Grid class, which draws a 2D array of tiles as filled rectangles.
Not that it's pertinent to the question but the the scale and offset arguments are there to adjust the Grid's tiles and tileSize variables such that it's drawn in the correct position and scale on the screen.
My question is it normal for this to lag considerably when the tiles variable is quite big? I normally get around 500 fps without any Grids on screen, and no noticeable reduction with a Grid of tiles[10][10] to [50][50]. But at tiles[1000][1000], or 1,000,000 total rectangles to draw, fps drops to 7.
I know a million is a lot, but they are just rectangles after all, and my pc can play a game like Skyrim on full settings no problem. I'd imagine there's more than a million polygons on display in Skyrim, and they comes with all sorts of highres textures and lighting and all so, why should a million gray squares be such a problem? Is Java's graphics library really poor? Am I expecting too much? Or, as I suspect, is there a much better way of drawing something like this?
I can provide the main class's paintComponent if that's important, but it's just a call to _Grid.draw() so I don't think the problem's there..
public Graphics draw(Graphics g, double scale, Point offset) {
Graphics2D g2 = (Graphics2D) g;
for(int i = 0; i != this.tiles.length; i++) {
for(int j = 0; j != this.tiles[0].length; j++) {
boolean draw = true;
if(this.tiles[i][j].type.equals("EMPTY")) {
draw = false;
} else if(this.tiles[i][j].type.equals("PATH")) {
g2.setColor(Color.LIGHT_GRAY);
} else if(this.tiles[i][j].type.equals("WALL")) {
g2.setColor(Color.DARK_GRAY);
}
if(draw) {
g2.fillRect((int)((this.xPos+i*this.tileSize)*scale + offset.x),
(int)((this.yPos+j*this.tileSize)*scale + offset.y),
(int)(this.tileSize*scale),
(int)(this.tileSize*scale));
}
}
}
return g2;
}
Java's BufferedImage class is slow. It's a known fact. If you want to do fast image manipulation then it's the wrong tool to use.
Additionally games like Skyrim are using the graphics card to do most of the work, using the Java Image stuff it is all being done in the CPU.
You should really look into using a game framework - there are 2d ones like Slick2d and 3d ones like jMonkeyEngine3.
A few things you might consider trying:
If your original Graphics object is being created by a BufferedImage, consider using VolatileImage instead. As others have said, everything in this draw method is happening on the CPU via the Swing Event Dispatch Thread, so you are limited to a single CPU for drawing (unless you spawn more Threads somewhere). VolatileImage, when used for double-buffering, takes advantage of the graphics hardware and can be MUCH faster for this kind of thing.
You're doing a TON of String comparisons, which can be very slow. I'd consider refactoring your type field to be a custom enum instead, then have that enum define its own draw method that takes a Graphics object and draws itself. That way, you could call this.tyles[i][j].type.draw(g2) and rely on late binding to draw the right colored rectangle, thereby eliminating the String comparisons.
Make sure you're only drawing the stuff that's on screen. I don't know anything about your tiles array, but if it's significantly larger than what's actually being rendered to screen, you could be wasting a lot of CPU cycles there.
This might sound silly, but you're actually doing twice as many memory accesses as you really need. I'm going to assume you have Tile[][] tiles ... defined somewhere in your source code. In the outer loop, grab the row first by writing something like Tile[] row = tiles[i];, then in your inner loop, get the type by calling row[j].type. This will cut your memory access count in half when iterating over the tile array.
I am currently making a paint like program with Java Applets. I want to simulate a bucket tool by using recursion and checking each pixel around a given point, however I am having trouble getting the RGB value at a given pixel.
Is there a way to do something such as
public void paint(Graphics g) {
g.getPixelAt(X, Y);
}
Or something?
Graphics is virtual concept and does not support what you are trying to do
What you need to do is paint to a surface that you can interact with, something like a BufferedImage.
The basic idea would be painting all effects to this buffered image and using Graphihcs#drawImage to actually render the image to the screen.
From there you can us BufferedImage#getRaster which will provide you with a WritableRaster object which has get/setPixel methods.
You could use Applet.getLocationOnScreen() and java.awt.Robot.createScreenCapture() to find and capture the part of the applet you want.
This question poses a similar problem.
Well I've got my game engine running smoothly, and perfectly on all machines! Before I continue adding functionality to the engine, I want to enhance the engine's graphical capabilities. The one I'm focused on is fading and blending images. I'm going to need some smooth transitions, so without using any 3rd party libraries like OpenGL, how does one apply opacity to images when drawing to a graphics object?
thanks for any replies :D
Perhaps using an AlphaComposite could be what you're looking for?
Image image = new Image(...);
float alpha = 0.5;
#Override
public void paintComponent(Graphics g) {
Graphics2D g2d = (Graphics2D)g;
AlphaComposite composite = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, alpha);
g2d.setComposite(composite);
g2d.drawImage(image, 0, 0, null);
}
You would set your alpha to whatever transparency level you desire (between 0.0 and 1.0). In this case, you would probably want to be using AlphaComposite.SRC_OVER to be able to overlay your transparent image and see behind it, unless your going for something else (if so, you can easily see all available constants and their explanations on the first link provided).
I am making a game where for the purpose of this question i have a a class that extends JFrame that adds a class that extends JPanel(GamePanel). in GamePanel i have a run method that has two functions update(); and repaint(); and then a Thread.sleep(20). The update function takes about 1-2ms. I have all my drawing stuff in paintComponent(Graphics g) which seems to correctly get called when i use repaint() since stuff shows up on screen.
My problem is that it is increeedibly laggy. When i didn't have Thread.sleep(20) it was unplayable with like 2fps. I read that this was because repaint() wasnt given enough time to finish or something so i added a delay before next loop. anything above or under 20ms seems to make it more laggy.
I've tried using graphics configuration stuff, double buffering and more but it stays laggy. On my home pc, which is an intel i5, quad core, 3.2GHz i'm only getting around 100fps, and on a school computer i get around 15fps (ok pc, like amd dual core i believe). The paintComponent loop is Super lightweight! just drawmap with offset depending on player position, then draw player in middle of screen! I am using a map that is 2000x2000, 0.8mb. Tried switching to 1000x1000 0.4mb and no difference.
Here is the code:
#Override
public void paintComponent(Graphics g) {
//Rendering settings and stuff
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
//Draw map gameMap.gameMap is a bufferedImage
g2d.drawImage(gameMap.gameMap, gameMap.x, gameMap.y, null);
//Draw health bar
g2d.setColor(healthBarColor);
g2d.setComposite(alphaHealthBarBackground);
g2d.fillRect(100, 19, 200, 23);
g2d.setFont(font);
g2d.setComposite(alphaNormal);
g2d.setColor(healthBarColor);
g2d.drawString("HP: ", 20, 40);
g2d.fillRect(100, 19, player.health * 2, 23);
//Draw player player.playerImage is a BufferedImage
rotatePlayer.setToRotation(rotation, rotationAnchor.x, rotationAnchor.y);
rotatePlayer.translate(xResolution / 2, yResolution / 2);
g2d.drawImage(player.playerImage, rotatePlayer, this);
g2d.setColor(Color.white);
}
This results in 100fps on relatively awesome computer and 15fps on descent computer!
Not pretending to be ultimate solution, just some tips:
Make sure your buffered images are have same color model as default of GraphicsDevice on which they drawn. The method GraphicsConfiguration.createCompatibleImage could create such images.
If it is possible, try to split whole huge map to tiles and skip rendering off-screen (out of game view) parts.
It seems you use passive rendering, as you mentioned calling of repaint(). Event Dispatch Thread used for many things in Swing and AWT, and it can't guarantee acceptable timing for actively rendered games. Maybe it worth to redesign rendering part of game to use Active Rendering. Tutorial and example can be found here.
Well, for game development I donot think drawing a huge image in a Swing paintComponent is ever going to win any speed records. 100 FPS is still amazing (and about 40 FPS more than the refresh rate of most LCD displays).
For more speed it is essential that the drawing primitives you are using are accelerated. On your school PC's they may simply lack a decent graphics card that provides the 2D acceleration needed (how do these PC's feel doing normal 2D operations in say browsers and paint programs?)
You may want to look into a framework that uses OpenGL instead, like JOGL. Another option is to try out JavaFX 2 -- it may have a better rendering pipeline.
I am also developing a Java game and have done many things the same as you (extending JPanel and calling repaint()). One difference is that I am using a javax.swing.Timer to call an ActionPerformed(ActionEvent e) method to update my game every 5ms. At the end of the method it calls repaint() to do the rendering.
Also if you are using Graphics2D to do your rendering you can use RenderingHints to guide the computer on how you want your game to be rendered (Eg, speed or quality, stuff like that).
I haven't measured the FPS exactly but I get no lag on my Ubuntu 12.10 system and JUST a bit slower on Windows 8.
I apologize if my response isn't exactly what you wanted but this is what has worked for me so far without problems.
One attempted approach was to use TexturePaint and g.fillRect() to paint the image. This however requires you to create a new TexturePaint and Rectangle2D object each time you paint an image, which isn't ideal - and doesn't help anyway.
When I use g.drawImage(BufferedImage,...), the rotated images appear to be blurred/soft.
I'm familiar with RenderingHints and double-buffering (which is what I'm doing, I think), I just find it difficult to believe that you can't easily and efficiently rotate an image in Java that produces sharp results.
Code for using TexturePaint looks something like this.
Grahics2D g2d = (Graphics2D)g;
g2d.setPaint(new TexturePaint(bufferedImage, new Rectangle2D.Float(0,0,50,50)));
g2d.fillRect(0,0,50,50);
I'm using AffineTransform to rotate a hand of cards into a fan.
What would be the best approach to paint good-looking images quickly?
Here is a screenshot:
The 9 is crisp but the rest of the cards are definitely not as sharp.
It could be possible that the problem lies in when I create each card image and store it in an array.
Here's how I'm doing it at the moment:
// i from 0 to 52, card codes.
...
GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
GraphicsDevice gs = ge.getDefaultScreenDevice();
GraphicsConfiguration gc = gs.getDefaultConfiguration();
BufferedImage img = gc.createCompatibleImage(86, 126, Transparency.TRANSLUCENT);
Graphics2D g = img.createGraphics();
setRenderingHints(g);
g.drawImage(shadow, 0, 0, 86, 126, null);
g.drawImage(white, 3, 3, 80, 120, null);
g.drawImage(suit, 3, 3, 80, 120, null);
g.drawImage(value, 3, 3, 80, 120, null);
g.dispose();
cardImages[i] = img;
}
private void setRenderingHints(Graphics2D g){
g.setRenderingHint(KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
g.setRenderingHint(KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
g.setRenderingHint(KEY_ANTIALIASING, VALUE_ANTIALIAS_ON);
}
How should I approach this differently?
Thanks.
Edit:
Without RenderingHints
Setting AA hints made no difference. Also, setting RenderingHints when creating the images makes no difference either. It's only when they are being rotated with AffineTransform and painted using g.drawImage(...) that they seem to blur. The image above shows the difference between default (nearest neighbor) and bilinear interpolation.
Here is how I'm currently painting them (much faster than TexturePaint):
// GamePanel.java
private void paintCard(Graphics2D g, int code, int x, int y){
g.drawImage(imageLoader.getCard(code), x, y, 86, 126, null);
}
// ImageLoader.java
public BufferedImage getCard(int code){
return cardImages[code];
}
All my cards are 80x120 and the shadow .png is 86x126, so as to leave 3px semi-transparent shadow around the card. It's not a realistic shadow I know, but it looks okay.
And so the question becomes... How can you produce sharp paint results when rotating a BufferedImage?
Reference to a previous question also regarding a fanned card hand:
How can you detect a mouse-click event on an Image object in Java?
Bounty-Edit:
Okay so after much discussion I made a few test .svg cards to see how SVG Salamander would go about rendering them. Unfortunately, the performance is terrible. My implementation is clean enough, seeing as with double-buffered BufferedImage's the painting was incredibly fast. Which means I have come full circle and I'm back to my original problem.
I'll give the 50 bounty to whoever can give me a solution to get sharp BufferedImage rotations. Suggestions have been to make the images bigger than they need to be and downscale before painting, and to use bicubic interpolation. If these are the only possible solutions, then I really don't know where to go from here and I may just have to deal with the blurred rotations - because both of those impose performance setbacks.
I can finish my game if I can find a way to do this well.
Thanks to everyone. :)
When you rotate a rasterized image (such as a BufferedImage), you lose data. The best solution is to save your images larger than you'll need them, and downscale on the fly when you paint them. I've found that 1.5x the size you need is a good starting point.
Then, when you're painting the image, resize on the fly:
g.drawImage(bufferedImage, x, y, desiredWidth, desiredHeight, observer);
Rotations using bilinear interpolation is recommended.
Credit for suggestion goes to guido.
This advice is probably a little late in your design, but may be worth mentioning.
Rasterized images is probably the wrong technology to use if a lot of rotations and animations are a part of your UI; especially with complicated images with lots of curves. Just wait until you try and scale your canvass. I might suggest looking at a vector based graphical library. They will render the sorts of effects you want with less potential for artifacts.
http://xmlgraphics.apache.org/batik/using/swing.htm
Setting the interpolation type, as well as anti-aliasing value, in an AffineTransformOp may offer some improvement. Type TYPE_BICUBIC, while slower, is typically the best quality; an example is outlined here. Note that you can supply multiple RenderingHints. Another pitfall arises from failing to apply the hints each time the image is rendered. You may also need to adjust the transparency of the background, as suggested here. Finally, consider creating an sscce that includes one of your actual images.