2013-05-31 3 views
-1

Я решил переписать свою игру с использованием метода рисования PaintComponent() Swing's (кто-то из SO на самом деле сказал мне использовать этот метод). Я решил использовать JPanel в качестве базы игры вместо Canvas. В моей предыдущей письменной игре используется Canvas, но игра не может появиться на моем 64-битном рабочем столе, но может появиться на моей 32-битной labatop, поэтому эту игру пришлось переписать.Медленное движение с использованием paintComponent method

Проблема сейчас, когда движение корабля работает, рисунок кажется ужасно медленным (если это не проблема с ноутбуком?) По сравнению с тем, что я делал до того, как использовалась техника рисования с двойной буферизацией AWT. Я провел целый день, но не мог понять, что может заставить корабль работать быстрее.

public class Ship extends JLabel implements KeyListener{ 

     private Image image; 
     private boolean turnRight; 
     private int x; 
     private int y; 
     private int speed = 5; 
     private boolean turnLeft; 

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

      try { 
       image = ImageIO.read(new File("Ship/Ship.PNG")); 
      } catch (IOException e) { 
       // TODO Auto-generated catch block 
       e.printStackTrace(); 
      } 

      addKeyListener(this); 

     } 

     public Image getImage() 
     { 
      return image; 
     } 

     @Override 
     public void keyPressed(KeyEvent e) { 
      // TODO Auto-generated method stub 

      if(e.getKeyCode() == KeyEvent.VK_RIGHT) 
      { 

       x += speed; 
       setTurnRight(true); 
       setTurnLeft(false); 

      } 
      else if(e.getKeyCode() == KeyEvent.VK_LEFT) 
      { 

       x -= speed; 
       setTurnLeft(true); 
       setTurnRight(false); 
      } 


      // redraw yourself 
      repaint(); 

     } 

     private void setTurnLeft(boolean turnLeft) { 
      // TODO Auto-generated method stub 
      this.turnLeft = turnLeft; 
     } 

     // swing custom painting 
     public void paintComponent(Graphics g) 
     { 
      if(x <= 0) 
      { 
       x = 0; 

      } 
      else if(x >= 610) 
      { 
       x = 610; 
      } 

      g.drawImage(getImage(), x, y, null); 

     } 

     public void setTurnRight(boolean turnRight) 
     { 
      this.turnRight = turnRight; 
     } 

     public boolean getTurnLeft() 
     { 
      return turnLeft; 
     } 

     public boolean getTurnRight() 
     { 
      return turnRight; 
     } 

     @Override 
     public void keyReleased(KeyEvent e) { 
      // TODO Auto-generated method stub 

     } 

     @Override 
     public void keyTyped(KeyEvent e) { 
      // TODO Auto-generated method stub 

     } 

    } 
+0

Для меня это кажется странным. Ваш корабль расширяет JLabel, и вы визуализируете положение изображения корабля в JLabel. JLable способен отображать свой собственный образ. Однако на самом деле вы должны визуализировать изображение в позиции 0x0 и разрешать контейнеру ярлыка определять, где его вообще можно визуализировать. – MadProgrammer

+0

Я, вероятно, должен расширять JComponent. правильно? Я думал, что в программировании вы должны явно вызывать методы. Когда вы говорите 0x0, вы имеете в виду (0,0)?Идея состояла в том, чтобы объект JLabel размещался поверх JFrame и управлял им с помощью keyListeners – Nicholas

+0

0x0 должен быть верхним/левым углом компонента. Размер компонентов должен быть размером изображения – MadProgrammer

ответ

2

Как правило, я бы создал концепцию рендерируемого элемента. Я бы сохранил список этих элементов и обновил их в моем основном цикле.

По крайней мере, у каждого была бы концепция местоположения, направления и вращения (при необходимости), они также были бы способны окрашиваться.

В моем основном компоненте я просто пропустил бы все эти элементы и «нарисовал» их, смещал бы контекст Graphics, чтобы разрешить там положение в игровом пространстве.

Но это не то, что вы делаете ...

Помните, что компоненты имеют смысл местоположения и размера уже, вы не должны пытаться повторно реализовать это требование, вместо этого, вы должны найти способы использовать его ... (т. е. не поддерживать ссылку на значения x/y;))

Ниже приведен простой пример. Для отображения основного изображения используется JPanel. Основной цикл (в данном случае a javax.swing.Timer) сообщает компоненту, что он должен обновлять его движение по мере необходимости.

Само судно реагирует на ключевые события, изменяя значение поворота на заданную переменную, дельта. Это позволяет контролировать скорость вращения, как вам нужно (я намеренно это низко, чтобы начать с, так что играть с ней)

Что вы должны сопротивляться делать, изменяет частоту кадров;)

enter image description here

import java.awt.BorderLayout; 
import java.awt.Color; 
import java.awt.Dimension; 
import java.awt.EventQueue; 
import java.awt.Graphics; 
import java.awt.Graphics2D; 
import java.awt.GridBagLayout; 
import java.awt.event.ActionEvent; 
import java.awt.event.ActionListener; 
import java.awt.event.KeyEvent; 
import java.awt.geom.AffineTransform; 
import java.awt.image.BufferedImage; 
import java.io.File; 
import java.io.IOException; 
import javax.imageio.ImageIO; 
import javax.swing.AbstractAction; 
import javax.swing.ActionMap; 
import javax.swing.InputMap; 
import javax.swing.JFrame; 
import javax.swing.JPanel; 
import javax.swing.KeyStroke; 
import javax.swing.Timer; 
import javax.swing.UIManager; 
import javax.swing.UnsupportedLookAndFeelException; 
import javax.swing.border.LineBorder; 

public class BattleShipGame { 

    public static void main(String[] args) { 
     new BattleShipGame(); 
    } 

    public BattleShipGame() { 
     EventQueue.invokeLater(new Runnable() { 
      @Override 
      public void run() { 
       try { 
        UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); 
       } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) { 
       } 

       JFrame frame = new JFrame("Testing"); 
       frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 
       frame.setLayout(new BorderLayout()); 
       frame.add(new OceanPane()); 
       frame.pack(); 
       frame.setLocationRelativeTo(null); 
       frame.setVisible(true); 
      } 
     }); 
    } 

    public class OceanPane extends JPanel { 

     private BattleShip ship; 

     public OceanPane() { 
      setLayout(new GridBagLayout()); 
      ship = new BattleShip(); 
      add(ship); 

      Timer timer = new Timer(40, new ActionListener() { 
       @Override 
       public void actionPerformed(ActionEvent e) { 
        ship.move(); 
        revalidate(); 
        repaint(); 
       } 
      }); 
      timer.setRepeats(true); 
      timer.setCoalesce(true); 
      timer.start(); 
     } 

     @Override 
     public Dimension getPreferredSize() { 
      return new Dimension(400, 400); 
     } 
    } 

    public static class BattleShip extends JPanel { 

     protected static final int MAX_TURN_RATE = 5; 

     private BufferedImage ship; 
     private float angle; 
     private float angleDelta; 

     public BattleShip() { 
      setOpaque(false); 
      try { 
       ship = ImageIO.read(new File("BattleShip.png")); 
      } catch (IOException ex) { 
       ex.printStackTrace(); 
      } 
      setFocusable(true); 
      InputMap im = getInputMap(WHEN_IN_FOCUSED_WINDOW); 
      ActionMap am = getActionMap(); 

      im.put(KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, 0), "leftTurn"); 
      im.put(KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, 0), "rightTurn"); 

      am.put("leftTurn", new TurnAction(-0.1f)); 
      am.put("rightTurn", new TurnAction(0.1f)); 
     } 

     public void move() { 

      angle += angleDelta; 

     } 

     public void setAngle(float angle) { 
      this.angle = angle; 
     } 

     public float getAngle() { 
      return angle; 
     } 

     @Override 
     public Dimension getPreferredSize() { 
      Dimension size = new Dimension(0, 0); 
      if (ship != null) { 
       double rads = Math.toRadians(getAngle()); 
       double sin = Math.abs(Math.sin(rads)), cos = Math.abs(Math.cos(rads)); 
       int w = ship.getWidth(); 
       int h = ship.getHeight(); 
       size.width = (int) Math.floor(w * cos + h * sin); 
       size.height = (int) Math.floor(h * cos + w * sin); 
      } 
      return size; 
     } 

     @Override 
     public Dimension getMinimumSize() { 
      return getPreferredSize(); 
     } 

     @Override 
     protected void paintComponent(Graphics g) { 
      super.paintComponent(g); 
      if (ship != null) { 
       Graphics2D g2d = (Graphics2D) g.create(); 

       double rads = Math.toRadians(getAngle()); 
       double sin = Math.abs(Math.sin(rads)), cos = Math.abs(Math.cos(rads)); 
       int w = ship.getWidth(); 
       int h = ship.getHeight(); 
       int newWidth = (int) Math.floor(w * cos + h * sin); 
       int newHeight = (int) Math.floor(h * cos + w * sin); 

       AffineTransform at = new AffineTransform(); 
       at.translate((newWidth - w)/2, (newHeight - h)/2); 
       at.rotate(Math.toRadians(getAngle()), w/2, h/2); 

       g2d.drawImage(ship, at, this); 
       g2d.dispose(); 
      } 
     } 

     protected class TurnAction extends AbstractAction { 

      protected float delta; 

      public TurnAction(float delta) { 
       this.delta = delta; 
      } 

      @Override 
      public void actionPerformed(ActionEvent e) { 
       angleDelta += delta; 
       if (angleDelta > MAX_TURN_RATE) { 
        angleDelta = MAX_TURN_RATE; 
       } else if (angleDelta < (MAX_TURN_RATE * -1)) { 
        angleDelta = (MAX_TURN_RATE * -1); 
       } 
      } 

     } 
    } 
} 
0

Я рекомендовал бы класс, который расширяет JPanel, используя javax.swing.Timer там, определяя свой 1000/кадров в секунду и ваш ActionListener, в котором вы используете перерисовки(), который использует paintComponent, что вам сделают это, чтобы вызвать метод draw на вашем корабле, который теперь называется paintComponent.

Так, как это было страшно объяснений, вот какой-то код:

public class Class_Name extends JPanel() 
{ 
    Ship ship = new Ship(0,0); 
    javax.swing.Timer timer = new javax.swing.Timer(1000/60, new ActionListener(){ 
     repaint(); 
    }); 

    public void paintComponent(Graphics g) 
    { 
     super.paintComponent(); 
     ship.draw(g); 
    } 
} 

and the draw is, what is now called paintComponent. 

Если это не ответ на ваш вопрос, пожалуйста, дайте мне знать.