I am currently programming a game which includes lots of graphical elements. Currently the code looks like this:
protected void paintComponent(Graphics g)
{
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g.drawImage(ImageLoader.gui_iconbar, xToRel(480), yToRel(810), xToRel(960), yToRel(270), null);
g.drawImage(ClientVariables.getSplashart(Main.selectedCharacter, 0), xToRel(495), yToRel(825), xToRel(135), yToRel(240), null);
//... (don't get confused with xToRel, it just makes changes to the drawing position if you move the camera)
}
The problem is: there are TONS of these g.draw(whatever); lines. I want to split them in multiple classes but just making 4 classes and copy paste gameFrame.add(GraphicclassXY); doesn't work, logically. Does anyone know how to fix this issue? (It's not a problem if the solution includes making changes from jlabel to canvas for example)
Create logical non-component classes (that don't extend JLabel, JPanel, JFrame, JComponent or other Swing component), give them a public void draw(Graphics g) method, give the class that does the drawing instances of your logical classes, perhaps within some sort of collection, and iterate through the collection and call draw on your instances within the paintComponent method.
Related
I'm working on project, which uses custom Layout Manager. My goal is rotate all components by 180°.
I have some screen which consists of JFrame. I also have some LayoutHandler, which defines some layers of layout. Each layer is instance of LayerLayout which implements LayerManager2 from java.awt
The flow works probably like this:
I have JFrame, which represents the display. This JFrame has some default contentpane. The application set to this contentpane Layout which is represented by LayerHandler:
graphicalDisplayJFrame.getContentPane().setLayout(new LayerLayout(layerHandler));//graphicalDisplayJFrame is JFrame which represents the display
The layerHandler just somehow manage the layers which are LayerLayouts which implemetns LayerLayout2 from java.awt. So it looks like layout of layouts or whatever it is. I'm not much experienced with creating custom layouts.
There I have a situation which shows creating of JFrame.
graphicalDisplayJPanel = new JPanel() {
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g2d = (Graphics2D) g;
g2d.rotate(Math.PI, anchorx, anchory); //rotation 180°
}
#Override
public void paintChildren(Graphics g) {
super.paintChildren(g);
Graphics2D g2d2 = (Graphics2D) g;
g2d2.rotate(Math.PI, anchorx, anchory);
}
};
graphicalDisplayJFrame = new JFrame(); //creation of JFrame
graphicalDisplayJFrame.setContentPane(graphicalDisplayJPanel); //setting JFrame to apply rotation
The first part should make the rotation 180°, but it does not, even when i call repaint on graphicalDisplayJFrame. BUT when I add this:
JLabel hwLabel = new JLabel("Hello world.");
hwLabel.setVisible(true);
graphicalDisplayJFrame.add(hwLabel);
I cause strange thing. The JFrame and all of the components on it is accidentaly rotated by 180°. But its only static behaviour, because there are some text components on display and when there is changed the text on them its again rendered in non rotated way.
So the question is why this could happens?
If i divide it in two parts:
a) Why the Graphics2D on JFrame does not affect the children components on its content pane. Why it only works when I add the JLabel which has no sense here but it was just experiment which caused what I wanted (= repaint with right rotation).
b) I should probably somehow apply the rotation => something like overriding the paint method on each components which are added on the contentPane of JFrame. But how should can I do it? Becouse upper described layout handler just manage a bunch of layouts (layers which are probably custom layouts implementig LayoutManager2 from java.awt) and it is just feeding the components which are put on the layouts and put them on content pane. So the question should probably be where could be the place where I want to implement my custom paining which means apply the rotation (which is this part of code g2d.rotate(Math.PI, anchorx, anchory); //rotation 180°).
I hope it understandable, I made pretty bit effort to understand where could be problem and trying find out the solution, but its behave really strange. So I hope someone with more experience with this could help me out off this struggling and show me the way.
You are applying the rotation at very strange points. Graphics2D.rotate() applies only to things painted after the rotate call, but your paint-overides rotate after calling super.
As for why adding a label causes rotation, hard to tell without seeing the entire code. Probably an effect of partial repaint.
You should not modify essentials like the graphics translation or rotation of the graphics passed to your paint methods. It has the potential to affect the parent components drawing in unexpected way and the effect can easily change depending on JRE version. Instead create a new graphics to pass down to your child components:
#Override
public void paintChildren(Graphics g) {
Graphics2D g2d = (Graphics2D) g.create();
try {
g2d.rotate(Math.PI, anchorx, anchory);
super.paintChildren(g2d);
} finally {
g2d.dispose();
}
}
This method leaves the original Graphics untouched, at least allowing the parent component to render normally. Rotating the graphics is still tricky and will probably have unintended side effects in the rendering process, but at least these effects are now carefully limited to the children of the rotating component.
I was taught in my class and also seen in this book (Big Java Early Objects) to include draw instructions in a class extending JComponent as:
public class Component extends JComponent {
public void paintComponent(Graphics g) {
Graphics2D g2 = (Graphics2D) g;
Rectangle r = new Rectangle(0,0,20,10);
g2.draw(r);
}
}
However, someone pointed out that the first line in the paintComponent method should be: super.paintComponent(g);
Based on my limited knowledge, I believe it's calling JComponent's version of the method (now overridden). Why does this need to happen? What happens if I just ignore this statement as I've been doing until now?
Why does this need to happen? What happens if I just ignore this statement as I've been doing until now?
A component is responsible for painting itself completely. The default painting may be different for each LAF, so by invoking super.paintComponent() you make sure you get the default painting which will basically just be the background.
If you don't invoke this method you have the potential for painting artifacts to occur. Then may not always occur, but you don't want to waste time debugging.
Read the API for the paintComponent() method of JComponent. Among other things it states:
Further, if you do not invoker super's implementation you must honor the opaque property, that is if this component is opaque, you must completely fill in the background in a non-opaque color. If you do not honor the opaque property you will likely see visual artifacts.
So at a minimum you need:
g.fillRect(0, 0, getWidth(), getHeight());
to make sure the background is cleared before you start your custom painting.
It is possible to draw from one Graphics2D to another Graphics2D?
Let me explain.
I have printing issues, when i display a JTextArea or JTextPanel in screen, internaly its used sun.java2d.SunGraphics2D, but when im printing its used sun.print.PeekGraphics and sun.awt.windows.WPathGraphics.
The problem is with some kind of Fonts, like Arial. In some sizes lines are cut.
I have tryed a lot of ways to render the text in printing, Graphics2D.drawString, SwingUtilities2.drawString, TextLayout.drawString, but in some cases lines are still cut, or lines are not cut but some kind of justification makes disapear white spaces.
So my idea is try to render components with sun.java2d.SunGraphics2D and "copy" the surface to the printer via sun.print.PeekGraphics or sun.awt.windows.WPathGraphics.
Thanks in advance.
Yes its possible, thats how double buffering is achieved in a lot of Java Games. What you need is the Graphics2D's drawImage() method which takes in another Graphics2D object to draw in. E.g. from a small game of mine:
private Main(){
...
/* Create the backbuffer as a BufferedImage object */
this.doubleBuffer = new BufferedImage(this.WIDTH, this.HEIGHT, BufferedImage.TYPE_INT_RGB);
/* create a Graphics 2D object to draw INTO this backbuffer */
this.doubleBufferG2D = (Graphics2D) doubleBuffer.createGraphics();
...
}
Somewhere else:
/*Now lets draw the backbuffer INTO the screen */
g2d.drawImage(doubleBuffer, null , 0, 0);
Edit: heh I realized its not exactly as above...lemme think on it.
Edit2: Alright the above can still be used a sample, but the sequence of steps to draw from one Graphics2D to another should be as such:
1. From a Graphics2D object to an Image/BufferedImage object using drawGraphics().
2. From the Image/BufferedImage above, extract its member Graphics2D object by using itscreateGraphics().
Looks like you can do one of two things:
create a Graphics2D on an image, do your rendering, then draw the image into another Graphics2D
or create Graphics2D from original Graphics2D using Graphics.create() methods and then do you rendering.
I thought the create() function of graphics object creates a new copy of the current graphics object
this was my code
Graphics temp;
temp = g.create();
temp.drawString("hello world",100,100);
g.fillRect(200,200,50,50);
Now my understanding was that since temp is now a copy of the g, any change on temp would not be reflected on g. So my output should have been just a rectangle due to the fillRect function. But I am getting both the string and the rectangle on my paint output. Why is this happening and how to stop it?
I am a beginner in java, but, after looking into your code i see that you have put
g.create();
(i'm not very sure but) this could possibly mean that everything declare with g. would be affected. I suggest doing so:
Graphics2D g2d = (Graphics2D) g;
Graphics temp;
temp = g2d.create();
temp.drawString("hello world",100,100);
g.fillRect(200,200,50,50);
hope it worked
Can't you just make a class for the object, for example TextString and Box, and make them have a paint method like so:
public void paint(Graphics g){
g.setColor(Color.RED);
g.fillRect(50, 50, 100, 100);
}
And then wherever you are drawing the objects, call
box.paint(g); or whatever you called your object.
This way you can change the properties of the objects and draw them independently anytime you want without affecting the other objects.
Graphics.create gives you a full or specified section of the Graphics object that generated it - it is not a new Graphics object.
If you wish to draw to a graphics object (and reuse said object) I would suggest using the BufferedImage derivative, OffscreenImage and, from there, draw to the OffscreenImage.getGraphics
I'm Basically programming a simple game engine but I'm having problems with my sprites/images not appearing when they should... or at all!
I'll try and keep this as simple as possible. I have a Sprite, GameEngine and Display class. In the gameloop, I have a method that sets the new position of my Sprite (so it just sets the x and y variables). Next I call a transform method which does the following:
public void transform() {
affineTransform.setToIdentity();
affineTransform.translate(x, y);
}
Following that, I then call a draw method in the Sprite:
public void draw() {
graphics2D.drawImage(image, affineTransform, jFrame);
}
Finally, in my thread I then call repaint() on the JFrame (the Display class). My paint method for that class is as follows:
public void paint(Graphics g) {
g.drawImage(backbuffer, insets.left, insets.top, this);
}
But nothing is appearing, apart from a black screen!
I'm also getting confused between Graphics g, and Graphics2D and when to use either. (The overridden paint method uses Graphics g). For the record, I do have a Graphics2D variable in the class that is created by calling backbuffer.createGraphics();
Another thing that is confusing me is this AffineTransform... I've read the documentation but I'm still utterly confused on how and when to use it - and what exactly it seems to do. Is there any explanation in relatively simple terms?
Surely this should be working... am I missing something out here?
To answer part of your question:
From the Graphics2D JavaDoc
This Graphics2D class extends the Graphics class to provide more sophisticated control over geometry, coordinate transformations, color management, and text layout. This is the fundamental class for rendering 2-dimensional shapes, text and images on the Java(tm) platform.
Essentially, with Graphics2D you can do much more than you can with Graphics. And with a Sun JVM 1.5+, it should be safe to cast the Graphics object you get in paint() to Graphics2D
I just noticed this: For the record, I do have a Graphics2D variable in the class that is created by calling backbuffer.createGraphics();
You should make sure you're not drawing on a Graphics[2D] canvas (I'll use this term to refer to the drawable area that the Graphics[2D] object provides) that you later throw away. If you're drawing your image on a separate canvas, you should ensure that you then draw that image onto your actual display canvas.
I don't really have a good explanation of AffineTransform but maybe these will help?
http://www.javalobby.org/java/forums/t19387.html
https://secure.wikimedia.org/wikipedia/en/wiki/Affine_transformation
From Wikipedia - In general, an affine transformation is composed of linear transformations (rotation, scaling or shear) and a translation (or "shift"). Several linear transformations can be combined into a single one. Basically, you use this class to perform operations such as rotation, translation, zoom etc.