2013-02-14 154 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