I am making a game in Java and I have a canvas class, which has the game tick, and I draw the images on the canvas using
public void paint(Graphics g) {
// draw stuff here
}
I want to move all the drawing functions to my Engine class.
I have this method in my Engine:
#Override
public void render(Graphics scene) {
// draw stuff here
}
In my Canvas I didn't have to call the paint method, but in the Engine I would have to call the render method, but since it takes as an argument Graphics scene, I am kind of at a loss. How would I be able to draw components from my Engine class(using the render method) and not from the Canvas class.
The engine class does not extend any JComponent, but it does initialize the Canvas object
I am making a game in Java and I have a canvas class, which has the
game tick, and I draw the images on the canvas using
note
public void paint(Graphics g) { for awt.Canvas, awt.Panel
public void paintComponent(Graphics g) { for swing.JComponent, swing.JPanel
any painting could be done only for J/Component, good practicies couldn't be move this methods outside of J/Component declarations
I want to move all the drawing functions to my Engine class. I have
this method in my Engine:
is good idea to prepare all Object before paint/paintComponent is executed,
there to put all Objects to the array
inside paint/paintComponent only loop inside arrays of prepared Obects, invoked from Swing Timer, Mouse/Key events
all events for paiting, to AWT/Swing GUI must be done on Event Dispatch Thread
for todays GUI to use Swing JComponent, JPanel and override paintComponent
a few very good code examples are here, tagged by paintComponent
One problem is that there is no way for Engine to know that paint(g) is called, since the call is run on the canvas. So you will most likely need to modify Canvas.paint so that it calls the Engine. It's possible there is some funky listener you could add to Canvas but I think that's probably more complicated than you need.
One way that would work would be to pass an instance of Engine to the Canvas on construction:
public class Engine {
private Canvas canvas;
public Engine() {
Canvas = new Canvas(this);
}
public void render(Graphics g) {
// do stuff
}
}
public class Canvas extends JPanel {
private Engine engine;
public Canvas(Engine engine) {
this.engine = engine;
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
engine.render(g);
}
}
Related
I have been practicing with java's swing features recently, and in one of my classes that extends the class JPanel, I have overriden the method paintComponent() so that it will paint my BufferedImage onto the JPanel. I also have a method on it to move around. Before this issue, I have had a problem that shows the process of moving as it repaints too quickly. So, I created a boolean variable called available which is set to false when the image is still in the moving process. But, now I see that the screen is taking away the entire image and putting it back, causing it to blink. Here is my basic pseudocode:
class A extends JPanel{
BufferedImage canvas;
public A(){
//create image here
}
public move(){
available = false;
//move things around in here
available = true;
}
#Override
protected void paintComponent(Graphics g){
super.paintComponent(g);
if(available){
g.drawImage(this.canvas, 0, 0, null);
}
g.dispose();
}
}
class B{
public static void main(String[] args){
//construct the class A JPanel
while(some_variable){
class_A_JPanel.repaint();
}
}
}
This is very old topic which is fixed in modern Java. But you prefer old way then use old techniques. For example Double Buffering
I am a beginner when it comes to making Java applets, and for my first applet, I drew a smiley face using paint(). Now, I want to make the smiley face blink. I have managed to get my timers and everything set up, but I need to use the start() method to get the timers going, and it seems that by including other methods, the paint method does not invoke itself. Because of this, I am assuming that I need to invoke paint() from start(), but the problem is I do not know what I am supposed to initialize the Graphics variable to in order to get paint() to actually work.
SSCCE
import java.awt.*;
import javax.swing.*;
import java.applet.Applet;
import java.awt.event.*;
public class Project2_15 extends Applet
{
public void paint(Graphics g)
{
setBackground(Color.lightGray);
}
// This handles the starting of timer execution.
public void start()
{
Graphics g; // What do I initialize this to?
paint(g);
}
// Timer Stuff
ActionListener blinkShut;
public Project2_15(final Graphics g) {
this.blinkShut = new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
g.setColor(Color.black);
}
};
}
}
Here is the code with some corrections:
import java.awt.*;
import javax.swing.*;
import java.applet.Applet;
import java.awt.event.*;
public class Project2_15 extends Applet
{
public boolean wink = false;
Timer timer;
public void paint(Graphics g)
{
super.paint(g);
// Graphics g; // What do I initialize this to? ALREADY INITIALIZED
//paint(g);
if (wink) {
g.drawLine(1,1,100,100);
} else {
g.drawOval(1,1,100,100);
}
}
// This handles the starting of timer execution. NO IT DOES NOT!
// public void start()
#Override
public void init()
{
setBackground(Color.lightGray);
timer = new Timer(250,blinkShut);
}
#Override
public void start() {
timer.start();
}
#Override
public void stop() {
timer.stop();
}
// Timer Stuff
ActionListener blinkShut = new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
wink = !wink;
repaint();
}
};
}
See Performing Custom Painting. It would be the same basic process with applet or frame.
Add a container (Panel/JPanel) to the top-level container.
Override the paint(..) AWT or paintComponent(..) Swing method.
Call super.. as the first statement.
Do the custom painting to the supplied Graphics instance.
Animation can be achieve using a Swing based Timer.
Of course I would tend to replace steps 1) to 4) with painting to a BufferedImage displayed in a JLabel/ImageIcon.
You need your timer to change the state of the applet, suggest that it be repainted, and then have your applet's paint method to react to the state. Some suggestions:
Use a Swing Timer for your timer
Give your applet a non-static boolean field called blink.
In the Timer's actionPerformed method, change the boolean field of the applet, blink, to its opposite state: blink = !blink;
Then call repaint(). This will tell the JVM to possibly repaint the applet.
In your paint(...) method use the state of the blink variable in an if block, and if true paint an eye, if false paint a closed eye.
You're better off using a Swing applet or JApplet.
If you're using a JApplet, then you'll do your painting in a JPanel's paintComponent(...) method, not in the paint method.
Either way, be sure to call the super method as the first method call in your painting method, either super.paint(g) if in the Applet's paint method or super.paintComponent(g) if in a JPanel's paintComponent method. This allows your GUI to erase previous painting.
Edit
Regarding your code:
public void start()
{
Graphics g; // What do I initialize this to?
paint(g);
}
and:
public Project2_15(final Graphics g) {
this.blinkShut = new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
g.setColor(Color.black);
}
};
}
Please throw this code out as you almost never paint this way or call paint directly. Please read or re-read my recommendations above.
Edit 2
Regarding your comments:
So I can't just create a separate timer and put the code in their?
I never said this. Feel free to use a separate timer, and put in decent code inside of it. You of course will have to discard your current code since you do not want to manipulate the Graphics object directly as you're trying to do.
In addition to his eyes blinking, I was also hoping to have his tongue go in and out using a separate timer.
Then go for it!
I'm writing a simple game. I have 3 classes the first one: ball which take care of every thing refereed to it, the second one game which made out of an array of "ball"s and the final one is windows, the one which contains the MAIN thread.
window.paint calls game.draw in order to receive the graphics of the game scene.While the game itself double buffering it in order that the Image object can be moved to the Player's ball location(yet to be implemented).
So my problem caused because I'm creating an Image object but somewhy this initialized to null, thus I get NullPointerException.
Here is the source of the methods which handle the painting:
public class MyWindow extends JFrame {
//...the other code
public void paint(Graphics g){
thegame.draw();
repaint();
}
}
public class Game extends JFrame implements Runnable {
ball[] cellmap;
//...the other code
public void draw(){
Image GameImage = createImage(800,800);
Graphics GameGraphics = GameImage.getGraphics();
for(int i = 0;i<cellmap.length;i++)
cellmap[i].draw(GameGraphics);
g.drawImage(GameImage, 0, 0, this);
}
}
public class Ball extends JFrame {
//...the other code
public void draw(Graphics g){
g.setColor(Color.red);
g.fillOval((int)(this.x+this.radious),(int)(this.y+this.radious),
(int)this.radious,(int)this.radious);
}
}
1) please read Java Naming Conventions
2) not good idea paint directly to the JFrame, put your painting to the JComponent, JLabel, JPanel
3) for Painting in Swing use method paintComponent, please not methods paint(Graphics g) or draw(Graphics g)
4) if you want to delay or animate you painting use javax.swing.Timer
I have a JComponent with a listener on it. On the JComponent, I draw a big image and the mouse listener adds small images where clicks occur (one big map on which I add some dots).
How can I programatically draw something outside the paintComponent method?
public void paintComponent(Graphics g) {
Graphics2D g2 = (Graphics2D) g;
g2.drawImage(img1, 0, 0, this);
g2.finalize();
}
private MouseListener listener;
public void initListener() {
myCanvas = this;
listener = new MouseAdapter() {
public void mouseClicked(MouseEvent e) {
myCanvas.getGraphics().drawImage(img,e.getX(),e.getY(), myCanvas);
}
};
addMouseListener(listener);
}
My problem is with this:
public void drawDot(int x, int y){
myCanvas.getGraphics().drawImage(img, x, y, myCanvas);
}
It doesn't do anything. I have tried repaint().
You can't do this. All drawing occurs in the paintComponent() method. What you should do is build a model that represents what you want to draw, and modify the model in your mouse listener. Then call repaint() to ask that this component be redrawn when the model is modified. Inside your paint() method render the full paint from the model. For example:
List<Point> pointsToDrawSmallerImage = new ArrayList<Point>();
...
listener = new MouseAdapter() {
public void mouseClicked(MouseEvent evt ) {
pointsToDrawSmallerImage.add( evt.getPoint() );
repaint();
}
}
...
public void paintComponent(Graphics g) {
g.clear(); // clear the canvas
for( Point p : pointsToDrawSmallerImage ) {
g.drawImage(img, p.x, p.y, myCanvas);
}
}
You have to manage the drawing inside the paintComponent method. Java Graphics is not stateful, you have to take care of what you actually need to draw whatever you want inside the method. Every time the paint method is called, everything must be drawn again, there is nothing that "stays" on the canvas while adding other components
This means that you should store a list of elements that the paint method will take care to draw, eg. ArrayList<Point> points, then in paint method you should iterate them:
for (Point p : points)
draw the point
so that you just add the point to the list with the listener and call repaint.
You can find guidelines for Swing/AWT drawing here..
A particual API has the behavior you would like to have though, it is called Cocos2D and it has a port for Android/Java that you can find here.
that is not how draw works, the draw method paints everything which is in the method itself on every repaint,
that means if you call a method to draw something once, it will only be drawed for one repaint cycle and that's it.
if you want something t be drawn on click you have to add it on on click to a collection and draw the whole collection in every paint cycle, so it will stay permanently.
In the main method, I am trying to run this:
public static void main(String[] args)
{
game.paintBlocks(g);
}
And getting a "cannot be resolved to variable" error for the "g" parameter.
Elsewhere I have this, which calls on another method in another class (paint(g)) to paint a grid of blocks:
public void paintBlocks(Graphics g)
{
for (int r = 0; r<7; r++)
{
for (int c = 0; c<5; c++)
{
block[r][c].paint(g);
}
}
Do I need to tell it that "g" is in another class? I'm new to this and any help would be awesome!
Where do you want to paint to? I'm assuming you probably want to paint to a window on the screen, in which case you won't be calling paint* yourself, you'll let the Swing framework call it at the appropriate times. In this case, if game is a JFrame, then you just need to make it visible; or if game is some other type of component then you'll need to add it to a visible window. This is the pattern I normally use when I'm teaching basic graphics in Java:
public class MyGame extends JPanel {
public static void main() {
JFrame window = new JFrame("My Game");
window.add(new MyGame());
window.pack();
window.setLocationRelativeTo(null); // Centers the window on the screen
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
window.setVisible();
}
public Dimension getPreferredSize() {
return new Dimension(800, 600);
}
protected void paintComponent(Graphics g) {
// Do my drawing here
}
}
If you want to paint to an off-screen image, then you'll need to create your own graphics context to pass to the paint* methods:
BufferedImage hello = new BufferedImage(game.getWidth(), game.getHeight(), BufferedImage.TYPE_INT_ARGB);
Graphics g = hello.getGraphics();
game.paintBlocks( g );
In the case of paintBlocks, g is a parameter that is being passed in to the method. In the case of main, g is referencing a variable that hasn't been created anywhere.
Graphics and Graphics2D are abstract classes, and aren't generally intended to be instantiated except by Swing. What Graphics and Graphics2D give you is a context for drawing on a component (like a JPanel or a BufferedImage).
Based on your description you probably want to draw blocks on a Swing component of some kind. (Though it's a little unclear, that would be a normal kind of thing to do.) What you would normally want to do if you are drawing the blocks on a JPanel, for example, is to create a class that extends JPanel and override the paintComponent() method. One way you might do that:
public class BlocksPanel extends JPanel {
// Normal class fields, etc.
// ...
// I would consider making this private, but this is your method from above:
public void paintBlocks(Graphics g) {
for (int r = 0; r<7; r++) {
for (int c = 0; c<5; c++) {
block[r][c].paint(g);
}
}
}
#Override
public void paintComponent(Graphics g) {
paintBlocks(Graphics g);
}
}
There is another example that might help you on page 9 of this document. The Java Tutorials for the Java 2D API may also help.
The variable g is undefined in the main context because you havent declared/initialised it. If you look at your paintBlocks(Graphics g) method, g is passed as a parameter, however the scope of that variable(g) is within the braces({}) of the method paintBlocks(Graphics g).
If you have a class called MyClass that extends a Component, say JPanel, you can do something like this:
class MyClass extends JPanel
{
public static void main(String[] args)
{
Graphics g = getGraphics(); //would return the graphics object for the JPanel
game.paintBlocks(g);
}
}
Its also good to note that the above method in some cases would be tagged as bad programming style. There is an alternative. You can make use of the paintComponent(Graphics g) method provided by the component.
Your main would then look like this:
public static void main(String[] args)
{
repaint(); //this repaints the component, calling the paintComponent method
}
Its also bad programming style to call paintComponent(Graphics g) yourself. You should allow your system to call that method and thats why you have the repaint() method. The system automatically invokes paintComponent(Graphics g) when you repaint.
From paintComponent(Graphics g) you can then do this:
public void paintComponent(Graphics g)
{
super.paintComponent(g); //repainting the panel,not necessary in some cases
game.paintBlocks(g); //passing the graphics object used by the component
}
Hope that helped!