0

I've been trying to make a java game loop in canvas that runs render(Graphics g) and logic() but it isn't working.

I have already tried making a script that runs the two functions then calls itself again to create a loop.

Thanks in advance.

I tried this:

public class Canvas extends JPanel {
static GraphicsDevice gd = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice();
static int width = gd.getDisplayMode().getWidth();
static int height = gd.getDisplayMode().getHeight();
//Get the system time
long lastTime = System.nanoTime();
//Specify how many seconds there are in a minute as a double
//store as a double cause 60 sec in nanosec is big and store as final so it can't be changed
final double ticks = 60D;
//Set definition of how many ticks per 1000000000 ns or 1 sec
double ns = 1000000000 / ticks;    
double delta = 0;

public static void main(String[] args) {
    JFrame frame = new JFrame("Zombie Run");
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    frame.add(new Canvas());
    frame.setSize(width, height);
    frame.setVisible(true);
    loop();
}

public static void loop() {
    logic();
    render(g);
    loop();
}
public static void render(Graphics g) {
    System.out.println("Running Render");
    g.setColor(Color.BLUE);
    g.fillRect(0, 0, 800, 500);

}
public static void logic() {
    System.out.println("Logic");

}

}

The error I got from eclipse is:

Logic
Running Render
Exception in thread "main" java.lang.NullPointerException
    at ZombieGame.Canvas.render(Canvas.java:41)
    at ZombieGame.Canvas.loop(Canvas.java:36)
    at ZombieGame.Canvas.main(Canvas.java:31)
Andrew Thompson
  • 168,117
  • 40
  • 217
  • 433
Jacob Goddard
  • 17
  • 1
  • 3
  • 2
    You could use a [Swing `Timer`](https://docs.oracle.com/javase/tutorial/uiswing/misc/timer.html). If that doesn't help, please post what you've tried in form of a [MCVE]. – Lukas Rotter Aug 31 '16 at 09:32
  • "a script that runs the two functions then calls itself again to create a loop" - a script should not be in java code. If its a method that calls itself you are on the way runnning into a stackoverflow. I wonder if you havn't heard of the mighty `while` loop. If not than don't try to write computer games. – ArcticLord Aug 31 '16 at 09:39
  • Yes i did try the while true loop but it threw an error and wouldn't work – Jacob Goddard Aug 31 '16 at 09:51

2 Answers2

1

You could use a Swing Timer. A while-loop and Thread.sleep would just cause the GUI to freeze.

I made an example using the Timer class. It creates a timer with the delay of 42 milliseconds. Every time after that delay, it performs the code inside the ActionListener. The example paints random rectangles onto the panel, kind of like that one linux screen saver :P I don't think you have to worry about how I specifically did that too much, it's just to illustrate how a Timer can be used.

import java.awt.Color;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Random;

import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;

public class Foo {

    public static void main(String[] args) throws IOException {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                new Foo().createAndShowGUI();
            }
        });
    }

    public void createAndShowGUI() {
        DrawingPanel panel = new DrawingPanel();

        Timer timer = new Timer(42, new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                panel.addNewRectangle();
                panel.repaint();
            }
        });
        timer.start();

        JFrame frame = new JFrame("Example");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setContentPane(panel);
        frame.setSize(800, 600);
        frame.setVisible(true);
    }

    private class GameRectangle extends Rectangle {
        private Color color;

        public GameRectangle(int x, int y, int width, int height, Color color) {
            setBounds(x, y, width, height);
            setColor(color);
        }

        public void draw(Graphics2D context) {
            context.setColor(getColor());
            context.fill(this);
        }

        public Color getColor() {
            return color;
        }

        public void setColor(Color color) {
            this.color = color;
        }
    }

    private class DrawingPanel extends JPanel {
        private final Random RANDOM = new Random();
        private ArrayList<GameRectangle> rectangles;

        public DrawingPanel() {
            setRectangles(new ArrayList<>());
        }

        @Override
        public void paintComponent(Graphics g) {
            super.paintComponent(g);
            Graphics2D g2 = (Graphics2D) g.create();
            for (GameRectangle rectangle : getRectangles()) {
                rectangle.draw(g2);
            }
            g2.dispose();
        }

        public void addNewRectangle() {
            getRectangles().add(createRandomRectangle());
        }

        private Color createRandomColor() {
            return new Color(RANDOM.nextInt(255), RANDOM.nextInt(255), RANDOM.nextInt(255));
        }

        private GameRectangle createRandomRectangle() {
            return new GameRectangle(RANDOM.nextInt(getWidth()), RANDOM.nextInt(getHeight()), RANDOM.nextInt(420),
                    RANDOM.nextInt(420), createRandomColor());
        }

        public ArrayList<GameRectangle> getRectangles() {
            return rectangles;
        }

        public void setRectangles(ArrayList<GameRectangle> rectangles) {
            this.rectangles = rectangles;
        }
    }

}
Lukas Rotter
  • 4,158
  • 1
  • 15
  • 35
  • Thanks but could you alter it to just display one square because i don't really need multi colored square spam – Jacob Goddard Aug 31 '16 at 10:08
  • @JacobGoddard Well, it's just an example I provided which demonstrates how a timer works. If you understand the basics of that, you can also achieve the thing you're trying to do with it. Just put your code of the `loop` method inside the `actionPerformed` method of a timer, without the recursive call, of course. – Lukas Rotter Aug 31 '16 at 10:12
0

Use java.lang.Thread and java.lang.Runnable.

Implement Runnable in your class and create a thread. Remember to implement the run() method.

public class Canvas extends JPanel implements Runnable{
    Thread mainThread = new Thread(this); //Creates a new thread with Canvas runnable object.

    //code

    @Override
    public void run(){}
}

So, in the main method we start the thread.

public static void main(String[] args){
    //your frame creation and initialization code.

    mainThread.start(); //starts the thread.
}

So you've started the thread. Now the thread will enter in the run() method.

boolean isStarted = false;
public void run(){
    isStarted = true;
    while(isStarted){
        logic();
        render(g);
    } /* The thread will return in this loop each has finished a task */
      // It's useless to call this method because the thread does all.
      // Leave it to the thread.
}

Use threads because swing isn't thread-safe!!!!!!!!

NullPointerException is throwed because Graphics are not initialized.

Frank Soll
  • 420
  • 1
  • 4
  • 15