移动玩家时滞后秒杀

播放器是一个面板,它将被移除,其位置发生变化,然后重新添加到另一个面板(这是包含此方法的面板),该面板被绘制到主框架。 还有许多其他小面板包含一个草精灵被绘制到主面板作为地形瓷砖。 我认为问题在于,当我调用revalidate() ,它也会重新validation所有这些小面板。 我怎么解决这个问题?

编辑:我应该提一下,我正在使用RelativeLayout来定位主要面板上的玩家。

 private class MainKeyAdapter extends KeyAdapter { @Override public void keyPressed(KeyEvent evt) { // TODO add your handling code here: if(AttentionedPlayer != null) { if (evt.getKeyCode() == KeyEvent.VK_UP) { AttentionedPlayer.Ypos -= 16; } if (evt.getKeyCode() == KeyEvent.VK_DOWN) { AttentionedPlayer.Ypos += 16; } if (evt.getKeyCode() == KeyEvent.VK_LEFT) { AttentionedPlayer.Xpos -= 16; } if (evt.getKeyCode() == KeyEvent.VK_RIGHT) { AttentionedPlayer.Xpos += 16; } remove(AttentionedPlayer); AttentionedPlayer.movePlayer(); System.out.println("!!!!"+AttentionedPlayer.constraints.toString()); add(AttentionedPlayer, AttentionedPlayer.constraints, AttentionedPlayer.Zpos); AttentionedPlayer.revalidate(); } } } 

AttentionedPlayer.movePlayer(); 似乎是一个密集的操作,你从EDT (GUI线程)内执行它。 而是从新线程或SwingWorker中执行它。

阅读此答案以了解有关SwingWorker的更多信息。

@ Eng.Fouad对他有一个好点+1,虽然我个人从来没有因为这个原因而需要这个,但你的移动方法可能非常密集。

只是为了展示一个例子(从我的评论中扩展)使用你的JPanel游戏逻辑 ,如果正确实现,就不需要在玩家移动时revalidate() (通过setLocation(..) )IMO也可能导致大量的滞后特别是如果有很多组件。 正如您将看到我的GamePanel扩展JPanel并使用Null / Absolute Layout(但在游戏中我们希望更多地控制布局)。

还使用KeyBinding来向您展示它们的用途。

在此处输入图像描述

 import java.awt.Color; import java.awt.Dimension; import java.awt.Graphics; import java.awt.event.ActionEvent; import javax.swing.AbstractAction; import javax.swing.JComponent; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.KeyStroke; import javax.swing.SwingUtilities; import javax.swing.border.LineBorder; public class GameLogic { public GameLogic() { initComponents(); } private void initComponents() { JFrame frame = new JFrame(); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); Entity entity = new Entity("Player", 100, 100, 50, 50);//starting location 100,100 and width/height 50x50 GamePanel gp = new GamePanel(300, 300); gp.addEntity(entity); setGamePanelKeyBindings(gp, entity); frame.add(gp); frame.pack(); frame.setVisible(true); } private void setGamePanelKeyBindings(GamePanel gp, final Entity entity) { gp.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke("D"), "D pressed"); gp.getActionMap().put("D pressed", new AbstractAction() { @Override public void actionPerformed(ActionEvent ae) { entity.move(Entity.RIGHT); } }); gp.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke("A"), "A pressed"); gp.getActionMap().put("A pressed", new AbstractAction() { @Override public void actionPerformed(ActionEvent ae) { entity.move(Entity.LEFT); } }); gp.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke("W"), "W pressed"); gp.getActionMap().put("W pressed", new AbstractAction() { @Override public void actionPerformed(ActionEvent ae) { entity.move(Entity.UP); } }); gp.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke("S"), "S pressed"); gp.getActionMap().put("S pressed", new AbstractAction() { @Override public void actionPerformed(ActionEvent ae) { entity.move(Entity.DOWN); } }); } public static void main(String[] args) { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { new GameLogic(); } }); } } class Entity extends JPanel { private int width = 50, height = 50; private int speed = 5; public static final int UP = 1, DOWN = 2, LEFT = 3, RIGHT = 4; public Entity(String text, int x, int y, int width, int height) { this.width = width; this.height = height; add(new JLabel(text)); setBorder(new LineBorder(Color.BLACK)); setBounds(x, y, width, height); } public void move(int direction) { switch (direction) { case UP: setLocation(getX(), getY() - speed); break; case DOWN: setLocation(getX(), getY() + speed); break; case LEFT: setLocation(getX() - speed, getY()); break; case RIGHT: setLocation(getX() + speed, getY()); break; } } @Override public void setBounds(int x, int y, int w, int h) { super.setBounds(x, y, width, height); } @Override protected void paintComponent(Graphics grphcs) { super.paintComponent(grphcs); grphcs.setColor(Color.CYAN); grphcs.fillRect(0, 0, getWidth(), getHeight()); } } class GamePanel extends JPanel { private int width, height; GamePanel(int w, int h) { setLayout(null); width = w; height = h; } @Override public Dimension getPreferredSize() { return new Dimension(width, height); } public void addEntity(Entity e) { add(e); } @Override protected void paintComponent(Graphics grphcs) { super.paintComponent(grphcs); grphcs.setColor(Color.GREEN); grphcs.fillRect(0, 0, getWidth(), getHeight()); } } 

对不起,我无法阻止自己

如果你感兴趣这里有一个高级版本的游戏环可以暂停,帧率等可以设置,2键(如WD可以同时按下,从而导致JPanel对角移动):

 import java.awt.Color; import java.awt.Dimension; import java.awt.Graphics; import java.awt.event.ActionEvent; import java.util.ArrayList; import javax.swing.AbstractAction; import javax.swing.JComponent; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.KeyStroke; import javax.swing.SwingUtilities; import javax.swing.border.LineBorder; public class GameLogic { public GameLogic() { initComponents(); } final GamePanel gp = new GamePanel(500, 500); private void initComponents() { JFrame frame = new JFrame(); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); Entity entity = new Entity("Player", 100, 100, 100, 100); gp.addEntity(entity); setGamePanelKeyBindings(gp, entity); frame.add(gp); frame.pack(); frame.setVisible(true); //start the game loop which will repaint the screen runGameLoop(); } //Starts a new thread and runs the game loop in it. public void runGameLoop() { Thread loop = new Thread(new Runnable() { @Override public void run() { gp.running = true; gp.gameLoop(); } }); loop.start(); } private void setGamePanelKeyBindings(GamePanel gp, final Entity entity) { gp.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke("D"), "D pressed"); gp.getActionMap().put("D pressed", new AbstractAction() { @Override public void actionPerformed(ActionEvent ae) { entity.RIGHT = true; } }); gp.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke("A"), "A pressed"); gp.getActionMap().put("A pressed", new AbstractAction() { @Override public void actionPerformed(ActionEvent ae) { entity.LEFT = true; } }); gp.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke("W"), "W pressed"); gp.getActionMap().put("W pressed", new AbstractAction() { @Override public void actionPerformed(ActionEvent ae) { entity.UP = true; } }); gp.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke("S"), "S pressed"); gp.getActionMap().put("S pressed", new AbstractAction() { @Override public void actionPerformed(ActionEvent ae) { entity.DOWN = true; } }); gp.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke("released D"), "D released"); gp.getActionMap().put("D released", new AbstractAction() { @Override public void actionPerformed(ActionEvent ae) { entity.RIGHT = false; } }); gp.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke("released A"), "A released"); gp.getActionMap().put("A released", new AbstractAction() { @Override public void actionPerformed(ActionEvent ae) { entity.LEFT = false; } }); gp.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke("released W"), "W released"); gp.getActionMap().put("W released", new AbstractAction() { @Override public void actionPerformed(ActionEvent ae) { entity.UP = false; } }); gp.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke("released S"), "S released"); gp.getActionMap().put("S released", new AbstractAction() { @Override public void actionPerformed(ActionEvent ae) { entity.DOWN = false; } }); } public static void main(String[] args) { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { new GameLogic(); } }); } } class Entity extends JPanel { private int width = 50, height = 50; private int speed = 5; public boolean UP = false, DOWN = false, LEFT = false, RIGHT = false; public Entity(String text, int x, int y, int width, int height) { this.width = width; this.height = height; add(new JLabel(text)); setBorder(new LineBorder(Color.BLACK)); setBounds(x, y, width, height); } public void move() { if (UP) { setLocation(getX(), getY() - speed); } if (DOWN) { setLocation(getX(), getY() + speed); } if (LEFT) { setLocation(getX() - speed, getY()); } if (RIGHT) { setLocation(getX() + speed, getY()); } } @Override public void setBounds(int x, int y, int w, int h) { super.setBounds(x, y, width, height); } @Override protected void paintComponent(Graphics grphcs) { super.paintComponent(grphcs); grphcs.setColor(Color.CYAN); grphcs.fillRect(0, 0, getWidth(), getHeight()); } } class GamePanel extends JPanel { private int width, height; private int frameCount = 0; private int fps = 0; public static boolean running = false, paused = false; final ArrayList entities = new ArrayList<>(); GamePanel(int w, int h) { setLayout(null); width = w; height = h; } @Override public Dimension getPreferredSize() { return new Dimension(width, height); } public void addEntity(Entity e) { add(e); entities.add(e); } @Override protected void paintComponent(Graphics grphcs) { super.paintComponent(grphcs); grphcs.setColor(Color.GREEN); grphcs.fillRect(0, 0, getWidth(), getHeight()); grphcs.setColor(Color.BLACK); grphcs.drawString("FPS: " + fps, 5, 10); frameCount++; } //Only run this in another Thread! public void gameLoop() { //This value would probably be stored elsewhere. final double GAME_HERTZ = 30.0; //Calculate how many ns each frame should take for our target game hertz. final double TIME_BETWEEN_UPDATES = 1000000000 / GAME_HERTZ; //At the very most we will update the game this many times before a new render. //If you're worried about visual hitches more than perfect timing, set this to 1. final int MAX_UPDATES_BEFORE_RENDER = 5; //We will need the last update time. double lastUpdateTime = System.nanoTime(); //Store the last time we rendered. double lastRenderTime = System.nanoTime(); //If we are able to get as high as this FPS, don't render again. final double TARGET_FPS = 60; final double TARGET_TIME_BETWEEN_RENDERS = 1000000000 / TARGET_FPS; //Simple way of finding FPS. int lastSecondTime = (int) (lastUpdateTime / 1000000000); while (running) { double now = System.nanoTime(); int updateCount = 0; if (!paused) { //Do as many game updates as we need to, potentially playing catchup. while (now - lastUpdateTime > TIME_BETWEEN_UPDATES && updateCount < MAX_UPDATES_BEFORE_RENDER) { updateGame(); lastUpdateTime += TIME_BETWEEN_UPDATES; updateCount++; } //If for some reason an update takes forever, we don't want to do an insane number of catchups. //If you were doing some sort of game that needed to keep EXACT time, you would get rid of this. if (now - lastUpdateTime > TIME_BETWEEN_UPDATES) { lastUpdateTime = now - TIME_BETWEEN_UPDATES; } drawGame(); lastRenderTime = now; //Update the frames we got. int thisSecond = (int) (lastUpdateTime / 1000000000); if (thisSecond > lastSecondTime) { System.out.println("NEW SECOND " + thisSecond + " " + frameCount); fps = frameCount; frameCount = 0; lastSecondTime = thisSecond; } //Yield until it has been at least the target time between renders. This saves the CPU from hogging. while (now - lastRenderTime < TARGET_TIME_BETWEEN_RENDERS && now - lastUpdateTime < TIME_BETWEEN_UPDATES) { //allow the threading system to play threads that are waiting to run. Thread.yield(); //This stops the app from consuming all your CPU. It makes this slightly less accurate, but is worth it. //You can remove this line and it will still work (better), your CPU just climbs on certain OSes. //FYI on some OS's this can cause pretty bad stuttering. Scroll down and have a look at different peoples' solutions to this. //On my OS it does not unpuase the game if i take this away try { Thread.sleep(1); } catch (Exception e) { } now = System.nanoTime(); } } } } private void updateGame() { for (Entity e : entities) { e.move(); } } private void drawGame() { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { repaint(); } }); } }