I am new to java and programming in general,and i am trying to write a little shooting game with a spaceship and aliens, but having a lot of trouble with the graphics. It seems that I am mixing a lot of different kind of components.
How should I do it right?
- Should I use Swing JFrame and then add it a Graphics object?
- Should I make a panel first and add the graphics on to it instead?
- Or maybe should I use a canvas instead of a JPanel?
There are a lot of options, and searching the net for answers makes me very confused. Some advise to use the paint() method while others demonstrates code while using the paintComponent()...
What is the preferred way in the right order for the graphics to be laid and what classes should I use?
For serious gaming, don't use Swing but rather other more game-specific GUI libraries such as the LWJGL. For simple Games, Swing is OK, but be sure to read the graphics tutorials first as your assumptions on how to draw may need to be changed (I know mine were). For instance you would not use a Graphics class field but would usually draw passively in a JComponent's paintComponent method.
For simple games, Swing is fine and you can rely on the followings:
Use Swing and forget about AWT. You will get far better results with a lot less code.
Use Swing Timer to pace your game
For each of your components, extends JComponent or JPanel and override paintComponent. In paintComponent perform only drawing operations (do not update your model or modify the state of your component).
Whenever when you want to draw something, modify your components state and then invoke repaint()
To handle layers, you can always use JLayeredPane
I can see the sense of using a game API as suggested by #Hovercraft, but feel that many simple games can do without it.
I agree with the first 2 points of #Guillaume, but would tend to go in a different direction after that.
Let us assume the game:
Is a fixed size (i.e. non-resizable).
Has no components appearing over it.
In that case, I would tend to do the rendering in a BufferedImage that is displayed in a label.
Answers to specific questions
Should I use Swing JFrame and than add it a Graphics object?
That would not compile. So ..no.
Should I make a panel first and add the graphics on to it instead?
That is basically what Guillaume is suggesting, though I prefer using an image as the canvas on which to paint.
Or maybe should I use a canvas instead of a JPanel?
If by 'canvas' you mean java.awt.Canvas then no.
If you mean a java.awt.image.BufferedImage then yes.
Some advise to use the paint() method while others demonstrates code while using the paintComponent()...
This is a common confusion since there is so much old and bad code out there in the World Wild Web.
The only components that overriding paint(Graphics) would work with are components of the AWT & Swing top-level containers such as JFrame, JApplet or JWindow.
AWT is last millennium's GUI component toolkit. We should use Swing this millennium.
It is unusual for an entire GUI to be custom painted. Instead it might be added as the major component amongst others that report player health, lives remaining, score etc. For that (and other) reasons, it is better to render to a 'non top-level container' which would leave us looking at doing custom painting in a JComponent or JPanel.
For custom painting in an extended panel or component, override paintComponent(Graphics). That is the correct way to do custom painting for those components.
Other tips
Each object of the game (e.g. Ship, Enemy, Missile) should know how to draw itself. Keep a reference to each of the game elements in the painting routine, and simply call gameElement.paint(Graphics) (or Graphics2D) on the instance of graphics it is painting.
If the game elements are drawn from Java-2D based Shape instances, or if a Shape can be defined from the existing sprite images, collision detection becomes simple. For details see:
This answer to Get mouse detection with a dynamic shape
This answer to Collision detection with complex shapes
I had to implement a lost-vikings like game for a course project and not having a single idea how to do it, had the exact same questions in mind.
I ended up writing methods that are responsible for drawing different elements(i.e., one to draw enemies, one for missiles, etc...), and then called them in paint() method. I called repaint() method in my main loop to redraw everything.
I am not sure if this is a good practice, but it worked. You can check the code here, though, I must warn you that it is a "damn, i must get it done" project. You should specifically have a look at GameFrame.java.
Related
I wanted to access the Graphics class to draw a rectangle, but wanted to do so without having to call the paintComponent method. Is it possible?
Painting should always be done in a painting method.
The most common way is to do the custom painting in the paintComponent(...) method of the component.
Another approach is to "decorate" a component using the JLayer class and implement the painting in the paint(...) method of the JLayer. Read the section from the Swing tutorial on Decorating Components Using the JLayer Class for more information and examples.
Yes, it is possible, the same way it is possible to paint in a BufferedImage (by using straight the Graphics object by calling into the getGraphics() method).
BUT, unlike the case for BufferedImage, doing so for a component is likely to cause a mess. The reason is the underlying AWT/Swing code expects the component to paint itself, (see Component.paint or JComponent.paintComponent) while it (the underlying AWT/Swing code) will take care by about when this rendering needs to occur (e.g. scrolling, resizing, etc).
As such, if you paint from outside the component, at any time the AWT/Swing decides "Well, there's some new painting/repainting to be done", it will invoke the "standard methods", with the expectation your drawing code is there. As you paint it externally, if you don't make special arrangements to be notified of the "need repainting" situation, parts or the entire "drawing-from-outside" will be painted over or not painted at all or all kind of different messy situation.
In other words, if you paint-from-outside, you'll need to write not only the code that does the painting, but also:
re-implement heaps of code already implemented by the AWT/Swing which deals with the circumstances when the component needs to be (re)painted.
disable somehow the AWT/Swing "native" handling of painting.
Rest assured, you don't want that (even if you think you do).
PS. if you describe better why do you need that, you may get more useful suggestions on how to do it the proper way.
I am am trying to build a user interface for graphical programming. I would like to use JUNG to handle the graph aspects of the drawing. Rather than draw all of my widgets using Graphics2D, I would like to use Swing widgets as often as possible to take advantage of Swing's event architecture.
I would like to be able to pan, zoom, rotate the canvas the graph is drawn on, but still retain the ability to manipulate the Swing widgets as long as they are large enough to see. I also want the widgets to be automatically laid out based on the JUNG layout algorithm I have chosen.
How do I combine Graphics2D drawings and Swing components?
I understand that JComponents take care of drawing themselves using their paint() methods. What I am having trouble understanding is how to create Swing objects that can be affected by Graphics2D transforms and still retain all of their event handling. I can't just show an image of the component. The component must be live.
I've tried subclassing a widget and adding a AffineTransform to the graphics object that is passed to the parent's paint method. It rotates the widget, but the clipping appears to be off.
Any suggestions? For the time being, I'd like to avoid adding another library if I can.
As an example, JDigit extends JButton and overrides paintComponent() to scale to the size of its enclosing Container. For speed, the component uses pre-rendered glyphs, but deriveFont() is only slightly slower. Similar results can be obtained by implementing the Icon interface. This example scales a grid of buttons to a few, pre-determined sizes, and this example forgoes components entirely, scaling the graphics context and all contents.
JXLayer project has implementation to transform the whole UI to different scales.
You may want to check out this demo
Based on my research, using Piccolo2D provides the ability to pan and zoom Swing elements. Using JUNG's graph layout algorithms to layout the graph and Piccolo2D PSwing nodes, I can generate a graph of Swing components that are usable, can be laid out by algorithm, and are zoom and panable.
This program will have an infinite canvas (ie as long as the user scrolls, it becomes bigger) with a tiled background image, and you can drag and drop blocks and draw arrows between blocks. Obviously I won't use a layout manager for placing blocks and lines, since they will be absolutely positioned (any link on this, possibily with a snapping feature?). The problem arises with blocks and lines. Basically I'll have two options:
Using a simple layout for each building block. This is the simplest and clearest approach, but does it scale well when you have hundreds of objects? This may not be uncommon, just imagine a database with 50 tables and dozens of relationships
Drawing everything with primitives (rectangles, bitmaps, etc). This seems too complicated (especially things like text padding and alignment) but may be more scalable if you have a large number of objects. Also there won't be any event handler
Please give me some hints based on your experience. I have never drawn with Java before - well I did something rather basic with PHP and on Android. Here is a simple preview
DISCLAIMER
You are not forced to answer this. I am looking for someone who did something like this before, what's the use of writing I can check an open source project? Do you know how difficult it is to understand someone else's code? I'm talking about implementations details here... Moreover, there is no guarantee that he's right. This project is just for study and will be funny, I don't want to sell it or anything and I don't need your authorization to start it.
Measuring and drawing text isn't such a pain, since java has built in classes for doing that. you may want to take a look at the 2D Text Tutorial for more information. In fact, I did some text drawing computations with a different graphics engine which is much more primitive, and in the end it was rather easy (at least for the single-line drawing, for going multiline see the previous link).
For the infinite canvas problem, that's also something I always wanted to be able to do. A quick search here at stackoverflow gives this which sounds nice, althought I'm not sure I like it. What you can do, is use the way GIMP has a scroll area that can extend as you move - catch the click of the middle mouse button for marking the initial intention to move the viewport. Then, when the mouse is dragged (while the button is clicked) move the viewport of the jscrollpane by the offset between the initial click and the current position. If we moved outside the bounds of the canvas, then you should simply enlarge the canvas.
In case you are still afraid of some of the manual drawing, you can actually have a JPanel as your canvas, with a fixed layout. Then you can override it's paint method for drawing the connectors, while having child components (such as buttongs and text areas) for other interaction (and each component may override it's own paint method in case it wants to have a custom-painted rect).
In my last drawing test in java, I made an application for drawing bezier curves (which are basically curves made of several control points). It was a JPanel with overidden paint method that drew the curve itself, and buttons with custom painting placed on the location of the control points. Clicking on the control point actually was clicking on a button, so it was easy to detect the matching control point (since each button had one control point associated with it). This is bad in terms of efficiency (manual hit detection may be faster) but it was easy in terms of programming.
Anyway, This idea can be extended by having one child JPanel for each class rectangle - this will provide easy click detection and custom painting, while the parent will draw the connectors.
So in short - go for nested JPanels with custom drawing, so that you can also place "on-canvas" widgets (and use real swing widgets such as text labels to do some ready drawing) while also having custom drawing (by overriding the paint method of the panels). Note that the con of this method is that some swing look-and-feel's may interfere with your drawing, so may need to mess a bit with that (as far as I remember, the metal and nimbus look-and-feel's were ok, and they are both cross-platform).
I'm making an application where I need to scroll some text on a java.awt.Canvas - at the moment I'm doing this by constantly calling the repaint() method, and overriding the paint() method in Canvas to adjust the position each time the method is called and then draw the string at the given position.
This works, but it really eats through CPU something silly for something so simple. Is there a better, hardware accelerated way to do the task? I've tried using VolatileImage but for some reason that made the performance noticeably worse.
I need to use Canvas not JPanel - I'm using VLCJ in the same project, and it needs a heavyweight component on which to play the video.
You may be able to use Double Buffering and Page Flipping in this context.
Addendum: You might look at pre-rendering the full text into a depth-compatible BuffreredImage; drawImage() does better of it doesn't have to scale or change depths.
I have trouble understanding a fundamental concept in Java 2D.
To give a specific example:
One can customize a swing component via implementing it's own version of the method paintComponent(Graphics g)
Graphics is available to the body of the method.
Question:
What is exactly this Graphics object, I mean how it is related to the object that has the method paintComponent? Ok, I understand that you can do something like:
g.setColor(Color.GRAY);
g.fillOval(0, 0, getWidth(), getHeight());
To get a gray oval painted. What I can not understand is how is the Graphics object related to the component and the canvas. How is this drawing actually done?
Another example:
public class MyComponent extends JComponent {
protected void paintComponent(Graphics g) {
System.out.println("Width:"+getWidth()+", Height:"+getHeight());
}
public static void main(String args[]) {
JFrame f = new JFrame("Some frame");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setSize(200, 90);
MyComponent component = new MyComponent ();
f.add(component);
f.setVisible(true);
}
}
This prints
Width:184, Height:52
What does this size mean? I have not added anything to the frame of size(200,90).
UPDATE:
I understand that I must override paint to give in the Graphics g object the hints required to do the repaint and that I do not have to create a Graphics object as one will be given by platform.
What happens after that is what I can not understand.
E.g. does Graphics represent the screen and the object is painted accordingly on screen as soon as I start calling the various g.setXXX methods?
Does it get stored in a queue and there is a 1-1 association among g and each component? So the framework uses each g of each component to paint it one at a time?
How does this work?
Any help on this is highly welcome
Thanks
I understand your problem as I struggled with it for some time when I was learning Java graphics. It's not just Java 2D graphics - it is part of AWT.
When you create a JFrame or some other top-level object, it does a lot of work "behind the scenes" - part of which is creating a Graphics object. (There is not explicit notification of this, though if you stepped through the code with a debugger you may see classes which create the Graphics).
You then create components which you add, or register with, the top-level object. These all have to implement a call-back method, including
paint(Graphics g);
You will then #Override these methods so that when the component is rendered it uses YOUR paint method.
Do not try to save the Graphic or create a new one. Think of it as the framework taking the responsibility off you.
The size of components is often taken out of your hands. If you use a layout manager then it may decide to resize your component.
If you are coming from a procedural imperative background you may well have problems (I came from FORTRAN). My advice would be to try a number of tutorials and - at some stage - enlightenment will start to come.
The general documentation for Java graphics is poor. There are many concepts which are opaque (see How does Java Graphics.drawImage() work and what is the role of ImageObserver ). The early implementation was rushed through and had many bugs. Even now it is often unclear whether and in what order you should call methods such as setPack() and setVisible().
This doesn't mean you shouldn't use it! Just that the learning curve is a bit longer than IMO it should be.
MORE:
Also YOU don't decide when something is painted, the framework does. paint(g) really means "The framweork is repainting its components. What to you want this component to provide at this stage".
Maybe providePaintingInstructionsWhenRequiredForComponentGraphics(Graphics g) would be a useful name.
Similarly repaint() does not repaint at your orders, but when the system thinks it should. I haven't found it useful.
If you (say) resize a component interactively every slight change will normally trigger a paint(g). try putting a LOG.debug() in the paint code and seeing when it gets called.
What does this size mean? I have not added anything to the frame of size(200,90).
You added your component to the frame and set the size of the frame to be (200, 90). The default layout manager for the content pane of the frame is the BorderLayout, which means the component you added gets all the available space. The frame needs some space for the title bar and borders, so your component gets the remaining space.
The component does not create a static Graphics object association.
The graphics object is the wrapper for a platform handle giving access to a physical device, like the screen. It's valid for the time when "paint" is executed only, you can't store it and reuse it later. It is a resource managed by the "toolkit".
The component itself is an abstraction on top of the windowing system, that gets asociated shortly with this device for getting rendered.
EDIT
You can force such an association calling "getGraphics" if you feel the need to paint out of the "paint" callback. This should be a very rare case and you ALWAYS should dispose the Graphics afterwards.
Think of a Graphics like a piece of paper which you draw on to show how the Component looks like at that moment. After you've drawn it, the framework toolkit will trim off the edges and show what you've drawn to display the component. Moreover, the next time you draw the component, you'll be drawing on a different piece of paper, so don't keep the old Graphics around.