I was wondering if you could help me out with the structure of my program. I'm making a game with Java and I have 2 classes. 1 class is a graphics class that extends JFrame with a paint method:
Graphics Class
paint() and calls various objects that use JFrame as a parameter.
Inside the class is a subclass that implements MouseListener and Mouse Motion Listener which interacts with paint method.
Canvas Class
I was planning to embed the first class into a larger canvas class that would have buttons, panels, and so forth so essentially use the class as a smaller (sub-program) inside a larger program.
Is this possible/ am I doing this the right way. Whenever I try to call the graphic class inside the canvas class, the graphic doesn't turn up.
public Canvas(){
graphic = new Graphic(name);
}
/**
* #param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
Canvas canvas = new Canvas();
canvas.setDefaultCloseOperation(EXIT_ON_CLOSE);
canvas.setSize(1500, 1500);
canvas.setVisible(true);
}
}
Any help/guidance would be appreciated. I just want to mainly to know if I'm thinking about graphic programming correctly.
Canvas is not "Window". In order to display anything on the screen, you components must be added to a window delegate.
I would, personally, avoid overriding the paint methods of top level containers. The main reason for this is top level containers are not double buffered and it makes your application less portable. It's much easier to add a component to other components, a frame is pretty final.
I would also use JPanel over Canvas, simply because JPanel is double buffered.
Related
I'm working on a project where I am displaying a square and a circle.
The circle moves on its own, but the user moves the square via the arrow keys. Whenever the circle touches the square, it rebounds.
Square and circle are different classes (2 different panels). I want to add those two to a frame, one on top of the other such that both are visible.
Can someone tell me how to do so?
JFrame n = new JFrame();
n.setTitle("Background Color for JFrame");
n.setSize(1000,600);
n.setLocationRelativeTo(null);
n.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
n.setResizable(false);
n.add(new Ball());
n.add(new Team());
n.setVisible(true);
User interface components at the same level in a hierarchy are assumed to be non-overlapping by default. You can explicitly work around this by making your components transparent with setOpaque(false), assuming you are taking care to only draw whats needed in the components, e.g. in case of a JPanel ensure that its background is not drawn. Its still somewhat random (implementation dependent) which component has precendence over another when doing this.
There is a component explicitly designed for that: JLayeredPane (https://docs.oracle.com/javase/tutorial/uiswing/components/layeredpane.html), which manages "layers" in which components can be put, giving you full control which overlays which.
Games often implement this by themselves, since the full features of JComponent are not needed to represent a simple graphical element. In that case a single component is used as a "canvas" to paint self-defined objects onto making use of an override of paintComponent (See: https://docs.oracle.com/javase/tutorial/uiswing/painting/)
If you want to do this in swing, as it sounds, I would really recommend making a new class that extends JPanel, and override it's paintComponent method. In this method you use the Graphics in the argument to paint to the canvas. Then you can add this custom panel instead of two separate components to your JFrame, and handle the rendering in there. This render panel can then keep track of all objects that needs to be rendered, preferable implementing some interface (Drawable?) with a draw(Graphics g) method. That way you can make your classes that needs to be rendered implement your Drawable interface, keep then as a list in your render panel and just iterate through them in your paintComponent method and call draw on your Drawables.
I'm currently able to draw rectangles, elipses, and lines in Java by means of adding a component that extends JComponent in which I modify a paintComponent method:
public class myComponent extends JComponent
{
public void paintComponent(Graphics g)
{
/* do simple draw stuff */
}
}
I also know how to have my class extend either JApplet or JPanel and then draw in a paint method:
public class myClass extends JPanel
{
public void paint(Graphics g)
{
/* do simple draw stuff */
}
}
But, both of these methods suffer from not allowing me to pass them parameters. In the case of a multiframe animated sprite, I could conceivably have some external variable that it reads to determine the frame number and then internally draws only the appropriate "sprite" contents based on that frame number, but I'd prefer to be able to pass the frame number to it directly. Unfortunately, not only do I not know where this is called from, I don't know where to the the Graphics g that it requires as input.
There may be a better way to accomplish what I want, to directly communicate with the draw routine to tell it to draw only what I want, whenever I desire, but I don't know how to accomplish this.
If such a method is possible, how would that be done?
If it is better to use the existing paint or paintComponent methods, how can I best pass additional information to them?
Apparently I wasn't clear in what I asked. I wish to have a component or other entity that has its own paintComponent or paint method, inside of which, based on either a frameNumber parameter that is passed to it, or apparently-more-likely, a class property such as frameNumber that it can access, the method determines which frame of a sprite to draw.
Importantly, though, I wish to be able to re-call paint or paintComponent to redraw the sprite when the frame number changes. My big confusion comes in not knowing how to re-call the method, which, to the best of my understanding, only is called when the frame is resized or otherwise redrawn.
So, how can I redraw my component/object/entity, frame-by-frame?
Firstly, don't override paint, use paintComponent.
Secondly, you need to define some kind of model which records the state of all the graphical objects. When paintComponent is called, you then need to render that state. Instead of trying to pass parameters to the paint methods, you should have a method which allows the paint methods to access the model (ie getModel) which is passed to the component at some earlier time.
Then the update engine would update the model and the component would paint that model
For an example ... Use a timer when a key is pressed
I could conceivably have some external variable that it reads to determine the frame number and then internally draws only the appropriate "sprite" contents based on that frame number
Your class would need to have some internal state, i.e. instance variables.
You can then inspect those within the paint method.
Think "member variables":
public class MyClass extends JPanel {
private final int frameNumber;
public MyClass() {
this(0);
}
public MyClass(int f) {
this.frameNumber = f;
}
public void paint(Graphics g)
{
if (this.frameNumber == x) {
/* do simple draw stuff */
}
}
}
I'm trying to create a java game in which balls randomly drop from the top of the screen and we need to catch the balls using a catcher which is located at the bottom of the screen.
I'm having a difficult time figuring out how to actually draw this onto my JFrame.
I've got a class for my 'catcher', 'ball', 'game space' and I would like to put it all together.
How do I draw my 'catcher' onto my screen?
Currently, I have a 'Game' class which looks like this.
public class Game extends JFrame implements KeyListener {
GameScreen gameScreen;
Catcher playerOneCatcher;
public static void main (String[] args) {
new Game();
}
public Game() {
super("CATCH");
setSize(640,480);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setVisible(true);
setLocationRelativeTo(null);
setResizable(false);
addKeyListener(this);
this.gameScreen = new GameScreen();
this.playerOneCatcher = new Catcher(40, 10);
}
I've tried something like this in my Catcher class...
public void paintComponent(Graphics g) {
g.setColor(Color.BLUE);
g.fillRect(positionX, positionY, this.width, this.height);
}
However, its not showing on my screen.
Any help would be greatly appreciated.
You need to rethink your strategy here. Swing is a component framework, with most components intended for building user interfaces. These components are not optimised for what's typically required in games. You want to look into double-buffering, sprites etc. The way to go will be to read up on Graphics2D class (or abandon Swing altogether!)
However answering to your question - if Catcher is a Swing component - you need to add it to the "parent" component, e.g. like this:
this.add(playerOneCatcher);
Same goes to gameScreen but from your snippet it is not obvious what this component is. I hope this helps.
Also, check this out for some ideas: 2D Java Game. Moving sprite above tiled images
Did you call super.paintComponent (g) ? That can cause a few bugs.
Did you call invalidate () or repaint () to repaint the thing you are painting on? I hope you have a special JComponent, and you are not drawing on a JFrame. That is NOT good.
I'm working on recreating "Legend of Zelda a Link to the Past" as part of an assignment.
And I've got the following problem, the game world is a BufferedImage of which I subImage() the part I need for the current location of the player. The game works but uses 80 / 110 percent of the CPU. A profile revealed that the culprit is the drawing of the image.
So I figured I put the background in a separate JPanel from the Player, enemies etc JPanel.
Render them on top off each other (JLayeredPane) and repaint the background panel far less often.
But how do I do this how do I tell swing to draw one panel x times a sec and the other y times? If you have a better way of optimizing let me know.
Here's what I've got at the moment:
public class Main extends JFrame
{
private ZeldaGame game = new ZeldaGame();
private View view = new View(game);
private BackgroundView bckView = new BackgroundView(game);
private Controller ctl = new Controller(game, view, bckView, this);
public Main()
{
setLayout(null);
view.setBounds(0, 0, game.getWidth(), game.getHeight());
bckView.setBounds(0, 0, game.getWidth(), game.getHeight());
JLayeredPane pane = new JLayeredPane();
pane.add(bckView, 1);
pane.add(view, 0);
setLayeredPane(pane);
setSize(game.getWidth(), game.getHeight());
setResizable(false);
setLocationRelativeTo(null);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setVisible(true);
}
public static void main(String[] args)
{
new Main();
}
}
Thank You.
It's not really possible to have different render times because of the way the framebuffer works -- what the result would be is loads of nasty flickering. What you need to do is using Canvas instead of JPanel and JLayeredPane and you can either override the paint() method of the canvas or use bi.getGraphics() and blit the background and the characters on a loop using either of these methods in the correct order. I'd advise using a thin engine like GTGE which will abstract from all the messy details of optimisation. These high level components you're using seriously aren't designed for games, and you shouldn't be using them at all.
Ok I've found the error of my ways. I was using passive rendering while I should have used active rendering. Active rendering basically shuts down the automatic repaints by calling setIgnoreRepaint(true); on the frame and do the loop yourself.
And as an added bonus I don't need to use JPanels.
I'm trying to make a paint editor with Java in which I have a toolbar with the objects that I would like to paste in the canvas. I'm using Swing components to make the GUI, but when I looked for the way of making the canvas, I only found the class canvas from AWT.
Is there any way to make something similar to canvas with Swing? (for example, JPanel?) I have read that using the class canvas from AWT with a GUI made with swing won't work correctly, is that true?
In order to make a custom 'Canvas' in swing you usually write a subclass of a JPanel. Then, you must overwrite the protected paintComponent(Graphics g) method of JPanel.
In the paint method, you can call methods on the Graphics object to actually draw on the JPanel.
As always, the Java Tutorials have a great reference on this to get you started.
You'll probably want to make a subclass of JPanel and implement your own way of painting components you want to draw onto the panel.
The basic approach will probably be along the line of assigning a MouseListener to the subclass of JPanel, then implement painting functionality.
The basic idea may be something along the line of:
class MyCanvas extends JPanel implements MouseListener
{
Image img; // Contains the image to draw on MyCanvas
public MyCanvas()
{
// Initialize img here.
this.addMouseListener(this);
}
public void paintComponent(Graphics g)
{
// Draws the image to the canvas
g.drawImage(img, 0, 0, null);
}
public void mouseClicked(MouseEvent e)
{
int x = e.getX();
int y = e.getY();
Graphics g = img.getGraphics();
g.fillOval(x, y, 3, 3);
g.dispose();
}
// ... other MouseListener methods ... //
}
The above example is incomplete (and not tested -- it definitely won't compile), but it gives an idea about how to implement a MyCanvas class in which a user can click on and draw circles.
The img object is used to hold the image of the canvas. The paintComponent method is used to paint the img object to the canvas. In the mouseClicked method, the Graphics object associated with img is retrieved in order to fillOval onto the image.
Since one the requirements is to paste images onto the canvas, it may be a good idea to hold some Images that you want to paste into the canvas. Perhaps something along the line of:
Image[] myImages; // Used to store images to paint to screen.
Then, in the routine to paint the image onto img stored in MyCanvas:
g.drawImage(myImage[INDEX_OF_DESIRED_IMAGE], 0, 0, null);
By using the drawImage method of the Graphics object, other Images can be drawn onto Images.
As for the question on AWT and Swing, yes, it is true that you do not want to mix components from the AWT and Swing, as they differ in the way they render GUI components. AWT is based on heavyweight components, meaning they native windowing for painting the GUI, while Swing is based on lightweight components, meaning the GUI is drawn by Java itself without using native components.
A good guide on the difference of AWT and Swing is provided in Painting in AWT and Swing article from Sun.
Simply subclass JComponent.
JPanel is an inappropriate class. It is often suggested as it appears to have setOpaque(true) invoked on it automatically. It's actually the PL&F which does that, and whether or not it actually happens is implementation and vendor dependent.
Canvas is a heavyweight component. That is to say that it is controlled by the underlying windowing system. The result is that it will typically be drawn over the top of Swing components, without respect to z-order or clipping (putting it in a scroll pane will give odd behaviour).
You might want to look at the Minueto API. It is a very simple to use graphics api, and you can combine the Java event listening with it to provide your drawing capability.
http://minueto.cs.mcgill.ca/