I'm making an online multiplayer game (Nothing too complex, just a top-down tank shooting game) on a Java Canvas. You would think I'm here to ask about networking stuff, sockets, etc, but I'm much farther along than that, and I've come across something strange.
Basically, there are a few strings that I'm drawing on the canvas with a bold font. When I run it from NetBeans (Not building, just pressing F6/The green run button), the text shows up bold as it should. But, when I build the project, and run the JAR file, the text is not bold.
Note:
I tried updating Java, as I was quite a few updates behind. Didn't fix it.
Here is a code snippet to show that I am drawing the bold text correctly, but I don't know what else I would need to show for this kind of issue as the code can't be the problem.
g.setFont(new Font("TimesRoman", Font.BOLD, 50));
g.setColor(Color.red);
g.drawString("Connection Failed.", x, y);
TL;DR - Drawing bold text on canvas isn't bold when running JAR file, but is when running from NetBeans.
All help is appreciated. Thanks!
It looks like you might be creating a Font in your paint function. Given the paint function may be called several times per second, this can be a source inefficiency, and possibly resource starvation. Create your Font when you create your class, perhaps as a static resource, and then use the same font object in each paint call.
The Graphics object is allowed to take shortcuts when drawing objects, in the name of efficiency. This can include not bolding large fonts, not using anti-aliasing on diagonal lines, etc. Speed is king. Of course, you can tell the graphics engine you care more about appearance than speed, using Graphics2D.setRendingHint(...)
import static java.awt.RenderingHints.*;
void paint(Graphics g) {
Graphics2D g2d = (Graphics2D) g;
g2d.setRendingHint(KEY_RENDERING, VALUE_RENDERING_QUALITY);
g2d.setRendingHint(KEY_TEXT_ANTIALIAS, VALUE_TEXT_ANTIALIAS_ON);
You can set multiple hints at once with setRendingHints(Map hints).
Related
I am working on an app that has a Swing user interface. Text is being written to the window with a Graphics2D object (specifically SunGraphics2D) using the drawString method.
I have an issue where on high DPI displays some of the text renders very poorly, for example on a 4K monitor set to 150% scale.
The rendering can be improved by using -Dsun.java2d.uiScale=1.0, but this is simply disabling scaling, so everything is tiny.
If I had to guess what was happening, I would say that the graphics is being rendered at 1.0 scale, anti-aliasing is being applied, and then it is being scaled 1.5 times using Nearest Neighbour Interpolation. My reasoning for this is that it appears as though sometimes "anti-aliased pixels" (i.e. for black text, the light grey ones) are sampled for the scaling operation, which causes specific letters to be rendered unusually.
For example, this image shows the string "123" (I have enlarged it by 200% to make the issue more obvious):
As you can see the line that makes up the bottom of the "2" is 1px high, but the anti-aliasing is 2px high.
Another example with the string "555", where the base of the "5" is again very skinny:
Interestingly this doesn't affect all text, for example the text on JButton and JLabel objects renders at the correct scale and looks good:
This issue does also affect images which are rendered within the window, but I am not very familiar with the image rendering classes of this particular application, so I cannot provide more info, other than to say this might point to the Graphics object being the issue.
I have tried to create a minimum example of the issue, but unfortunately when I create a brand new project the text renders perfectly, so it seems that this issue is specific to the application I am working on, which had many thousands of lines of code.
Is there something I should be looking into or trying? Perhaps there is a "Scaling Mode" for the graphics object, which is set to "Nearest Neighbour" or something like that?
I have tried to use a few RenderingHints such as RenderingHints.VALUE_FRACTIONALMETRICS_ON, but that didn't make any difference.
JDK is Eclipse Temurin 11.
Edit:
In case it is helpful, here are the RenderingHints being used:
I am creating a Java game, and in this, I have a plane that can be moved around by the user, however if the plane hits any of the sides, it should explode - meaning there should be an explosion at that location. I used links such as: Why gif animation doesn't animate when using it in paintComponent()? and other ones to find out how to animate a gif, however, my code doesn't work.
Here is the relevant code:
ImageIcon ii = new ImageIcon(this.getClass().getResource("explosion.gif"));
Graphics2D g2d = (Graphics2D)g;
g2d.drawImage(ii.getImage(), planeX, planeY, this);
System.out.println("drawn");
Although "drawn" is being printed out on the console, the explosion gif doesn't load. I checked the file name and it was correct. However, I did use the same gif somewhere else in my code and it worked there, so is there something that prevents having a gif being used twice in Java?
Here is my entire paintComponent() method:
super.paintComponent(g);
setFocusable(true);
requestFocus();
background = new ImageIcon("bg.png").getImage();
g.drawImage(background, -6, 0, 700, 700, null); //background of JPanel
if (!hit) g.drawImage(plane, planeX, planeY, planeLen, planeHei, null); //only draw plane if not exploded
else if (count == 0)
{
ImageIcon ii = new ImageIcon(this.getClass().getResource("explosion.gif"));
Graphics2D g2d = (Graphics2D)g;
g2d.drawImage(ii.getImage(), planeX, planeY, this); //doesn't work
System.out.println("drawn"); //printed
}
title = new ImageIcon("title.png").getImage(); //transparent, is drawn
g.drawImage(title, -9, 20, 700, 218, null);
title2 = new ImageIcon("title2.png").getImage(); //transparent, is drawn
g.drawImage(title2, 10, 245, 670, 20, null);
I have checked title and title2 and they are transparent, meaning that the explosion should be seen. I even tried it below those two images, and the explosion still is not visible. I am using JButtons and KeyListeners on this panel as well.
Please let me know if I should add anything else, and thank you in advance!
You should always pass this as the final argument to drawImage in a painting method. If an image has multiple frames, or the image is loaded in the background, this tells the component to paint itself again as more of the image becomes available.
Painting of components happens frequently. It is controlled by the system, not just by code calling the component’s repaint methods. In fact, it’s common for painting to happen many times per second. Many system events beyond a programmer’s control can trigger painting, such as a user moving a window, or another window moving, or even the user moving the mouse.
For this reason, painting should not perform slow operations like loading files. Move the loading of your images out of your painting method, and into the class’s constructor. Store the Image objects in private fields, where paintComponent can use them again and again without reloading them for every painting run.
Since the timing of painting is beyond your control, it is important that you never change the state of a component in a painting method. Move setFocusable and requestFocus out of your paintComponent method. Those calls should be in the code that builds your component and adds it to a window.
Finally, be aware that loading images from files will not work if you ever package your application in a .jar file. A .jar file is a single compressed archive; the entries in it are not files anymore, they’re just sequences of bytes inside the archive. Loading all files using getResource will address this, since getResource looks in every .jar file in the classpath.
Never mind, I had to make a duplicate of "explosion.gif". It works fine now!
However, please let me know if there is another way of doing this, much faster AND efficiently. Thanks!
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.
I've been working on a Battleship game for a school assignment in Eclipse using the latest version of java, Java 6, until I realized that I need to provide compatibility for the 1.4.2 JRE.
I've been careful to not use any features and library classes that are not available in the 1.4.2 compiler, and my code compiles fine. The problem is that the background image for the game is not rendering properly--only a small corner in the top left is being rendered.
For reference, here are screenshots showing my problem:
The game rendering properly under the Java 6 runtime environment
Image Link (I can't embed them yet.)
Only the corner being rendered under the 1.4.2 JRE
Image Link (I can't embed them yet.)
And, of course, there is no difference in the code in the two screenshots, just different runtime environments.
The background is being drawn on a JPanel that is visible, and added to the JFrame using setContentPane(). There are no errors with loading the image into the program.
I've done a lot of testing and noticed that my input handler is only responding to input within that small corner that has an image in it, which would mean that my JPanel is not filling the whole screen--however I've double checked its size and it matches the JFrame's size (800 x 600).
And here's my rendering code:
public void render() {
Graphics2D g = (Graphics2D) getGraphics();
Graphics2D gb = (Graphics2D) buffer.getGraphics();
gb.drawImage(background, 0, 0, null);
gb.setColor(new Color(45, 45, 45));
gb.setFont(new Font("Arial", Font.BOLD, 18));
gb.drawString("Outwit the computer. Are you up for the challenge?", 175, 185);
cont.draw(gb, contState);
//Draw buffer to screen
g.drawImage(buffer, 0, 0, null);
}
This method is called repeatedly to render the game's frames. It is part of a JPanel class.
I've tried nearly everything, and this is my last resort. Could this perhaps be a bug with the older runtime environment? Any insight would be greatly appreciated!
Thanks so much in advance. :)
G'day, I have JPanel with some Line2D objects on it. Issue is when I draw this line it doesn't appear as I want them to. Lines are not smooth, It's hard to explain in word so I am posting an Image,
Zoomed Area,
How to make them look more polished rather than wrinkly.
Thanks
The problem is likely that you don't have antialiasing turned on your Graphics context. Try the following line before you draw:
graphics.setRenderingHint(
RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
(where, of course, graphics is your Graphics2D instance).
Later on when you discover that the text you're drawing is also ugly and jagged, you'll want to use
graphics.setRenderingHint(
RenderingHints.KEY_TEXT_ANTIALIASING,
RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
However, text is a little more complicated; there's several values for this hint that you can use depending on (among other things) the type of screen you're drawing the text to. You should read the RenderingHints.KEY_TEXT_ANTIALIASING API doc for those details.