简体   繁体   English

Java 卡顿/闪烁/滞后 Animation 在游戏中

[英]Java Stuttery/Flickery/Laggy Animation In Game

To be honest, I'm not to sure how to describe my problem, you can see it here .老实说,我不确定如何描述我的问题,你可以在这里看到。 In that video of my code running, it is most prominent in the first few seconds.在我的代码运行的那个视频中,它在最初的几秒钟内最为突出。

Occasionally and randomly I am getting a weird flickery/stuttery/laggy glitch in my moving pipe across the screen, I've been trying to sort it out for 3 days now and I truly have no idea why it is occurring.. I have tested my code on other computers and the same happens.偶尔地,我在屏幕上移动的 pipe 中偶尔会出现奇怪的闪烁/卡顿/滞后故障,我已经尝试整理了 3 天,但我真的不知道它为什么会发生。我已经测试过我的代码在其他计算机上也是如此。 As this has been happening I have remained at 60fps.由于这种情况一直在发生,我一直保持在 60fps。

Does anyone know why this would be occurring...?有谁知道为什么会发生这种情况......?

My code: (I know this code is bad, its a rushed example) It will probably take a few tries to experience the same bug that I am facing......我的代码:(我知道这段代码很糟糕,这是一个匆忙的例子)可能需要几次尝试才能遇到我面临的相同错误......

import java.awt.Canvas;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.image.BufferStrategy;
import java.awt.image.BufferedImage;
import java.awt.image.DataBufferInt;
import java.io.IOException;

import javax.imageio.ImageIO;
import javax.swing.JFrame;

public class Test extends Canvas implements Runnable {
    private static final long serialVersionUID = 1L;

    private static int width = 300 - 80;
    private static int height = 168;
    private static int scale = 3;
    public static String title = "Test";

    private Thread thread;
    private JFrame frame;
    private boolean running = false;

    private BufferedImage image;

    public Test() {
        setPreferredSize(new Dimension(width * scale, height * scale));
        frame = new JFrame();
        image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
    }

    public static int getWindowWidth() {
        return width * scale;
    }

    public static int getWindowHeight() {
        return height * scale;
    }

    public synchronized void start() {
        running = true;
        thread = new Thread(this);
        thread.start();
    }

    public synchronized void stop() {
        running = false;
        try {
            thread.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public void run() {
        long lastTime = System.nanoTime();
        long timer = System.currentTimeMillis();
        final double ns = 1000000000.0 / 60.0;
        double delta = 0;
        int frames = 0;
        int updates = 0;
        requestFocus();
        while (running) {
            long now = System.nanoTime();
            delta += (now - lastTime) / ns;
            lastTime = now;
            while (delta >= 1) {
                update();
                updates++;
                delta--;
                render();
                frames++;
            }

            if (System.currentTimeMillis() - timer > 1000) {
                timer += 1000;
                System.out.println(updates + " tps, " + frames + " fps");
                updates = 0;
                frames = 0;
            }
        }
        stop();
    }
    
    int x = width;
    
    static BufferedImage img;
    
    static {
        try {
            img = ImageIO.read(Test.class.getResource("/pipe.png"));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public void update() {
        if (x + 30 <= -1) x = width;
        else x-=2;
    }
    
    public void draw(Graphics2D g2)
    {
        g2.setColor(Color.red);
        g2.drawImage(img, x, 10, 30, 90, null);
    }

    public void render() {
        BufferStrategy bs = getBufferStrategy();
        if (bs == null) {
            createBufferStrategy(3);
            return;
        }
        
        drawToImage();
        
        Graphics g = bs.getDrawGraphics();
        g.setColor(new Color(0xff00ff));
        g.fillRect(0,  0, getWidth(), getHeight());
        g.drawImage(image, 0, 0, width * scale, height * scale, null);

        g.dispose();
        bs.show();
    }
    
    public void drawToImage()
    {
        Graphics2D g2 = (Graphics2D) image.getGraphics();
        g2.clearRect(0, 0, width, height);
        draw(g2);
        g2.dispose();
    }
    
    public static void main(String[] args) {
        Test game = new Test();
        game.frame.setResizable(false);
        game.frame.setTitle(Test.title);
        game.frame.add(game);
        game.frame.pack();
        game.frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        game.frame.setLocationRelativeTo(null);
        game.frame.setVisible(true);

        game.start();
    }

}

EDIT: Paint component code that produces same result:编辑:绘制产生相同结果的组件代码:

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

public class GamePanel extends JPanel implements Runnable
{
    private int WIDTH = 200 * 4;
    private int HEIGHT = 150 * 4;

    int playerX = 30;
    int playerY = 30;

    Thread gameThread;

    public GamePanel()
    {
        setPreferredSize(new Dimension(WIDTH, HEIGHT));
        setBackground(Color.BLACK);
        setDoubleBuffered(true);
    }

    public void startGameThread()
    {
        this.gameThread = new Thread(this);
        this.gameThread.start();
    }

    public void update()
    {
        this.playerX+= 5;
    }

    public void paintComponent(Graphics g)
    {
        super.paintComponent(g);

        Graphics2D g2 = (Graphics2D) g;

        g2.setColor(Color.WHITE);
        g2.fillRect(playerX, playerY, 32, 32);

        g2.dispose();
    }

    @Override
    public void run()
    {
        double drawInterval = 1000000000/60;
        double delta = 0;
        long lastTime = System.nanoTime();
        long currentTime;
        long timer = 0;
        int drawCount = 0;

        while (gameThread != null)
        {
            currentTime = System.nanoTime();

            delta += (currentTime - lastTime) / drawInterval;
            timer += (currentTime - lastTime);
            lastTime = currentTime;

            if (delta >= 1)
            {
                update();
                repaint();
                drawCount++;
                delta--;
            }

            if (timer >= 1000000000)
            {
                System.out.println("FPS: " + drawCount);
                drawCount = 0;
                timer = 0;
            }
        }
    }

    public static void main(String[] args)
    {
        System.setProperty("sun.java2d.opengl", "true");

        JFrame window = new JFrame();
        window.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
        window.setResizable(false);
        window.setTitle("Test");

        GamePanel gamePanel = new GamePanel();
        window.add(gamePanel);

        window.pack();
        window.setVisible(true);

        gamePanel.startGameThread();
    }
}

Oracle has a helpful tutorial, Creating a GUI With Swing . Oracle 有一个有用的教程,使用 Swing 创建 GUI Skip the Learning Swing with the NetBeans IDE section.跳过学习 Swing 和 NetBeans IDE 部分。

I rearranged your code.我重新排列了你的代码。 The motion seems pretty smooth to me when I run the modified code.当我运行修改后的代码时,这个动作对我来说似乎很流畅。

When I create a Swing GUI, I use the model-view-controller pattern.当我创建 Swing GUI 时,我使用model-view-controller模式。 This pattern allows me to separate my concerns and focus on one part of the application at a time.这种模式使我能够分离我的关注点并一次专注于应用程序的一个部分。

I created an application model using a plain Java getter/setter class to hold the player position and the size of the playing field.我使用普通的 Java getter/setter class 创建了一个应用程序 model 来容纳玩家 Z4757FE07FD492A38BEEZEA6 的大小为 760FD492A38BEEZEA6。

All Swing applications must start with a call to the SwingUtilities invokeLater method.所有 Swing 应用程序必须从调用SwingUtilitiesinvokeLater方法开始。 This method ensures that all Swing components are created and executed on the Event Dispatch Thread .此方法确保所有 Swing 组件都在Event Dispatch Thread上创建和执行。

Your biggest mistake was disposing of the Graphics instance in your paintComponent method.您最大的错误是在您的paintComponent方法中处理了Graphics实例。 Never dispose of a Graphics instance unless you create it.除非您创建它,否则永远不要处置Graphics实例。

I used a Swing Timer rather than your complicated timing code.我使用了 Swing Timer ,而不是您复杂的计时码。 Pick a frame rate and stick with it.选择一个帧速率并坚持下去。 Most monitors can't refresh any faster than 60 frames per second.大多数显示器的刷新速度不能超过每秒 60 帧。

I modified your update code so the player rectangle moves back and forth.我修改了您的更新代码,使播放器矩形来回移动。

Here's the complete runnable code.这是完整的可运行代码。 I made all the additional classes inner classes so I could post the code as one block.我将所有额外的类都做成了内部类,这样我就可以将代码作为一个块发布。

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.Timer;

public class ExampleGame implements Runnable {

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new ExampleGame());
    }

    private final GameModel model;

    private final GamePanel gamePanel;

    public ExampleGame() {
        this.model = new GameModel();
        this.gamePanel = new GamePanel(model);
    }

    @Override
    public void run() {
        System.setProperty("sun.java2d.opengl", "true");

        JFrame window = new JFrame();
        window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//        window.setResizable(false);
        window.setTitle("Test");

        window.add(gamePanel, BorderLayout.CENTER);

        window.pack();
        window.setLocationByPlatform(true);
        window.setVisible(true);

        Timer timer = new Timer(20, new PlayerListener(this, model));
        timer.start();
    }

    public void repaint() {
        gamePanel.repaint();
    }

    public class GamePanel extends JPanel {

        private static final long serialVersionUID = 1L;

        private final GameModel model;

        public GamePanel(GameModel model) {
            this.model = model;
            setPreferredSize(model.getPlayingField());
            setBackground(Color.BLACK);
        }

        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);

            Graphics2D g2 = (Graphics2D) g;
            g2.setColor(Color.WHITE);
            Point point = model.getPlayerePosition();
            g2.fillRect(point.x, point.y, 32, 32);
        }

    }

    public class PlayerListener implements ActionListener {

        private int increment;

        private final ExampleGame view;

        private final GameModel model;

        public PlayerListener(ExampleGame view, GameModel model) {
            this.view = view;
            this.model = model;
            this.increment = 5;
        }

        @Override
        public void actionPerformed(ActionEvent event) {
            update();
            view.repaint();
        }

        public void update() {
            Dimension d = model.getPlayingField();
            Point point = model.getPlayerePosition();
            point.x += increment;
            if (point.x > (d.width - 64)) {
                increment = -5;
                point.x += increment;
            }
            if (point.x < 32) {
                increment = 5;
                point.x += increment;
            }

            model.setPlayerePosition(point);
        }

    }

    public class GameModel {

        private final Dimension playingField;

        private Point playerePosition;

        public GameModel() {
            this.playingField = new Dimension(800, 600);
            this.playerePosition = new Point(32, 32);
        }

        public Point getPlayerePosition() {
            return playerePosition;
        }

        public void setPlayerePosition(Point playerePosition) {
            this.playerePosition = playerePosition;
        }

        public Dimension getPlayingField() {
            return playingField;
        }

    }

}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM