Problems with Java's Paint method, ridiculous refresh velocity

后端 未结 3 1428
爱一瞬间的悲伤
爱一瞬间的悲伤 2020-11-27 23:12

I\'m developing a very simple version of R-Type as work for the university, but despite it works, the craft velocity is a lot of slow, so the movement is ugly and clumsy. I

相关标签:
3条回答
  • 2020-11-27 23:45

    Below is simple example using a background as simple game loop. It updates the state of the game objects and calculates the required delay in order to maintain the required fps.

    The game object (Ship) has the ability to accelerate/decelerate over a short period of time

    import java.awt.BorderLayout;
    import java.awt.Dimension;
    import java.awt.EventQueue;
    import java.awt.Graphics;
    import java.awt.Graphics2D;
    import java.awt.event.ActionEvent;
    import java.awt.event.KeyEvent;
    import java.awt.geom.Path2D;
    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.UIManager;
    import javax.swing.UnsupportedLookAndFeelException;
    
    public class AnimationTest {
    
        public static void main(String[] args) {
            new AnimationTest();
        }
    
        public AnimationTest() {
            EventQueue.invokeLater(new Runnable() {
                @Override
                public void run() {
                    try {
                        UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                    } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                    }
    
                    JFrame frame = new JFrame("Test");
                    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                    frame.setLayout(new BorderLayout());
                    frame.add(new GamePane());
                    frame.pack();
                    frame.setLocationRelativeTo(null);
                    frame.setVisible(true);
                }
    
            });
        }
    
        public class GamePane extends JPanel {
    
            private Ship ship;
    
            public GamePane() {
    
                ship = new Ship();
                Thread thread = new Thread(new MainLoop(this));
                thread.setDaemon(true);
                thread.start();
    
                InputMap im = getInputMap(WHEN_IN_FOCUSED_WINDOW);
                ActionMap am = getActionMap();
    
                // Key controls...
                im.put(KeyStroke.getKeyStroke(KeyEvent.VK_UP, 0, false), "upPressed");
                im.put(KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, 0, false), "downPressed");
                im.put(KeyStroke.getKeyStroke(KeyEvent.VK_UP, 0, true), "upReleased");
                im.put(KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, 0, true), "downReleased");
    
                am.put("upPressed", new AbstractAction() {
                    @Override
                    public void actionPerformed(ActionEvent e) {
                        // Change the direction...
                        ship.setDirection(-1);
                        // Accelerate by 1 per frame
                        ship.setVelocity(1);
                    }
    
                });
                am.put("downPressed", new AbstractAction() {
                    @Override
                    public void actionPerformed(ActionEvent e) {
                        // Change direction
                        ship.setDirection(1);
                        // Accelerate by 1 per frame
                        ship.setVelocity(1);
                    }
    
                });
                am.put("upReleased", new AbstractAction() {
                    @Override
                    public void actionPerformed(ActionEvent e) {
                        // Deccelerate by 1 per frame
                        ship.setVelocity(-1);
                    }
    
                });
                am.put("downReleased", new AbstractAction() {
                    @Override
                    public void actionPerformed(ActionEvent e) {
                        // Deccelerate by 1 per frame
                        ship.setVelocity(-1);
                    }
    
                });
            }
    
            @Override
            public Dimension getPreferredSize() {
                return new Dimension(200, 200);
            }
    
            public void updateState() {
                // Update the state of the game objects.
                // This would typically be better done in 
                // some kind of model
                ship.update(getWidth(), getHeight());
            }
    
            @Override
            protected void paintComponent(Graphics g) {
                super.paintComponent(g);
                // Paint the game state...
                Graphics2D g2d = (Graphics2D) g.create();
                ship.paint(g2d);
                g2d.dispose();
            }
    
        }
    
        public class MainLoop implements Runnable {
    
            private GamePane pane;
            private int fps = 25;
    
            public MainLoop(GamePane pane) {
                this.pane = pane;
            }
    
            @Override
            public void run() {
                // Wait until the screen is ready
                while (pane.getHeight() <= 0) {
                    try {
                        Thread.sleep(125);
                    } catch (InterruptedException ex) {
                    }
                }
                // Main loop
                while (true) {
                    // Start time loop
                    long startTime = System.currentTimeMillis();
                    // Update the game state
                    pane.updateState();
                    // Calculate the amount of time it took to update
                    long elasped = System.currentTimeMillis() - startTime;
                    // Calculate the number of milliseconds we need to sleep
                    long sleep = Math.round((1000f / fps) - elasped);
                    pane.repaint();
                    if (sleep > 0) {
                        try {
                            Thread.sleep(sleep);
                        } catch (InterruptedException ex) {
                        }
                    }
                }
            }
    
        }
    
        public static class Ship {
    
            public static int MAX_SPEED = 8;
            private int direction = 0;
            private int velocity = 0;
            private int x;
            private int y;
            private int speed = 0;
            private Path2D shape;
            private boolean initState;
    
            public Ship() {
                shape = new Path2D.Float();
                shape.moveTo(0, 0);
                shape.lineTo(5, 5);
                shape.lineTo(0, 10);
                shape.lineTo(0, 0);
                shape.closePath();
                initState = true;
            }
    
            public void setDirection(int direction) {
                this.direction = direction;
            }
    
            public void setVelocity(int velocity) {
                this.velocity = velocity;
            }
    
            public void update(int width, int height) {
                if (initState) {
                    y = (height - 10) / 2;
                    initState = false;
                } else {
                    // Add the velocity to the speed
                    speed += velocity;
                    // Don't over accelerate
                    if (speed > MAX_SPEED) {
                        speed = MAX_SPEED;
                    } else if (speed < 0) {
                        speed = 0;
                    }
                    // Adjust out position if we're moving
                    if (speed > 0) {
                        y += (direction * speed);
                    }
    
                    // Bounds check...
                    if (y - 5 < 0) {
                        y = 5;
                    } else if (y + 5 > height) {
                        y = height - 5;
                    }
                }
            }
    
            public void paint(Graphics2D g2d) {
                int yPos = y - 5;
                g2d.translate(10, yPos);
                g2d.fill(shape);
                g2d.translate(-10, -yPos);
            }
    
        }
    
    }
    
    0 讨论(0)
  • 2020-11-27 23:51
    • The image fondo should already be scaled to 1200x600.
    • I am not sure, but is super.paint(g) needed? You might also use paintComponent.

    The event handling (you seem to be moving by 1 pixel on key down), must be done correctly. I would have set the direction and speed (1px), and leave it to a swing timer to do the continuous moving.

    Repainting best is done resilient/flexible: repaint(20L) (50 frames per second); events like key-down maybe with EventQueue.invokeLater(new Runnable() { ... });.

    Especially you might use repaint with the changed area.

    0 讨论(0)
  • 2020-11-27 23:55

    You can find a great example of a similar program here. The example demonstrates creating a new thread and having that thread sleep every iteration through the main loop.

    Here is another question about loading images for games in Java.

    It looks like swing itself is pretty crummy for using images in games. You may want to consider using a more suitable library.

    0 讨论(0)
提交回复
热议问题