Currantly trying to make a simple game but i cant get the renderer to repaint. My problem is that i used to make everything in 1 class... much simpler.

But now im just not sure anymore.

This is what i have so far... it just wont repaint. (Note i have a thread for now that calls the renderer in my gamethread class... the last class posted. )

my main class...

package ballgame;

import javax.swing.JFrame;

/**
 *
 * @author jonathan
 */
public class BallGame {

    public static Renderer gamePainter;
    public static BallGame ballGame;
    public static Dimensions dimensions;
    public static GameThread gameThread;
    private final int WIDTH;
    private final int HIEGHT;

    public BallGame() {

           gamePainter = new Renderer();

        WIDTH = dimensions.getWIDTH();
        HIEGHT = dimensions.getHIEGHT();

        JFrame jframe = new JFrame("Ball Game");
        jframe.setResizable(false);
        jframe.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        jframe.setSize(WIDTH, HIEGHT);
        jframe.setVisible(true);

        jframe.add(gamePainter);

    }

    public static void main(String[] args) {

        //initializing components

         dimensions = new Dimensions();
        ballGame = new BallGame();      
        gameThread = new GameThread();

    }

}

Than here i have my Renderer class

package ballgame;

import java.awt.Graphics;
import javax.swing.JPanel;

/**
 *
 * @author jonathan
 */
public class Renderer extends JPanel {

    @Override
    protected void paintComponent(Graphics g) {

        super.paintComponent(g); //To change body of generated methods, choose Tools | Templates. 

        BallGame.gameThread.repaint(g);    
    }    
}

And Finally here i have my GameThread class
kinda where i wanna paint everything... I also want to make a another class called ball class... what will make many balls...
starting with 1 and the user has to keep them from hitting the floor, any how... here is the class

/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package ballgame;

import static ballgame.BallGame.dimensions;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Rectangle;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import static java.util.concurrent.TimeUnit.MILLISECONDS;
import javax.swing.JPanel;

/**
 *
 * @author jonathan
 */
public final class GameThread extends JPanel {

    private final int HIEGHT;
    private final int WIDTH;
    private final int paddleHieght;
    private final int paddleWidth;
    private Rectangle paddle;

    public GameThread() {

        WIDTH = dimensions.getWIDTH();
        HIEGHT = dimensions.getHIEGHT();
        paddleHieght = dimensions.getPaddleHIEGHT();
        paddleWidth = dimensions.getPaddleWidth();
        paddle = new Rectangle(WIDTH / 2 - paddleWidth / 2, HIEGHT - paddleHieght, paddleWidth, paddleHieght);

        new ScheduledThreadPoolExecutor(1).
                scheduleAtFixedRate(this::gameLoop,
                        0, 30, MILLISECONDS);
    }

    void gameLoop() {

        updatePositions();
        BallGame.gamePainter.repaint();
    }

    void updatePositions() {
//here i will be updating all the positions of the rectangles... and circles... based on user input

    }

    void repaint(Graphics g) {
System.out.println("in here");
        g.setColor(Color.CYAN);
        g.fillRect(0, 0, WIDTH, HIEGHT);
        paintPaddle(g);
    }

    void paintPaddle(Graphics g) {
        g.setColor(Color.black);
        g.fillRect(paddle.x, paddle.y, paddle.width, paddle.height);
    }

}

Any Help would be appreciated.

Thanks

Recommended Answers

All 9 Replies

it just won't repaint ... that's a pretty vague description.
what does it do? do you get error messages? does it compile?

what behaviour exactly did you expect, and which lines of code did you expect to be executed, but appear not to be?

did you debug your code?

commented: I expected the repaint method to be called every 30ms via the renderer +3

Like he said... plus specifically: do you get "in here" printed 30 times per sec?

ps: I don't think ypur class structure is helping here... your GameThread class
1) Isn't a thread
2) Extends JPanel, but is never visible
3) Has lots of painting code that belongs in Renderer

It looks like maybe you have created classes to answer "where can I put this code?" rather than "what are the objects and their responsibilities needed for this application?"

For a Java Swing game the answer is almost always some kind of MVC as in (just an example):

class Model - represents the state of the game, update method moves it on one time unit.
class Renderer - extends JPanel, paintComponent displays the current state of the Model
class WIndow - extends JFrame, contains a Renderer plus buttons & controls as required, responds to keyboard.
class Controller - ties it all together, creates Window, Model and Renderer, starts a timer to update Model and repaint Renderer.

If you do that kind of design first then the resulting code will be much cleaner.

ok, james, ill do that, ill redo everything with the classes making sense and stuff, but first i want to know why it isnt painting.

No error codes are appearing...

The in here is not being printed....( in the repaint method)

but the game loops is being called every 30ms... which calls the renderer which should call the repaint method???

also james, so all my painting methods can go in my renderer/gamepainter class??

Also what do you mean by number 2?? it is never visible?

Thanks you guys so much for your help so far.

I'm not sure, but my best guess is...
you add gamePainter to the JFrame after makingthe JFrame visible and don't pack() or invalidate() or anything else, so gamePainter may well be invisible, or maybe 0 pixels by 0 pixels. So when you call repaint for gamePainter Swing decides there is nothing visible to repaint, so it never calls its paintComponent.

number 2: you have two JPanels, only one of which is added to the JFrame, both of which contain Swing paint code. They are also tighly coupled so it's anybody's guess as to what is really going on and what will or won't be painted where.

My advice would be to fix that structural problem, and waste no time trying to debug or fix the current code. You need exactly one JPanel to display your game, and that should contain all the graphics drawing code. (Except later, when you have a Ball class, then maybe the JPanel will delegate drawing Balls to the Balls class.) And add it to the JFrame before pack() and setVisible

ok im going to totally redo it.

But anyhow, i just added the renderer to before I set the frame visible....
and now its working... its just the first time the renderer gets called i get a null pointer and than its fine...
Why would this be??

Note, I also took away the extends JPanel on the Gamethread class, so now i should only have 1 jpanel correct?? because everytime i do this it creates a new one??

This is the error im getting... only the first time...

When its prints out Here its in the renderer... and when it prints out In Here
it is in the repaint method in the gamethread class.

Exception in thread "AWT-EventQueue-0" java.lang.NullPointerException
    at ballgame.Renderer.paintComponent(Renderer.java:18)
    at javax.swing.JComponent.paint(JComponent.java:1056)
    at javax.swing.JComponent.paintChildren(JComponent.java:889)
    at javax.swing.JComponent.paint(JComponent.java:1065)
    at javax.swing.JComponent.paintChildren(JComponent.java:889)
    at javax.swing.JComponent.paint(JComponent.java:1065)

here

    at javax.swing.JLayeredPane.paint(JLayeredPane.java:586)
        at javax.swing.JComponent.paintChildren(JComponent.java:889)
        at javax.swing.JComponent.paintToOffscreen(JComponent.java:5217)
        at javax.swing.RepaintManager$PaintManager.paintDoubleBuffered(RepaintManager.java:1579)
        at javax.swing.RepaintManager$PaintManager.paint(RepaintManager.java:1502)
        at javax.swing.RepaintManager.paint(RepaintManager.java:1272)
        at javax.swing.JComponent.paint(JComponent.java:1042)
        at java.awt.GraphicsCallback$PaintCallback.run(GraphicsCallback.java:39)
        at sun.awt.SunGraphicsCallback.runOneComponent(SunGraphicsCallback.java:79)
        at sun.awt.SunGraphicsCallback.runComponents(SunGraphicsCallback.java:116)
        at java.awt.Container.paint(Container.java:1975)
        at java.awt.Window.paint(Window.java:3904)
        at javax.swing.RepaintManager$4.run(RepaintManager.java:842)
        at javax.swing.RepaintManager$4.run(RepaintManager.java:814)
        at java.security.AccessController.doPrivileged(Native Method)
        at java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:76)
        at javax.swing.RepaintManager.paintDirtyRegions(RepaintManager.java:814)
        at javax.swing.RepaintManager.paintDirtyRegions(RepaintManager.java:789)
        at javax.swing.RepaintManager.prePaintDirtyRegions(RepaintManager.java:738)
        at javax.swing.RepaintManager.access$1200(RepaintManager.java:64)
        at javax.swing.RepaintManager$ProcessingRunnable.run(RepaintManager.java:1732)
        at java.awt.event.InvocationEvent.dispatch(InvocationEvent.java:311)
        at java.awt.EventQueue.dispatchEventImpl(EventQueue.java:756)
        at java.awt.EventQueue.access$500(EventQueue.java:97)

in here

at java.awt.EventQueue$3.run(EventQueue.java:709)
        at java.awt.EventQueue$3.run(EventQueue.java:703)
        at java.security.AccessController.doPrivileged(Native Method)
        at java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:76)
        at java.awt.EventQueue.dispatchEvent(EventQueue.java:726)
        at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:201)
        at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:116)
        at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:105)
        at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:101)
        at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:93)
        at java.awt.EventDispatchThread.run(EventDispatchThread.java:82)

I changed this

 new ScheduledThreadPoolExecutor(1).
                scheduleAtFixedRate(this::gameLoop,
                        0, 30, MILLISECONDS);

To This

 new ScheduledThreadPoolExecutor(1).
                scheduleAtFixedRate(this::gameLoop,
                        1000, 30, MILLISECONDS);

  It now waits 1 seconds before the threads starts looping
becuase i thought maybe it wasnt initialized... but it still gets the same error after starting 1st time...

first time the renderer gets called i get a null

You create a BallGame before you create a GameThread, but BallGame creates the window and makes it visible, which causes a call to Renderer's paintComponent, which tries to call the GameThread, which doesn't exist yet.
Don't try to fix this before you fix the structure, it;s just an artifact of the two JPanels mistake.

You may find this code useful - it's taken from a sample program I wrote to help teach sprite-based animation - but the following excerpt shows one way for the model view and controller to fit together and split up the responsibilities between them (ignore the Sprite references for the moment).

public class Animation2 {
    // Outline class structure demo for multi-sprite animation

    public static void main(String[] args) {
        SwingUtilities.invokeLater(Animation2::new);
    }

    // shared constants
    final AnimationModel model;
    final AnimationView view;
    final int TIMER_MSEC = 16; // mSec per clock tick

    Animation2() {
        model = new AnimationModel(600, 400);

        view = new AnimationView(model);
        view.setPreferredSize(new Dimension(model.width, model.height));

        new MainWindow(this);

        new ScheduledThreadPoolExecutor(1).
                scheduleAtFixedRate(this::updateAnimation, 0, TIMER_MSEC, MILLISECONDS);
    }

    void updateAnimation() {
        model.updateSimulation();
        view.repaint();
    }

    void close() { // tidies up and quits application
        // do any cleanup etc here
        System.exit(0);
    }

}

class AnimationModel {

    final List<Sprite> sprites = new ArrayList<>();
    final int width, height;

    AnimationModel(int width, int height) {
        this.width = width;
        this.height = height;
        sprites.add(new Sprite(this).velocity(2, 1));
        sprites.add(new BouncingSprite(this).
                color(green).size(80, 80).at(0, 250).velocity(7, -10));
        sprites.add(new BouncingHeavySprite(this).
                color(RED).size(60, 60).at(0, 0).velocity(4, 0));
    }

    void updateSimulation() {
        for (Sprite s : new ArrayList<>(sprites)) {
            //use copy of list because Sprites may be deleted in this loop
            s.update(); // ask sprite to update itself
        }
    }

}

class AnimationView extends JPanel {

    final AnimationModel model;

    public AnimationView(AnimationModel model) {
        this.model = model;
    }

    @Override
    public void paintComponent(Graphics g) {
        Graphics2D g2d = (Graphics2D) g;
        g2d.setRenderingHint(KEY_ANTIALIASING, VALUE_ANTIALIAS_ON);
        paintBackground(g2d);
        g2d.setClip(0, 0, model.width, model.height);
        for (Sprite s : model.sprites) {
            s.draw(g2d);// ask the sprite to draw itself at its current position
        }
    }

    void paintBackground(Graphics2D g2d) {
        // could be an image file etc, this is just plain grey
        g2d.setColor(LIGHT_GRAY);
        g2d.fillRect(0, 0, model.width, model.height);
    }

}

class MainWindow extends JFrame {

    private final Animation2 controller;

    MainWindow(Animation2 controller) {
        super("Close window to exit");
        this.controller = controller;
        add(new JScrollPane(controller.view));
        // add other buttons, menus, toolbars etc here
        pack();
        setLocationRelativeTo(null);
        setVisible(true);
    }

    @Override
    public void processWindowEvent(WindowEvent e) {
        if (e.getID() == WindowEvent.WINDOW_CLOSING) {
            controller.close();
        } else {
            super.processWindowEvent(e);
        }
    }

}

Feel free to ask questions!
ps: If you find this useful I can post the complete runnable code so you can see how it works in practice.

thank you very much. I will rewrite the class structure, and look over the code and see, and I will let you know if I have any quistions.

Thanks
Scheppy.

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.