移动一个非常不稳定的矩形

时间:2014-02-25 17:15:49

标签: java jframe jpanel

我创建了一个简单的程序,绘制一个以恒定速率下降到屏幕的矩形。我首先运行Main.java:

package highst;

public class Main {

    public static void main(String args[]){
        new GameFrame();
    }

}

创建GameFrame.java的新实例:

package highst;

import javax.swing.JFrame;

public class GameFrame extends JFrame {

    public GameFrame() {
        super("Falling rectangle");
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        this.setSize(800, 600);
        GameLogic game = new GameLogic();
        this.getContentPane().add(game);
        this.setVisible(true);
        game.run();
    }
}

反过来又创建了GameLogic.java的新实例:

package highst;

import java.awt.Color;
import java.awt.Graphics;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;

import javax.swing.JPanel;

public class GameLogic extends JPanel implements Runnable, KeyListener {

    Marvin marvin;
    private enum GameState{
        Running, Dead
    }

    GameState state = GameState.Running;

    public GameLogic(){
        marvin = new Marvin(50, 50);
        Thread thread = new Thread(this);
        thread.start();
        addKeyListener(this);
        setFocusable(true);
        this.setBackground(Color.black);
    }

    public void paintComponent(Graphics g){
        super.paintComponent(g);
        g.setColor(Color.white);
        g.fillRect(marvin.getX(), marvin.getY(), 50, 50);
    }

    @Override
    public void keyPressed(KeyEvent e) {
        if(e.getKeyCode() == KeyEvent.VK_SPACE){
            marvin.jump();
        }

    }

    @Override
    public void keyReleased(KeyEvent e) {
        // TODO Auto-generated method stub

    }

    @Override
    public void keyTyped(KeyEvent e) {
        // TODO Auto-generated method stub

    }

    @Override
    public void run() {
        if(state == GameState.Running){
            while(true){
                marvin.update();
                repaint();
                try {
                    Thread.sleep(17);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }

    }

}

最终利用了我的可玩角色Marvin.java,现在是一个白色矩形:

package highst;

public class Marvin {

    private int x, y;

    public Marvin(int x, int y){
        this.x = y;
        this.y = y;
    }

    public void update(){
        y -= -1;
    }

    public int getX() {
        return x;
    }

    public int getY() {
        return y;
    }

    public void setX(int x) {
        this.x = x;
    }

    public void setY(int y) {
        this.y = y;
    }

    public void jump() {
        x += 1;
    }

}

它运行正常但矩形在页面下落时没有平滑地绘制。它似乎一次跳几个像素。我认为将线程休眠17毫秒会导致一切顺利呈现。我究竟做错了什么?

3 个答案:

答案 0 :(得分:1)

要获得流畅的动画,您需要以恒定速率对屏幕进行更新。

在这里,你正在做一个图形重绘,这可能需要花费任何时间,然后等待17ms无论如何。这导致每个帧花费不同的时间量。第一帧可能在2ms完成,下一帧可能需要5ms,然后是3ms等等......你的帧将显示19ms然后22ms然后20ms ......

您需要的是一个专用线程,其唯一的工作是等待适当的时间,然后通知主线程重绘。然后你的画面(假设画面不超过17毫秒)每隔17毫秒出现一次,完全是在提示上。

Here's an tutorial on animation in Java applets,您应该发现它相关。

答案 1 :(得分:0)

试试这些:

1>减少线程的休眠时间以查看效果并获得最佳的跌落速度。

2 - ;利用双缓冲(一种在屏幕上首先绘制内存然后在显示屏上绘制的概念):

Double Buffering - Docs

Double Buffering - Google Search Results

3>简单的建议:避免使用sleep()。而是使用计时器。它是一个非常有趣和强大的线程替代品。此外,它不会在游戏开发的后期阶段产生问题。检查这些链接:

Concurrency in java

Timer in Java

4>查看这些有关Java动画的有趣教程:

Java World

Clear Rice

答案 2 :(得分:0)

有几件事情错了......

  1. 您忽略了Swing的Initial Threads而没有在EDT的上下文中启动您的UI,这导致...
  2. 您正在run的实例上调用GameLogic并创建Thread,再次调用run,设置为循环。这实际上是愚蠢的运气。这样做是调用marvin.update两次......和随机间隔,意味着对象以不一致的速率移动
  3. 基本上,您应该删除行game.run()并在new GameFrame();来电的上下文中包裹EventQueue.invokeLater ...

    public static void main(String[] args) {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                }
    
                GameFrame frame = new GameFrame();
            }
        });
    }
    

    就我个人而言,我建议您不要直接从JFrame扩展,不要在课程中添加任何功能,只需在main中创建一个添加GameLogic的实例面板到。这使得游戏在部署方面变得更加灵活,因为您没有将自己锁定在单个容器中。

    我还建议您不要使用KeyListener,而应使用Key Bindings API,因为它解决了与KeyListener相关的焦点问题