2013-02-14 197 views
3

我使用Java Swing運行當前動畫時出現問題。這是一個離散事件模擬和基於文本的模擬工作正常,我只是有模擬連接到GUI輸出時出現問題。Swing動畫運行速度極慢

對於這個例子,我將有10輛汽車進行模擬。這些車是由JPanels代表,我將在一會兒詳細闡述。

因此,請考慮事件process_car_arrival。每次計劃執行此事件時,我都會在我的Model類中將Car對象添加到名爲carsArrayList中。該Car類具有以下相關屬性:

Point currentPos; // The current position, initialized in another method when knowing route. 
double speed; // giving the speed any value still causes the same problem but I have 5 atm. 
RouteType route; // for this example I only consider one simple route 

另外它具有以下方法move()

switch (this.route) { 
    case EAST: 
     this.currentPos.x -= speed; 
     return this.currentPos; 
. 
. 
. 
//only above is relevant in this example 

這是一個好。所以從理論上講,汽車從東向西沿直線行駛,因爲我只爲每輛我想要移動的汽車調用move()方法。

返回到process_car_arrival事件。在添加一個Car對象之後,它調用View類中的方法addCarToEast()。這在從東到西的道路開始時增加了一個JPanel。

要去View類現在我有一個** **單獨的線程,其執行以下操作(在run()方法):

@Override 
    public void run() { 
     while (true) { 
      try { 
       Thread.sleep(30); 
      } catch (InterruptedException e) { 
       e.printStackTrace(); 
      } 
      if (!cars.isEmpty()) { 

       cars.get(i).setLocation(
         new Point(getModel.getCars().get(i).move())); 

       if (i == cars.size() - 1) { 
        i = 0; 
       } else { 
        i++; 
       } 
      } 
     } 
    } 

上面並從東動車向西在平穩第一。但是,在有3-4輛汽車行駛之後,它會變得極其緩慢,而當我有10輛汽車行駛時,它最終只會移動很少。

只是爲了清理,在Model類的那一刻有CarArrayList對象,並在View類也有代表汽車JPanelArrayList對象。我試圖將Car的對象與JPanels匹配,但我顯然正在做一件大事。

我懷疑自己正在瘋狂地做一些效率低下的事情,但我不知道該怎麼做。我想最初可能是這樣訪問ArrayList,我想這會讓它非常慢。

任何指向我可以改變,使其運行順利?

+1

我首先關注的是它似乎你是從外面事件更新UI元素調度線程。這是危險的,極不明智的。查看[Swing中的併發](http://docs.oracle.com/javase/tutorial/uiswing/concurrency/index.html)以獲取更多詳細信息。我的第二個問題是,這還遠遠不夠。你能製作一個簡單的工作例子嗎?不需要爲汽車提供圖像,簡單的矩形表示就足夠了。 – MadProgrammer 2013-02-15 00:08:31

+1

除了MadProgrammer所說的之外,另一個紅旗是您正在創建單獨的JPanel。它已經有一段時間了,因爲我已經完成了任何與圖形相關的工作,但我認爲你應該使用2D庫,而不是移動「n」個JPanel。 – Joe 2013-02-15 00:13:28

+0

@MadProgrammer我只是通過你給我的鏈接工作,試圖看看我是否可以抓到任何東西。如果我無法使其工作,將在稍後發佈一個簡單的工作示例。 – DSF 2013-02-15 00:57:03

回答

11

基於此前的answer,下面的示例模擬了一個由三個駕駛室隨機在矩形網格上移動的車隊。 A javax.swing.Timer以5Hz驅動動畫。模型和視圖緊密耦合在CabPanel中,但動畫可能會提供一些有用的見解。特別是,您可能會增加駕駛室數量或降低計時器延遲。

image

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.GridLayout; 
import java.awt.Point; 
import java.awt.RenderingHints; 
import java.awt.event.ActionEvent; 
import java.awt.event.ActionListener; 
import java.text.DecimalFormat; 
import java.util.ArrayList; 
import java.util.List; 
import java.util.Random; 
import javax.swing.JButton; 
import javax.swing.JComboBox; 
import javax.swing.JFrame; 
import javax.swing.JLabel; 
import javax.swing.JPanel; 
import javax.swing.Timer; 

/** 
* @see https://stackoverflow.com/a/14887457/230513 
* @see https://stackoverflow.com/questions/5617027 
*/ 

public class FleetPanel extends JPanel { 

    private static final Random random = new Random(); 
    private final MapPanel map = new MapPanel(); 
    private final JPanel control = new JPanel(); 
    private final List<CabPanel> fleet = new ArrayList<CabPanel>(); 
    private final Timer timer = new Timer(200, null); 

    public FleetPanel() { 
     super(new BorderLayout()); 
     fleet.add(new CabPanel("Cab #1", Hue.Cyan)); 
     fleet.add(new CabPanel("Cab #2", Hue.Magenta)); 
     fleet.add(new CabPanel("Cab #3", Hue.Yellow)); 
     control.setLayout(new GridLayout(0, 1)); 
     for (CabPanel cp : fleet) { 
      control.add(cp); 
      timer.addActionListener(cp.listener); 
     } 
     this.add(map, BorderLayout.CENTER); 
     this.add(control, BorderLayout.SOUTH); 
    } 

    public void start() { 
     timer.start(); 
    } 

    private class CabPanel extends JPanel { 

     private static final String format = "000000"; 
     private final DecimalFormat df = new DecimalFormat(format); 
     private JLabel name = new JLabel("", JLabel.CENTER); 
     private Point point = new Point(); 
     private JLabel position = new JLabel(toString(point), JLabel.CENTER); 
     private int blocks; 
     private JLabel odometer = new JLabel(df.format(0), JLabel.CENTER); 
     private final JComboBox colorBox = new JComboBox(); 
     private final JButton reset = new JButton("Reset"); 
     private final ActionListener listener = new ActionListener() { 

      @Override 
      public void actionPerformed(ActionEvent e) { 
       int ds = random.nextInt(3) - 1; 
       if (random.nextBoolean()) { 
        point.x += ds; 
       } else { 
        point.y += ds; 
       } 
       blocks += Math.abs(ds); 
       update(); 
      } 
     }; 

     public CabPanel(String s, Hue hue) { 
      super(new GridLayout(1, 0)); 
      name.setText(s); 
      this.setBackground(hue.getColor()); 
      this.add(map, BorderLayout.CENTER); 
      for (Hue h : Hue.values()) { 
       colorBox.addItem(h); 
      } 
      colorBox.setSelectedIndex(hue.ordinal()); 
      colorBox.addActionListener(new ActionListener() { 

       @Override 
       public void actionPerformed(ActionEvent e) { 
        Hue h = (Hue) colorBox.getSelectedItem(); 
        CabPanel.this.setBackground(h.getColor()); 
        update(); 
       } 
      }); 
      reset.addActionListener(new ActionListener() { 

       @Override 
       public void actionPerformed(ActionEvent e) { 
        point.setLocation(0, 0); 
        blocks = 0; 
        update(); 
       } 
      }); 
      this.add(name); 
      this.add(odometer); 
      this.add(position); 
      this.add(colorBox); 
      this.add(reset); 
     } 

     private void update() { 
      position.setText(CabPanel.this.toString(point)); 
      odometer.setText(df.format(blocks)); 
      map.repaint(); 
     } 

     private String toString(Point p) { 
      StringBuilder sb = new StringBuilder(); 
      sb.append(Math.abs(p.x)); 
      sb.append(p.x < 0 ? " W" : " E"); 
      sb.append(", "); 
      sb.append(Math.abs(p.y)); 
      sb.append(p.y < 0 ? " N" : " S"); 
      return sb.toString(); 
     } 
    } 

    private class MapPanel extends JPanel { 

     private static final int SIZE = 16; 

     public MapPanel() { 
      this.setPreferredSize(new Dimension(32 * SIZE, 32 * SIZE)); 
      this.setBackground(Color.lightGray); 
     } 

     @Override 
     protected void paintComponent(Graphics g) { 
      super.paintComponent(g); 
      Graphics2D g2d = (Graphics2D) g; 
      g2d.setRenderingHint(
       RenderingHints.KEY_ANTIALIASING, 
       RenderingHints.VALUE_ANTIALIAS_ON); 
      int w = this.getWidth(); 
      int h = this.getHeight(); 
      g2d.setColor(Color.gray); 
      for (int col = SIZE; col <= w; col += SIZE) { 
       g2d.drawLine(col, 0, col, h); 
      } 
      for (int row = SIZE; row <= h; row += SIZE) { 
       g2d.drawLine(0, row, w, row); 
      } 

      for (CabPanel cp : fleet) { 
       Point p = cp.point; 
       int x = SIZE * (p.x + w/2/SIZE) - SIZE/2; 
       int y = SIZE * (p.y + h/2/SIZE) - SIZE/2; 
       g2d.setColor(cp.getBackground()); 
       g2d.fillOval(x, y, SIZE, SIZE); 
      } 
     } 
    } 

    public enum Hue { 

     Cyan(Color.cyan), Magenta(Color.magenta), Yellow(Color.yellow), 
     Red(Color.red), Green(Color.green), Blue(Color.blue), 
     Orange(Color.orange), Pink(Color.pink); 
     private final Color color; 

     private Hue(Color color) { 
      this.color = color; 
     } 

     public Color getColor() { 
      return color; 
     } 
    } 

    private static void display() { 
     JFrame f = new JFrame("Dispatch"); 
     f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 
     FleetPanel fp = new FleetPanel(); 
     f.add(fp); 
     f.pack(); 
     f.setLocationRelativeTo(null); 
     f.setVisible(true); 
     fp.start(); 
    } 

    public static void main(String[] args) { 
     EventQueue.invokeLater(new Runnable() { 

      @Override 
      public void run() { 
       display(); 
      } 
     }); 
    } 
} 
+0

另請參閱此MVC [示例](http://stackoverflow.com/a/3072979/230513)。 – trashgod 2013-02-15 02:23:02

+0

謝謝先生:)偉大的洞察力。在1小時內閱讀您的代碼所獲得的收穫要比我在挫折中整夜花費更多! – DSF 2013-02-15 05:05:40

+0

+1非常好... – 2013-02-15 12:38:53

5

我無法抗拒...

enter image description here

我有500輛汽車在屏幕上與運行有點慢下來(這不是最快的......約200-300很不錯...

這裏使用的面板來表示每個車輛上。如果你想獲得更好的性能,你可能需要看看使用某種類型的後盾緩衝。

public class TestAnimation10 { 

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

    public TestAnimation10() { 
     EventQueue.invokeLater(new Runnable() { 
      @Override 
      public void run() { 
       try { 
        UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); 
       } catch (Exception ex) { 
       } 

       final TrackPane trackPane = new TrackPane(); 
       JSlider slider = new JSlider(1, 500); 
       slider.addChangeListener(new ChangeListener() { 
        @Override 
        public void stateChanged(ChangeEvent e) { 
         trackPane.setCongestion(((JSlider)e.getSource()).getValue()); 
        } 
       }); 
       slider.setValue(5); 

       JFrame frame = new JFrame("Test"); 
       frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 
       frame.setLayout(new BorderLayout()); 
       frame.add(trackPane); 
       frame.add(slider, BorderLayout.SOUTH); 
       frame.pack(); 
       frame.setLocationRelativeTo(null); 
       frame.setVisible(true); 

      } 
     }); 
    } 

    public class TrackPane extends JPanel { 

     private List<Car> cars; 
     private int maxCars = 1; 

     private List<Point2D[]> points; 

     private Ellipse2D areaOfEffect; 

     public TrackPane() { 

      points = new ArrayList<>(25); 

      cars = new ArrayList<>(25); 
      setLayout(null); 

      Timer timer = new Timer(40, new ActionListener() { 
       @Override 
       public void actionPerformed(ActionEvent e) { 

        Rectangle bounds = areaOfEffect.getBounds(); 
        List<Car> tmp = new ArrayList<>(cars); 
        for (Car car : tmp) { 
         car.move(); 
         if (!bounds.intersects(car.getBounds())) { 
          remove(car); 
          cars.remove(car); 
         } 
        } 
        updatePool(); 
        repaint(); 
       } 
      }); 

      timer.setRepeats(true); 
      timer.setCoalesce(true); 
      timer.start(); 

      updateAreaOfEffect(); 
     } 

     protected void updateAreaOfEffect() { 
      double radius = Math.max(getWidth(), getHeight()) * 1.5d; 
      double x = (getWidth() - radius)/2d; 
      double y = (getHeight() - radius)/2d; 
      areaOfEffect = new Ellipse2D.Double(x, y, radius, radius); 
     } 

     @Override 
     public void invalidate() { 
      super.invalidate(); 
      updateAreaOfEffect(); 
     } 

     protected void updatePool() { 
      while (cars.size() < maxCars) { 
//   if (cars.size() < maxCars) { 
       Car car = new Car(); 
       double direction = car.getDirection(); 
       double startAngle = direction - 180; 

       double radius = areaOfEffect.getWidth(); 
       Point2D startPoint = getPointAt(radius, startAngle); 

       int cx = getWidth()/2; 
       int cy = getHeight()/2; 

       double x = cx + (startPoint.getX() - car.getWidth()/2); 
       double y = cy + (startPoint.getY() - car.getHeight()/2); 
       car.setLocation((int)x, (int)y); 

       Point2D targetPoint = getPointAt(radius, direction); 

       points.add(new Point2D[]{startPoint, targetPoint}); 

       add(car); 

       cars.add(car); 
      } 
     } 

     @Override 
     public void paint(Graphics g) { 
      super.paint(g); 
      Font font = g.getFont(); 
      font = font.deriveFont(Font.BOLD, 48f); 
      FontMetrics fm = g.getFontMetrics(font); 
      g.setFont(font); 
      g.setColor(Color.RED); 
      String text = Integer.toString(maxCars); 
      int x = getWidth() - fm.stringWidth(text); 
      int y = getHeight() - fm.getHeight() + fm.getAscent(); 
      g.drawString(text, x, y); 
      text = Integer.toString(getComponentCount()); 
      x = getWidth() - fm.stringWidth(text); 
      y -= fm.getHeight(); 
      g.drawString(text, x, y); 
      text = Integer.toString(cars.size()); 
      x = getWidth() - fm.stringWidth(text); 
      y -= fm.getHeight(); 
      g.drawString(text, x, y); 
     } 

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

     public void setCongestion(int value) { 
      maxCars = value; 
     } 
    } 

    protected static Point2D getPointAt(double radius, double angle) { 

     double x = Math.round(radius/2d); 
     double y = Math.round(radius/2d); 

     double rads = Math.toRadians(-angle); 

     double fullLength = Math.round((radius/2d)); 

     double xPosy = (Math.cos(rads) * fullLength); 
     double yPosy = (Math.sin(rads) * fullLength); 

     return new Point2D.Double(xPosy, yPosy); 

    } 

    public class Car extends JPanel { 

     private double direction; 
     private double speed; 
     private BufferedImage background; 

     public Car() { 
      setOpaque(false); 
      direction = Math.random() * 360; 
      speed = 5 + (Math.random() * 10); 
      int image = 1 + (int) Math.round(Math.random() * 5); 
      try { 
       String name = "/Car0" + image + ".png"; 
       background = ImageIO.read(getClass().getResource(name)); 
      } catch (IOException ex) { 
       ex.printStackTrace(); 
      } 
      setSize(getPreferredSize()); 
//   setBorder(new LineBorder(Color.RED)); 
     } 

     public void setDirection(double direction) { 
      this.direction = direction; 
      revalidate(); 
      repaint(); 
     } 

     public double getDirection() { 
      return direction; 
     } 

     public void move() { 
      Point at = getLocation(); 
      at.x += (int)(speed * Math.cos(Math.toRadians(-direction))); 
      at.y += (int)(speed * Math.sin(Math.toRadians(-direction))); 
      setLocation(at); 
     } 

     @Override 
     public Dimension getPreferredSize() { 
      Dimension size = super.getPreferredSize(); 
      if (background != null) { 
       double radian = Math.toRadians(direction); 
       double sin = Math.abs(Math.sin(radian)), cos = Math.abs(Math.cos(radian)); 
       int w = background.getWidth(), h = background.getHeight(); 
       int neww = (int) Math.floor(w * cos + h * sin); 
       int newh = (int) Math.floor(h * cos + w * sin); 
       size = new Dimension(neww, newh); 
      } 
      return size; 
     } 

     @Override 
     protected void paintComponent(Graphics g) { 
      super.paintComponent(g); 
      Graphics2D g2d = (Graphics2D) g.create(); 
      int x = (getWidth() - background.getWidth())/2; 
      int y = (getHeight() - background.getHeight())/2; 
      g2d.rotate(Math.toRadians(-(direction + 180)), getWidth()/2, getHeight()/2); 
      g2d.drawImage(background, x, y, this); 
      g2d.dispose(); 

//   Debug graphics... 
//   int cx = getWidth()/2; 
//   int cy = getHeight()/2; 
// 
//   g2d = (Graphics2D) g.create(); 
//   g2d.setColor(Color.BLUE); 
//   double radius = Math.min(getWidth(), getHeight()); 
//   Point2D pointAt = getPointAt(radius, direction); 
//   g2d.draw(new Ellipse2D.Double(cx - (radius/2d), cy - (radius/2d), radius, radius)); 
//    
//   double xo = cx; 
//   double yo = cy; 
//   double xPos = cx + pointAt.getX(); 
//   double yPos = cy + pointAt.getY(); 
//    
//   g2d.draw(new Line2D.Double(xo, yo, xPos, yPos)); 
//   g2d.draw(new Ellipse2D.Double(xPos - 2, yPos - 2, 4, 4)); 
//   g2d.dispose(); 
     } 
    } 
} 

更新與優化版本

我在創建汽車對象時做了一點代碼優化(還有改進空間),並且增加了圖形輸出(使它看起來更好)。

基本上,現在,當一輛汽車離開屏幕時,它被放置在一個游泳池中。當需要另一輛汽車時,如果可能的話,它會從游泳池中拉出來,否則就會造成一輛新車。這減少了創建和刪除很多(相對)短暫對象的開銷,這使得內存使用更加穩定。

在我的2560x1600分辨率屏幕上(運行最大化),我能夠同時運行4500輛汽車。一旦對象創建減少,它運行得相對順利(它永遠不會跑到10,但它並沒有受到速度顯着降低的影響)。

public class TestAnimation10 { 

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

    public TestAnimation10() { 
     EventQueue.invokeLater(new Runnable() { 
      @Override 
      public void run() { 
       try { 
        UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); 
       } catch (Exception ex) { 
       } 

       final TrackPane trackPane = new TrackPane(); 
       JSlider slider = new JSlider(1, 5000); 
       slider.addChangeListener(new ChangeListener() { 
        @Override 
        public void stateChanged(ChangeEvent e) { 
         trackPane.setCongestion(((JSlider) e.getSource()).getValue()); 
        } 
       }); 
       slider.setValue(5); 

       JFrame frame = new JFrame("Test"); 
       frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 
       frame.setLayout(new BorderLayout()); 
       frame.add(trackPane); 
       frame.add(slider, BorderLayout.SOUTH); 
       frame.pack(); 
       frame.setLocationRelativeTo(null); 
       frame.setVisible(true); 

      } 
     }); 
    } 

    public class TrackPane extends JPanel { 

     private List<Car> activeCarList; 
     private List<Car> carPool; 
     private int maxCars = 1; 
     private List<Point2D[]> points; 
     private Ellipse2D areaOfEffect; 

     public TrackPane() { 

      points = new ArrayList<>(25); 

      activeCarList = new ArrayList<>(25); 
      carPool = new ArrayList<>(25); 
      setLayout(null); 

      Timer timer = new Timer(40, new ActionListener() { 
       @Override 
       public void actionPerformed(ActionEvent e) { 

        Rectangle bounds = areaOfEffect.getBounds(); 
        List<Car> tmp = new ArrayList<>(activeCarList); 
        for (Car car : tmp) { 
         car.move(); 
         if (!bounds.intersects(car.getBounds())) { 
          remove(car); 
          activeCarList.remove(car); 
          carPool.add(car); 
         } 
        } 
        updatePool(); 
        repaint(); 
       } 
      }); 

      timer.setRepeats(true); 
      timer.setCoalesce(true); 
      timer.start(); 

      updateAreaOfEffect(); 
     } 

     protected void updateAreaOfEffect() { 
      double radius = Math.max(getWidth(), getHeight()) * 1.5d; 
      double x = (getWidth() - radius)/2d; 
      double y = (getHeight() - radius)/2d; 
      areaOfEffect = new Ellipse2D.Double(x, y, radius, radius); 
     } 

     @Override 
     public void invalidate() { 
//   super.invalidate(); 
      updateAreaOfEffect(); 
     } 

     protected void updatePool() { 
      if (activeCarList.size() < maxCars) { 
       int count = Math.min(maxCars - activeCarList.size(), 10); 
       for (int index = 0; index < count; index++) { 
        Car car = null; 

        if (carPool.isEmpty()) { 
         car = new Car(); 
        } else { 
         car = carPool.remove(0); 
        } 

        double direction = car.getDirection(); 
        double startAngle = direction - 180; 

        double radius = areaOfEffect.getWidth(); 
        Point2D startPoint = getPointAt(radius, startAngle); 

        int cx = getWidth()/2; 
        int cy = getHeight()/2; 

        double x = cx + (startPoint.getX() - car.getWidth()/2); 
        double y = cy + (startPoint.getY() - car.getHeight()/2); 
        car.setLocation((int) x, (int) y); 

        Point2D targetPoint = getPointAt(radius, direction); 

        points.add(new Point2D[]{startPoint, targetPoint}); 

        add(car); 

        activeCarList.add(car); 
       } 
      } 
     } 

     @Override 
     public void paint(Graphics g) { 
      super.paint(g); 
      Font font = g.getFont(); 
      font = font.deriveFont(Font.BOLD, 48f); 
      FontMetrics fm = g.getFontMetrics(font); 
      g.setFont(font); 
      g.setColor(Color.RED); 
      String text = Integer.toString(maxCars); 
      int x = getWidth() - fm.stringWidth(text); 
      int y = getHeight() - fm.getHeight() + fm.getAscent(); 
      g.drawString(text, x, y); 
      text = Integer.toString(getComponentCount()); 
      x = getWidth() - fm.stringWidth(text); 
      y -= fm.getHeight(); 
      g.drawString(text, x, y); 
      text = Integer.toString(activeCarList.size()); 
      x = getWidth() - fm.stringWidth(text); 
      y -= fm.getHeight(); 
      g.drawString(text, x, y); 
      text = Integer.toString(carPool.size()); 
      x = getWidth() - fm.stringWidth(text); 
      y -= fm.getHeight(); 
      g.drawString(text, x, y); 
     } 

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

     public void setCongestion(int value) { 
      maxCars = value; 
     } 

     @Override 
     public void validate() { 
     } 

     @Override 
     public void revalidate() { 
     } 

//  @Override 
//  public void repaint(long tm, int x, int y, int width, int height) { 
//  } 
// 
//  @Override 
//  public void repaint(Rectangle r) { 
//  } 
//  public void repaint() { 
//  } 
     @Override 
     protected void firePropertyChange(String propertyName, Object oldValue, Object newValue) { 
      System.out.println(propertyName); 
//   // Strings get interned... 
//   if (propertyName == "text" 
//       || propertyName == "labelFor" 
//       || propertyName == "displayedMnemonic" 
//       || ((propertyName == "font" || propertyName == "foreground") 
//       && oldValue != newValue 
//       && getClientProperty(javax.swing.plaf.basic.BasicHTML.propertyKey) != null)) { 
// 
//    super.firePropertyChange(propertyName, oldValue, newValue); 
//   } 
     } 

     @Override 
     public void firePropertyChange(String propertyName, boolean oldValue, boolean newValue) { 
     } 
    } 

    protected static Point2D getPointAt(double radius, double angle) { 

     double x = Math.round(radius/2d); 
     double y = Math.round(radius/2d); 

     double rads = Math.toRadians(-angle); 

     double fullLength = Math.round((radius/2d)); 

     double xPosy = (Math.cos(rads) * fullLength); 
     double yPosy = (Math.sin(rads) * fullLength); 

     return new Point2D.Double(xPosy, yPosy); 

    } 

    public class Car extends JPanel { 

     private double direction; 
     private double speed; 
     private BufferedImage background; 

     public Car() { 
      setOpaque(false); 
      direction = Math.random() * 360; 
      speed = 5 + (Math.random() * 10); 
      int image = 1 + (int) Math.round(Math.random() * 5); 
      try { 
       String name = "/Car0" + image + ".png"; 
       background = ImageIO.read(getClass().getResource(name)); 
      } catch (IOException ex) { 
       ex.printStackTrace(); 
      } 
      setSize(getPreferredSize()); 
//   setBorder(new LineBorder(Color.RED)); 
     } 

     public void setDirection(double direction) { 
      this.direction = direction; 
      revalidate(); 
      repaint(); 
     } 

     public double getDirection() { 
      return direction; 
     } 

     public void move() { 
      Point at = getLocation(); 
      at.x += (int) (speed * Math.cos(Math.toRadians(-direction))); 
      at.y += (int) (speed * Math.sin(Math.toRadians(-direction))); 
      setLocation(at); 
     } 

     @Override 
     public Dimension getPreferredSize() { 
      Dimension size = super.getPreferredSize(); 
      if (background != null) { 
       double radian = Math.toRadians(direction); 
       double sin = Math.abs(Math.sin(radian)), cos = Math.abs(Math.cos(radian)); 
       int w = background.getWidth(), h = background.getHeight(); 
       int neww = (int) Math.floor(w * cos + h * sin); 
       int newh = (int) Math.floor(h * cos + w * sin); 
       size = new Dimension(neww, newh); 
      } 
      return size; 
     } 

     @Override 
     protected void paintComponent(Graphics g) { 
      super.paintComponent(g); 
      Graphics2D g2d = (Graphics2D) g.create(); 
      g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); 
      g2d.setRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION, RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY); 
      g2d.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_QUALITY); 
      g2d.setRenderingHint(RenderingHints.KEY_DITHERING, RenderingHints.VALUE_DITHER_ENABLE); 
      g2d.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON); 
      g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR); 
      g2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY); 
      g2d.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE); 
      int x = (getWidth() - background.getWidth())/2; 
      int y = (getHeight() - background.getHeight())/2; 
      g2d.rotate(Math.toRadians(-(direction + 180)), getWidth()/2, getHeight()/2); 
      g2d.drawImage(background, x, y, this); 
      g2d.dispose(); 

//   Debug graphics... 
//   int cx = getWidth()/2; 
//   int cy = getHeight()/2; 
// 
//   g2d = (Graphics2D) g.create(); 
//   g2d.setColor(Color.BLUE); 
//   double radius = Math.min(getWidth(), getHeight()); 
//   Point2D pointAt = getPointAt(radius, direction); 
//   g2d.draw(new Ellipse2D.Double(cx - (radius/2d), cy - (radius/2d), radius, radius)); 
//    
//   double xo = cx; 
//   double yo = cy; 
//   double xPos = cx + pointAt.getX(); 
//   double yPos = cy + pointAt.getY(); 
//    
//   g2d.draw(new Line2D.Double(xo, yo, xPos, yPos)); 
//   g2d.draw(new Ellipse2D.Double(xPos - 2, yPos - 2, 4, 4)); 
//   g2d.dispose(); 
     } 

     @Override 
     public void invalidate() { 
     } 

     @Override 
     public void validate() { 
     } 

     @Override 
     public void revalidate() { 
     } 

     @Override 
     public void repaint(long tm, int x, int y, int width, int height) { 
     } 

     @Override 
     public void repaint(Rectangle r) { 
     } 

     @Override 
     public void repaint() { 
     } 

     @Override 
     protected void firePropertyChange(String propertyName, Object oldValue, Object newValue) { 
//   System.out.println(propertyName); 
//   // Strings get interned... 
//   if (propertyName == "text" 
//       || propertyName == "labelFor" 
//       || propertyName == "displayedMnemonic" 
//       || ((propertyName == "font" || propertyName == "foreground") 
//       && oldValue != newValue 
//       && getClientProperty(javax.swing.plaf.basic.BasicHTML.propertyKey) != null)) { 
// 
//    super.firePropertyChange(propertyName, oldValue, newValue); 
//   } 
     } 

     @Override 
     public void firePropertyChange(String propertyName, boolean oldValue, boolean newValue) { 
     } 
    } 
} 

PS - 我要補充1我十個月大愛它的2-它讓我想起運行的工作:P

+0

+1好玩;也可以用[RobotChase](http:// sourceforge。net/p/robotchase/code/67/tree/trunk/images /)演員。 – trashgod 2013-02-15 21:05:57

+0

@MadProgrammer - 謝謝你。將研究你的代碼,看看我能從中學到什麼(可能很多!)。出於好奇,你在Swing編程多久了?我剛剛剛剛開始一個月,但發現它壓倒性的,因爲有很多不同的方式做事情,有時很難弄清楚什麼是最好的等。這只是我有一個問題,或者它是一般的東西新手體驗?我希望我會像自己一樣好,並且有一天會被垃圾處理,儘管現在看起來它非常非常遙遠! – DSF 2013-02-16 17:44:06

+3

自Java 1.3以來,我一直在使用Java/Swing,大約在1999年左右。每個開發人員都面臨的問題是,我認爲你知道的越多,得到的最差。不同之處在於,你傾向於能夠更快地拋棄某些想法的經驗。不要害怕嘗試某些東西並放棄它。你可以做的最糟糕的事情就是繼續下去,因爲你已經投入了很多時間。如果你有更好的辦法,最好重新開始 – MadProgrammer 2013-02-16 20:01:40