Why does the height of the rectangles jump vary?

2019-08-06 12:49发布

Why does the height of the rectangle's jump vary? It seems to go in a cycle. First it jumps low then it doesn't jump at all then it jumps high then it doesn't jump at all. i can't figure out why as the same code is used and it is triggered by the same event.

import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.event.KeyEvent;

import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;

import javax.swing.JFrame;
import javax.swing.JPanel;


@SuppressWarnings("serial")
 class Game extends JPanel{
Square square = new Square(this);
 Ground ground = new Ground (this);
public Game() {
    addKeyListener(new KeyListener() {
        @Override
        public void keyTyped(KeyEvent e) {
        }

        @Override
        public void keyReleased(KeyEvent e) {
            square.keyReleased(e);
        }

        @Override
        public void keyPressed(KeyEvent e) {
            square.keyPressed(e);
        }

    });
    setFocusable(true);
}
public static void main(String[] args) throws InterruptedException {

    JFrame frame = new JFrame("My Mario");
    Game game = new Game();
    frame.add(game);
    frame.setSize(600, 700);
    frame.setVisible(true);

    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);


    while (true) {
        game.move();
        game.repaint();
        Thread.sleep(30);





    }

}
private void move() {

    square.move();
}
@Override
public void paint(Graphics g) {
    super.paint(g);
    Graphics2D g2d = (Graphics2D) g;
    g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
            RenderingHints.VALUE_ANTIALIAS_ON);
    square.paint(g2d);
    ground.paint(g2d);
}
}

public class Square {

Square square;
int x,xa;
static int y;
int ya;
private Game game;
public static int fistX,fistY;
static int d = 60;
int wide;
boolean onGround;

public Square(Game game) {
this.game = game;
x = 100;
y = 631;
xa = 0;
ya = 0;
onGround = false;
wide = game.getWidth();
}

public void move() {


if (x + xa > 0 && x + xa < game.getWidth()-30)
    x = x + xa;

if (y + ya > 0 && y + ya < game.getHeight()-60){


    for(int i=12; i< 0; i--);
    ya+=10;
    y = y + ya;
}
if  (  collision()  ) {
    y-=10;
    onGround = true;

}

Square.y+=10;



}



public void paint(Graphics2D g) {
g.setColor(Color.RED);
g.fillRoundRect(x, y-d, 30, d, 10, 10);



}
private boolean collision() {
return game.ground.getBounds().intersects(getBounds());
}
public Rectangle getBounds() {
return new Rectangle(x, y, 30, 60);
}
public void keyReleased(KeyEvent e) {



if (e.getKeyCode() == KeyEvent.VK_LEFT)
xa=0;

if (e.getKeyCode() == KeyEvent.VK_RIGHT)
    xa=0;
if(e.getKeyCode() == KeyEvent.VK_DOWN)
    d = 60;

if(e.getKeyCode() == KeyEvent.VK_UP);





}



public void keyPressed(KeyEvent e) {
// TODO Auto-generated method stub
if (e.getKeyCode() == KeyEvent.VK_LEFT)

    xa = xa -3;

if (e.getKeyCode() == KeyEvent.VK_RIGHT)

    xa = xa + 3;

if(e.getKeyCode() == KeyEvent.VK_DOWN)
    d = 30;

if(e.getKeyCode() == KeyEvent.VK_UP)

        ya  -= 60;





}









}


class Ground {

    int y,x,h,w;
public Ground(Game game){
    x = 0;
    y = game.getHeight()-30;
    w = game.getWidth();
    h = 30;
}
public void paint(Graphics2D g){
    g.setColor(Color.BLACK);
    g.fillRect(0, 700, 99999999, 30);

}
public Rectangle getBounds() {
return new Rectangle(0, 700, 99999999, 30);
}
}

2条回答
唯我独甜
2楼-- · 2019-08-06 13:11
  1. Don't block the EDT (Event Dispatch Thread) - the GUI will 'freeze' when that happens. Instead of calling Thread.sleep(n) implement a Swing Timer for repeated tasks like animation. See Concurrency in Swing for more details.
  2. frame.setSize(600, 700); I recommend instead setting a preferred size for the content and packing the frame around it. This suggests a constant size for the game on different PLAFs or OS'.
  3. For Swing components other than top-level containers (e.g JFrame or JWindow), override paintComponent(Graphics) rather than paint(Graphics).
  4. Look into key bindings instead of key listeners for Swing.

The first 3 are implemented, the last one (and other one commented in the code) is TODO - BNI.

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

class SuperMarioGame extends JPanel {

    private static final long serialVersionUID = 1L;
    Square square = new Square(this);
    Ground ground = new Ground (this);

    public SuperMarioGame() {
        // TODO Update to Key Bindings
        addKeyListener(new KeyListener() {
            @Override
            public void keyTyped(KeyEvent e) {
            }

            @Override
            public void keyReleased(KeyEvent e) {
                square.keyReleased(e);
            }

            @Override
            public void keyPressed(KeyEvent e) {
                square.keyPressed(e);
            }

        });
        setFocusable(true);

        // Use a listener/timer combo.
        ActionListener gameAnimation = new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent e) {
                move();
                repaint();
            }
        };
        Timer timer = new Timer(30,gameAnimation);
        timer.start();

        // Set a preferred size for the panel.
        Dimension preferred = new Dimension(600,700);
        this.setPreferredSize(preferred);
    }

    public static void main(String[] args) throws InterruptedException {
        JFrame frame = new JFrame("My Mario");
        SuperMarioGame game = new SuperMarioGame();
        frame.add(game);
        // Pack the frame to the preferred size.
        frame.pack();
        frame.setVisible(true);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    }

    private void move() {
        square.move();
    }

    @Override
    // for Swing components, generally override 
    // paintComponent rather than paint
    //public void paint(Graphics g) {
    public void paintComponent(Graphics g) {
        super.paint(g);
        Graphics2D g2d = (Graphics2D) g;
        g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                RenderingHints.VALUE_ANTIALIAS_ON);
        square.paint(g2d);
        ground.paint(g2d);
    }
}

class Square {

    Square square;
    int x,xa;
    static int y;
    int ya;
    private SuperMarioGame game;
    public static int fistX,fistY;
    static int d = 60;
    int wide;
    boolean onGround;

    public Square(SuperMarioGame game) {
        this.game = game;
        x = 100;
        y = 631;
        xa = 0;
        ya = 0;
        onGround = false;
        wide = game.getWidth();
    }

    public void move() {
        if (x + xa > 0 && x + xa < game.getWidth()-30)
            x = x + xa;

        if (y + ya > 0 && y + ya < game.getHeight()-60){
            for(int i=12; i< 0; i--);
            ya+=10;
            y = y + ya;
        }

        if  (  collision()  ) {
            y-=10;
            onGround = true;
        }

        Square.y+=10;
    }

    public void paint(Graphics2D g) {
        g.setColor(Color.RED);
        g.fillRoundRect(x, y-d, 30, d, 10, 10);
    }

    private boolean collision() {
        return game.ground.getBounds().intersects(getBounds());
    }

    public Rectangle getBounds() {
        return new Rectangle(x, y, 30, 60);
    }

    public void keyReleased(KeyEvent e) {
        // TODO Else-if would be better here..
        if (e.getKeyCode() == KeyEvent.VK_LEFT)
            xa=0;
        if (e.getKeyCode() == KeyEvent.VK_RIGHT)
            xa=0;
        if(e.getKeyCode() == KeyEvent.VK_DOWN)
            d = 60;
        if(e.getKeyCode() == KeyEvent.VK_UP);
    }

    public void keyPressed(KeyEvent e) {
        // TODO Else-if would be better here..
        if (e.getKeyCode() == KeyEvent.VK_LEFT)
            xa = xa -3;
        if (e.getKeyCode() == KeyEvent.VK_RIGHT)
            xa = xa + 3;
        if(e.getKeyCode() == KeyEvent.VK_DOWN)
            d = 30;
        if(e.getKeyCode() == KeyEvent.VK_UP)
            ya  -= 60;
    }
}

class Ground {

    int y,x,h,w;

    public Ground(SuperMarioGame game){
        x = 0;
        y = game.getHeight()-30;
        w = game.getWidth();
        h = 30;
    }

    public void paint(Graphics2D g){
        g.setColor(Color.BLACK);
        g.fillRect(0, 700, 99999999, 30);
    }

    public Rectangle getBounds() {
        return new Rectangle(0, 700, 99999999, 30);
    }
}
查看更多
爷、活的狠高调
3楼-- · 2019-08-06 13:15
  • Don't block the Event Dispatching Thread any way, it will prevent the EDT from processing repaint requests (and other events) which will make it look like your program has crashed. Have a read of Concurrency in Swing for more information.
  • Only modify the UI from the EDT, NEVER from any other thread, this includes creating UI elements
  • Favor key bindings over KeyListeners, they are more capable of resolving focus issues amongst other things. Have a read of How to Use Key Bindings for more details
  • Favor overriding paintComponent rather then paint. Paint does a lot of important work which you should avoid messing with if you can. Apart from any thing else paintComponent is going to be included in the double buffering of the component where as paint isn't (as super.paint sets it up)
  • Avoid using static state variables where possible
  • I don't know if this was deliberate or not, but, for (int i = 12; i < 0; i--); isn't going to achive anything, as the semi colen at the end means, do nothing for a count of 1
  • Personally, try not to use absolute values for things like width and height, which is actually reliant on parent container. You should also, where possible, provide sizing hints to allow the parent container to make better decisions on how much space it actually needs

enter image description here

UPDATED

Fixed bug in my movement code :P

I had a look at your collision detection code (in move) and, frankly, couldn't make heads or tails of it. I corrected it as well as changed in the way the paint method works so that x,y is always the top, left corner

public class TestGame {

    public static void main(String[] args) throws InterruptedException {
        new TestGame();
    }

    public TestGame() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException ex) {
                } catch (InstantiationException ex) {
                } catch (IllegalAccessException ex) {
                } catch (UnsupportedLookAndFeelException ex) {
                }

                Game game = new Game();

                JFrame frame = new JFrame("Test");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.setLayout(new BorderLayout());
                frame.add(game);
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public class GameThread extends Thread {

        private Game game;

        public GameThread(Game game) {
            setDaemon(false);
            this.game = game;
        }

        @Override
        public void run() {
            while (true) {
                game.move();
                try {
                    long startedAt = System.currentTimeMillis();
                    SwingUtilities.invokeAndWait(new Runnable() {
                        @Override
                        public void run() {
                            game.repaint();
                        }
                    });
                    long completedAt = System.currentTimeMillis();
                    long sleepFor = 30 - (completedAt - startedAt);
                    if (sleepFor < 0) {
                        sleepFor = 30;
                    }
                    Thread.sleep(sleepFor);
                } catch (Exception exp) {
                    exp.printStackTrace();
                }
            }

        }
    }

    public class Game extends JPanel {

        Square square = new Square(this);
        Ground ground = new Ground(this);

        public Game() {
//            addKeyListener(new KeyListener() {
//                @Override
//                public void keyTyped(KeyEvent e) {
//                }
//
//                @Override
//                public void keyReleased(KeyEvent e) {
//                    square.keyReleased(e);
//                }
//
//                @Override
//                public void keyPressed(KeyEvent e) {
//                    square.keyPressed(e);
//                }
//            });
            setFocusable(true);

            InputMap im = getInputMap(WHEN_IN_FOCUSED_WINDOW);
            ActionMap am = getActionMap();

            im.put(KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, 0, false), "press-left");
            im.put(KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, 0, false), "press-right");
            im.put(KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, 0, false), "press-down");
            im.put(KeyStroke.getKeyStroke(KeyEvent.VK_UP, 0, false), "press-up");

            im.put(KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, 0, true), "release-left");
            im.put(KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, 0, true), "release-right");
            im.put(KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, 0, true), "release-down");
            im.put(KeyStroke.getKeyStroke(KeyEvent.VK_UP, 0, true), "release-up");    

            am.put("press-left", new PressLeftAction(square));
            am.put("press-right", new PressRightAction(square));
            am.put("press-down", new PressDownAction(square));
            am.put("press-up", new PressUpAction(square));

            am.put("release-left", new ReleaseLeftAction(square));
            am.put("release-right", new ReleaseRightAction(square));
            am.put("release-down", new ReleaseDownAction(square));
            am.put("release-up", new ReleaseUpAction(square));

            new GameThread(this).start();

//        public void keyReleased(KeyEvent e) {
//
//
//
//            if (e.getKeyCode() == KeyEvent.VK_LEFT) {
//                xa = 0;
//            }
//
//            if (e.getKeyCode() == KeyEvent.VK_RIGHT) {
//                xa = 0;
//            }
//            if (e.getKeyCode() == KeyEvent.VK_DOWN) {
//                d = 60;
//            }
//
//            if (e.getKeyCode() == KeyEvent.VK_UP);
//        }
//
//        public void keyPressed(KeyEvent e) {
//// TODO Auto-generated method stub
//            if (e.getKeyCode() == KeyEvent.VK_LEFT) {
//                xa = xa - 3;
//            }
//
//            if (e.getKeyCode() == KeyEvent.VK_RIGHT) {
//                xa = xa + 3;
//            }
//
//            if (e.getKeyCode() == KeyEvent.VK_DOWN) {
//                d = 30;
//            }
//
//            if (e.getKeyCode() == KeyEvent.VK_UP) {
//                ya -= 60;
//            }
//
//
//
//
//
//        }
        }

        public void move() {
            square.move();
        }

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

        // Don't override paint, use paintComponent instead
//        @Override
//        public void paint(Graphics g) {
        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            Graphics2D g2d = (Graphics2D) g.create();
            g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
            square.paint(g2d);
            ground.paint(g2d);
            g2d.dispose();
        }
    }

    public class Square {

        Square square;
        private int x, xa;
//        static int y;
        private int y;
        private int ya;
        private Game game;
//        public static int fistX, fistY;
        private int fistX, fistY;
//        static int d = 60;
        private int d = 60;
        private int wide;
        private boolean onGround;

        public Square(Game game) {
            this.game = game;
            x = 100;
            y = 100;
            xa = 0;
            ya = 0;
            onGround = false;
            wide = game.getWidth();
        }

        public void move() {

            y += ya;
            x += xa;

            if (x < 0) {
                x = 0;
            } else if (x + 30 > game.getWidth()) {
                x = game.getWidth() - 30;
            }

            if (y < 0) {
                y = 0;
            } else if (collision()) {
                onGround = true;
                y = game.ground.getBounds().y - d;
            }

//            if (x + xa > 0 && x + xa < game.getWidth() - 30) {
//                x = x + xa;
//            }
//
//            if (y + ya > 0 && y + ya < game.getHeight() - 60) {
//                // This was never going to do anything, look at the
//                // end of the line...the `;` is going to prevent the
//                // statemt ya += 10 from begin called within the loop
////                for (int i = 12; i < 0; i--);
//                for (int i = 12; i < 0; i--) {
//                    ya += 10;
//                }
//                y = y + ya;
//            }
//            if (collision()) {
//                y -= 10;
//                onGround = true;
//
//            }
//
//            y += 10;

        }

        public void paint(Graphics2D g) {
            g.setColor(Color.RED);
            System.out.println(x + "x" + (y - d));
            g.fillRoundRect(x, y, 30, d, 10, 10);
        }

        private boolean collision() {
            return game.ground.getBounds().intersects(getBounds());
        }

        public Rectangle getBounds() {
            return new Rectangle(x, y, 30, 60);
        }
    }

    public class Ground {

        private Game game;

        public Ground(Game game) {
            this.game = game;
        }

        public void paint(Graphics2D g) {
            g.setColor(Color.BLACK);
            g.fillRect(0, game.getHeight() - 30, game.getWidth(), 30);
        }

        public Rectangle getBounds() {
            return new Rectangle(0, game.getHeight() - 30, game.getWidth(), 30);
        }
    }

    public abstract class AbstractSquareAction extends AbstractAction {

        private Square square;

        public AbstractSquareAction(Square square) {
            this.square = square;
        }

        public Square getSquare() {
            return square;
        }

    }

    public class PressLeftAction extends AbstractSquareAction {

        public PressLeftAction(Square square) {
            super(square);
        }

        @Override
        public void actionPerformed(ActionEvent e) {
            getSquare().xa = -3;
            System.out.println("pressLeft");
        }

    }

    public class PressRightAction extends AbstractSquareAction {

        public PressRightAction(Square square) {
            super(square);
        }

        @Override
        public void actionPerformed(ActionEvent e) {
            getSquare().xa = 3;
        }

    }

    public class PressDownAction extends AbstractSquareAction {

        public PressDownAction(Square square) {
            super(square);
        }

        @Override
        public void actionPerformed(ActionEvent e) {
            getSquare().ya = 30;
        }

    }

    public class PressUpAction extends AbstractSquareAction {

        public PressUpAction(Square square) {
            super(square);
        }

        @Override
        public void actionPerformed(ActionEvent e) {
            getSquare().ya -= 30;
        }

    }

    public class ReleaseLeftAction extends AbstractSquareAction {

        public ReleaseLeftAction(Square square) {
            super(square);
        }

        @Override
        public void actionPerformed(ActionEvent e) {
            getSquare().xa = 0;
        }

    }

    public class ReleaseRightAction extends AbstractSquareAction {

        public ReleaseRightAction(Square square) {
            super(square);
        }

        @Override
        public void actionPerformed(ActionEvent e) {
            getSquare().xa = 0;
        }

    }

    public class ReleaseDownAction extends AbstractSquareAction {

        public ReleaseDownAction(Square square) {
            super(square);
        }

        @Override
        public void actionPerformed(ActionEvent e) {
            getSquare().ya = 0;
        }

    }

    public class ReleaseUpAction extends AbstractSquareAction {

        public ReleaseUpAction(Square square) {
            super(square);
        }

        @Override
        public void actionPerformed(ActionEvent e) {
            getSquare().ya = 0;
        }

    }

}
查看更多
登录 后发表回答