Java如何实现不断重绘界面

3

我想制作贪吃蛇游戏,但目前我一直在努力不断地重绘贪吃蛇。我使用了一个计时器,在将间隔设置为150毫秒时,它可以工作,但当我将它设置得更快,例如30毫秒时,屏幕上没有任何显示。定时器和repaint()调用在Game类中。谢谢!

游戏类

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.FlowLayout;
import java.awt.Frame;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridLayout;
import java.awt.Panel;
import java.awt.RenderingHints;
import java.awt.Toolkit;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;

import javax.swing.*;
import java.util.*;
import java.util.Timer;
import java.util.TimerTask;

public class Game extends JFrame{

    JPanel mainPanel = new JPanel();

    BorderLayout layout = new BorderLayout();
    //Game game;
    Snake snake = new Snake(this);

    public Game(){

        super("Snake by Alex 2017");
        setSize((int)(Toolkit.getDefaultToolkit().getScreenSize().getWidth()),(int)(Toolkit.getDefaultToolkit().getScreenSize().getHeight()));
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setVisible(true);

        mainPanel.setLayout(null);
        mainPanel.setSize((int)(Toolkit.getDefaultToolkit().getScreenSize().getWidth()),(int)(Toolkit.getDefaultToolkit().getScreenSize().getHeight()));
        mainPanel.setBackground(Color.BLACK);
        add(mainPanel);
        mainPanel.setVisible(true);

        start();

        addKeyListener(new KeyListener() {

            @Override
            public void keyTyped(KeyEvent e) {
            }

            @Override
            public void keyReleased(KeyEvent e) {
            }

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

            }
        });
        setFocusable(true);



    }

    public void paint(Graphics g){
        super.paint(g);
        Graphics2D g2d = (Graphics2D) g;
        g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        snake.paint(g2d);

        //System.out.println("hello");

    }

    Timer timer = new Timer();
    TimerTask task = new TimerTask() {
        public void run() {
            try {
                move();
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }

        }
    };
    TimerTask update = new TimerTask() {

        public void run() {
            repaint();

        }
    };
    public void start(){
        timer.scheduleAtFixedRate(task, 0, 250);
        timer.schedule(update, 0, 30);

    }

    public void move() throws InterruptedException{
        snake.move();

    }

    public static void main(String[] args) throws InterruptedException{
        Game game = new Game();


    }

}

蛇类

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

import javax.swing.RepaintManager;

public class Snake {

    public int x = 60;
    public int y = 80;
    public int xa = 0;
    public int ya = 0;
    public int speed = 20;
    private Game game;

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

    public void move(){
        x += xa;
        y += ya;
    }

    public void keyPressed(KeyEvent e){
        //RIGHT
        if(e.getKeyCode() == 39){
            xa = speed;
            ya = 0;
        }
        //LEFT
        if(e.getKeyCode() == 37){
            xa = -speed;
            ya = 0;
        }
        //UP
        if(e.getKeyCode() == 38){
            ya = -speed;
            xa = 0;
        }
        //DOWN
        if(e.getKeyCode() == 40){
            ya = speed;
            xa = 0;
        }
        //move();
        //game.repaint();

    }
    public Rectangle getBounds(){
        return new Rectangle(x, y, 20, 20);
    }
    public void paint(Graphics2D g2d){
        g2d.setColor(new Color(48, 255, 55));
        g2d.fillRect(x, y, 20, 20);

    }

}

3
你应该使用 Swing 定时器(javax.swing.Timer)而不是 java.util.Timer。Swing 定时器专门设计用于与 Swing 程序一起使用,并在 Swing 线程上执行。https://docs.oracle.com/javase/tutorial/uiswing/misc/timer.html - Radiodef
我尝试了Swing计时器,但仍然无法将延迟设置得足够快以实现持续的重绘。 - Alex
那么就有其他问题了。即使是通过定时器调用repaint()这样的被动渲染,Swing肯定可以处理相当高的重绘率。根据我的经验,像贪吃蛇这样简单的2D游戏可以达到大约200或300FPS的帧率。(限制在于Swing事件队列能够跟上不断的事件发布速度,以及它是否会合并重复的绘制事件。) - Radiodef
你应该查看我最近的一个回答中的代码,链接为:https://stackoverflow.com/a/44371593/2891664。这个示例展示了一个简单的游戏,包括使用计时器和按键绑定实现动画效果的移动。 - Radiodef
2个回答

0
你需要做的是创建一个线程,它会一直循环并渲染你的游戏,例如:
public void run() {
    while (true) {
        render();
    }
}

你的运行方法是正确的,但你还需要扩展Thread类,在一个额外的线程中重复地调用repaint()方法请参考这篇关于多线程的文章

关于BufferStrategy和更多细节,请查看教程。我认为它解释得非常好,我自己也使用过。


不行,我已经尝试了几天,但无法使其正常工作。由于某种原因,当计时器速度快于大约150毫秒时,我无法使其绘制。while循环也无法工作。 - Alex

0

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