0

I'm currently working on my first game using Java. I have a red square (the player) in a grid of stone blocks. The game is looking like this: https://i.stack.imgur.com/P6qSE.png

After I accomplished this, I moved on to trying out the KeyListener. I also have a Player class with a few variables and a paint funcition. Here is the Player class:

import java.awt.Color;
import java.awt.Graphics;

public class Player {
    int width, height;
    int x = 300;
    int y = 300;
    
    public Player() {
        width = 25;
        height = 25;
    }
    
    public void drawPlayer(Graphics g) {
        g.setColor(Color.red);
        g.fillRect(x, y, width, height);
    }
}

And here is the KeyListener:

public void keyPressed(KeyEvent e) {
    if(e.getKeyCode() == KeyEvent.VK_W) {
        player.y += 25;
        repaint();
    } else if(e.getKeyCode() == KeyEvent.VK_A) {
        player.x -= 25;
        repaint();
    } else if(e.getKeyCode() == KeyEvent.VK_S) {
        player.y -= 25;
        repaint();
    } else if(e.getKeyCode() == KeyEvent.VK_D) {
        player.x += 25;
        repaint();
    }
    
    System.out.println(player.x + ", " + player.y);
    
}

As you can see at the bottom of the keyPressed function, I added: System.out.println(player.x + ", " + player.y); to make sure the KeyListener was working well (it is).

Now, since everything is working, last step was repainting the JPanel to update the players position. This is that code:

@Override
public void paint(Graphics g) {
    super.paint(g);
    
    stone.drawStone(g);
    player.drawPlayer(g);
    
    g.setColor(Color.gray);
    for (int j = 0; j < Display.WIDTH; j += tileSize) {
        for (int i = 0; i < Display.HEIGHT; i += tileSize) {
            g.drawRect(i, j, tileSize, tileSize);
        }
    }
}

In theory, my player should update position (since I already did repaint() in the KeyListener), but it doesn't. My player doesn't move. What could this be? I'm guessing it has something to do with repainting the JPanel but I don't know how to fix this. Do you have any suggestions? Thanks in advance.

Side note: I have 4 main classes, Player, Game (here is where 95% of the code came from), Display (JFrame), and Main (draws JFrame and starts gameloop (but im not currently using a gameloop)).

camickr
  • 321,443
  • 19
  • 166
  • 288
alexholstv
  • 146
  • 1
  • 10
  • 1
    *In theory, my player should update position (since I already did repaint() in the KeyListener)* - don't give us the theory. Give us the facts. Do some debugging to tell us where the problem is and then we can suggest a solution. Does the code in your KeyListener even get executed? Does the repaint() method get invoked? My guess is that is doesn't. See: [Motion Using the Keyboard](https://tips4java.wordpress.com/2013/06/09/motion-using-the-keyboard/) for issues with using a KeyListener and the better solution which is to use Key Bindings. – camickr May 25 '21 at 22:25
  • I can confirm the keylistener works fine. I tried it out printing something like "Works" after each key press. I'm not sure how I could test the repaint() method. I assume its working since I see my player and the surrounding blocks? But, I tried something out like using a variable called wKey, and setting that to true when I press the w key. And in the paint method, add an if statement to print something out when the key is pressed, but it doesn't print (although I haven't removed the repaint() line in the keylistener). Not sure if this makes complete sense, but I hope this helped. – alexholstv May 25 '21 at 23:05
  • Did you add code in the Player class to see what the x/y values are when the drawPlayer() method is invoked? Why would you paint the board AFTER you paint the player. Should the Player be painted on top of the board? I understand that you are using only a drawRect() and not a fillRect(), but isn't it logical to paint the board before the players are added to the board. You code should reflect the logic. Maybe in the future you will want to use fillRect() and the code will be broken. – camickr May 25 '21 at 23:15
  • We don't know what you are invoking repaint() on. Post a proper [mre] if you need more help. – camickr May 25 '21 at 23:17
  • I tried printing the players x and y in the Player class. It just prints 300 and 300 (that's the x and y I gave it at the start). Yes, the Player should be printed on top of the board. Actually, its more like 3 layers: the stone blocks, the player and finally the grid (with drawRect and not fill rect). The player should be on top of the blocks but not on top of the grid. I will work on posting a minimal reproducible example right now. – alexholstv May 26 '21 at 01:08
  • Well if your KeyLIstener code executes and updates the players x/y value, but the value doesn't change when you paint it, that would indicate you have multiple instances of your Player object. Without an [mre] we can't guess why. – camickr May 26 '21 at 02:54
  • Hi, finished the minimal reproducible example. Sorry it took so long, I had to do homework and sleep. Here's the least amount of code you'd need to reproduce the problem: https://pastebin.com/bPuFTW6r Hope this helps! – alexholstv May 26 '21 at 12:58
  • Code should be posted in the forum and replace all the other code so people only look at one set of code to reduce confusion. – camickr May 26 '21 at 14:26

1 Answers1

1

As I suggested in my comment you are creating multiple instances of your classes all over the place.

Your Frame class has plenty of issues:

public class Frame extends JFrame {
private static Game game = new Game();

    public Frame() {
        JFrame frame = new JFrame();
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setPreferredSize(new Dimension(640, 640));
        frame.pack();
        frame.setLocationRelativeTo(null);
        frame.setResizable(false);
        frame.setVisible(true);
        frame.addKeyListener(new Game());
        frame.setFocusable(true);
        frame.setContentPane(game);
    }
  1. Don't use static variables. Note how you have a static variable for the "Game". The you add a KeyListener to a second instance of the "Game" class. So your KeyListener does nothing, because that instance of the Game class is never added to the frame.

  2. the frame needs to non-resizable BEFORE you pack() the frame.

  3. components need to be added to the frame BEFORE you pack() the frame.

  4. don't set a preferred size on the frame. Your Game class should override the getPreferredSize() method to return the size of the panel. This class is responsible for doing the painting, so only it knows what it preferred size should be. Also a frame has decorations (the titlebar and borders) so you game panel will not be 640x640 as you expect.

The job of the Frame class should be to simply create the JFrame and add the Game panel to the frame. All other Game logic will be contained in the Game and your other classes. Your Game class with add the KeyListener to itself in the constructor of your class.

Check out: get width and height of JPanel outside of the class for an example of this design structure. It basically does what you need except is doesn't have the KeyListener code.

camickr
  • 321,443
  • 19
  • 166
  • 288
  • Yup, this was it. Thanks a lot. One question though, why shouldn't I use static variables? Do you mean this only in this context or should I stop using them in general? – alexholstv May 26 '21 at 19:40
  • Definitely not in this context. There is no need for a variable of any kind. – camickr May 26 '21 at 21:12