如何防止屏幕闪烁?

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

public class Racing extends JFrame {
private static final long serialVersionUID = -198172151996959655L;

//makes the screen size
final int WIDTH = 900, HEIGHT = 650;

//keeps track of player speed
double plSpeed = .5;

//numbers that represent direction
final int UP = 0, RIGHT = 1, DOWN = 2, LEFT = 3, STOP = 5, START = 6;

//keeps track of player direction
int p1Direction = START;

//makes player 1's car
Rectangle p1 = new Rectangle ( 100, 325, 30, 30 );
Rectangle foreground = new Rectangle( 500, 500, 200, 200 );

//constructor
public Racing() {
    //define defaults for the JFrame
    super ("Racing");
    setSize( WIDTH, HEIGHT );
    setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
    setBackground(Color.BLACK);

    //start the inner class
    Move1 m1 = new Move1();
    m1.start();
}

//draws the cars and race track
public void paint(Graphics g) {

    super.paint(g);
    //draw p1
    g.setColor(Color.RED);
    g.fillRect(p1.x,p1.y,p1.width,p1.height);
    g.setColor(Color.GREEN);
    g.fillRect(foreground.x,foreground.y,foreground.width,foreground.height);

}
private class Move1 extends Thread implements KeyListener {
    public void run() {
        //makes the key listener "wake up"
        addKeyListener(this);

        //should be done in an infinite loop, so it repeats
        while (true) {
            //make try block, so it can exit if it errors
            try {
                //refresh screen
                repaint();

                //makes car increase speed a bit
                if (plSpeed <= 7) {
                    plSpeed += .2;
                }

                //lets the car stop
                if (plSpeed==0) {
                    p1Direction = STOP;
                }

                //moves player based on direction
                if (p1Direction==UP) {
                    p1.y -= (int) plSpeed;
                }
                if (p1Direction==DOWN) {
                    p1.y += (int) plSpeed;
                }
                if (p1Direction==LEFT) {
                    p1.x -= (int) plSpeed;
                }
                if (p1Direction==RIGHT) {
                    p1.x += (int) plSpeed;
                }
                if (p1Direction==STOP) {
                    plSpeed = 0;
                }

                //delays refresh rate
                Thread.sleep(75);

            }
            catch(Exception e) {
                //if an error, exit
                break;
            }
        }
    }

    //have to input these (so it will compile)
    public void keyPressed(KeyEvent event) {
        try {
            //makes car increase speed a bit
            if (event.getKeyChar()=='w' ||
                event.getKeyChar()=='a' ||
                event.getKeyChar()=='s' ||
                event.getKeyChar()=='d') {
                    plSpeed += .2;
                    repaint();
            }
        } catch (Exception I) {}
    }
    public void keyReleased(KeyEvent event) {}

    //now, to be able to set the direction
    public void keyTyped(KeyEvent event) {
        if (plSpeed > 0) {
            if (event.getKeyChar()=='a') {
                if (p1Direction==RIGHT) {
                    p1Brake();
                } else {
                    if (p1Direction==LEFT) {
                    } else {
                        p1Direction = LEFT;
                    }
                }
            }
            if (event.getKeyChar()=='s') {
                if (p1Direction==UP) {
                    p1Brake();
                } else {
                    if (p1Direction==DOWN) {
                    } else {
                        p1Direction = DOWN;
                    }
                }
            }
            if (event.getKeyChar()=='d') {
                if (p1Direction==LEFT) {
                    p1Brake();
                } else {
                    if (p1Direction==RIGHT) {
                    } else {
                        p1Direction = RIGHT;
                    }
                }
            }
            if (event.getKeyChar()=='w') {
                if (p1Direction==DOWN) {
                    p1Brake();
                } else {
                    if (p1Direction==UP) {
                    } else {
                        p1Direction = UP;
                    }
                }
            }
            if (event.getKeyChar()=='z') {
                p1Brake();
            }
        }
    }

    public void p1Brake () {
        try {
            while (plSpeed != 0) {
                plSpeed -= .2;
                Thread.sleep(75);
            }
        } catch (Exception e) {
            plSpeed = 0;
        }
    }
}

//finally, to start the program
public static void main(String[] args) {
    Racing frame = new Racing();
    frame.setVisible( true );
    frame.setLocationRelativeTo( null );
    frame.setResizable( false );
}

这是我的代码的SSCCE(Short, Self Contained, Correct Example)版本。如果我在类内部添加super.paint(g);,则会出现闪烁问题。如果不加这个语句,那么每当您移动玩家时,就会创建一条玩家行进路径的线条-而不进行重绘。我需要知道如何在哪里进行重绘。我在这里找到了离答案最近的地方:

http://www.java-forums.org/awt-swing/37406-repaint-without-flashing.html

但他们使用了一个小程序(我以前从未接触过,假定将代码从小程序转换为框架可能相当棘手)。有人能帮我吗?

备注:

我不知道您可以使用AWT创建一个框架,因为我对Swing很熟悉并且适应它,所以我不想改变。抱歉。您可以看到,无论我绘制什么都会闪烁,不仅仅是玩家。

Andrew,这是我的屏幕截图: the game

哦,它没有注册P2。


3
在这个帖子中,关于“我会确保下次这样做”的承诺发生了什么事情?我没有看到一个SSCCE(Short, Self Contained, Correct (Compilable), Example,简短自包含且正确可编译的示例),只有一些无法编译的代码片段,无法确定使用的是Swing还是AWT。评分为-1。 - Andrew Thompson
1
那里,这展示了我的问题。 - Nick Catlin
现在那个是一个SSCCE!:)看看我的答案,我认为我修复了大部分问题。一条注释:即使在p1Brake的情况下,也永远不要在EDT上使用Thread.sleep(n)。;) - Andrew Thompson
我很喜欢并熟悉Swing,所以我不想改变。坚持使用Swing组件吧。它是一个更新、更强大的工具包,而且更容易得到答案(大多数使用AWT组件的人已经忘记了细节)。 - Andrew Thompson
你不应该覆盖JFrame的paint()方法。请参阅自定义绘制以获取解释和示例。 - camickr
显示剩余3条评论
3个回答

3

Racing UI

我做了很多更改,以下是我能回想起来的一些。

  1. 将自定义绘画从顶级容器重构为JPanel,并将绘画移动到paintComponent()中。
  2. 删除了ThreadThread.sleep(n),并使用Timer/ActionListener进行替换。
  3. 在EDT上构建GUI。
  4. 不再设置JFrame的大小。而是为JPanel(实际绘图区域)设置首选大小,并调用JFrame.pack()以获得正确的整体大小。
  5. 使用setLocationByPlatform(true)代替非常类似于setLocationRelativeTo(null)

仔细检查代码以获取进一步提示。

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

public class Racing extends JPanel implements KeyListener {
private static final long serialVersionUID = -198172151996959655L;

//keeps track of player speed
double plSpeed = .5;

//numbers that represent direction
final int UP = 0, RIGHT = 1, DOWN = 2, LEFT = 3, STOP = 5, START = 6;

//keeps track of player direction
int p1Direction = START;

//makes player 1's car
Rectangle p1 = new Rectangle ( 100, 25, 30, 30 );

//constructor
public Racing() {
    //define defaults for the JFrame
    setBackground(Color.BLACK);

    //makes the screen size
    setPreferredSize(new Dimension(400,50));

    //makes the key listener "wake up"
    addKeyListener(this);
    setFocusable(true);

    ActionListener al = new ActionListener() {
        public void actionPerformed(ActionEvent ae) {
            //refresh screen
            repaint();

            //makes car increase speed a bit
            if (plSpeed <= 7) {
                plSpeed += .2;
            }

            //lets the car stop
            if (plSpeed==0) {
                p1Direction = STOP;
            }

            //moves player based on direction
            if (p1Direction==UP) {
                p1.y -= (int) plSpeed;
            }
            if (p1Direction==DOWN) {
                p1.y += (int) plSpeed;
            }
            if (p1Direction==LEFT) {
                p1.x -= (int) plSpeed;
            }
            if (p1Direction==RIGHT) {
                p1.x += (int) plSpeed;
            }
            if (p1Direction==STOP) {
                plSpeed = 0;
            }
        }
    };

    Timer t = new Timer(75,al);
    t.start();
}

//draws the cars and race track
@Override
public void paintComponent(Graphics g) {

    super.paintComponent(g);
    //draw p1
    g.setColor(Color.RED);
    g.fillRect(p1.x,p1.y,p1.width,p1.height);

}

//have to input these (so it will compile)
public void keyPressed(KeyEvent event) {
    System.out.println(event);
    try {
        //makes car increase speed a bit
        if (event.getKeyChar()=='w' ||
            event.getKeyChar()=='a' ||
            event.getKeyChar()=='s' ||
            event.getKeyChar()=='d') {
                plSpeed += .2;
                //repaint();
        }
    } catch (Exception I) {}
}
public void keyReleased(KeyEvent event) {}

//now, to be able to set the direction
public void keyTyped(KeyEvent event) {
    if (plSpeed > 0) {
        if (event.getKeyChar()=='a') {
            if (p1Direction==RIGHT) {
                p1Brake();
            } else {
                if (p1Direction==LEFT) {
                } else {
                    p1Direction = LEFT;
                }
            }
        }
        if (event.getKeyChar()=='s') {
            if (p1Direction==UP) {
                p1Brake();
            } else {
                if (p1Direction==DOWN) {
                } else {
                    p1Direction = DOWN;
                }
            }
        }
        if (event.getKeyChar()=='d') {
            if (p1Direction==LEFT) {
                p1Brake();
            } else {
                if (p1Direction==RIGHT) {
                } else {
                    p1Direction = RIGHT;
                }
            }
        }
        if (event.getKeyChar()=='w') {
            if (p1Direction==DOWN) {
                p1Brake();
            } else {
                if (p1Direction==UP) {
                } else {
                    p1Direction = UP;
                }
            }
        }
        if (event.getKeyChar()=='z') {
            p1Brake();
        }
    }
}

public void p1Brake () {
    try {
        while (plSpeed != 0) {
            plSpeed -= .2;
        }
    } catch (Exception e) {
        plSpeed = 0;
    }
}

//finally, to start the program
public static void main(String[] args) {
    SwingUtilities.invokeLater( new Runnable() {
        public void run() {
            JFrame f = new JFrame("Racing");
            f.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
            f.add(new Racing());
            f.pack();
            f.setLocationByPlatform(true);
            f.setResizable( false );
            f.setVisible( true );
        }
    });
}
}

哦,还有一件事我很困惑:EDT是什么? - Nick Catlin
我已经将这些更改应用到我的游戏代码中,不幸的是,我忘记保存旧代码以防万一它不能正常工作 - 而它确实没有。我仍然得到相同的轨迹,但计算机无法识别矩形重叠,并且速度完全混乱。如果您有兴趣,我可以将我的完整当前代码发送给您。我在我的问题中放了一张截图。 - Nick Catlin

1

你永远不应该尝试直接在画布上绘制,而是使用一个后台线程,始终保持下一帧准备就绪,并将其与旧帧交换。

    while (state == RUNNING)
    {
        long beforeTime = System.nanoTime();
        gEngine.update(); // update stuff like game score life etc..

        Canvas c = null;
        try
        {
            c = mSurfaceHolder.lockCanvas(null);
            synchronized (mSurfaceHolder)
            {                   
                drawable.setBounds(0, 0, 800, 600);
                drawable.draw(c); // flash new background if required for the new frame
                gEngine.draw(c);    // update game state
            }
        } finally
        {
            if (c != null)
            {
                mSurfaceHolder.unlockCanvasAndPost(c);
            }
        }

        this.sleepTime = delay
                - ((System.nanoTime() - beforeTime) / 1000000L);

        try
        {
            if (sleepTime > 0)
            {
                Thread.sleep(sleepTime);
            }
        } catch (InterruptedException ex)
        {
            Logger.getLogger(PaintThread.class.getName()).log(Level.SEVERE,
                    null, ex);
        }

    }
}

啊,好主意。现在,要想办法如何实现它。我会先睡一晚上,然后明天再回来找你。 - Nick Catlin
1
问题在于这是一个AWT代码建议,而在Swing中不起作用,这是海报上一个问题的基础。在他最后一篇帖子中建议他学习如何发布一个SSCCE之前,我们只是在猜测。海报甚至没有从他的问题所引用的帖子中复制绘画代码。 - camickr
唯一不同的是添加了一行代码 "super.paint(g)",我在问题中提到过,但我认为不值得再次放入我的代码中。 我尽可能地发布了最少的代码,而没有省略绘画代码。 看起来有点混乱-我正在使用swing。 - Nick Catlin

0

将代码从applet更改为JFrame并不难。 将类的扩展名更改为JPanel:

public class Racing extends JPanel

然后在实例化新Racing的方法中,将其更改为实例化新的JFrame, 并将Racing中的任何方法(例如setTitle(...))现在抛出错误的方法移动到实例化新JFrame的方法中,如(您命名的JFrame引用).setTitle(...) 然后将以下代码应用于JFrame的其余部分:

JFrame.add(new Racing());
JFrame.setSize(*the size of Racing window*);

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接