0

I am working on multiplayer game where when the server sends distinct message, ie. type of enemy and number of enemies. that particular sprite loads onto screen. I have 4 views, seperated by CardLayout and buttons on the top to switch between screens.

Here's the chain that I have set up. When I try to draw the enemies.. I get an error.

In Main class I have this method:

public void handleAttack(String message){
  ViewA.spawnEnemy(type,amt);
}

This is the class for ViewA which has the spawnEnemy() method.

public class BattleView extends JPanel implements ActionListener {  
  private Player player;
  private Timer timer;
  private int B_WIDTH;
  private int B_HEIGHT;
  private boolean ingame;
  private ArrayList aliens;

  public BattleView(Player player) {
    this.player = player;
    addKeyListener(new TAdapter());
    setFocusable(true);
    setBackground(Color.RED);
    setDoubleBuffered(true);
    setSize(652, 480);
    ingame = true;
    timer = new Timer(5, this);
    timer.start();
  }

  public void addNotify() {
    super.addNotify();
    B_WIDTH = getWidth();
    B_HEIGHT = getHeight();   

    super.setPreferredSize(new Dimension(B_WIDTH,B_HEIGHT));
  }

  public void spawnEnemy(int type, int amount) {
    aliens = new ArrayList();
    for (int i=1; i<amount+1; i++ ) {
      aliens.add(new Enemy(0-i*40,400));
      System.out.println("Attack Message Recieved "+i);
    }
  }

  public void paint(Graphics g) {
    super.paint(g);

    Graphics2D g2d = (Graphics2D)g;
    g2d.drawImage(background, 0, 0, null);

    if (player.isVisible()){
      g2d.drawImage(player.getImage(), player.getX(), player.getY(), this);
    }
    ArrayList ms = player.getMissiles();

    /* for (int i = 0; i < ms.size(); i++) {
      Missile m = (Missile)ms.get(i);
      g2d.drawImage(m.getImage(), m.getX(), m.getY(), this);
    }*/

    /*    for (int i = 0; i < aliens.size(); i++) {
      Enemy a = (Enemy)aliens.get(i);
      if (a.isVisible()){
        g2d.drawImage(a.getImage(), a.getX(), a.getY(), this);
      }
    }*/

    g2d.setColor(Color.BLACK);
    g2d.drawString("BattleView: ", 5, 15);

    Toolkit.getDefaultToolkit().sync();
    g.dispose();
  }

  @Override public void actionPerformed(ActionEvent e) {
    /* if (aliens.size()==0) {
      //send message to server that enemies have been killed
    }*/

    ArrayList ms = player.getMissiles();
    /*  for (int i = 0; i < ms.size(); i++) {
      Missile m = (Missile) ms.get(i);
      if (m.isVisible()){
        m.move();
      }else{
        ms.remove(i);
      }
    }*/

    /*  for (int i = 0; i < aliens.size(); i++) {
      Enemy a = (Enemy) aliens.get(i);
      if (a.isVisible()){
        a.move();
      }else{
        aliens.remove(i);
      }
    }*/

    player.move();
    checkCollisions();
    repaint();  
  }

  public void checkCollisions() {
    /* Rectangle r3 = player.getBounds();

    for (int j = 0; j<aliens.size(); j++) {
      Enemy a = (Enemy) aliens.get(j);
      Rectangle r2 = a.getBounds();

        if (r3.intersects(r2)) {
          player.setVisible(false);
          a.setVisible(false);
          ingame = false;
        }
    }*/

    ArrayList ms = player.getMissiles();
    /*  for (int i = 0; i < ms.size(); i++) {
      Missile m = (Missile) ms.get(i);

      Rectangle r1 = m.getBounds();
      for (int j = 0; j<aliens.size(); j++) {
        Enemy a = (Enemy) aliens.get(j);
        Rectangle r2 = a.getBounds();

        if (r1.intersects(r2)) {
          m.setVisible(false);
          a.setVisible(false);
        } // r1.intersects
      } // for aliens.size()
    }*/ // for ms.size()
  }

  private class TAdapter extends KeyAdapter {

    public void keyReleased(KeyEvent e) {
      player.keyReleased(e);
    }

    public void keyPressed(KeyEvent e) {
      player.keyPressed(e);
    }
  }
}

This part fires five System.out.println("Attack Message Recieved "+i); when server sends message. So if server sends 1,5.. 5 units of type 1 are "recieved" and System.out.println prints 5 times.

Now when I try to use paint(g) to draw the enemy units using the for-loop.. I get an error When I comment out the for-loop everything compiles fine and loads (but no screen sprites are created of the enemy, only the System.out.println fires the N number of times sent via the server.

How can I get it to draw the enemies right after the System.out.println fires?

This is the error:

at ViewA.paint(ViewA.java:85)
at javax.swing.JComponent.paintToOffscreen(Unknown Source)
at javax.swing.RepaintManager$PaintManager.paintDoubleBuffered(Unknown Source)
at javax.swing.RepaintManager$PaintManager.paint(Unknown Source)
at javax.swing.RepaintManager.paint(Unknown Source)
at javax.swing.JComponent._paintImmediately(Unknown Source)
at javax.swing.JComponent.paintImmediately(Unknown Source)
at javax.swing.RepaintManager.paintDirtyRegions(Unknown Source)
at javax.swing.RepaintManager.paintDirtyRegions(Unknown Source)
at javax.swing.RepaintManager.prePaintDirtyRegions(Unknown Source)
at javax.swing.RepaintManager.access$700(Unknown Source)
at javax.swing.RepaintManager$ProcessingRunnable.run(Unknown Source)
at java.awt.event.InvocationEvent.dispatch(Unknown Source)
at java.awt.EventQueue.dispatchEventImpl(Unknown Source)
at java.awt.EventQueue.access$000(Unknown Source)
at java.awt.EventQueue$3.run(Unknown Source)
at java.awt.EventQueue$3.run(Unknown Source)
at java.security.AccessController.doPrivileged(Native Method)
at java.security.ProtectionDomain$1.doIntersectionPrivilege(Unknown Source)
at java.awt.EventQueue.dispatchEvent(Unknown Source)
at java.awt.EventDispatchThread.pumpOneEventForFilters(Unknown Source)
at java.awt.EventDispatchThread.pumpEventsForFilter(Unknown Source)
at java.awt.EventDispatchThread.pumpEventsForHierarchy(Unknown Source)
at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
at java.awt.EventDispatchThread.run(Unknown Source)

at ViewA.paint(ViewA.java:85)

corresponds to this line:

        for (int i = 0; i < aliens.size(); i++) {
            Enemy a = (Enemy)aliens.get(i);
            if (a.isVisible())
                g2d.drawImage(a.getImage(), a.getX(), a.getY(), this);
        }

        g2d.setColor(Color.BLACK);
        g2d.drawString("BattleView: ", 5, 15);
user2556304
  • 159
  • 2
  • 15

2 Answers2

1

Start by trying to call repaint to request that the panel should be repainted...

public void spawnEnemy(int type, int amount) {
    aliens = new ArrayList();
    for (int i = 1; i < amount + 1; i++) {
        aliens.add(new Enemy(0 - i * 40, 400));
        System.out.println("Attack Message Recieved " + i);
    }
    repaint() // <<-- 
}

Also, it is recommended that should be overriding paintComponent instead of paint

Updated

Don't dispose of any resource you didn't create...

public void paint(Graphics g) {
    super.paint(g);
    Graphics2D g2d = (Graphics2D) g;
    //  g2d.drawImage(background, 0, 0, null);
    if (player.isVisible()) {
        g2d.drawImage(player.getImage(), player.getX(), player.getY(),
                this);
    }
    ArrayList ms = player.getMissiles();
    g2d.setColor(Color.BLACK);
    g2d.drawString("BattleView: ", 5, 15);
    Toolkit.getDefaultToolkit().sync();
    // This is a bad idea..
    //g.dispose();
}

Graphics is a shared resource, meaning that other components in your application may need to use them. Also disposing may prevent Swing from begin able to paint anything at all...

Updated

You define a ArrayList as an instance field, but you never initialise until the spawnEnemy method is called. paint is likely to called a long time before that.

public class BattleView extends JPanel implements ActionListener {  
    //...//
    private ArrayList aliens;

    //..//

    public void spawnEnemy(int type, int amount) {
        aliens = new ArrayList();

You have a number of options. You could check for to see if the aliens list is null before painting...

if (aliens != null) { ... }

But there is a problem here, what happens if you get more enemies? You could actually lose some (if paint isn't called before more updates arrive)

Instead, you should create the aliens list in the constructor and simply update it as new enemies arrive...

public BattleView(Player player) {
    aliens = new ArrayList();
    //...//
}

Add new enemies to the list as they arrive...

public void spawnEnemy(int type, int amount) {
    for (int i=1; i<amount+1; i++ ) {
        aliens.add(new Enemy(0-i*40,400));
        System.out.println("Attack Message Recieved "+i);
    }
    repaint();
}

And then paint...

for (int i = 0; i < aliens.size(); i++) {
    Enemy a = (Enemy)aliens.get(i);
    if (a.isVisible())
        g2d.drawImage(a.getImage(), a.getX(), a.getY(), this);
}
MadProgrammer
  • 343,457
  • 22
  • 230
  • 366
1

from the top

EDIT

  1. timer = new Timer(5, this); 5 miliseconds is refresh frequency under latency for all Native OS, you can to play with amount at 25 or more


  2. remove whole code block public void addNotify() { and to replace with getPreferredSize then,

    a) all coordinates are based on get width/height

    b) Objects painted in JPanel are/could be resizable with JFrame

  3. use paintComponent for Swing JComponent and custom painting in Java instead of public void paint(Graphics g) {

  4. public void actionPerformed(ActionEvent e) { missing code line repaint() for programatically repainting

  5. don't to use KeyListener for Swing JComponents, use KeyBindings added to JPanel instead of KeyListener,

Community
  • 1
  • 1
mKorbel
  • 109,525
  • 20
  • 134
  • 319