0

These are the classes I'm working on for a simple game with Java and Graphics

Main:

import javax.swing.*;
import java.awt.*;

public class FrameTest extends JFrame {

    public FrameTest() {
        setSize(800,600);
        setVisible(true);
    }

    public static void main(String[] args) {
        FrameTest frame = new FrameTest();
        GamePanel panel = new GamePanel();
        frame.add(panel);
    }
}

PanelTest:

import javax.swing.*;
import java.awt.*;
import java.awt.event.*;

public class PanelTest extends JPanel {

    private PlayerTest playerRunnable;
    private Thread playerThread;

    public PanelTest() {
        setSize(800,600);

        playerRunnable = new PlayerTest();
        playerThread = new Thread(playerRunnable);

        //New
        setLayout(new BorderLayout());
        add(playerRunnable, BorderLayout.NORTH);
    }

    public void paintComponent(Graphics g) {
        super.paintComponent(g);
        setBackground(Color.red);
    }


}

It just create a Panel which has a red background (I've done this to see if paintComponent() on this work, and it works in fact).

PlayerTest:

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.*;
import javax.swing.JComponent;

public class PlayerTest extends JComponent implements Runnable, KeyListener {

    public int x;
    public int y;
    private int speed;

    public PlayerTest() {
        speed = 10;
        x = 800/2;
        y = 600/4;

        addKeyListener(this);
        setFocusable(true);
    }

    public void run() {
        while(true) {}
    }

    //New
    public Dimension getPreferredSize() {
        return new Dimension(30,30);
    }

    public void paintComponent(Graphics g) {
        g.setColor(Color.BLUE);
        g.fillRect(x, y, 30, 30);
    }

    public void keyReleased(KeyEvent e) {}
    public void keyTyped(KeyEvent e) {}

    public void keyPressed(KeyEvent e) {
        if(e.getKeyCode() == KeyEvent.VK_LEFT) {
            //Spostamento a sinistra player
            move(speed*-1);
        }

        if(e.getKeyCode() == KeyEvent.VK_RIGHT) {
            //Spostamento a destra player
            move(speed);
        }

        if(e.getKeyCode() == KeyEvent.VK_SPACE) {
            //Lancio bomba
        }
    }

    public void move(int s) {
        if (s < 0) {
            if(x-s > 0) {
                x += s;
                System.out.println("[PLAY] move left, x: " + x);
                repaint();
            }
        } else {
            if(x+s < 800) {
                x += s;
                System.out.println("[PLAY] move right, x: " + x);
                repaint();
            }
        }
    }
}

Here comes the problem. This class is a Runnable (for the animation), a KeyListener (for the player movement) and a JComponent (to show the image of the player, which for now let's say it's a rectangle) I've added this component on the Panel class but it doesn't show up if I try to paint it. I don't understand why. I've benn stuck on this for so long I've finally decided to ask a question here.

sonnhy
  • 13
  • 2

1 Answers1

1

I see several problems with your code, but the biggest immediate problem is that your PlayerTest component has no preferred size set and so it is sized [1, 1] and thus won't be seen at all. Give its container, PanelTest, a BorderLayout so that the PlayerTest component fills its container.

Other problems:

  • Don't set the sizes of components. Rather override getPreferredSize() if you must, and have it return the best Dimension for your GUI. This will work better with your layout managers.
  • Be sure to use layout managers such as BorderLayout noted above, so that your GUI's expand when need be and are placed where they need to go.
  • Rather than a thread and an empty run() method, consider using a Swing Timer since it is easier to use this and guarantee that your code is Swing-thread-safe.
  • Avoid KeyListeners but instead use Key Bindings as they are much easier to use with regards to component focus issues.

For example: How to make an image move while listening to a keypress in Java.


Put a border around your JPanel and you'll find that it's size is = to the width of the GUI but only 30 points high, since you're constraining its preferredSize to 30 by 30, but trying to draw something in it well outside of this range, and so the drawing component is not big enough to show your drawing. I would structure things differently by making the Player class not extend from a Swing component but rather make it a logical class, one that knows how to draw itself. I'd make the main JPanel the drawing JPanel and give it a Player object to move and to display.

For example, this code displays and moves a square, but only to the right on right arrow press:

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.image.BufferedImage;

import javax.swing.*;

public class MyAnimationTest extends JPanel {
   private static final int PREF_W = 800;
   private static final int PREF_H = 600;
   private static final Color BG = Color.RED;
   private static final int TIMER_DELAY = 15;
   public static final int X_STEP = 5;
   private MyPlayer player = new MyPlayer(PREF_W / 2, PREF_H / 4);
   private boolean right = false;
   private Timer timer = new Timer(TIMER_DELAY, new TimerListener());

   public MyAnimationTest() {
      setBackground(BG);
      setUpKeyBindings();
      timer.start();
   }

   private void setUpKeyBindings() {
      int condition = JComponent.WHEN_IN_FOCUSED_WINDOW;
      InputMap inMap = getInputMap(condition);
      ActionMap actMap = getActionMap();

      KeyStroke rightArrowDownStroke = KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, 0, false);
      KeyStroke rightArrowUpStroke = KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, 0, true);

      inMap.put(rightArrowDownStroke, rightArrowDownStroke.toString());
      inMap.put(rightArrowUpStroke, rightArrowUpStroke.toString());

      actMap.put(rightArrowDownStroke.toString(), new RightMoveAction(true));
      actMap.put(rightArrowUpStroke.toString(), new RightMoveAction(false));
   }

   @Override
   public Dimension getPreferredSize() {
      if (isPreferredSizeSet()) {
         return super.getPreferredSize();
      }
      return new Dimension(PREF_W, PREF_H);
   }

   @Override
   protected void paintComponent(Graphics g) {
      super.paintComponent(g);
      player.draw(g);
   }

   private class RightMoveAction extends AbstractAction {
      private boolean move;

      public RightMoveAction(boolean move) {
         this.move = move;
      }

      @Override
      public void actionPerformed(ActionEvent e) {
         right = move;
      }
   }

   private class TimerListener implements ActionListener {
      @Override
      public void actionPerformed(ActionEvent e) {
         if (right) {
            int newX = player.getX() + X_STEP;
            player.setX(newX);
            repaint();
         }
      }
   }

   private static void createAndShowGui() {
      MyAnimationTest mainPanel = new MyAnimationTest();

      JFrame frame = new JFrame("My Animation");
      frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
      frame.getContentPane().add(mainPanel);
      frame.pack();
      frame.setLocationByPlatform(true);
      frame.setVisible(true);
   }

   public static void main(String[] args) {
      SwingUtilities.invokeLater(new Runnable() {
         public void run() {
            createAndShowGui();
         }
      });
   }
}

/**
 * A logical class that does not extend a Swing component
 * 
 * @author Pete
 *
 */
class MyPlayer {
   public static final Color COLOR = Color.blue;
   public static final int WIDTH = 30;
   private BufferedImage sprite = new BufferedImage(WIDTH, WIDTH, BufferedImage.TYPE_INT_ARGB);
   private int x;
   private int y;

   public MyPlayer(int x, int y) {
      this.x = x;
      this.y = y;

      Graphics g = sprite.getGraphics();
      g.setColor(COLOR);
      g.fillRect(0, 0, WIDTH, WIDTH);
      g.dispose();
   }

   public int getX() {
      return x;
   }

   public void setX(int x) {
      this.x = x;
   }

   public int getY() {
      return y;
   }

   public void setY(int y) {
      this.y = y;
   }

   public void draw(Graphics g) {
      g.drawImage(sprite, x, y, null);
   }
}
Community
  • 1
  • 1
Hovercraft Full Of Eels
  • 283,665
  • 25
  • 256
  • 373
  • Thanks for answer. Though, if I override `getPreferredSize()` I can't see the component painted yet. I can't use Swing Timer yet, because we are doing it for school and we are on just Thread paragraph. EDIT: I will try to use containers and report back. – sonnhy Nov 02 '14 at 20:39
  • @sonnhy: The devil's in the details. Consider [editing your question](http://stackoverflow.com/posts/26701922/edit) and adding your latest code attempt below your current text and code. – Hovercraft Full Of Eels Nov 02 '14 at 20:41
  • I may not have understood what you really meant with your answer or the program just doesn't work :| in any case thanks help. – sonnhy Nov 02 '14 at 21:02
  • @sonnhy: see edit to answer. You're drawing in a 30 by 30 component (actually since it is placed BorderLayout.NORTH it does stretch horizontally), but drawing well outside of these boundaries, and so seeing nothing. – Hovercraft Full Of Eels Nov 02 '14 at 21:04
  • Thanks a lot for your support. – sonnhy Nov 02 '14 at 21:08