为什么矩形跳跃的高度会有所不同?

时间:2012-11-10 02:09:32

标签: java swing animation graphics game-physics

为什么矩形跳跃的高度会有所不同?它似乎进入了一个循环。首先它跳低然后它根本不跳,然后它跳高然后它根本不跳。我无法弄清楚为什么使用相同的代码并且它是由同一事件触发的。

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 个答案:

答案 0 :(得分:6)

  1. 不要阻止EDT(事件调度线程) - 当发生这种情况时,GUI将“冻结”。而不是调用Thread.sleep(n)为动画等重复任务实现Swing Timer。有关详细信息,请参阅Concurrency in Swing
  2. frame.setSize(600, 700);我建议改为为内容设置首选大小并在其周围打包框架。这表明在不同的PLAF或OS'上游戏的大小不变。
  3. 对于除顶级容器之外的其他组件(例如JFrameJWindow),请覆盖paintComponent(Graphics)而不是paint(Graphics)
  4. 查看键绑定而不是Swing的键侦听器。
  5. 实现前3个,最后一个(以及代码中注释的其他一个)是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);
        }
    }
    

答案 1 :(得分:4)

  • 不要以任何方式阻止事件调度线程,它将阻止EDT处理重绘请求(以及其他事件),这将使您的程序看起来已崩溃。阅读Concurrency in Swing以获取更多信息。
  • 仅从EDT修改UI,绝不从任何其他线程修改,这包括创建UI元素
  • 支持KeyListeners上的键绑定,它们更能解决焦点问题。阅读How to Use Key Bindings了解更多详情
  • 支持覆盖paintComponent,而不是paint。如果可以的话,油漆会做很多重要的工作,你应该避免弄乱。除了paintComponent之外,paint将包含在组件的双缓冲中,而super.paint不是static设置它
  • 尽可能避免使用for (int i = 12; i < 0; i--);状态变量
  • 我不知道这是否是故意的,但是,move不会发生任何事情,因为最后的半通道意味着,没有做任何事情来计算1
  • 就个人而言,尽量不要使用宽度和高度等绝对值,这实际上依赖于父容器。在可能的情况下,您还应该提供大小调整提示,以允许父容器更好地决定实际需要的空间。

enter image description here

<强>已更新

修正了我的移动代码中的错误:P

我查看了你的碰撞检测代码(在paint中),坦率地说,它无法做出正面或反面。我对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; } } } 方法的工作方式进行了更正和更改,以便x,y始终位于左上角

{{1}}