1

I decided I wanted to experiment with making a game and I like Java, so I started following a tutorial here. I did deviate from the video a few times when I felt it was cleaner while being synonymous with the tutorial's code, but not in any way I thought would affect how the code worked. For example, it made sense to me that there should only ever be one instance of the Renderer, and Object Registry so I made them Singletons.

The code I have so far is supposed to create a window with a black background, and a blue square in the middle of the window representing the player, that much is working. However, it should also be sliding around in response to the wasd keys, and even more slowly drifting in one direction regardless. Instead it's doing nothing.

I spent no less than an hour trying to figure out why it wasn't working. It seems to be ticking just fine and the data looks like it's updating properly, but even though my render methods are also being called, the screen just isn't changing.

This is all the code I have so far, since I'm out of ideas as to the problem. I apologize for listing a whole project.

public class Game implements Runnable {
    private final Thread thread;
    private boolean running = false;

    Game() {
        thread = new Thread(this);
    }

    public static void main(String[] args) {
        Game game = new Game();
        game.start();
        new Player(600,450,0));
    }

    private void start() {
        thread.start();
        running = true;
    }

    @Override
    public void run() {
        double tps = 10.0;
        double nsPerTick = 1000000000 / tps;
        double delta = 0;
        int frames = 0;
        long timer = System.currentTimeMillis();
        long lTime = System.nanoTime();
        long now;
        while (running) {
            now = System.nanoTime();
            delta += (now - lTime) / nsPerTick;
            lTime = now;
            while (delta >= 1) {
                Registry.getInstance().tick();
                delta--;
            }
            if (running) Renderer.getInstance().run();
            frames++;

            if (System.currentTimeMillis() - timer > 1000) {
                timer += 1000;
                System.out.println("FPS: " + frames);
                frames = 0;
            }

        }
        stop();
    }

    private void stop() {
        try {
            thread.join();
        } catch (Exception e) {
            e.printStackTrace();
        }
        running = false;
    }

}
public class Renderer extends Canvas {
    private static final Renderer renderer = new Renderer();
    private final Window window;
    private final BufferStrategy bs;
    private final Graphics g;
    boolean black = true;

    private Renderer() {
        window = new Window(1200, 900, "First Game", this);
        this.createBufferStrategy(2);
        bs = this.getBufferStrategy();
        g = bs.getDrawGraphics();
        addKeyListener(Controller.getInstance());
    }

    public static Renderer getInstance() {return renderer;}

    public void run() {
        g.setColor(Color.BLACK);
        //this was to see if even the background would update, it wouldn't
        //g.setColor(black ? Color.BLACK : Color.WHITE);
        //black = !black;
        g.fillRect(0,0,1200, 900);

        Registry.getInstance().render();

        g.dispose();
        bs.show();
    }

    public Graphics getGraphics() {return g;}

    private static class Window extends Canvas {
        private Window(int width, int height, String title, Renderer renderer) {
            JFrame frame = new JFrame(title);
            frame.setPreferredSize(new Dimension(width, height));
            frame.setMinimumSize(new Dimension(width, height));
            frame.setMaximumSize(new Dimension(width, height));
            frame.setResizable(false);
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            frame.setLocationRelativeTo(null);
            frame.add(renderer);
            frame.setVisible(true);
        }
    }

}
public class Registry {
    private static final Registry reg = new Registry();
    private final LinkedList<GameObject> objects = new LinkedList<>();

    private Registry() {

    }

    public static Registry getInstance() { return reg; }

    public void tick() { //System.out.println("tick");
        objects.forEach(GameObject::tick); }

    public void render() { objects.forEach(GameObject::render); }

    public void add(GameObject gameObject) { objects.add(gameObject); }

    public void remove(GameObject gameObject) { objects.remove(gameObject);}

}
public class Controller extends KeyAdapter {
    private static final Controller controller = new Controller();
    private final HashMap<Character,Boolean> keyStates = new HashMap<>();

    private Controller() {

    }

    public static Controller getInstance() {
        return controller;
    }

    public void keyPressed(KeyEvent e) {
        if (!keyStates.getOrDefault(e.getKeyChar(), true)) System.out.println(e.getKeyChar() + " down");
        keyStates.put(e.getKeyChar(),true);
    }

    public void keyReleased(KeyEvent e) {
        keyStates.put(e.getKeyChar(),false);
        System.out.println(e.getKeyChar() + " up " + keyStates.size());
    }

    public boolean isKeyDown(char c) {return keyStates.getOrDefault(c,false);}

}
public abstract class GameObject {
    protected Graphics graphics = Renderer.getInstance().getGraphics();
    protected final ObjectType type;
    protected float x,y,r;

    protected GameObject(ObjectType objectType, float x, float y, float r) {
        this.type = objectType;
        this.x = x;
        this.y = y;
        this.r = r;
        Registry.getInstance().add(this);
    }

    public abstract void tick();
    public abstract void render();

    public void destroy() { Registry.getInstance().remove(this); }

    public float getX() { return x; }
    public void setX(float x) { this.x = x; }
    public float getY() { return y; }
    public void setY(float y) { this.y = y; }
    public float getR() { return r; }
    public void setR(float r) { this.r = r; }
}
public class Player extends GameObject {
    private final Controller controller;

    public Player(float x, float y, float r) {
        super(ObjectType.PLAYER, x, y, r);
        controller = Controller.getInstance();
    }

    @Override
    public void tick() {
        this.x += 1;
        if (controller.isKeyDown('w')) x += 2;
        if (controller.isKeyDown('a')) y -= 2;
        if (controller.isKeyDown('s')) x -= 2;
        if (controller.isKeyDown('d')) y += 2;
    }

    @Override
    public void render() {
        graphics.setColor(Color.BLUE);
        graphics.fillRect((int) (this.x-12),(int) (this.y-12), 24,24);
    }
}
PotatoCode
  • 53
  • 1
  • 6
  • Oracle has a helpful tutorial, [Creating a GUI With JFC/Swing](https://docs.oracle.com/javase/tutorial/uiswing/index.html) that will help you develop a proper Swing GUI. Skip the Netbeans section. Random video tutorials can lead you into all kinds of problems, and as you've found out, almost no one can help you. – Gilbert Le Blanc Jul 12 '21 at 04:22

1 Answers1

1

The problem lies in your handling of Graphics. There's only ONE active Graphics object you can effectively address.

Refactor your code, so that you pass the current Graphics object via parameter through the target methods (like here: Player.render() should become Player.render(Gpahics g). And get rid of the Gameobject.graphics member variable. That is the culprit.

Adding to that, the best way to do simple rendering is to override the paint(Graphics g) method or the paintComponents(Graphics g), and from outside call repaint() on the JPanel/Canvas. This way the UI instigates drawing itself, takes care of the actual frequency, and draws default components/design too if there is some.

JayC667
  • 2,418
  • 2
  • 17
  • 31
  • 1
    I strongly recommend only overriding the `paintComponent` method rather than `paint` method. There are a lot more pitfalls working directly with paint especially if you are layering components or working with borders etc. See here for more info: https://stackoverflow.com/questions/15103553/difference-between-paint-and-paintcomponent – sorifiend Jul 12 '21 at 04:53
  • 2
    Thank you, I misunderstood how the `Graphics` object worked and thought only one instance was needed. I thought it was more like a tool for drawing the canvas but it seems like it's actually the drawing that gets put on the canvas. I'll get to going more in-depth with Java Graphics at some point, but for now I'll see what I can make. Thanks again. – PotatoCode Jul 12 '21 at 18:56