Component.setBounds调用Component.repaint?

所以我正在制作游戏,我有EnemyAIplayer ,他们都扩展了JPanel 。 世界有一个null布局,所以我使用setBounds(); “移动”(我实际上只是移动世界图像) entitiesplayerAI )并正确定位它们。 但是当我添加(看起来像是我测试的最小数量)5时,它会完全调用repaint() 。 这使得玩家在视觉上行走到位。 我添加的实体越多,间隔越快(即5个实体调用repaint()比500慢很多)。

注意:下面的类window只是一个JFrame

主类:

 public class Game(){ public static boolean fighting = false; public static void startGame(){ WorldPanel game = new WorldPanel(); game.setPreferredSize(new Dimension(window.getWidth(), window.getHeight())); PlayerPane player = new PlayerPane(32,32, "Player 1"); game.addKeyListener(new KeyListener(){ public void keyPressed(KeyEvent arg0) { if(fighting == false){ move(player, game, arg0.isShiftDown(), arg0.getKeyCode()); } else { System.out.println("Fighting = " + fighting); } } @Override public void keyReleased(KeyEvent arg0) { gameTimer.stop(); player.setIndex(0); game.repaint(); } @Override public void keyTyped(KeyEvent arg0) { } }); window.add(game); game.setLayout(null); game.requestFocus(); setImages(player, PLAYER_DOWN); player.setDrawY(349); player.setDrawX(493); player.updateBounds(); game.add(player); entities.add(player); addEntities(game); redoWindow(); } public static void updateEntityBounds(){ PlayerPane player = null; EnemyAI enemy = null; for(int i = 0; i < entities.size(); i++){ if(i == 0){ player = (PlayerPane) entities.get(i); } else { enemy = (EnemyAI) entities.get(i); if(enemy.getBounds().intersects(player.getBounds())){ startFight(i); } } if(player != null){ player.updateBounds(); } if(enemy != null){ enemy.updateBounds(); } } } public static void addEntities(WorldPanel game){ EnemyAI enemies[] = new EnemyAI[5]; for(int i = 0; i  10) || player.getDrawY() >= 349)){ player.setDrawY(player.getDrawY() - 2); setImages(player, PLAYER_UP); gameTimer.start(); } else if(keyPressed == KeyEvent.VK_S && ((game.getImageY() == -3868 && player.getDrawY() < 681) || player.getDrawY()  10) || player.getDrawX() > 493 )){ player.setDrawX(player.getDrawX() - 2); setImages(player, PLAYER_LEFT); gameTimer.start(); } else if(keyPressed == KeyEvent.VK_D && ((game.getImageX() == -5126 && player.getDrawX() < player.getHeight() - 10) || player.getDrawX() < 493 )){ player.setDrawX(player.getDrawX() + 2); setImages(player, PLAYER_RIGHT); gameTimer.start(); } else if(keyPressed == KeyEvent.VK_W && game.getImageY() < 0){ if(game.getImageY() == -1){ game.setImageY(game.getImageY() + 1); } else { game.setImageY(game.getImageY() + 2); } for(int i = 1; i < entities.size(); i++){ EnemyAI enemy = (EnemyAI)entities.get(i); enemy.setEnY(enemy.getEnY() + 2); } setImages(player, PLAYER_UP); gameTimer.start(); } else if(keyPressed == KeyEvent.VK_A && game.getImageX() < 0){ if(game.getImageX() == -1){ game.setImageX(game.getImageX() + 1); } else { game.setImageX(game.getImageX() + 2); } for(int i = 1; i  -3868){ if(game.getImageY() == -3867){ game.setImageY(game.getImageY() - 1); } else { game.setImageY(game.getImageY() - 2); } for(int i = 1; i  -5126){ if(game.getImageX() == -5125){ game.setImageX(game.getImageX() - 1); } else { game.setImageX(game.getImageX() - 2); } for(int i = 1; i  10) || player.getDrawY() > 349)){ player.setDrawY(player.getDrawY() - 1); setImages(player, PLAYER_UP); gameTimer.start(); } else if(keyPressed == KeyEvent.VK_S && ((game.getImageY() == -3868 && player.getDrawY() < 681) || player.getDrawY()  10) || player.getDrawX() > 493 )){ player.setDrawX(player.getDrawX() - 1); setImages(player, PLAYER_LEFT); gameTimer.start(); } else if(keyPressed == KeyEvent.VK_D && ((game.getImageX() == -5126 && player.getDrawX() < player.getHeight() - 10) || player.getDrawX() < 493 )){ player.setDrawX(player.getDrawX() + 1); setImages(player, PLAYER_RIGHT); gameTimer.start(); } else if(keyPressed == KeyEvent.VK_W && game.getImageY() < 0){ game.setImageY(game.getImageY() + 1); setImages(player, PLAYER_UP); for(int i = 1; i < entities.size(); i++){ EnemyAI enemy = (EnemyAI)entities.get(i); enemy.setEnY(enemy.getEnY() + 1); } gameTimer.start(); } else if(keyPressed == KeyEvent.VK_A && game.getImageX() < 0){ game.setImageX(game.getImageX() + 1); setImages(player, PLAYER_LEFT); for(int i = 1; i  -3868){ game.setImageY(game.getImageY() - 1); setImages(player, PLAYER_DOWN); for(int i = 1; i  -5126){ game.setImageX(game.getImageX() - 1); setImages(player, PLAYER_RIGHT); for(int i = 1; i < entities.size(); i++){ EnemyAI enemy = (EnemyAI)entities.get(i); enemy.setEnX(enemy.getEnX() - 1); } gameTimer.start(); } } } } 

玩家:

 public class PlayerPane extends JPanel{ private static final long serialVersionUID = 8946273935579723365L; private ImageIcon images[] = new ImageIcon[9]; public static final int PLAYER_FRAMES = 9; private String name; private int index; private int imageX, imageY; private int HP = 100; public PlayerPane(int width, int height, String playerName) { setPreferredSize(new Dimension(width, height)); imageX = imageY = 0; name = playerName; // Border border = BorderFactory.createBevelBorder(BevelBorder.RAISED); // PlayerPane.this.setBorder(border); } public String getName(){ return name; } public void paintComponent(Graphics g){ try { g.drawImage(ImageIO.read(new File("H:\\Java\\Game\\src\\res\\TransparentImg.png")),0,0,getWidth(),getHeight(), null); } catch (IOException e) { e.printStackTrace(); } g.drawImage(images[index].getImage(), 0,0, null); if(index == images.length-1){ index = 0; } else { index++; } } public void setIndex(int index){ this.index = index; } public void stop(){ index = 0; } public void addImage(ImageIcon image, int x){ images[x] = image; } public void setDrawX(int x){ imageX = x; } public void setDrawY(int y){ imageY = y; } public int getDrawY(){ return imageY; } public int getDrawX(){ return imageX; } public void updateBounds(){ setBounds(imageX, imageY, 32,32); } public int getHP(){ return HP; } public void setHP(int hp){ HP = hp; } } 

EnemyAI类:

 public class EnemyAI extends AI{ private static final long serialVersionUID = -2417438750134536982L; private Rectangle rect; private BufferedImage backgroundImg; private int HP = 1; private int damage = 15; public EnemyAI(int width, int height, BufferedImage backgroundImg){ super(); rect = new Rectangle(width, height); this.backgroundImg = backgroundImg; } @Override public void paintComponent(Graphics g) { try{ g.drawImage(ImageIO.read(new File("H:\\Java\\Game\\src\\res\\TransparentImg.png")), 0,0,null); }catch(IOException e){ e.printStackTrace(); } g.drawImage(backgroundImg, 0, 0, null); } public boolean intersects(Rectangle r){ return rect.intersects(r); } public int getRandomX(){ Random ran = new Random(); int rand = ran.nextInt(6144); return rand; } public int getRandomY(){ Random ran = new Random(); int rand = ran.nextInt(4608); return rand; } public int getHP(){ return HP; } public void setHP(int hp){ HP = hp; } public int getDamage(){ return damage; } } 

上述类扩展的AI类:

 public abstract class AI extends JPanel implements Runnable{ private static final long serialVersionUID = 283692586329054555L; private boolean running = false; private Thread moveThread; private int x = 0, y = 0; public AI(){ moveThread = new Thread(this); } public void start(){ running = true; moveThread.start(); } public void stop(){ running = false; } public boolean isRunning(){ return running; } public void run(){ while(running){ if(Game.fighting == false){ Random direction = new Random(); int dir = direction.nextInt(4); switch(dir){ case 1: if(this.getX()  0){ this.setEnX(this.getX() - 1); } break; case 3: if(this.getX() < Game.getWindowHeight()){ this.setEnY(this.getY() + 1); } break; case 4: if(this.getX() < 0){ this.setEnY(this.getY() - 1); } break; } updateBounds(); try{ Thread.sleep(200); }catch(InterruptedException e){ e.printStackTrace(); } } } } public void setEnX(int x){ this.x = x; } public void setEnY(int y){ this.y = y; } public int getEnX(){ return x; } public int getEnY(){ return y; } public void updateBounds(){ setBounds(x, y, 32,32); } public abstract void paintComponent(Graphics g); } 

我知道我只是向你们扔了很多代码。 我尽力不去,但回头看它我试图提供一个运行的例子。 如果我错过了任何代码,请告诉我,我会添加它。

无论如何,我需要知道的是如何使setBounds()停止调用repaint() (除了使用少于5个entities )。 此外,我在添加entities时删除了EnemyAI.start()并且确实停止了它。 所以我有理由相信问题在AIrun()方法中。 这几乎只调用setBounds()

这是一种正常行为,这就是为什么你不能修改paintComponent状态。 我们无法控制何时重绘:系统有时会自行完成。

这是我的意思的一个例子,你不应该这样做:

 public class PlayerPane extends JPanel{ ... public void paintComponent(Graphics g){ ... // modifying index if(index == images.length-1){ index = 0; } else { index++; } } } 

您需要查看所有代码,查找您在paintComponent修改此变量的每个位置,并将其移出到其他位置。

作为旁注,您还应该移动ImageIO.read调用,使它们不在paintComponent 。 程序启动时加载一次图像, static变量或类似的东西。

作为一般提示,您应该只使用绘画来查看动画,而不是尝试为组件设置动画。 从长远来看,它将为你带来巨大的好处。


总结如下:

  • 保持paintComponent无状态。
  • 在非UI对象中包含具有游戏/动画状态的图像。
  • paintComponent绘制这些图像。

这是一个最小的例子,它通过动画从窗口落下的形状来演示:

坠落的形状

 import java.net.*; import javax.swing.*; import javax.imageio.*; import java.awt.image.*; import java.awt.event.*; import java.awt.Dimension; import java.awt.Color; import java.awt.Graphics; import java.util.List; import java.util.ArrayList; import java.util.Random; class FallingShapes implements Runnable { public static void main(String[] args) { SwingUtilities.invokeLater(new FallingShapes()); } @Override public void run() { List entities = new ArrayList(); int w = 0; int h = 0; for (BufferedImage img : Resources.images) { entities.add(new Entity(img)); w += img.getWidth(); h += img.getHeight(); } PaintPanel p = new PaintPanel(entities); p.setPreferredSize(new Dimension(w, (2 * h))); JFrame f = new JFrame(); f.setContentPane(p); f.pack(); f.setLocationRelativeTo(null); f.setResizable(false); f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); f.setVisible(true); new Animator((1000 / 60), p, entities).start(); } static class Animator implements ActionListener { int period; JPanel context; int height; List entities; Animator(int period, JPanel context, List entities) { this.context = context; this.height = context.getHeight(); this.period = period; this.entities = entities; } @Override public void actionPerformed(ActionEvent a) { for (Entity e : entities) { double dist = (period / 1000.0) * (height * e.rate); ey += dist; ey %= height; } context.repaint(); } void start() { Random r = new Random(); int x = 0; for (Entity e : entities) { ex = x; ey = r.nextInt(height); e.rate = (0.25 + (0.75 * r.nextDouble())); x += e.width; } new Timer(period, this).start(); } } static class Entity { BufferedImage img; double x, y, rate; int width, height; Entity(BufferedImage img) { this.img = img; this.width = img.getWidth(); this.height = img.getHeight(); } void paint(Graphics g, JPanel context) { int x = (int) Math.round(this.x); int y = (int) Math.round(this.y); g.drawImage(img, x, y, null); int cHeight = context.getHeight(); if ((y + height) > cHeight) { g.drawImage(img, x, y - cHeight, null); } } } static class PaintPanel extends JPanel { List entities; PaintPanel(List entities) { this.entities = entities; setBackground(Color.white); } @Override protected void paintComponent(Graphics g) { super.paintComponent(g); for (Entity e : entities) { e.paint(g, this); } } } static class Resources { static final String[] paths = { "http://i.stack.imgur.com/wCF8S.png", "http://i.stack.imgur.com/5v2TX.png", "http://i.stack.imgur.com/F0JHK.png", "http://i.stack.imgur.com/4EVv1.png", "http://i.stack.imgur.com/xj49g.png", }; static final List images = new ArrayList(); static { for (String path : paths) { try { images.add(ImageIO.read(new URL(path))); } catch (Exception e) { throw new AssertionError(e); } } } } } 

(图片来自这里 。)

其他有用的动画和绘画示例:

  • Java Bouncing Ball
  • Java游戏循环(绘画)冻结了我的窗口
  • 这是使用Java 2D Graphics API的正确方法吗?